Songtext aus Datei-Tags, Warteschlangen-Aktionen, Kategorie-Cover im

Sperrbildschirm und ReplayGain

- DB-Schema 6: lyrics + gain_db
- Songtext bevorzugt lokalen Tag, Server nur als Rueckfall; Song-ID-Bug
  beim Lyrics-Aufruf behoben
- "Als Naechstes spielen" / "Zur Warteschlange hinzufuegen" ohne Eingriff
  in Favoriten oder Wiedergabelisten
- songToMediaItem nimmt eine Cover-Vorgabe: Sperrbildschirm zeigt das
  Kategorie-Cover
- ReplayGain: Tags werden gelesen, laute Titel abgesenkt, abschaltbar

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
This commit is contained in:
2026-08-20 18:41:36 +02:00
co-authored by Claude Opus 5
parent 1325badf90
commit 4ebd0a8dd4
17 changed files with 534 additions and 46 deletions
+30
View File
@@ -6,6 +6,7 @@ import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';
import '../player/replay_gain.dart';
import 'categories.dart';
import 'database.dart';
@@ -64,6 +65,11 @@ Future<int> scanFolders(
// Unlesbare/kaputte Tags: Datei trotzdem mit Dateinamen aufnehmen.
}
// ReplayGain steht je nach Format an unterschiedlicher Stelle und ist im
// vereinheitlichten AudioMetadata nicht enthalten — daher der zweite,
// gezielte Blick in die Roh-Tags.
final gainDb = _readGain(file);
final rawTitle = meta?.title?.trim();
final title = (rawTitle != null && rawTitle.isNotEmpty)
? rawTitle
@@ -92,6 +98,8 @@ Future<int> scanFolders(
dateAddedMs: prev?.dateAddedMs ?? now,
updatedAtMs: now,
deleted: const Value(false),
lyrics: Value(meta?.lyrics),
gainDb: Value(gainDb),
));
if (prev?.categoriesEdited != true) {
categories[id] = parseCategoryList(meta?.genres ?? const []);
@@ -111,3 +119,25 @@ Future<int> scanFolders(
}
return companions.length;
}
/// Liest den ReplayGain-Wert des Titels aus den Roh-Tags: bei FLAC/OGG aus
/// dem Vorbis-Kommentar, bei MP3 aus einem TXXX-Feld. Fehlt der Tag oder
/// lässt sich die Datei nicht lesen, gibt es keine Angleichung.
double? _readGain(File file) {
try {
final tags = readAllMetadata(file, getImage: false);
if (tags is VorbisMetadata) {
return parseReplayGain(tags.replayGainTrackGain.firstOrNull);
}
if (tags is Mp3Metadata) {
for (final entry in tags.customMetadata.entries) {
if (entry.key.toLowerCase() == 'replaygain_track_gain') {
return parseReplayGain(entry.value);
}
}
}
} catch (_) {
// Kein ReplayGain ist kein Fehler — dann bleibt die Lautstärke wie sie ist.
}
return null;
}