chore: remove redundant comments and update donor list

This commit is contained in:
zarzet
2026-04-03 02:21:40 +07:00
parent 355f2eba2a
commit 59f2fe880a
15 changed files with 39 additions and 114 deletions
@@ -163,10 +163,6 @@ class MainActivity: FlutterFragmentActivity() {
"sm-t225",
"hammerhead",
)
/**
* Check if device should use Skia instead of Impeller.
* Returns true for devices with old/problematic GPUs or old Android versions.
*/
private fun shouldDisableImpeller(): Boolean {
val hardware = Build.HARDWARE.lowercase(Locale.ROOT)
val board = Build.BOARD.lowercase(Locale.ROOT)
@@ -215,7 +211,6 @@ class MainActivity: FlutterFragmentActivity() {
}
/**
* Try to get GPU renderer string.
* Note: This may return empty on some devices before OpenGL context is created.
*/
private fun getGpuRenderer(): String {
+7 -29
View File
@@ -80,13 +80,11 @@ func readAPETagAtOffset(f *os.File, fileSize, footerOffset int64) (*APETag, erro
return nil, fmt.Errorf("invalid footer offset")
}
// Read the 32-byte footer/header
footer := make([]byte, apeTagHeaderSize)
if _, err := f.ReadAt(footer, footerOffset); err != nil {
return nil, fmt.Errorf("failed to read APE footer: %w", err)
}
// Verify preamble
if string(footer[0:8]) != apeTagPreamble {
return nil, fmt.Errorf("APE preamble not found")
}
@@ -96,7 +94,6 @@ func readAPETagAtOffset(f *os.File, fileSize, footerOffset int64) (*APETag, erro
itemCount := binary.LittleEndian.Uint32(footer[16:20])
flags := binary.LittleEndian.Uint32(footer[20:24])
// Sanity checks
if version != apeTagVersion2 && version != 1000 {
return nil, fmt.Errorf("unsupported APE tag version: %d", version)
}
@@ -113,7 +110,6 @@ func readAPETagAtOffset(f *os.File, fileSize, footerOffset int64) (*APETag, erro
return nil, fmt.Errorf("expected APE footer but found header")
}
// Calculate where the items data starts.
// tagSize includes items + footer (32 bytes), but NOT the header.
itemsSize := int64(tagSize) - apeTagHeaderSize
if itemsSize < 0 {
@@ -125,13 +121,11 @@ func readAPETagAtOffset(f *os.File, fileSize, footerOffset int64) (*APETag, erro
return nil, fmt.Errorf("APE tag items extend before file start")
}
// Read all items data
itemsData := make([]byte, itemsSize)
if _, err := f.ReadAt(itemsData, itemsOffset); err != nil {
return nil, fmt.Errorf("failed to read APE items: %w", err)
}
// Parse individual items
items, err := parseAPEItems(itemsData, int(itemCount))
if err != nil {
return nil, fmt.Errorf("failed to parse APE items: %w", err)
@@ -167,9 +161,8 @@ func parseAPEItems(data []byte, count int) ([]APETagItem, error) {
}
key := string(data[pos:keyEnd])
pos = keyEnd + 1 // skip null terminator
pos = keyEnd + 1
// Read value
if pos+valueSize > len(data) {
break
}
@@ -190,19 +183,16 @@ func parseAPEItems(data []byte, count int) ([]APETagItem, error) {
// If the file already has APEv2 tags, they are replaced.
// The tag is written with both header and footer.
func WriteAPETags(filePath string, tag *APETag) error {
// First, read existing file to find and strip any existing APE tag
existingSize, err := findExistingAPETagSize(filePath)
if err != nil {
return fmt.Errorf("failed to check existing APE tag: %w", err)
}
// Build the new tag data
tagData, err := marshalAPETag(tag)
if err != nil {
return fmt.Errorf("failed to marshal APE tag: %w", err)
}
// If there's an existing tag, we need to truncate the file first
if existingSize > 0 {
fi, err := os.Stat(filePath)
if err != nil {
@@ -214,7 +204,6 @@ func WriteAPETags(filePath string, tag *APETag) error {
}
}
// Append the new tag
f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return fmt.Errorf("failed to open file for writing: %w", err)
@@ -243,7 +232,6 @@ func findExistingAPETagSize(filePath string) (int64, error) {
}
fileSize := fi.Size()
// Try to read footer
offsets := []int64{fileSize - apeTagHeaderSize}
if fileSize > apeTagHeaderSize+128 {
offsets = append(offsets, fileSize-apeTagHeaderSize-128)
@@ -263,7 +251,7 @@ func findExistingAPETagSize(filePath string) (int64, error) {
flags := binary.LittleEndian.Uint32(footer[20:24])
if (flags & apeTagFlagHeader) != 0 {
continue // This is a header, not footer
continue
}
tagSize := int64(binary.LittleEndian.Uint32(footer[12:16]))
@@ -292,7 +280,6 @@ func marshalAPETag(tag *APETag) ([]byte, error) {
return nil, fmt.Errorf("empty APE tag")
}
// Build items data
var itemsData []byte
for _, item := range tag.Items {
keyBytes := []byte(item.Key)
@@ -309,7 +296,7 @@ func marshalAPETag(tag *APETag) ([]byte, error) {
itemsData = append(itemsData, sizeBuf...)
itemsData = append(itemsData, flagsBuf...)
itemsData = append(itemsData, keyBytes...)
itemsData = append(itemsData, 0) // null terminator
itemsData = append(itemsData, 0)
itemsData = append(itemsData, valueBytes...)
}
@@ -322,12 +309,10 @@ func marshalAPETag(tag *APETag) ([]byte, error) {
version = tag.Version
}
// Build header
// flags: bit 29 = 1 (is header), bit 31 = 1 (contains header)
headerFlags := uint32(apeTagFlagHeader | (1 << 31))
header := buildAPEHeaderFooter(version, tagSize, itemCount, headerFlags)
// Build footer
// flags: bit 29 = 0 (is footer), bit 31 = 1 (contains header)
footerFlags := uint32(1 << 31)
footer := buildAPEHeaderFooter(version, tagSize, itemCount, footerFlags)
@@ -463,7 +448,6 @@ func AudioMetadataToAPEItems(metadata *AudioMetadata) []APETagItem {
// the metadata fields map sent by the editor. This is used during merge to
// ensure that even empty (cleared) fields override old values.
func apeKeysFromFields(fields map[string]string) map[string]struct{} {
// Map from fields-map key → APE tag key.
mapping := map[string]string{
"title": "TITLE",
"artist": "ARTIST",
@@ -490,29 +474,25 @@ func apeKeysFromFields(fields map[string]string) map[string]struct{} {
result[strings.ToUpper(apeKey)] = struct{}{}
}
}
// The reader accepts both YEAR and DATE for the date field; the writer
// always emits "Year". Ensure both variants are overridden so that an
// old "DATE" tag from another tagger is removed when the user edits date.
// Some fields have reader aliases that must also be cleared when the
// canonical key is updated (e.g. "Year" writer ↔ DATE/YEAR reader,
// DISC ↔ DISCNUMBER, TRACK ↔ TRACKNUMBER, "ALBUM ARTIST" ↔ ALBUMARTIST,
// LABEL ↔ PUBLISHER, LYRICS ↔ UNSYNCEDLYRICS).
if _, present := fields["date"]; present {
result["DATE"] = struct{}{}
}
// Similarly, DISCNUMBER is an alias for DISC in the reader.
if _, present := fields["disc_number"]; present {
result["DISCNUMBER"] = struct{}{}
}
// TRACKNUMBER is an alias for TRACK in the reader.
if _, present := fields["track_number"]; present {
result["TRACKNUMBER"] = struct{}{}
}
// ALBUMARTIST is an alias for ALBUM ARTIST in the reader.
if _, present := fields["album_artist"]; present {
result["ALBUMARTIST"] = struct{}{}
}
// PUBLISHER is an alias for LABEL in the reader.
if _, present := fields["label"]; present {
result["PUBLISHER"] = struct{}{}
}
// UNSYNCEDLYRICS is an alias for LYRICS in the reader.
if _, present := fields["lyrics"]; present {
result["UNSYNCEDLYRICS"] = struct{}{}
}
@@ -538,7 +518,6 @@ func MergeAPEItems(existing, newItems []APETagItem, overrideKeys map[string]stru
combined[strings.ToUpper(item.Key)] = struct{}{}
}
// Start with existing items whose keys are NOT in the combined set
var merged []APETagItem
for _, item := range existing {
if _, overwritten := combined[strings.ToUpper(item.Key)]; !overwritten {
@@ -546,7 +525,6 @@ func MergeAPEItems(existing, newItems []APETagItem, overrideKeys map[string]stru
}
}
// Append all new items
merged = append(merged, newItems...)
return merged
-2
View File
@@ -62,7 +62,6 @@ func resolveDeezerTrackURL(req DownloadRequest) (string, error) {
return trackURL, nil
}
// Try SongLink
spotifyID := strings.TrimSpace(req.SpotifyID)
if spotifyID != "" && isLikelySpotifyTrackID(spotifyID) {
songlink := NewSongLinkClient()
@@ -82,7 +81,6 @@ func resolveDeezerTrackURL(req DownloadRequest) (string, error) {
}
}
// Try ISRC
isrc := strings.TrimSpace(req.ISRC)
if isrc != "" {
ctx, cancel := context.WithTimeout(context.Background(), SongLinkTimeout)
+17 -27
View File
@@ -171,10 +171,6 @@ func applyReEnrichTrackMetadata(req *reEnrichRequest, track ExtTrackMetadata) {
req.SpotifyID = track.ID
}
if req.shouldUpdateField("basic_tags") {
// Title and Artist are not overwritten — they are used for search matching
// and should remain as the user's original values.
}
if req.shouldUpdateField("basic_tags") {
if track.AlbumName != "" {
req.AlbumName = track.AlbumName
@@ -768,8 +764,7 @@ func DownloadTrack(requestJSON string) (string, error) {
return string(jsonBytes), nil
}
// DownloadByStrategy routes a unified download request to the appropriate flow.
// Routing priority: YouTube service > extension fallback > built-in fallback > direct service.
// DownloadByStrategy routes download requests with priority: YouTube > extension fallback > built-in fallback > direct service.
func DownloadByStrategy(requestJSON string) (string, error) {
var req DownloadRequest
if err := json.Unmarshal([]byte(requestJSON), &req); err != nil {
@@ -1067,7 +1062,6 @@ func ReadFileMetadata(filePath string) (string, error) {
result["copyright"] = metadata.Copyright
result["composer"] = metadata.Composer
result["comment"] = metadata.Comment
// ReplayGain fields
result["replaygain_track_gain"] = metadata.ReplayGainTrackGain
result["replaygain_track_peak"] = metadata.ReplayGainTrackPeak
result["replaygain_album_gain"] = metadata.ReplayGainAlbumGain
@@ -1214,8 +1208,7 @@ func ReadFileMetadata(filePath string) (string, error) {
return string(jsonBytes), nil
}
// ParseCueSheet parses a .cue file and returns JSON with split information.
// This is called from Dart to get track listing and timing data for CUE splitting.
// ParseCueSheet is called from Dart to get track listing and timing data for CUE splitting.
// audioDir, if non-empty, overrides the directory used for resolving the
// referenced audio file (useful for SAF temp file scenarios).
func ParseCueSheet(cuePath string, audioDir string) (string, error) {
@@ -1260,9 +1253,7 @@ func ScanCueSheetForLibraryWithCoverCacheKey(cuePath, audioDir, virtualPathPrefi
return string(jsonBytes), nil
}
// EditFileMetadata writes metadata to an audio file.
// For FLAC files, uses native Go FLAC library.
// For MP3/Opus, returns the metadata map so Dart can use FFmpeg.
// EditFileMetadata writes audio file tags: FLAC via native Go library, MP3/Opus returns map for Dart/FFmpeg.
func EditFileMetadata(filePath, metadataJSON string) (string, error) {
var fields map[string]string
if err := json.Unmarshal([]byte(metadataJSON), &fields); err != nil {
@@ -1299,20 +1290,19 @@ func EditFileMetadata(filePath, metadataJSON string) (string, error) {
}
meta := &AudioMetadata{
Title: fields["title"],
Artist: fields["artist"],
Album: fields["album"],
AlbumArtist: fields["album_artist"],
Date: fields["date"],
TrackNumber: trackNum,
DiscNumber: discNum,
ISRC: fields["isrc"],
Genre: fields["genre"],
Label: fields["label"],
Copyright: fields["copyright"],
Composer: fields["composer"],
Comment: fields["comment"],
// ReplayGain fields
Title: fields["title"],
Artist: fields["artist"],
Album: fields["album"],
AlbumArtist: fields["album_artist"],
Date: fields["date"],
TrackNumber: trackNum,
DiscNumber: discNum,
ISRC: fields["isrc"],
Genre: fields["genre"],
Label: fields["label"],
Copyright: fields["copyright"],
Composer: fields["composer"],
Comment: fields["comment"],
ReplayGainTrackGain: fields["replaygain_track_gain"],
ReplayGainTrackPeak: fields["replaygain_track_peak"],
ReplayGainAlbumGain: fields["replaygain_album_gain"],
@@ -2917,7 +2907,7 @@ func CustomSearchWithExtensionJSON(extensionID, query string, optionsJSON string
"album_name": track.AlbumName,
"album_artist": track.AlbumArtist,
"duration_ms": track.DurationMS,
"images": track.ResolvedCoverURL(), // Use helper to get cover URL from either field
"images": track.ResolvedCoverURL(),
"release_date": track.ReleaseDate,
"track_number": track.TrackNumber,
"disc_number": track.DiscNumber,
-1
View File
@@ -104,7 +104,6 @@ func RunWithTimeout(vm *goja.Runtime, script string, timeout time.Duration) (goj
func RunWithTimeoutAndRecover(vm *goja.Runtime, script string, timeout time.Duration) (goja.Value, error) {
result, err := RunWithTimeout(vm, script, timeout)
// Clear any interrupt state so VM can be reused
if vm != nil {
vm.ClearInterrupt()
}
+1 -1
View File
@@ -51,7 +51,7 @@ func GetLogBuffer() *LogBuffer {
globalLogBuffer = &LogBuffer{
entries: make([]LogEntry, 0, defaultLogBufferSize),
maxSize: defaultLogBufferSize,
loggingEnabled: false, // Default: disabled for performance (user can enable in settings)
loggingEnabled: false,
}
})
return globalLogBuffer
+1 -4
View File
@@ -309,7 +309,6 @@ func ReadMetadata(filePath string) (*Metadata, error) {
metadata.Composer = getComment(cmt, "COMPOSER")
metadata.Comment = getComment(cmt, "COMMENT")
// ReplayGain tags
metadata.ReplayGainTrackGain = getComment(cmt, "REPLAYGAIN_TRACK_GAIN")
metadata.ReplayGainTrackPeak = getComment(cmt, "REPLAYGAIN_TRACK_PEAK")
metadata.ReplayGainAlbumGain = getComment(cmt, "REPLAYGAIN_ALBUM_GAIN")
@@ -350,7 +349,7 @@ func EditFlacFields(filePath string, fields map[string]string) error {
cmt = flacvorbis.New()
}
artistMode := fields["artist_tag_mode"] // may be ""
artistMode := fields["artist_tag_mode"]
// Mapping from fields-map key → one or more Vorbis Comment keys.
// Each entry is handled with set-or-clear semantics.
@@ -448,12 +447,10 @@ func EditFlacFields(filePath string, fields map[string]string) error {
f.Meta = append(f.Meta, &cmtBlock)
}
// Cover art
coverPath := strings.TrimSpace(fields["cover_path"])
if coverPath != "" && fileExists(coverPath) {
coverData, err := os.ReadFile(coverPath)
if err == nil && len(coverData) > 0 {
// Remove existing pictures
for i := len(f.Meta) - 1; i >= 0; i-- {
if f.Meta[i].Type == flac.Picture {
f.Meta = append(f.Meta[:i], f.Meta[i+1:]...)
-3
View File
@@ -175,7 +175,6 @@ func (s *SongLinkClient) doResolveRequest(payload []byte) (map[string]songLinkPl
return nil, fmt.Errorf("resolve API returned success=false")
}
// Map resolve API keys to SongLink-compatible platform keys
keyMap := map[string]string{
"Spotify": "spotify",
"Deezer": "deezer",
@@ -509,8 +508,6 @@ func extractYouTubeIDFromURL(youtubeURL string) string {
return ""
}
// isNumeric is defined in library_scan.go
func (s *SongLinkClient) GetDeezerIDFromSpotify(spotifyTrackID string) (string, error) {
availability, err := s.CheckTrackAvailability(spotifyTrackID, "")
if err != nil {
@@ -2473,7 +2473,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
_locallyCancelledItemIds.remove(id);
}
// Clean accumulator entry for non-completed items.
if (item.status != DownloadStatus.completed) {
final key = _albumRgKey(item.track);
final accumulator = _albumRgData[key];
@@ -2499,7 +2498,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
}
void clearCompleted() {
// Purge accumulator entries for failed/skipped items being removed.
final removedItems = state.items.where(
(item) =>
item.status == DownloadStatus.completed ||
@@ -2760,7 +2758,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
}
void clearFailedDownloads() {
// Purge accumulator entries for failed items before removing them.
final failedItems = state.items
.where((item) => item.status == DownloadStatus.failed)
.toList();
@@ -2883,7 +2880,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final key = _albumRgKey(track);
final accumulator = _albumRgData[key];
if (accumulator == null) return;
// Find the entry for this track and update its file path in-place.
for (final entry in accumulator.entries) {
if (entry.trackId == track.id) {
entry.filePath = finalPath;
@@ -2969,7 +2965,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
'Album ReplayGain for "$key": gain=$albumGain, peak=$albumPeak (${validEntries.length} tracks, album LUFS=${albumLufs.toStringAsFixed(1)})',
);
// Write album gain to every completed track file.
for (final entry in validEntries) {
try {
await _writeAlbumReplayGain(entry.filePath, albumGain, albumPeak);
+5 -8
View File
@@ -496,7 +496,6 @@ class _HomeTabState extends ConsumerState<HomeTab>
}
}
/// Check if live search is available (extension is set as search provider)
bool _isLiveSearchEnabled() {
final settings = ref.read(settingsProvider);
final extState = ref.read(extensionProvider);
@@ -564,7 +563,6 @@ class _HomeTabState extends ConsumerState<HomeTab>
}
}
/// Built-in search providers that are not extensions
static const _builtInSearchProviders = {'tidal', 'qobuz'};
Future<void> _performSearch(String query, {String? filterOverride}) async {
@@ -599,7 +597,6 @@ class _HomeTabState extends ConsumerState<HomeTab>
.read(trackProvider.notifier)
.customSearch(searchProvider, query, options: options);
} else if (isBuiltInProvider) {
// Use built-in Tidal or Qobuz search
await ref
.read(trackProvider.notifier)
.search(
@@ -1122,7 +1119,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
title: Text(
context.l10n.homeTitle,
style: TextStyle(
fontSize: 20 + (14 * expandRatio), // 20 -> 34
fontSize: 20 + (14 * expandRatio),
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
@@ -1496,7 +1493,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
) {
final hasGreeting = greeting != null && greeting.isNotEmpty;
final sectionOffset = hasGreeting ? 1 : 0;
final totalCount = sections.length + sectionOffset + 1; // + bottom padding
final totalCount = sections.length + sectionOffset + 1;
return [
SliverList(
@@ -2939,7 +2936,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
albumId: album.id,
albumName: album.name,
coverUrl: album.imageUrl,
tracks: const [], // Will be fetched by AlbumScreen
tracks: const [],
),
),
);
@@ -2965,7 +2962,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
builder: (context) => PlaylistScreen(
playlistName: playlist.name,
coverUrl: playlist.imageUrl,
tracks: const [], // Will be fetched
tracks: const [],
playlistId: playlist.id,
),
),
@@ -3694,7 +3691,7 @@ class _TrackItemWithStatus extends ConsumerWidget {
thickness: 1,
indent:
thumbWidth +
24, // Adjust divider indent based on thumbnail width
24,
endIndent: 12,
color: colorScheme.outlineVariant.withValues(alpha: 0.3),
),
+6 -9
View File
@@ -1132,11 +1132,11 @@ class _QueueTabState extends ConsumerState<QueueTab> {
String? _filterCacheFormat;
String? _filterCacheMetadata;
String _filterCacheSortMode = 'latest';
String? _filterSource; // null = all, 'downloaded', 'local'
String? _filterQuality; // null = all, 'hires', 'cd', 'lossy'
String? _filterFormat; // null = all, 'flac', 'mp3', 'm4a', 'opus', 'ogg'
String? _filterMetadata; // null = all, 'complete', 'missing-*'
String _sortMode = 'latest'; // 'latest', 'oldest', 'a-z', 'z-a'
String? _filterSource;
String? _filterQuality;
String? _filterFormat;
String? _filterMetadata;
String _sortMode = 'latest';
double _effectiveTextScale() {
final textScale = MediaQuery.textScalerOf(context).scale(1.0);
@@ -2036,7 +2036,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
return quality.split('/').first;
}
// Supports "MP3 320k", "Opus 256kbps", etc.
final bitrateTextMatch = RegExp(
r'(\d+)\s*k(?:bps)?',
caseSensitive: false,
@@ -2045,7 +2044,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
return '${bitrateTextMatch.group(1)}k';
}
// Supports legacy quality IDs like "opus_256" / "mp3_320".
final bitrateIdMatch = RegExp(r'_(\d+)$').firstMatch(q);
if (bitrateIdMatch != null) {
return '${bitrateIdMatch.group(1)}k';
@@ -2301,7 +2299,7 @@ class _QueueTabState extends ConsumerState<QueueTab> {
List<UnifiedLibraryItem> _applySorting(List<UnifiedLibraryItem> items) {
if (_sortMode == 'latest') {
return items; // Already sorted newest first from _getUnifiedItems
return items;
}
final sorted = List<UnifiedLibraryItem>.of(items);
switch (_sortMode) {
@@ -3364,7 +3362,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
final selectionItems = getFilterData(
historyFilterMode,
).filteredUnifiedItems;
// Only sync overlays when selection mode is active
if (_isSelectionMode || _isPlaylistSelectionMode) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_isSelectionMode) {
+1 -7
View File
@@ -164,13 +164,7 @@ class _RecentDonorsCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
const donorNames = <String>[
'McNuggets Jimmy',
'zcc09',
'micahRichie',
'a fan',
'CJBGR',
];
const donorNames = <String>['R4ND0MIZ3D', 'Isra', 'bigJr48'];
// Match SettingsGroup color logic
final cardColor = isDark
-4
View File
@@ -2210,7 +2210,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
final baseName = _buildSaveBaseName();
if (_isSafFile) {
// SAF file: save to temp, then copy to SAF tree
final tempDir = await Directory.systemTemp.createTemp('cover_');
final tempOutput =
'${tempDir.path}${Platform.pathSeparator}$baseName.jpg';
@@ -2293,7 +2292,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
}
}
} else {
// No SAF tree info, keep in temp
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
@@ -5192,7 +5190,6 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
final method = result['method'] as String?;
if (method == 'ffmpeg') {
// MP3/Opus: use FFmpeg to write metadata
// For SAF files, Kotlin returns temp_path + saf_uri
final tempPath = result['temp_path'] as String?;
final safUri = result['saf_uri'] as String?;
@@ -5280,7 +5277,6 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
} catch (_) {}
}
} catch (_) {
// No cover to preserve, continue without
}
}
-5
View File
@@ -892,7 +892,6 @@ class FFmpegService {
///
/// Returns a [ReplayGainResult] on success, or null if the scan fails.
static Future<ReplayGainResult?> scanReplayGain(String filePath) async {
// Run FFmpeg with ebur128 filter + astats for true peak.
// -nostats suppresses the interactive progress line.
// ebur128=peak=true prints integrated loudness + true peak.
// framelog=quiet suppresses per-frame measurements (very verbose),
@@ -941,7 +940,6 @@ class FFmpegService {
}
}
// ReplayGain reference level: -18 LUFS
const replayGainReferenceLufs = -18.0;
final gainDb = replayGainReferenceLufs - integratedLufs;
@@ -949,13 +947,11 @@ class FFmpegService {
// If no true peak was found, fall back to 1.0 (0 dBFS).
double peakLinear;
if (truePeakDbfs != null) {
// 10^(dBFS/20) converts dBFS to linear amplitude
peakLinear = math.pow(10, truePeakDbfs / 20.0).toDouble();
} else {
peakLinear = 1.0;
}
// Format to standard ReplayGain precision
final trackGain =
'${gainDb >= 0 ? "+" : ""}${gainDb.toStringAsFixed(2)} dB';
final trackPeak = peakLinear.toStringAsFixed(6);
@@ -1791,7 +1787,6 @@ class FFmpegService {
vorbis['LYRICS'] = value;
vorbis['UNSYNCEDLYRICS'] = value;
break;
// ReplayGain fields
case 'REPLAYGAINTRACKGAIN':
vorbis['REPLAYGAIN_TRACK_GAIN'] = value;
break;
+1 -4
View File
@@ -354,7 +354,7 @@ class PlatformBridge {
return jsonDecode(result as String) as Map<String, dynamic>;
}
/// Sets the lyrics provider order. Providers not in the list are disabled.
/// Providers not in the list are disabled.
static Future<void> setLyricsProviders(List<String> providers) async {
final providersJSON = jsonEncode(providers);
await _channel.invokeMethod('setLyricsProviders', {
@@ -362,14 +362,12 @@ class PlatformBridge {
});
}
/// Returns the current lyrics provider order.
static Future<List<String>> getLyricsProviders() async {
final result = await _channel.invokeMethod('getLyricsProviders');
final List<dynamic> decoded = jsonDecode(result as String) as List<dynamic>;
return decoded.cast<String>();
}
/// Returns metadata about all available lyrics providers.
static Future<List<Map<String, dynamic>>>
getAvailableLyricsProviders() async {
final result = await _channel.invokeMethod('getAvailableLyricsProviders');
@@ -387,7 +385,6 @@ class PlatformBridge {
});
}
/// Returns current advanced lyrics fetch options.
static Future<Map<String, dynamic>> getLyricsFetchOptions() async {
final result = await _channel.invokeMethod('getLyricsFetchOptions');
return jsonDecode(result as String) as Map<String, dynamic>;