Fix: Review-Findings korrigiert (Tag-Leiste, Profil, Singleton u.a.)

CRIT:
- Tag-Leiste: Toggle-Kopf 'Tags & Filter' eingebaut (war unerreichbar) + TagStats
- Profil: Cloud-Count vor Dialog aufloesen (kein 'Instance of Future' mehr)
HIGH:
- CloudEinstellungen: toten Sync-Timer entfernt (echter Timer im ViewModel)
- StatistikCard + RecentWidget jetzt eingebaut (gesamtMB/gesamtMin endlich genutzt)
- CloudService als Singleton (konsistenter Token-Zustand in allen Services)
MED/LOW:
- Playlist-Erkennung nur noch via list= Parameter
- Player: fehlende Quelle wird geloggt statt still
- song_tile: null-ID-Guard vor Tag-Dialog
- Scanner-Log mit Exception-Objekt
- FavoritenService: anzahlFavoriten() fuer StatistikCard
This commit is contained in:
Hermes (Server)
2026-07-31 15:42:54 +02:00
parent b3aedc53f9
commit 8b6a04ead8
18 changed files with 767 additions and 503 deletions
+55
View File
@@ -96,6 +96,18 @@ class DbHelper {
);
}
/// Löscht fehlerhafte Einträge ohne Dateipfad
Future<void> alteDummiesLoeschen() async {
final d = await db;
await d.delete('songs', where: "datei_pfad = '' OR datei_pfad IS NULL");
}
/// Einzelnen Song löschen
Future<void> loeschSong(int id) async {
final d = await db;
await d.delete('songs', where: 'id = ?', whereArgs: [id]);
}
// ─── Songs ──────────────────────────────────────
Future<int> songEinfuegen(Song song) async {
@@ -193,6 +205,34 @@ class DbHelper {
return rows.map((r) => Tag.fromMap(r)).toList();
}
Future<void> songTagEntfernen(int songId, int tagId) async {
final d = await db;
await d.delete('song_tags',
where: 'song_id = ? AND tag_id = ?',
whereArgs: [songId, tagId]);
}
Future<List<int>> songIdsFuerTag(String tagName) async {
final d = await db;
final rows = await d.rawQuery('''
SELECT st.song_id FROM song_tags st
JOIN tags t ON t.id = st.tag_id
WHERE t.name = ?
''', [tagName]);
return rows.map((r) => r['song_id'] as int).toList();
}
Future<Map<String, int>> tagCountsBerechnen() async {
final d = await db;
final rows = await d.rawQuery('''
SELECT t.name, COUNT(st.song_id) as cnt
FROM tags t
LEFT JOIN song_tags st ON t.id = st.tag_id
GROUP BY t.name
''');
return {for (final r in rows) r['name'] as String: r['cnt'] as int};
}
// ─── Playlists ───────────────────────────────────
Future<int> playlistErstellen(String name) async {
@@ -224,6 +264,21 @@ class DbHelper {
whereArgs: [playlistId, songId]);
}
/// Aktualisiert die Position aller Songs in einer Playlist (nach Drag & Drop)
Future<void> playlistReihenfolgeAktualisieren(int playlistId, List<int> songIds) async {
final d = await db;
await d.transaction((txn) async {
for (int i = 0; i < songIds.length; i++) {
await txn.update(
'playlist_songs',
{'position': i},
where: 'playlist_id = ? AND song_id = ?',
whereArgs: [playlistId, songIds[i]],
);
}
});
}
Future<void> metadatenAktualisieren(int songId, {String? titel, String? kuenstler, String? album}) async {
final d = await db;
final update = <String, dynamic>{};