// lib/entities/pinepods_search.dart class PinepodsSearchResult { final String? status; final int? resultCount; final List? feeds; final List? results; PinepodsSearchResult({ this.status, this.resultCount, this.feeds, this.results, }); factory PinepodsSearchResult.fromJson(Map json) { return PinepodsSearchResult( status: json['status'] as String?, resultCount: json['resultCount'] as int?, feeds: json['feeds'] != null ? (json['feeds'] as List) .map((item) => PinepodsPodcast.fromJson(item as Map)) .toList() : null, results: json['results'] != null ? (json['results'] as List) .map((item) => PinepodsITunesPodcast.fromJson(item as Map)) .toList() : null, ); } Map toJson() { return { 'status': status, 'resultCount': resultCount, 'feeds': feeds?.map((item) => item.toJson()).toList(), 'results': results?.map((item) => item.toJson()).toList(), }; } List getUnifiedPodcasts() { final List unified = []; // Add PodcastIndex results if (feeds != null) { unified.addAll(feeds!.map((podcast) => UnifiedPinepodsPodcast.fromPodcast(podcast))); } // Add iTunes results if (results != null) { unified.addAll(results!.map((podcast) => UnifiedPinepodsPodcast.fromITunesPodcast(podcast))); } return unified; } } class PinepodsPodcast { final int id; final String title; final String url; final String originalUrl; final String link; final String description; final String author; final String ownerName; final String image; final String artwork; final int lastUpdateTime; final Map? categories; final bool explicit; final int episodeCount; PinepodsPodcast({ required this.id, required this.title, required this.url, required this.originalUrl, required this.link, required this.description, required this.author, required this.ownerName, required this.image, required this.artwork, required this.lastUpdateTime, this.categories, required this.explicit, required this.episodeCount, }); factory PinepodsPodcast.fromJson(Map json) { return PinepodsPodcast( id: json['id'] as int, title: json['title'] as String? ?? '', url: json['url'] as String? ?? '', originalUrl: json['originalUrl'] as String? ?? '', link: json['link'] as String? ?? '', description: json['description'] as String? ?? '', author: json['author'] as String? ?? '', ownerName: json['ownerName'] as String? ?? '', image: json['image'] as String? ?? '', artwork: json['artwork'] as String? ?? '', lastUpdateTime: json['lastUpdateTime'] as int? ?? 0, categories: json['categories'] != null ? Map.from(json['categories'] as Map) : null, explicit: json['explicit'] as bool? ?? false, episodeCount: json['episodeCount'] as int? ?? 0, ); } Map toJson() { return { 'id': id, 'title': title, 'url': url, 'originalUrl': originalUrl, 'link': link, 'description': description, 'author': author, 'ownerName': ownerName, 'image': image, 'artwork': artwork, 'lastUpdateTime': lastUpdateTime, 'categories': categories, 'explicit': explicit, 'episodeCount': episodeCount, }; } } class PinepodsITunesPodcast { final String wrapperType; final String kind; final int collectionId; final int trackId; final String artistName; final String trackName; final String collectionViewUrl; final String feedUrl; final String artworkUrl100; final String releaseDate; final List genres; final String collectionExplicitness; final int? trackCount; PinepodsITunesPodcast({ required this.wrapperType, required this.kind, required this.collectionId, required this.trackId, required this.artistName, required this.trackName, required this.collectionViewUrl, required this.feedUrl, required this.artworkUrl100, required this.releaseDate, required this.genres, required this.collectionExplicitness, this.trackCount, }); factory PinepodsITunesPodcast.fromJson(Map json) { return PinepodsITunesPodcast( wrapperType: json['wrapperType'] as String? ?? '', kind: json['kind'] as String? ?? '', collectionId: json['collectionId'] as int? ?? 0, trackId: json['trackId'] as int? ?? 0, artistName: json['artistName'] as String? ?? '', trackName: json['trackName'] as String? ?? '', collectionViewUrl: json['collectionViewUrl'] as String? ?? '', feedUrl: json['feedUrl'] as String? ?? '', artworkUrl100: json['artworkUrl100'] as String? ?? '', releaseDate: json['releaseDate'] as String? ?? '', genres: json['genres'] != null ? List.from(json['genres'] as List) : [], collectionExplicitness: json['collectionExplicitness'] as String? ?? '', trackCount: json['trackCount'] as int?, ); } Map toJson() { return { 'wrapperType': wrapperType, 'kind': kind, 'collectionId': collectionId, 'trackId': trackId, 'artistName': artistName, 'trackName': trackName, 'collectionViewUrl': collectionViewUrl, 'feedUrl': feedUrl, 'artworkUrl100': artworkUrl100, 'releaseDate': releaseDate, 'genres': genres, 'collectionExplicitness': collectionExplicitness, 'trackCount': trackCount, }; } } class UnifiedPinepodsPodcast { final int id; final int indexId; final String title; final String url; final String originalUrl; final String link; final String description; final String author; final String ownerName; final String image; final String artwork; final int lastUpdateTime; final Map? categories; final bool explicit; final int episodeCount; UnifiedPinepodsPodcast({ required this.id, required this.indexId, required this.title, required this.url, required this.originalUrl, required this.link, required this.description, required this.author, required this.ownerName, required this.image, required this.artwork, required this.lastUpdateTime, this.categories, required this.explicit, required this.episodeCount, }); factory UnifiedPinepodsPodcast.fromJson(Map json) { return UnifiedPinepodsPodcast( id: json['id'] as int? ?? 0, indexId: json['indexId'] as int? ?? 0, title: json['title'] as String? ?? '', url: json['url'] as String? ?? '', originalUrl: json['originalUrl'] as String? ?? '', link: json['link'] as String? ?? '', description: json['description'] as String? ?? '', author: json['author'] as String? ?? '', ownerName: json['ownerName'] as String? ?? '', image: json['image'] as String? ?? '', artwork: json['artwork'] as String? ?? '', lastUpdateTime: json['lastUpdateTime'] as int? ?? 0, categories: json['categories'] != null ? Map.from(json['categories'] as Map) : null, explicit: json['explicit'] as bool? ?? false, episodeCount: json['episodeCount'] as int? ?? 0, ); } Map toJson() { return { 'id': id, 'indexId': indexId, 'title': title, 'url': url, 'originalUrl': originalUrl, 'link': link, 'description': description, 'author': author, 'ownerName': ownerName, 'image': image, 'artwork': artwork, 'lastUpdateTime': lastUpdateTime, 'categories': categories, 'explicit': explicit, 'episodeCount': episodeCount, }; } factory UnifiedPinepodsPodcast.fromPodcast(PinepodsPodcast podcast) { return UnifiedPinepodsPodcast( id: 0, // Internal database ID - will be fetched when needed indexId: podcast.id, // Podcast index ID title: podcast.title, url: podcast.url, originalUrl: podcast.originalUrl, author: podcast.author, ownerName: podcast.ownerName, description: podcast.description, image: podcast.image, link: podcast.link, artwork: podcast.artwork, lastUpdateTime: podcast.lastUpdateTime, categories: podcast.categories, explicit: podcast.explicit, episodeCount: podcast.episodeCount, ); } factory UnifiedPinepodsPodcast.fromITunesPodcast(PinepodsITunesPodcast podcast) { // Convert genres list to map final Map genreMap = {}; for (int i = 0; i < podcast.genres.length; i++) { genreMap[i.toString()] = podcast.genres[i]; } // Parse release date to timestamp int timestamp = 0; try { final dateTime = DateTime.parse(podcast.releaseDate); timestamp = dateTime.millisecondsSinceEpoch ~/ 1000; } catch (e) { // Default to 0 if parsing fails } return UnifiedPinepodsPodcast( id: podcast.trackId, indexId: 0, title: podcast.trackName, url: podcast.feedUrl, originalUrl: podcast.feedUrl, author: podcast.artistName, ownerName: podcast.artistName, description: 'Descriptions not provided by iTunes', image: podcast.artworkUrl100, link: podcast.collectionViewUrl, artwork: podcast.artworkUrl100, lastUpdateTime: timestamp, categories: genreMap, explicit: podcast.collectionExplicitness == 'explicit', episodeCount: podcast.trackCount ?? 0, ); } } enum SearchProvider { podcastIndex, itunes, } extension SearchProviderExtension on SearchProvider { String get name { switch (this) { case SearchProvider.podcastIndex: return 'Podcast Index'; case SearchProvider.itunes: return 'iTunes'; } } String get value { switch (this) { case SearchProvider.podcastIndex: return 'podcast_index'; case SearchProvider.itunes: return 'itunes'; } } }