diff --git a/go_backend/exports_metadata.go b/go_backend/exports_metadata.go index cc3f533d..f1a75585 100644 --- a/go_backend/exports_metadata.go +++ b/go_backend/exports_metadata.go @@ -144,6 +144,13 @@ func ReadFileMetadata(filePath string) (string, error) { } if quality.SampleRate > 0 && quality.TotalSamples > 0 { result["duration"] = int(quality.TotalSamples / int64(quality.SampleRate)) + // Average bitrate from file size: helps spot lossy audio + // repackaged as "24-bit" FLAC (real hi-res sits well above + // ~1500 kbps, upconverted files far below). + durationSec := float64(quality.TotalSamples) / float64(quality.SampleRate) + if info, statErr := os.Stat(filePath); statErr == nil && info.Size() > 0 && durationSec > 0 { + result["bitrate"] = int(float64(info.Size()) * 8 / durationSec / 1000) + } } } } @@ -164,6 +171,10 @@ func ReadFileMetadata(filePath string) (string, error) { } if quality.Bitrate > 0 && !isLosslessLibraryFormat(fmt.Sprint(result["format"])) { result["bitrate"] = quality.Bitrate + } else if quality.Duration > 0 { + if info, statErr := os.Stat(filePath); statErr == nil && info.Size() > 0 { + result["bitrate"] = int(float64(info.Size()) * 8 / float64(quality.Duration) / 1000) + } } } } else if isMp3 { diff --git a/lib/screens/track_metadata_cards.dart b/lib/screens/track_metadata_cards.dart index 6573e6c4..70cc8d64 100644 --- a/lib/screens/track_metadata_cards.dart +++ b/lib/screens/track_metadata_cards.dart @@ -706,6 +706,27 @@ extension _TrackMetadataCards on _TrackMetadataScreenState { ), ), ), + if (!_isBitrateFormatLabel(fileExtension) && + _audioBitrate != null && + _audioBitrate! > 0) + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: colorScheme.tertiaryContainer, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + '${_audioBitrate}kbps', + style: TextStyle( + color: colorScheme.onTertiaryContainer, + fontWeight: FontWeight.w600, + fontSize: 12, + ), + ), + ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, diff --git a/lib/screens/track_metadata_screen.dart b/lib/screens/track_metadata_screen.dart index 8ab84139..afd4670f 100644 --- a/lib/screens/track_metadata_screen.dart +++ b/lib/screens/track_metadata_screen.dart @@ -315,11 +315,11 @@ class _TrackMetadataScreenState extends ConsumerState final formatChanged = resolvedFormat != null && resolvedFormat != normalizeAudioFormatValue(storedFormat); - final resolvedBitrate = _isBitrateFormatValue(resolvedFormat) - ? _readPlausibleBitrateKbps( - metadata['bitrate'] ?? metadata['bit_rate'], - ) - : null; + // Lossless files carry an average bitrate too (computed by the backend + // from file size / duration) — useful for spotting fake 24-bit rips. + final resolvedBitrate = _readPlausibleBitrateKbps( + metadata['bitrate'] ?? metadata['bit_rate'], + ); final resolvedDuration = readPositiveInt(metadata['duration']); final resolvedAlbum = metadata['album']?.toString(); final resolvedTitle = metadata['title']?.toString();