mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	Trakt meta provider for extensions
This commit is contained in:
		
							parent
							
								
									0a24661e4c
								
							
						
					
					
						commit
						1248d5d7a1
					
				
					 1 changed files with 586 additions and 0 deletions
				
			
		|  | @ -0,0 +1,586 @@ | |||
| package com.lagradost.cloudstream3.metaproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.fasterxml.jackson.annotation.JsonAlias | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import java.util.Locale | ||||
| import java.text.SimpleDateFormat | ||||
| import kotlin.math.roundToInt | ||||
| 
 | ||||
| open class TraktProvider : MainAPI() { | ||||
|     override var name = "Trakt" | ||||
|     override val hasMainPage = true | ||||
|     override val providerType = ProviderType.MetaProvider | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|         TvType.Anime, | ||||
|     ) | ||||
|     // Override this to control random Posters on every extension reload | ||||
|     open val randomPosters = false | ||||
|     private val traktClientId = "d9f434f48b55683a279ffe88ddc68351cc04c9dc9372bd95af5de780b794e770" | ||||
|     private val tmdbApiKey = "e6333b32409e02a4a6eba6fb7ff866bb" | ||||
|     private val traktApiUrl = "https://api.trakt.tv" | ||||
|     private val tmdbApiUrl = "https://api.themoviedb.org/3" | ||||
| 
 | ||||
|     // You can override mainPage in extension to pass the needed APIs. | ||||
|     override val mainPage = mainPageOf( | ||||
|         "$traktApiUrl/movies/trending" to "Trending Movies", //Most watched movies right now | ||||
|         "$traktApiUrl/movies/popular" to "Popular Movies", //The most popular movies for all time | ||||
|         "$traktApiUrl/shows/trending" to "Trending Shows", //Most watched Shows right now | ||||
|         "$traktApiUrl/shows/popular" to "Popular Shows", //The most popular Shows for all time | ||||
|     ) | ||||
|     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { | ||||
| 
 | ||||
|         val apiResponse = getApi("${request.data}?page=$page") | ||||
| 
 | ||||
|         val results = parseJson<List<Results>>(apiResponse).map { element -> | ||||
|             element.toSearchResponse() | ||||
|         } | ||||
|         return newHomePageResponse(request.name, results) | ||||
|     } | ||||
|     private suspend fun Results.toSearchResponse(): SearchResponse { | ||||
| 
 | ||||
|         val ids = this.ids ?: this.media?.ids | ||||
| 
 | ||||
|         val tmdbId = ids?.tmdb.toString() | ||||
|         val tvdbId = ids?.tvdb | ||||
| 
 | ||||
|         val mediaType = if (tvdbId == null) TvType.Movie else TvType.TvSeries | ||||
| 
 | ||||
|         if (mediaType == TvType.Movie) { | ||||
|             val images = getImages(tmdbId, true) | ||||
|             val posterUrl = getPosterUrl(images, randomPosters) | ||||
| 
 | ||||
|             return newMovieSearchResponse( | ||||
|                 name = this.media?.title ?: this.title!!, | ||||
|                 url = Data( | ||||
|                     title = this.media?.title ?: this.title!!, | ||||
|                     movieBool = true, | ||||
|                     ids = ids, | ||||
|                     images = images, | ||||
|                     type = mediaType | ||||
|                 ).toJson(), | ||||
|                 type = TvType.Movie, | ||||
|             ) { | ||||
|                 this.posterUrl = posterUrl | ||||
|             } | ||||
|         } else { | ||||
|             val images = getImages(tmdbId, false) | ||||
|             val posterUrl = getPosterUrl(images, randomPosters) | ||||
| 
 | ||||
|             return newTvSeriesSearchResponse( | ||||
|                 name = this.media?.title ?: this.title!!, | ||||
|                 url = Data( | ||||
|                     title = this.media?.title ?: this.title!!, | ||||
|                     movieBool = false, | ||||
|                     ids = ids, | ||||
|                     images = images, | ||||
|                     type = mediaType | ||||
|                 ).toJson(), | ||||
|                 type = TvType.TvSeries, | ||||
|             ) { | ||||
|                 this.posterUrl = posterUrl | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     override suspend fun search(query: String): List<SearchResponse>? { | ||||
|         val apiResponse = getApi("$traktApiUrl/search/movie,show?query=$query") | ||||
| 
 | ||||
|         val results = parseJson<List<Results>>(apiResponse).map { element -> | ||||
|             element.toSearchResponse() | ||||
|         } | ||||
| 
 | ||||
|         return results | ||||
|     } | ||||
|     override suspend fun load(url: String): LoadResponse { | ||||
|         val data = parseJson<Data>(url) | ||||
| 
 | ||||
|         val posterUrl = getOrigPosterUrl(data.images, randomPosters) | ||||
|         val backDropUrl = getBackdropUrl(data.images) | ||||
| 
 | ||||
|         val moviesOrShows = if (data.movieBool) "movies" else "shows" | ||||
|         val res = getApi("$traktApiUrl/$moviesOrShows/${data.ids?.trakt}?extended=full") | ||||
| 
 | ||||
|         val mediaDetails = parseJson<MediaDetails>(res) | ||||
| 
 | ||||
|         //Trakt ID, Trakt slug, or IMDB ID | ||||
|         val peopleDetails = getProfileImages(data.ids?.tmdb.toString(), data.movieBool) | ||||
| 
 | ||||
|         val actors = peopleDetails.cast?.filter { it.knownForDepartment == "Acting" }?.map { | ||||
|             ActorData( | ||||
|                 Actor( | ||||
|                     name = it.name, | ||||
|                     image = getImageUrl(it.profilePath) | ||||
|                 ), | ||||
|                 roleString = it.character | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         val resRelated = getApi("$traktApiUrl/$moviesOrShows/${data.ids?.trakt}/related") | ||||
| 
 | ||||
|         val relatedMedia = parseJson<List<Results>>(resRelated).map { it.toSearchResponse() } | ||||
| 
 | ||||
|         val isCartoon = (mediaDetails.genres?.contains("animation") == true || mediaDetails.genres?.contains("anime") == true) | ||||
|         val isAnime = isCartoon && (mediaDetails.language == "zh" || mediaDetails.language == "ja") | ||||
|         val isAsian = !isAnime && (mediaDetails.language == "zh" || mediaDetails.language == "ko") | ||||
|         val isBollywood = mediaDetails.country == "in" | ||||
| 
 | ||||
|         if (data.type == TvType.Movie) { | ||||
| 
 | ||||
|             val linkData = LinkData( | ||||
|                 id = mediaDetails.ids?.tmdb, | ||||
|                 imdbId = mediaDetails.ids?.imdb.toString(), | ||||
|                 tvdbId = mediaDetails.ids?.tvdb, | ||||
|                 type = data.type.toString(), | ||||
|                 title = mediaDetails.title, | ||||
|                 year = mediaDetails.year, | ||||
|                 orgTitle = mediaDetails.title, | ||||
|                 isAnime = isAnime, | ||||
|                 //jpTitle = later if needed as it requires another network request, | ||||
|                 airedDate = mediaDetails.released | ||||
|                     ?: mediaDetails.firstAired, | ||||
|                 isAsian = isAsian, | ||||
|                 isBollywood = isBollywood, | ||||
|             ).toJson() | ||||
| 
 | ||||
|             return newMovieLoadResponse( | ||||
|                 name = mediaDetails.title, | ||||
|                 url = data.toJson(), | ||||
|                 dataUrl = linkData.toJson(), | ||||
|                 type = if (isAnime) TvType.AnimeMovie else TvType.Movie, | ||||
|             ) { | ||||
|                 this.name = mediaDetails.title | ||||
|                 this.apiName = "Trakt" | ||||
|                 this.type = if (isAnime) TvType.AnimeMovie else TvType.Movie | ||||
|                 this.posterUrl = posterUrl | ||||
|                 this.year = mediaDetails.year | ||||
|                 this.plot = mediaDetails.overview | ||||
|                 this.rating = mediaDetails.rating?.times(1000)?.roundToInt() | ||||
|                 this.tags = mediaDetails.genres | ||||
|                 this.duration = mediaDetails.runtime | ||||
|                 addTrailer(mediaDetails.trailer) | ||||
|                 this.recommendations = relatedMedia | ||||
|                 this.actors = actors | ||||
|                 this.comingSoon = isUpcoming(mediaDetails.released) | ||||
|                 //posterHeaders | ||||
|                 this.backgroundPosterUrl = backDropUrl | ||||
|                 this.contentRating = mediaDetails.certification | ||||
|             } | ||||
|         } else { | ||||
| 
 | ||||
|             val resSeasons = getApi("$traktApiUrl/shows/${data.ids?.trakt.toString()}/seasons?extended=episodes") | ||||
|             val episodes = mutableListOf<Episode>() | ||||
| 
 | ||||
|             val seasons = parseJson<List<Seasons>>(resSeasons) | ||||
| 
 | ||||
|             seasons.forEach { season -> | ||||
|                 parseJson<TmdbSeason>( | ||||
| 
 | ||||
|                     // Using tmdb here because trakt requires a network request for every episode to get its details which make the ext so slow | ||||
| 
 | ||||
|                     getApi("$tmdbApiUrl/tv/${data.ids?.tmdb.toString()}/season/${season.number}?api_key=$tmdbApiKey") | ||||
|                 ).episodes?.map { episode -> | ||||
| 
 | ||||
|                     val linkData = LinkData( | ||||
|                         id = mediaDetails.ids?.tmdb, | ||||
|                         imdbId = mediaDetails.ids?.imdb.toString(), | ||||
|                         tvdbId = mediaDetails.ids?.tvdb, | ||||
|                         type = data.type.toString(), | ||||
|                         season = episode.seasonNumber, | ||||
|                         episode = episode.episodeNumber, | ||||
|                         title = mediaDetails.title, | ||||
|                         year = mediaDetails.year, | ||||
|                         orgTitle = mediaDetails.title, | ||||
|                         isAnime = isAnime, | ||||
|                         airedYear = mediaDetails.year, | ||||
|                         lastSeason = seasons.size, | ||||
|                         epsTitle = episode.name, | ||||
|                         //jpTitle = later if needed as it requires another network request, | ||||
|                         date = episode.airDate, | ||||
|                         airedDate = episode.airDate, | ||||
|                         isAsian = isAsian, | ||||
|                         isBollywood = isBollywood, | ||||
|                         isCartoon = isCartoon | ||||
|                     ).toJson() | ||||
| 
 | ||||
|                     episodes.add( | ||||
|                         Episode( | ||||
|                             data = linkData.toJson(), | ||||
|                             name = episode.name  + if (isUpcoming(episode.airDate)) " • [UPCOMING]" else "", | ||||
|                             season = episode.seasonNumber, | ||||
|                             episode = episode.episodeNumber, | ||||
|                             posterUrl = getImageUrl(episode.stillPath), | ||||
|                             rating = episode.voteAverage?.times(10)?.roundToInt(), | ||||
|                             description = episode.overview, | ||||
|                         ).apply { | ||||
|                             this.addDate(episode.airDate) | ||||
|                         } | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return newTvSeriesLoadResponse( | ||||
|                 name = mediaDetails.title, | ||||
|                 url = data.toJson(), | ||||
|                 type = if (isAnime) TvType.Anime else TvType.TvSeries, | ||||
|                 episodes = episodes | ||||
|             ) { | ||||
|                 this.name = mediaDetails.title | ||||
|                 this.apiName = "Trakt" | ||||
|                 this.type = if (isAnime) TvType.Anime else TvType.TvSeries | ||||
|                 this.episodes = episodes | ||||
|                 this.posterUrl = posterUrl | ||||
|                 this.year = mediaDetails.year | ||||
|                 this.plot = mediaDetails.overview | ||||
|                 this.showStatus = getStatus(mediaDetails.status) | ||||
|                 this.rating = mediaDetails.rating?.times(1000)?.roundToInt() | ||||
|                 this.tags = mediaDetails.genres | ||||
|                 this.duration = mediaDetails.runtime | ||||
|                 addTrailer(mediaDetails.trailer) | ||||
|                 this.recommendations = relatedMedia | ||||
|                 this.actors = actors | ||||
|                 //comingSoon | ||||
|                 //posterHeaders | ||||
|                 this.backgroundPosterUrl = backDropUrl | ||||
|                 this.contentRating = mediaDetails.certification | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     private suspend fun getApi(url: String) : String { | ||||
|         return app.get( | ||||
|             url = url, | ||||
|             headers = mapOf( | ||||
|                 "Content-Type" to "application/json", | ||||
|                 "trakt-api-version" to "2", | ||||
|                 "trakt-api-key" to traktClientId, | ||||
|             ) | ||||
|         ).toString() | ||||
|     } | ||||
| 
 | ||||
|     private fun getPosterUrl(images: Images, random: Boolean) : String? { | ||||
| 
 | ||||
|         val imagePath = when { | ||||
|             random -> { | ||||
|                 images.posters?.filter { it.iso6391 == "en" }?.randomOrNull() ?: images.posters?.randomOrNull() | ||||
|             } | ||||
|             else -> { | ||||
|                 images.posters?.firstOrNull { it.iso6391 == "en" } ?: images.posters?.firstOrNull() | ||||
|             } | ||||
|         } | ||||
|             ?: return null | ||||
|         return "https://image.tmdb.org/t/p/w500${imagePath.filePath}" | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private fun getOrigPosterUrl(images: Images, random: Boolean) : String? { | ||||
| 
 | ||||
|         val imagePath = when { | ||||
|             random -> { | ||||
|                 images.posters?.filter { it.iso6391 == null }?.randomOrNull() ?: images.posters?.randomOrNull() | ||||
|             } | ||||
|             else -> { | ||||
|                 images.posters?.firstOrNull { it.iso6391 == null } ?: images.posters?.firstOrNull() | ||||
|             } | ||||
|         } | ||||
|             ?: return null | ||||
|         return "https://image.tmdb.org/t/p/original${imagePath.filePath}" | ||||
|     } | ||||
| 
 | ||||
|     private fun getBackdropUrl(images: Images) : String? { | ||||
|         val imagePath = images.backdrops?.filter { it.iso6391 == null }?.randomOrNull() ?: images.backdrops?.randomOrNull() | ||||
|         ?: return null | ||||
|         return "https://image.tmdb.org/t/p/original${imagePath.filePath}" | ||||
|     } | ||||
| 
 | ||||
|     private fun getImageUrl(path: String?) : String? { | ||||
|         if (path == null) return null | ||||
|         return "https://image.tmdb.org/t/p/w500${path}" | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun getProfileImages(castTmdbId: String, movie: Boolean) : Images { | ||||
|         val imageApiUrl = if (movie) { | ||||
|             "$tmdbApiUrl/movie/$castTmdbId/credits?api_key=$tmdbApiKey" | ||||
|         } else { | ||||
|             "$tmdbApiUrl/tv/$castTmdbId/credits?api_key=$tmdbApiKey" | ||||
|         } | ||||
| 
 | ||||
|         val req = app.get( | ||||
|             url = imageApiUrl, | ||||
|             headers = mapOf( | ||||
|                 "accept" to "application/json", | ||||
|             ) | ||||
|         ).toString() | ||||
|         return parseJson<Images>(req) | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun getImages(tmdbId: String, movie: Boolean = true) : Images { | ||||
| 
 | ||||
|         val imageApiUrl = if (movie) { | ||||
|             "$tmdbApiUrl/movie/${tmdbId}/images?api_key=${tmdbApiKey}" | ||||
|         } else { | ||||
|             "$tmdbApiUrl/tv/${tmdbId}/images?api_key=${tmdbApiKey}" | ||||
|         } | ||||
| 
 | ||||
|         val req = app.get( | ||||
|             url = imageApiUrl, | ||||
|             headers = mapOf( | ||||
|                 "accept" to "application/json", | ||||
|             ) | ||||
|         ).toString() | ||||
| 
 | ||||
|         return parseJson<Images>(req) | ||||
|     } | ||||
| 
 | ||||
|     private fun isUpcoming(dateString: String?): Boolean { | ||||
|         return try { | ||||
|             val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) | ||||
|             val dateTime = dateString?.let { format.parse(it)?.time } ?: return false | ||||
|             System.currentTimeMillis() < dateTime | ||||
|         } catch (t: Throwable) { | ||||
|             logError(t) | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun getStatus(t: String?): ShowStatus { | ||||
|         return when (t) { | ||||
|             "returning series" -> ShowStatus.Ongoing | ||||
|             "continuing" -> ShowStatus.Ongoing | ||||
|             else -> ShowStatus.Completed | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     data class Data( | ||||
|         val title: String, | ||||
|         val movieBool: Boolean, | ||||
|         val type: TvType, | ||||
|         val ids: Ids? = null, | ||||
|         val images: Images, | ||||
|     ) | ||||
| 
 | ||||
|     data class Results( | ||||
|         val watchers: Long? = null, | ||||
|         val revenue: Long? = null, | ||||
|         val title: String? = null, | ||||
|         val year: Int? = null, | ||||
|         @JsonProperty("movie") | ||||
|         @JsonAlias("show") | ||||
|         val media: Media? = null, | ||||
|         val ids: Ids? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Media( | ||||
|         val title: String, | ||||
|         val year: Int, | ||||
|         val ids: Ids, | ||||
|     ) | ||||
| 
 | ||||
|     data class MediaDetails( | ||||
|         val title: String, | ||||
|         val year: Int? = null, | ||||
|         val ids: Ids? = null, | ||||
|         val tagline: String? = null, | ||||
|         val overview: String? = null, | ||||
|         val released: String? = null, | ||||
|         val runtime: Int? = null, | ||||
|         val country: String? = null, | ||||
|         @JsonProperty("updated_at") | ||||
|         val updatedAt: String? = null, | ||||
|         val trailer: String? = null, | ||||
|         val homepage: String? = null, | ||||
|         val status: String? = null, | ||||
|         val rating: Double? = null, | ||||
|         val votes: Long? = null, | ||||
|         @JsonProperty("comment_count") | ||||
|         val commentCount: Long? = null, | ||||
|         val language: String? = null, | ||||
|         val languages: List<String>? = null, | ||||
|         @JsonProperty("available_translations") | ||||
|         val availableTranslations: List<String>? = null, | ||||
|         val genres: List<String>? = null, | ||||
|         val certification: String? = null, | ||||
|         @JsonProperty("aired_episodes") | ||||
|         val airedEpisodes: Int? = null, | ||||
|         @JsonProperty("first_aired") | ||||
|         val firstAired: String? = null, | ||||
|         val airs: Airs? = null, | ||||
|         val network: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Airs( | ||||
|         val day: String? = null, | ||||
|         val time: String? = null, | ||||
|         val timezone: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Ids( | ||||
|         val trakt: Int? = null, | ||||
|         val slug: String? = null, | ||||
|         val tvdb: Int? = null, | ||||
|         val imdb: String? = null, | ||||
|         val tmdb: Int? = null, | ||||
|         val tvrage: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Images( | ||||
|         val backdrops: List<Backdrop>? = null, | ||||
|         val id: Long? = null, | ||||
|         val logos: List<Logo>? = null, | ||||
|         val posters: List<Poster>? = null, | ||||
|         val cast: List<Cast>? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Backdrop( | ||||
|         @JsonProperty("aspect_ratio") | ||||
|         val aspectRatio: Double? = null, | ||||
|         val height: Long? = null, | ||||
|         @JsonProperty("iso_639_1") | ||||
|         val iso6391: String? = null, | ||||
|         @JsonProperty("file_path") | ||||
|         val filePath: String? = null, | ||||
|         @JsonProperty("vote_average") | ||||
|         val voteAverage: Double? = null, | ||||
|         @JsonProperty("vote_count") | ||||
|         val voteCount: Long? = null, | ||||
|         val width: Long? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Logo( | ||||
|         @JsonProperty("aspect_ratio") | ||||
|         val aspectRatio: Double? = null, | ||||
|         val height: Long? = null, | ||||
|         @JsonProperty("iso_639_1") | ||||
|         val iso6391: String? = null, | ||||
|         @JsonProperty("file_path") | ||||
|         val filePath: String? = null, | ||||
|         @JsonProperty("vote_average") | ||||
|         val voteAverage: Double? = null, | ||||
|         @JsonProperty("vote_count") | ||||
|         val voteCount: Long? = null, | ||||
|         val width: Long? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Poster( | ||||
|         @JsonProperty("aspect_ratio") | ||||
|         val aspectRatio: Double? = null, | ||||
|         val height: Long? = null, | ||||
|         @JsonProperty("iso_639_1") | ||||
|         val iso6391: String? = null, | ||||
|         @JsonProperty("file_path") | ||||
|         val filePath: String? = null, | ||||
|         @JsonProperty("vote_average") | ||||
|         val voteAverage: Double? = null, | ||||
|         @JsonProperty("vote_count") | ||||
|         val voteCount: Long? = null, | ||||
|         val width: Long? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Cast( | ||||
|         val adult: Boolean? = null, | ||||
|         val gender: Long? = null, | ||||
|         val id: Long? = null, | ||||
|         @JsonProperty("known_for_department") | ||||
|         val knownForDepartment: String? = null, | ||||
|         val name: String, | ||||
|         @JsonProperty("original_name") | ||||
|         val originalName: String? = null, | ||||
|         val popularity: Double? = null, | ||||
|         @JsonProperty("profile_path") | ||||
|         val profilePath: String? = null, | ||||
|         @JsonProperty("cast_id") | ||||
|         val castId: Long? = null, | ||||
|         val character: String? = null, | ||||
|         @JsonProperty("credit_id") | ||||
|         val creditId: String? = null, | ||||
|         val order: Long? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class Seasons( | ||||
|         val number: Int? = null, | ||||
|         val ids: Ids? = null, | ||||
|         @JsonProperty("episodes") | ||||
|         val episodes: List<TraktEpisode>? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class TraktEpisode( | ||||
|         val season: Int? = null, | ||||
|         val number: Int? = null, | ||||
|         val title: String? = null, | ||||
|         val ids: Ids? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class TmdbSeason( | ||||
|         @JsonProperty("_id") | ||||
|         val id: String? = null, | ||||
|         @JsonProperty("air_date") | ||||
|         val airDate: String? = null, | ||||
|         val episodes: List<TmdbEpisode>? = null, | ||||
|         val name: String? = null, | ||||
|         val overview: String? = null, | ||||
|         @JsonProperty("id") | ||||
|         val id2: Int? = null, | ||||
|         @JsonProperty("poster_path") | ||||
|         val posterPath: String? = null, | ||||
|         @JsonProperty("season_number") | ||||
|         val seasonNumber: Int? = null, | ||||
|         @JsonProperty("vote_average") | ||||
|         val voteAverage: Double? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class TmdbEpisode( | ||||
|         @JsonProperty("air_date") | ||||
|         val airDate: String? = null, | ||||
|         @JsonProperty("episode_number") | ||||
|         val episodeNumber: Int? = null, | ||||
|         @JsonProperty("episode_type") | ||||
|         val episodeType: String? = null, | ||||
|         val id: Int? = null, | ||||
|         val name: String? = null, | ||||
|         val overview: String? = null, | ||||
|         @JsonProperty("production_code") | ||||
|         val productionCode: String? = null, | ||||
|         val runtime: Int? = null, | ||||
|         @JsonProperty("season_number") | ||||
|         val seasonNumber: Int? = null, | ||||
|         @JsonProperty("show_id") | ||||
|         val showId: Int? = null, | ||||
|         @JsonProperty("still_path") | ||||
|         val stillPath: String? = null, | ||||
|         @JsonProperty("vote_average") | ||||
|         val voteAverage: Double? = null, | ||||
|         @JsonProperty("vote_count") | ||||
|         val voteCount: Int? = null, | ||||
|         //val crew: List<Crew>, | ||||
|         //@JsonProperty("guest_stars") | ||||
|         //val guestStars: List<GuestStar>, | ||||
|     ) | ||||
| 
 | ||||
|     data class LinkData( | ||||
|         val id: Int? = null, | ||||
|         val imdbId: String? = null, | ||||
|         val tvdbId: Int? = null, | ||||
|         val type: String? = null, | ||||
|         val season: Int? = null, | ||||
|         val episode: Int? = null, | ||||
|         val aniId: String? = null, | ||||
|         val animeId: String? = null, | ||||
|         val title: String? = null, | ||||
|         val year: Int? = null, | ||||
|         val orgTitle: String? = null, | ||||
|         val isAnime: Boolean = false, | ||||
|         val airedYear: Int? = null, | ||||
|         val lastSeason: Int? = null, | ||||
|         val epsTitle: String? = null, | ||||
|         val jpTitle: String? = null, | ||||
|         val date: String? = null, | ||||
|         val airedDate: String? = null, | ||||
|         val isAsian: Boolean = false, | ||||
|         val isBollywood: Boolean = false, | ||||
|         val isCartoon: Boolean = false, | ||||
|     ) | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue