UI: Alben sind Kategorien — Reiter Lieder/Kategorie/Kuenstler
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
This commit is contained in:
co-authored by
Claude Opus 5
parent
cd79213bb3
commit
c7a7a7c458
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter/foundation.dart' show visibleForTesting;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
@@ -129,7 +130,7 @@ class MeloDb extends _$MeloDb {
|
||||
MeloDb([QueryExecutor? executor]) : super(executor ?? _open());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 8;
|
||||
int get schemaVersion => 9;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
@@ -160,6 +161,14 @@ class MeloDb extends _$MeloDb {
|
||||
if (from < 8) {
|
||||
await m.addColumn(songs, songs.cloudId);
|
||||
}
|
||||
if (from < 9) {
|
||||
// Album-Titel gelten ab jetzt als Kategorie (siehe
|
||||
// kategorienAusTags). Ohne diese Nachrüstung bekämen bestehende
|
||||
// Bibliotheken das erst beim nächsten vollständigen Scan zu
|
||||
// sehen — der auf Android nur läuft, wenn sich die Anzahl der
|
||||
// Dateien ändert.
|
||||
await _ergaenzeAlbumKategorien();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -448,6 +457,52 @@ class MeloDb extends _$MeloDb {
|
||||
));
|
||||
}
|
||||
|
||||
/// Der Song mit genau diesem Dateipfad, oder `null`.
|
||||
Future<Song?> songByPath(String path) =>
|
||||
(select(songs)..where((s) => s.path.equals(path))).getSingleOrNull();
|
||||
|
||||
/// Verwirft das Album-Tag eines Titels und markiert ihn als von Hand
|
||||
/// bearbeitet, damit der nächste Scan es nicht wieder hereinholt.
|
||||
/// Für YouTube-Downloads: deren Album-Tag ist keine sinnvolle Kategorie.
|
||||
Future<void> verwirfAlbum(String songId) async {
|
||||
await (update(songs)..where((s) => s.id.equals(songId))).write(
|
||||
const SongsCompanion(
|
||||
album: Value(null),
|
||||
metadataEdited: Value(true),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Nur für Tests: führt die Nachrüstung aus Schema 9 auf einer bereits
|
||||
/// angelegten Datenbank aus.
|
||||
@visibleForTesting
|
||||
Future<void> ergaenzeAlbumKategorienFuerTest() => _ergaenzeAlbumKategorien();
|
||||
|
||||
/// Trägt für jeden Titel seinen Album-Namen als **erste** Kategorie nach.
|
||||
/// Von Hand gepflegte Kategorien (`categoriesEdited`) bleiben unberührt.
|
||||
Future<void> _ergaenzeAlbumKategorien() async {
|
||||
const betroffen = "SELECT id, TRIM(album) AS album_name FROM songs "
|
||||
"WHERE album IS NOT NULL AND TRIM(album) <> '' "
|
||||
"AND categories_edited = 0";
|
||||
// 1) War der Album-Name schon Kategorie, kommt er gleich wieder vorn
|
||||
// dazu — den alten Eintrag deshalb entfernen.
|
||||
await customStatement(
|
||||
'DELETE FROM song_categories WHERE EXISTS ('
|
||||
'SELECT 1 FROM ($betroffen) b '
|
||||
'WHERE b.id = song_categories.song_id AND b.album_name = song_categories.name)',
|
||||
);
|
||||
// 2) Platz an Position 0 schaffen.
|
||||
await customStatement(
|
||||
'UPDATE song_categories SET position = position + 1 '
|
||||
'WHERE song_id IN (SELECT id FROM ($betroffen))',
|
||||
);
|
||||
// 3) Album-Name als erste Kategorie setzen.
|
||||
await customStatement(
|
||||
'INSERT INTO song_categories (song_id, name, position) '
|
||||
'SELECT id, album_name, 0 FROM ($betroffen)',
|
||||
);
|
||||
}
|
||||
|
||||
// === Cloud-Sync ===
|
||||
/// Verknüpft einen Titel des Geräts mit seinem Gegenstück in der Cloud.
|
||||
Future<void> setCloudId(String songId, String cloudId) async {
|
||||
|
||||
Reference in New Issue
Block a user