/// Mirrors web/src/lib/api/types.ts ArtistSuggestion / SeedContribution — /// one out-of-library artist from GET /api/discover/suggestions. image_url /// is resolved on-demand from Lidarr server-side (may be empty). class SeedContribution { const SeedContribution({required this.name, required this.isLiked}); final String name; final bool isLiked; factory SeedContribution.fromJson(Map j) => SeedContribution( name: j['name'] as String? ?? '', isLiked: j['is_liked'] as bool? ?? false, ); } class ArtistSuggestion { const ArtistSuggestion({ required this.mbid, required this.name, required this.imageUrl, required this.attribution, }); final String mbid; final String name; final String imageUrl; final List attribution; factory ArtistSuggestion.fromJson(Map j) => ArtistSuggestion( mbid: j['mbid'] as String? ?? '', name: j['name'] as String? ?? '', imageUrl: j['image_url'] as String? ?? '', attribution: ((j['attribution'] as List?) ?? const []) .map((e) => SeedContribution.fromJson((e as Map).cast())) .toList(growable: false), ); /// Mirrors web SuggestionFeed.attributionText (Oxford comma, max 3). String get attributionText { if (attribution.isEmpty) return ''; final phrases = attribution .map((s) => '${s.isLiked ? 'liked' : 'played'} ${s.name}') .toList(growable: false); if (phrases.length == 1) return 'Because you ${phrases[0]}.'; if (phrases.length == 2) { return 'Because you ${phrases[0]} and ${phrases[1]}.'; } return 'Because you ${phrases[0]}, ${phrases[1]}, and ${phrases[2]}.'; } }