v2.53.1 — MED-2: Neue Server-Songs werden nach Download in die lokale DB eingefügt
## MED-2 (Review-Fix, KRITISCH) - sync_service.dart: Nach erfolgreichem cloud.download wird der Song jetzt MIT cloud_id + Titeldaten (Titel/Künstler/Dauer/Größe/istKorrupt aus der Server-Liste) in die lokale DB eingefügt — vorher fehlte der Song in der Bibliothek, songNachCloudId blieb null und jeder Sync lud alle 325 Songs erneut (≈ GB/Sync) und meldete sie fälschlich als „+neu“ - Neuer statischer Helfer `songAusServerMap` (testbar): baut den Song aus den handle_list-Feldern; ytUrl wird bewusst NICHT übernommen (v2.52-F2: Server bleibt YT-Link-Quelle) - Zielpfad-Kollisionen abgesichert: existiert die Datei bereits (verwaister Download aus früherem Sync ohne DB-Eintrag), wird „ (2)“, „ (3)“ … an- gehängt — sonst scheiterte der Insert am UNIQUE-Constraint datei_pfad
This commit is contained in:
@@ -153,6 +153,38 @@ class SyncService {
|
|||||||
return letzterSync == null || d == null || d.isAfter(letzterSync);
|
return letzterSync == null || d == null || d.isAfter(letzterSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Baut den lokalen [Song]-DB-Eintrag aus einem Server-Listeneintrag
|
||||||
|
/// (handle_list-Felder: id/title/artist/duration/size/istKorrupt).
|
||||||
|
///
|
||||||
|
/// Grundlage des MED-2-Fixes: Neue Server-Songs werden nach dem Download
|
||||||
|
/// MIT cloud_id + Titeldaten in die lokale DB eingefügt — vorher fehlten
|
||||||
|
/// sie in der Bibliothek und wurden bei jedem Sync erneut heruntergeladen.
|
||||||
|
/// `ytUrl` wird bewusst NICHT übernommen (v2.52-F2: Der Server ist die
|
||||||
|
/// Quelle für YT-Links, lokal wird yt_url nicht mehr persistiert).
|
||||||
|
/// Reine, statische Funktion → testbar.
|
||||||
|
@visibleForTesting
|
||||||
|
static Song songAusServerMap(Map song, String destPath) {
|
||||||
|
final dauerRaw = song['duration'];
|
||||||
|
final dauer = dauerRaw is num
|
||||||
|
? dauerRaw.toInt()
|
||||||
|
: int.tryParse(dauerRaw?.toString() ?? '') ?? 0;
|
||||||
|
final groesseRaw = song['size'];
|
||||||
|
final groesse = groesseRaw is num
|
||||||
|
? groesseRaw.toInt()
|
||||||
|
: int.tryParse(groesseRaw?.toString() ?? '') ?? 0;
|
||||||
|
return Song(
|
||||||
|
titel: (song['title'] ?? 'unknown').toString(),
|
||||||
|
kuenstler: (song['artist']?.toString() ?? '').trim(),
|
||||||
|
dauerSekunden: dauer,
|
||||||
|
groesseBytes: groesse,
|
||||||
|
dateiPfad: destPath,
|
||||||
|
downloadQuelle: 'cloud',
|
||||||
|
istHeruntergeladen: true,
|
||||||
|
istKorrupt: song['istKorrupt'] == true,
|
||||||
|
cloudId: song['id']?.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Lokale Löschung eines Cloud-Songs an den Server melden (Tombstone).
|
/// Lokale Löschung eines Cloud-Songs an den Server melden (Tombstone).
|
||||||
/// Der Server speichert `deleted=1` + `deleted_at` → andere Geräte löschen
|
/// Der Server speichert `deleted=1` + `deleted_at` → andere Geräte löschen
|
||||||
/// den Song nach ihrem nächsten Sync ebenfalls lokal (keine Zombie-Songs).
|
/// den Song nach ihrem nächsten Sync ebenfalls lokal (keine Zombie-Songs).
|
||||||
@@ -351,13 +383,26 @@ class SyncService {
|
|||||||
onFortschritt?.call('Download: $title…',
|
onFortschritt?.call('Download: $title…',
|
||||||
0.1 + (0.4 * processed / (totalNew > 0 ? totalNew : 1)));
|
0.1 + (0.4 * processed / (totalNew > 0 ? totalNew : 1)));
|
||||||
final safeTitle = sanitizeDateiname(title);
|
final safeTitle = sanitizeDateiname(title);
|
||||||
final dest = '${dir.path}/$safeTitle';
|
// Eindeutiger Zielpfad: Kollisionen (gleichnamiger Titel oder
|
||||||
|
// verwaiste Datei aus einem früheren Sync ohne DB-Eintrag) würden
|
||||||
|
// die Datei überschreiben bzw. am UNIQUE-Constraint der
|
||||||
|
// datei_pfad-Spalte scheitern (Insert würde still ignoriert).
|
||||||
|
var dest = '${dir.path}/$safeTitle';
|
||||||
|
var kollision = 2;
|
||||||
|
while (await File(dest).exists()) {
|
||||||
|
dest = '${dir.path}/$safeTitle ($kollision)';
|
||||||
|
kollision++;
|
||||||
|
}
|
||||||
if (await cloud.download(sid, dest)) {
|
if (await cloud.download(sid, dest)) {
|
||||||
downloaded++;
|
downloaded++;
|
||||||
neueSongs++;
|
neueSongs++;
|
||||||
neueDetails.add('+ Neuer Song: $title');
|
neueDetails.add('+ Neuer Song: $title');
|
||||||
// In DB eintragen mit cloud_id
|
// MED-2: Song mit cloud_id + Titeldaten aus der Server-Liste in
|
||||||
// (vereinfacht: ID3-Reader würde Titel extrahieren)
|
// die lokale DB einfügen — sonst fehlt der Song in der
|
||||||
|
// Bibliothek, songNachCloudId bleibt null und der Song wird bei
|
||||||
|
// JEDEM Sync erneut heruntergeladen (GB/Sync) und fälschlich als
|
||||||
|
// „+neu“ gemeldet.
|
||||||
|
await _db.songEinfuegen(songAusServerMap(song, dest));
|
||||||
}
|
}
|
||||||
await Future.delayed(
|
await Future.delayed(
|
||||||
const Duration(milliseconds: 50)); // UI-Update erlauben
|
const Duration(milliseconds: 50)); // UI-Update erlauben
|
||||||
|
|||||||
Reference in New Issue
Block a user