/// Public service metadata. Source visibility is not an authorization grant. library; import 'content_policy.dart'; Map _cleanJson(Map value) { final clean = ContentPolicy.sanitizeJson(value); return clean is Map ? Map.from(clean) : {}; } String _text(dynamic value, [String fallback = '']) => value is String || value is num || value is bool ? '$value' : fallback; int _integer(dynamic value) { if (value is int) return value; if (value is num) return value.isFinite ? value.toInt() : 0; return int.tryParse(_text(value).trim()) ?? 0; } double _decimal(dynamic value) { final parsed = value is num ? value.toDouble() : double.tryParse(_text(value).trim()); return parsed != null && parsed.isFinite ? parsed : 0; } List _records(dynamic value, T Function(Map) parse) => value is List ? value .whereType() .map((item) => parse(Map.from(item))) .toList() : []; class Tag { final int id; final String title, image; const Tag({this.id = 0, this.title = '', this.image = ''}); factory Tag.fromJson(Map json) => Tag( id: _integer(json['id']), title: ContentPolicy.sanitizeText(_text(json['title'])), image: ContentPolicy.allowsUrl(_text(json['image'])) ? _text(json['image']) : '', ); Map toJson() => {'id': id, 'title': title, 'image': image}; } class MediaSource { final int id; final String quality, type, url; const MediaSource({ this.id = 0, this.quality = '', this.type = '', this.url = '', }); factory MediaSource.fromJson(Map json) => MediaSource( id: _integer(json['id']), quality: ContentPolicy.sanitizeText(_text(json['quality'])), type: _text(json['type']), url: ContentPolicy.allowsUrl(_text(json['url'])) ? _text(json['url']) : '', ); bool get isTrailer => RegExp( r'تیزر|تریلر|trailer|teaser', caseSensitive: false, ).hasMatch(quality) || type.toLowerCase() == 'trailer'; Map toJson() => { 'id': id, 'quality': quality, 'type': type, 'url': url, }; } class EpisodeRecord { final int id; final String title, description, duration; final List sources; final Map raw; const EpisodeRecord({ this.id = 0, this.title = '', this.description = '', this.duration = '', this.sources = const [], this.raw = const {}, }); factory EpisodeRecord.fromJson(Map value) { final json = _cleanJson(value); return EpisodeRecord( id: _integer(json['id']), title: _text(json['title']), description: _text(json['description']), duration: _text(json['duration']), sources: _records(json['sources'], MediaSource.fromJson), raw: Map.unmodifiable(json), ); } Map toJson() => { ...raw, 'id': id, 'title': title, 'description': description, 'duration': duration, 'sources': sources.map((source) => source.toJson()).toList(), }; } class SeasonRecord { final int id; final String title; final List episodes; final Map raw; const SeasonRecord({ this.id = 0, this.title = '', this.episodes = const [], this.raw = const {}, }); factory SeasonRecord.fromJson(Map value) { final json = _cleanJson(value); return SeasonRecord( id: _integer(json['id']), title: _text(json['title']), episodes: _records(json['episodes'], EpisodeRecord.fromJson), raw: Map.unmodifiable(json), ); } Map toJson() => { ...raw, 'id': id, 'title': title, 'episodes': episodes.map((episode) => episode.toJson()).toList(), }; } class TitleRecord { final int id, year; final String type, title, description, duration, classification, image, cover; final double imdb, rating; final List genres, countries; final List sources; final Map raw; const TitleRecord({ this.id = 0, this.type = 'movie', this.title = '', this.description = '', this.duration = '', this.classification = '', this.image = '', this.cover = '', this.year = 0, this.imdb = 0, this.rating = 0, this.genres = const [], this.countries = const [], this.sources = const [], this.raw = const {}, }); factory TitleRecord.fromJson(Map value) { final json = _cleanJson(value); return TitleRecord( id: _integer(json['id'] ?? json['posted_id'] ?? json['movie_id']), type: _text(json['type'] ?? json['kind'], 'movie'), title: _text(json['title'] ?? json['name']), description: _text(json['description']), duration: _text(json['duration']), classification: _text(json['classification']), image: _text(json['image'] ?? json['img'] ?? json['poster']), cover: _text(json['cover']), year: _integer(json['year']), imdb: _decimal(json['imdb']), rating: _decimal(json['rating']), genres: _records(json['genres'], Tag.fromJson), countries: _records(json['country'] ?? json['countries'], Tag.fromJson), sources: _records(json['sources'], MediaSource.fromJson), raw: Map.unmodifiable(json), ); } bool get isSeries => type == 'serie' || type == 'series'; bool get isChannel => type == 'channel'; Map toJson() => { ...raw, 'id': id, 'type': type, 'title': title, 'description': description, 'duration': duration, 'classification': classification, 'image': image, 'cover': cover, 'year': year, 'imdb': imdb, 'rating': rating, 'genres': genres.map((tag) => tag.toJson()).toList(), 'country': countries.map((tag) => tag.toJson()).toList(), 'sources': sources.map((source) => source.toJson()).toList(), }; }