import 'package:freezed_annotation/freezed_annotation.dart'; part 'tmdb.freezed.dart'; part 'tmdb.g.dart'; @freezed abstract class TmdbCastMember with _$TmdbCastMember { const factory TmdbCastMember({ @JsonKey(fromJson: _intRequired) required int id, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, String? character, @JsonKey(name: 'profile_path') String? profilePath, @JsonKey(fromJson: _intOrZero) @Default(0) int order, }) = _TmdbCastMember; factory TmdbCastMember.fromJson(Map json) => _$TmdbCastMemberFromJson(json); } @freezed abstract class TmdbCrewMember with _$TmdbCrewMember { const factory TmdbCrewMember({ @JsonKey(fromJson: _intRequired) required int id, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, String? job, String? department, @JsonKey(name: 'profile_path') String? profilePath, }) = _TmdbCrewMember; factory TmdbCrewMember.fromJson(Map json) => _$TmdbCrewMemberFromJson(json); } @freezed abstract class TmdbCreditsRes with _$TmdbCreditsRes { const factory TmdbCreditsRes({ @Default([]) List cast, @Default([]) List crew, }) = _TmdbCreditsRes; factory TmdbCreditsRes.fromJson(Map json) => _$TmdbCreditsResFromJson(json); } @freezed abstract class TmdbRecommendation with _$TmdbRecommendation { const factory TmdbRecommendation({ @JsonKey(fromJson: _intRequired) required int id, required String title, @JsonKey(name: 'poster_path') String? posterPath, @JsonKey(name: 'backdrop_path') String? backdropPath, String? overview, @JsonKey(name: 'vote_average', fromJson: _doubleOrNull) double? voteAverage, @JsonKey(name: 'media_type') String? mediaType, }) = _TmdbRecommendation; factory TmdbRecommendation.fromJson(Map json) => _$TmdbRecommendationFromJson({ ...json, 'title': json['title'] ?? json['name'] ?? '', }); } @freezed abstract class TmdbRecommendationsRes with _$TmdbRecommendationsRes { const factory TmdbRecommendationsRes({ @Default([]) List results, @JsonKey(name: 'total_results', fromJson: _intOrZero) @Default(0) int totalResults, }) = _TmdbRecommendationsRes; factory TmdbRecommendationsRes.fromJson(Map json) => _$TmdbRecommendationsResFromJson(json); } @freezed abstract class TmdbEpisode with _$TmdbEpisode { const factory TmdbEpisode({ @JsonKey(name: 'episode_number', fromJson: _intRequired) required int episodeNumber, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, String? overview, @JsonKey(name: 'still_path') String? stillPath, @JsonKey(name: 'air_date') String? airDate, @JsonKey(name: 'vote_average', fromJson: _doubleOrNull) double? voteAverage, @JsonKey(fromJson: _intOrNull) int? runtime, }) = _TmdbEpisode; factory TmdbEpisode.fromJson(Map json) => _$TmdbEpisodeFromJson(json); } @freezed abstract class TmdbSeasonDetail with _$TmdbSeasonDetail { const factory TmdbSeasonDetail({ @JsonKey(name: 'season_number', fromJson: _intRequired) required int seasonNumber, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, String? overview, @JsonKey(name: 'poster_path') String? posterPath, @Default([]) List episodes, }) = _TmdbSeasonDetail; factory TmdbSeasonDetail.fromJson(Map json) => _$TmdbSeasonDetailFromJson(json); } @freezed abstract class TmdbVideo with _$TmdbVideo { const TmdbVideo._(); const factory TmdbVideo({ @JsonKey(fromJson: _strOrEmpty) @Default('') String id, @JsonKey(fromJson: _strOrEmpty) @Default('') String key, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, @JsonKey(fromJson: _strOrEmpty) @Default('') String site, @JsonKey(fromJson: _strOrEmpty) @Default('') String type, @JsonKey(fromJson: _intOrZero) @Default(0) int size, @JsonKey(fromJson: _boolOrFalse) @Default(false) bool official, }) = _TmdbVideo; factory TmdbVideo.fromJson(Map json) => _$TmdbVideoFromJson(json); String? get youtubeUrl => site == 'YouTube' && key.isNotEmpty ? 'https://www.youtube.com/watch?v=$key' : null; String? get youtubeThumbnailUrl => site == 'YouTube' && key.isNotEmpty ? 'https://img.youtube.com/vi/$key/mqdefault.jpg' : null; } @freezed abstract class TmdbVideosRes with _$TmdbVideosRes { const factory TmdbVideosRes({ @Default([]) List results, }) = _TmdbVideosRes; factory TmdbVideosRes.fromJson(Map json) => _$TmdbVideosResFromJson(json); } @freezed abstract class TmdbPersonDetail with _$TmdbPersonDetail { const factory TmdbPersonDetail({ @JsonKey(fromJson: _intRequired) required int id, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, String? biography, String? birthday, String? deathday, @JsonKey(name: 'place_of_birth') String? placeOfBirth, @JsonKey(name: 'profile_path') String? profilePath, @JsonKey(name: 'known_for_department') String? knownForDepartment, @JsonKey(fromJson: _doubleOrNull) double? popularity, }) = _TmdbPersonDetail; factory TmdbPersonDetail.fromJson(Map json) => _$TmdbPersonDetailFromJson(json); } @freezed abstract class TmdbPersonCredit with _$TmdbPersonCredit { const factory TmdbPersonCredit({ @JsonKey(fromJson: _intRequired) required int id, required String title, String? character, @JsonKey(name: 'poster_path') String? posterPath, @JsonKey(name: 'release_date') String? releaseDate, @JsonKey(name: 'vote_average', fromJson: _doubleOrNull) double? voteAverage, @JsonKey(name: 'media_type') String? mediaType, }) = _TmdbPersonCredit; factory TmdbPersonCredit.fromJson(Map json) => _$TmdbPersonCreditFromJson({ ...json, 'title': json['title'] ?? json['name'] ?? '', 'release_date': json['release_date'] ?? json['first_air_date'], }); } @freezed abstract class TmdbPersonCreditsRes with _$TmdbPersonCreditsRes { const factory TmdbPersonCreditsRes({ @Default([]) List cast, }) = _TmdbPersonCreditsRes; factory TmdbPersonCreditsRes.fromJson(Map json) => _$TmdbPersonCreditsResFromJson(json); } @freezed abstract class TmdbGenre with _$TmdbGenre { const factory TmdbGenre({ @JsonKey(fromJson: _intRequired) required int id, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, }) = _TmdbGenre; factory TmdbGenre.fromJson(Map json) => _$TmdbGenreFromJson(json); } @freezed abstract class TmdbMediaDetail with _$TmdbMediaDetail { const factory TmdbMediaDetail({ @JsonKey(fromJson: _intRequired) required int id, @JsonKey(fromJson: _strOrEmpty) @Default('') String title, @JsonKey(name: 'poster_path') String? posterPath, @JsonKey(name: 'backdrop_path') String? backdropPath, @JsonKey(name: 'release_date') String? releaseDate, @Default([]) List genres, @JsonKey(fromJson: _intOrNull) int? runtime, String? tagline, String? overview, String? status, String? homepage, @JsonKey(name: 'vote_average', fromJson: _doubleOrNull) double? voteAverage, @JsonKey(name: 'vote_count', fromJson: _intOrNull) int? voteCount, @JsonKey(fromJson: _intOrNull) int? revenue, @JsonKey(fromJson: _intOrNull) int? budget, }) = _TmdbMediaDetail; factory TmdbMediaDetail.fromJson(Map json) => _$TmdbMediaDetailFromJson({ ...json, 'title': json['title'] ?? json['name'] ?? '', 'release_date': json['release_date'] ?? json['first_air_date'], 'runtime': json['runtime'] ?? (json['episode_run_time'] is List && (json['episode_run_time'] as List).isNotEmpty ? (json['episode_run_time'] as List).first : null), }); } @freezed abstract class TmdbImage with _$TmdbImage { const TmdbImage._(); const factory TmdbImage({ @JsonKey(name: 'file_path', fromJson: _strOrEmpty) @Default('') String filePath, @JsonKey(fromJson: _intOrZero) @Default(0) int width, @JsonKey(fromJson: _intOrZero) @Default(0) int height, @JsonKey(name: 'vote_average', fromJson: _doubleOrNull) double? voteAverage, @JsonKey(name: 'iso_639_1') String? iso639, }) = _TmdbImage; factory TmdbImage.fromJson(Map json) => _$TmdbImageFromJson(json); bool get isLanguageNeutral => iso639 == null; } @freezed abstract class TmdbImageSet with _$TmdbImageSet { const factory TmdbImageSet({ @Default([]) List backdrops, @Default([]) List posters, @Default([]) List logos, }) = _TmdbImageSet; factory TmdbImageSet.fromJson(Map json) => _$TmdbImageSetFromJson(json); } @freezed abstract class TmdbNetwork with _$TmdbNetwork { const factory TmdbNetwork({ @JsonKey(fromJson: _intRequired) required int id, @JsonKey(fromJson: _strOrEmpty) @Default('') String name, @JsonKey(name: 'logo_path') String? logoPath, @JsonKey(name: 'origin_country') String? originCountry, }) = _TmdbNetwork; factory TmdbNetwork.fromJson(Map json) => _$TmdbNetworkFromJson(json); } @freezed abstract class TmdbEnrichedDetail with _$TmdbEnrichedDetail { const factory TmdbEnrichedDetail({ required TmdbMediaDetail detail, TmdbVideosRes? videos, TmdbCreditsRes? credits, TmdbRecommendationsRes? recommendations, TmdbImageSet? images, String? imdbId, @Default([]) List networks, }) = _TmdbEnrichedDetail; factory TmdbEnrichedDetail.fromJson(Map json) => _$TmdbEnrichedDetailFromJson({ 'detail': json, 'videos': json['videos'], 'credits': json['credits'], 'recommendations': json['recommendations'], 'images': json['images'], 'imdbId': _imdbIdOf(json['external_ids']), 'networks': json['networks'], }); } String? _imdbIdOf(Object? externalIds) { if (externalIds is! Map) return null; final v = externalIds['imdb_id']; if (v is! String) return null; final trimmed = v.trim(); return trimmed.isEmpty ? null : trimmed; } int _intRequired(Object? v) => (v as num).toInt(); int _intOrZero(Object? v) => (v as num?)?.toInt() ?? 0; int? _intOrNull(Object? v) => (v as num?)?.toInt(); double? _doubleOrNull(Object? v) => (v as num?)?.toDouble(); bool _boolOrFalse(Object? v) => (v as bool?) ?? false; String _strOrEmpty(Object? v) => v?.toString() ?? '';