feat: detect FLAC/ALAC/EAC3/AC3/AC4 codecs inside MP4 containers

GetM4AQuality now recognizes fLaC, alac, ec-3, ac-3, and ac-4 sample entries and parses the MP4 FLACSpecificBox so library entries carry the real codec rather than the container extension. The AudioQuality struct exposes Codec and Bitrate fields (with an estimator for compressed streams), and ReadFileMetadata publishes format + audio_codec so Flutter and Kotlin can make format decisions based on the actual stream.

Downstream: library_scan labels M4A-family items as flac/alac/eac3/ac3/ac4/m4a, zeroes the bitrate for lossless formats, and the filter UI + quality badges use the codec-derived format instead of only the file extension. Scans and SAF importers also accept .mp4 and .aac file extensions. New unit tests cover codec name mapping and MP4 FLACSpecificBox decoding.
This commit is contained in:
zarzet
2026-05-10 22:14:47 +07:00
parent b4031936a0
commit d664d46ca4
10 changed files with 434 additions and 46 deletions
+13 -3
View File
@@ -34,7 +34,7 @@ class LocalLibraryItem {
final String? composer;
final String? label;
final String? copyright;
final String? format; // flac, mp3, opus, m4a
final String? format; // flac, alac, eac3, ac3, ac4, mp3, opus, m4a
const LocalLibraryItem({
required this.id,
@@ -1299,6 +1299,7 @@ class LibraryDatabase {
args,
request,
filePathExpr: 'h.file_path',
formatExpr: null,
qualityExpr: 'h.quality',
bitDepthExpr: 'h.bit_depth',
artistExpr: 'h.artist_name',
@@ -1335,6 +1336,7 @@ class LibraryDatabase {
args,
request,
filePathExpr: 'l.file_path',
formatExpr: 'l.format',
qualityExpr: 'NULL',
bitDepthExpr: 'l.bit_depth',
artistExpr: 'l.artist_name',
@@ -1353,6 +1355,7 @@ class LibraryDatabase {
List<Object?> args,
QueueLibraryDbQuery request, {
required String filePathExpr,
required String? formatExpr,
required String qualityExpr,
required String bitDepthExpr,
required String artistExpr,
@@ -1385,8 +1388,15 @@ class LibraryDatabase {
final format = request.format?.trim().toLowerCase();
if (format != null && format.isNotEmpty) {
where.add('LOWER($filePathExpr) LIKE ?');
args.add('%.$format');
if (formatExpr == null) {
where.add('LOWER($filePathExpr) LIKE ?');
args.add('%.$format');
} else {
where.add(
'(LOWER(COALESCE($formatExpr, \'\')) = ? OR LOWER($filePathExpr) LIKE ?)',
);
args.addAll([format, '%.$format']);
}
}
final metadata = request.metadata?.trim();