feat: add local library scanning with duplicate detection

- Add Go backend library scanner for FLAC, M4A, MP3, Opus, OGG files
- Read metadata from file tags (ISRC, track name, artist, album, bit depth, sample rate)
- Fallback to filename parsing when tags unavailable
- Add SQLite database for O(1) duplicate lookups
- Show 'In Library' badge on search results for existing tracks
- Match by ISRC (exact) or track name + artist (fuzzy)
- Add Library Settings page with scan, cleanup, and clear actions
- Add 30+ localization strings for library feature
This commit is contained in:
zarzet
2026-02-03 19:24:28 +07:00
parent 3d6a3f8d04
commit 26d464d3c7
29 changed files with 4377 additions and 16 deletions
+18 -1
View File
@@ -42,8 +42,13 @@ class AppSettings {
final String cloudProvider; // 'none', 'webdav', 'sftp', 'gdrive'
final String cloudServerUrl; // WebDAV/SFTP server URL
final String cloudUsername; // Server username
final String cloudPassword; // Server password (encrypted)
final String cloudPassword; // Server password (stored securely)
final String cloudRemotePath; // Remote folder path (e.g. /Music/SpotiFLAC)
// Local Library Settings
final bool localLibraryEnabled; // Enable local library scanning
final String localLibraryPath; // Path to scan for audio files
final bool localLibraryShowDuplicates; // Show indicator when searching for existing tracks
const AppSettings({
this.defaultService = 'tidal',
@@ -85,6 +90,10 @@ class AppSettings {
this.cloudUsername = '',
this.cloudPassword = '',
this.cloudRemotePath = '/Music/SpotiFLAC',
// Local Library defaults
this.localLibraryEnabled = false,
this.localLibraryPath = '',
this.localLibraryShowDuplicates = true,
});
AppSettings copyWith({
@@ -128,6 +137,10 @@ class AppSettings {
String? cloudUsername,
String? cloudPassword,
String? cloudRemotePath,
// Local Library
bool? localLibraryEnabled,
String? localLibraryPath,
bool? localLibraryShowDuplicates,
}) {
return AppSettings(
defaultService: defaultService ?? this.defaultService,
@@ -169,6 +182,10 @@ class AppSettings {
cloudUsername: cloudUsername ?? this.cloudUsername,
cloudPassword: cloudPassword ?? this.cloudPassword,
cloudRemotePath: cloudRemotePath ?? this.cloudRemotePath,
// Local Library
localLibraryEnabled: localLibraryEnabled ?? this.localLibraryEnabled,
localLibraryPath: localLibraryPath ?? this.localLibraryPath,
localLibraryShowDuplicates: localLibraryShowDuplicates ?? this.localLibraryShowDuplicates,
);
}
+7
View File
@@ -48,6 +48,10 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
cloudUsername: json['cloudUsername'] as String? ?? '',
cloudPassword: json['cloudPassword'] as String? ?? '',
cloudRemotePath: json['cloudRemotePath'] as String? ?? '/Music/SpotiFLAC',
localLibraryEnabled: json['localLibraryEnabled'] as bool? ?? false,
localLibraryPath: json['localLibraryPath'] as String? ?? '',
localLibraryShowDuplicates:
json['localLibraryShowDuplicates'] as bool? ?? true,
);
Map<String, dynamic> _$AppSettingsToJson(AppSettings instance) =>
@@ -90,4 +94,7 @@ Map<String, dynamic> _$AppSettingsToJson(AppSettings instance) =>
'cloudUsername': instance.cloudUsername,
'cloudPassword': instance.cloudPassword,
'cloudRemotePath': instance.cloudRemotePath,
'localLibraryEnabled': instance.localLibraryEnabled,
'localLibraryPath': instance.localLibraryPath,
'localLibraryShowDuplicates': instance.localLibraryShowDuplicates,
};