Auf Wunsch von Dustin: seine beste Freundin sortiert ihre Sammlung ueber das Album-Feld. In Melo ist der Album-Titel deshalb ab jetzt die Kategorie — sie muss nichts neu machen. 1) Album = Kategorie - kategorienAusTags(): Album-Titel steht VORN in der Kategorienliste (die erste Kategorie bestimmt das Coverbild), Genres dahinter. - Beide Scans (android_scan + scan_service) tragen ihn ein; von Hand gepflegte Kategorien (categoriesEdited) bleiben unberuehrt. - DB-Schema 9: einmalige Nachruestung bestehender Bibliotheken, damit das nicht erst beim naechsten vollstaendigen Scan sichtbar wird (der auf Android nur laeuft, wenn sich die Dateianzahl aendert). 2) Reiter: 'Songs/Kuenstler/Alben' -> 'Lieder/Kategorie/Kuenstler' - Neu: library/category_list.dart mit groupByCategory(); Lieder ohne Kategorie sammeln sich am Ende unter "Ohne Kategorie". - Entfernt: library/album_list.dart, groupByAlbum(), albumArtistLabel() — mit dem Alben-Reiter tot geworden. Das Album-FELD bleibt erhalten. 3) YouTube-Downloads ohne Original-Album - Feld "Kategorie (optional)" im YouTube-Bereich, mit Vorschlaegen aus der Bibliothek und freier Eingabe. - ordneDownloadEin() verwirft nach dem Scan das Album-Tag (yt-dlp leitet es aus Kanal/Playlist ab — als Kategorie waere das Unsinn) und setzt stattdessen die gewaehlte Kategorie. Beides als "von Hand gesetzt" markiert, damit der naechste Scan es nicht zurueckholt. - MeloDb.songByPath() und MeloDb.verwirfAlbum() neu. - CategoryService.alleNamen: Kategorienamen ohne zusaetzliche Abfrage — ohne das flackerte die Vorschlagsliste und im Widget-Test blieb ein Aufraeum-Timer von drift haengen. 295 Tests gruen (19 neue, 1 uebersprungen), flutter analyze ohne Befund. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FpPu4nuKjKKeX1RpdDeX81
70 lines
1.8 KiB
Dart
70 lines
1.8 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:melo/library/database.dart';
|
|
import 'package:melo/library/song_grouping.dart';
|
|
|
|
Song _song({
|
|
required String id,
|
|
required String title,
|
|
String? artist,
|
|
String? album,
|
|
}) {
|
|
return Song(
|
|
id: id,
|
|
path: '/$id.mp3',
|
|
title: title,
|
|
artist: artist,
|
|
album: album,
|
|
dateAddedMs: 0,
|
|
updatedAtMs: 0,
|
|
deleted: false,
|
|
playCount: 0,
|
|
categoriesEdited: false,
|
|
metadataEdited: false,
|
|
);
|
|
}
|
|
|
|
void main() {
|
|
group('groupByArtist', () {
|
|
test('gruppiert mehrere Künstler getrennt', () {
|
|
final songs = [
|
|
_song(id: '1', title: 'A', artist: 'Bob'),
|
|
_song(id: '2', title: 'B', artist: 'Alice'),
|
|
_song(id: '3', title: 'C', artist: 'Alice'),
|
|
];
|
|
|
|
final grouped = groupByArtist(songs);
|
|
|
|
expect(grouped.keys, ['Alice', 'Bob']);
|
|
expect(grouped['Alice']!.length, 2);
|
|
expect(grouped['Bob']!.length, 1);
|
|
});
|
|
|
|
test('gruppiert Songs ohne Künstler unter "Unbekannt"', () {
|
|
final songs = [
|
|
_song(id: '1', title: 'A', artist: null),
|
|
_song(id: '2', title: 'B', artist: null),
|
|
];
|
|
|
|
final grouped = groupByArtist(songs);
|
|
|
|
expect(grouped.keys, [unbekannterKuenstler]);
|
|
expect(grouped[unbekannterKuenstler]!.length, 2);
|
|
});
|
|
|
|
test('sortiert Künstlernamen alphabetisch', () {
|
|
final songs = [
|
|
_song(id: '1', title: 'A', artist: 'Zebra'),
|
|
_song(id: '2', title: 'B', artist: 'Anton'),
|
|
_song(id: '3', title: 'C', artist: 'Mitte'),
|
|
];
|
|
|
|
final grouped = groupByArtist(songs);
|
|
|
|
expect(grouped.keys.toList(), ['Anton', 'Mitte', 'Zebra']);
|
|
});
|
|
|
|
test('leere Liste ergibt leere Map', () {
|
|
expect(groupByArtist(const []), isEmpty);
|
|
});
|
|
});
|
|
} |