import 'database.dart'; /// Trennzeichen, mit denen Genre-Tags mehrere Werte in einem Feld ablegen. final _separators = RegExp(r'[;,|/\n]'); /// Zerlegt einen Genre-Tag in einzelne Kategorien. Doppelte Einträge fallen /// weg (ohne Rücksicht auf Groß-/Kleinschreibung), die erste Schreibweise /// gewinnt. List parseCategories(String? raw) { if (raw == null) return const []; return _dedupe(raw.split(_separators).map((e) => e.trim())); } /// Wie [parseCategories], aber für Tag-Formate, die bereits mehrere Felder /// liefern (z. B. ID3v2 mit mehreren GENRE-Frames). List parseCategoryList(Iterable raw) { return _dedupe(raw.expand((e) => e.split(_separators)).map((e) => e.trim())); } List _dedupe(Iterable candidates) { final seen = {}; final result = []; for (final candidate in candidates) { if (candidate.isEmpty) continue; if (seen.add(candidate.toLowerCase())) result.add(candidate); } return result; } /// Ermittelt je Kategorie ein Coverbild: das erste vorhandene Cover unter den /// Songs der Kategorie, nach Titel geordnet — damit dieselbe Bibliothek immer /// dasselbe Bild ergibt. Kategorien ohne jedes Cover fehlen im Ergebnis. Map categoryCovers( List songs, Map> categoriesBySong, ) { final ordered = [...songs] ..sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase())); final covers = {}; for (final song in ordered) { final cover = song.coverPath; if (cover == null) continue; for (final category in categoriesBySong[song.id] ?? const []) { covers.putIfAbsent(category, () => cover); } } return covers; } /// Das anzuzeigende Cover eines Songs: bei aktiver Einstellung "Gleiche /// Kategorie = gleiches Coverbild" das Bild seiner **ersten** Kategorie, /// sonst (oder wenn die Kategorie kein Bild hat) sein eigenes. String? effectiveCover( Song song, List categories, Map covers, { required bool groupByCategory, }) { if (!groupByCategory || categories.isEmpty) return song.coverPath; return covers[categories.first] ?? song.coverPath; }