refactor: clean up redundant comments and code

This commit is contained in:
zarzet
2026-02-04 10:05:32 +07:00
parent 2dc4cef583
commit 24897e25e2
16 changed files with 43 additions and 232 deletions
@@ -1034,14 +1034,12 @@ void removeItem(String id) {
}
try {
// Get base download directory
String baseDir = state.outputDir;
if (baseDir.isEmpty) {
final dir = await getApplicationDocumentsDirectory();
baseDir = dir.path;
}
// Create failed_downloads subfolder
final failedDownloadsDir = '$baseDir/failed_downloads';
final failedDir = Directory(failedDownloadsDir);
if (!await failedDir.exists()) {
@@ -1057,11 +1055,9 @@ void removeItem(String id) {
final file = File(filePath);
final bool fileExists = await file.exists();
// Build content for new entries
final buffer = StringBuffer();
if (!fileExists) {
// New file - add header
buffer.writeln('# SpotiFLAC Failed Downloads');
buffer.writeln('# Date: $dateStr');
buffer.writeln('#');
@@ -1069,7 +1065,6 @@ void removeItem(String id) {
buffer.writeln('');
}
// Add timestamp for this batch
final timeStr = '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}';
for (final item in failedItems) {
@@ -1081,7 +1076,6 @@ void removeItem(String id) {
buffer.writeln('[$timeStr] ${track.name} - ${track.artistName} | $spotifyUrl | $error');
}
// Append or create file
if (fileExists) {
await file.writeAsString(buffer.toString(), mode: FileMode.append);
_log.i('Appended ${failedItems.length} failed downloads to: $filePath');
@@ -1553,7 +1547,6 @@ void removeItem(String id) {
_log.d('Opus Metadata map content: $metadata');
// Handle lyrics based on lyricsMode setting
final lyricsMode = settings.lyricsMode;
final shouldEmbed = lyricsMode == 'embed' || lyricsMode == 'both';
final shouldSaveExternal = lyricsMode == 'external' || lyricsMode == 'both';
@@ -1571,13 +1564,11 @@ void removeItem(String id) {
);
if (lrcContent.isNotEmpty) {
// Embed lyrics in file metadata if mode is 'embed' or 'both'
if (shouldEmbed) {
metadata['LYRICS'] = lrcContent;
_log.d('Lyrics fetched for Opus embedding (${lrcContent.length} chars)');
}
// Save external LRC file if mode is 'external' or 'both'
if (shouldSaveExternal) {
try {
final lrcPath = opusPath.replaceAll(RegExp(r'\.opus$', caseSensitive: false), '.lrc');
@@ -2137,7 +2128,6 @@ result = await PlatformBridge.downloadWithExtensions(
progress: 0.95,
);
// Convert M4A to the selected format
final format = tidalHighFormat.startsWith('opus') ? 'opus' : 'mp3';
final convertedPath = await FFmpegService.convertM4aToLossy(
filePath,
@@ -2154,7 +2144,6 @@ result = await PlatformBridge.downloadWithExtensions(
actualQuality = '${format.toUpperCase()} $bitrateDisplay';
_log.i('Successfully converted M4A to $format: $convertedPath');
// Embed metadata
_log.i('Embedding metadata to $format...');
updateItemStatus(
item.id,
@@ -2333,13 +2322,11 @@ result = await PlatformBridge.downloadWithExtensions(
_completedInSession++;
// Check if this track is already in download history
final historyNotifier = ref.read(downloadHistoryProvider.notifier);
final existingInHistory = historyNotifier.getBySpotifyId(trackToDownload.id) ??
(trackToDownload.isrc != null ? historyNotifier.getByIsrc(trackToDownload.isrc!) : null);
if (wasExisting && existingInHistory != null) {
// File exists and is already in download history - skip adding
_log.i('Track already in library, skipping history update');
await _notificationService.showDownloadComplete(
trackName: item.track.name,
-10
View File
@@ -10,7 +10,6 @@ final _log = AppLogger('LocalLibrary');
const _lastScannedAtKey = 'local_library_last_scanned_at';
/// State for local library
class LocalLibraryState {
final List<LocalLibraryItem> items;
final bool isScanning;
@@ -92,7 +91,6 @@ class LocalLibraryState {
}
}
/// Provider for local library state management
class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
final LibraryDatabase _db = LibraryDatabase.instance;
Timer? _progressTimer;
@@ -120,7 +118,6 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
.map((e) => LocalLibraryItem.fromJson(e))
.toList();
// Load lastScannedAt from SharedPreferences
DateTime? lastScannedAt;
try {
final prefs = await SharedPreferences.getInstance();
@@ -161,7 +158,6 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
scanErrorCount: 0,
);
// Set cover cache directory before scanning
try {
final cacheDir = await getApplicationCacheDirectory();
final coverCacheDir = '${cacheDir.path}/library_covers';
@@ -171,23 +167,19 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
_log.w('Failed to set cover cache directory: $e');
}
// Start progress polling
_startProgressPolling();
try {
final results = await PlatformBridge.scanLibraryFolder(folderPath);
// Convert results to LocalLibraryItem and save to database
final items = <LocalLibraryItem>[];
for (final json in results) {
final item = LocalLibraryItem.fromJson(json);
items.add(item);
}
// Batch insert into database
await _db.upsertBatch(items.map((e) => e.toJson()).toList());
// Save lastScannedAt to SharedPreferences
final now = DateTime.now();
try {
final prefs = await SharedPreferences.getInstance();
@@ -197,7 +189,6 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
_log.w('Failed to save lastScannedAt: $e');
}
// Update state
state = state.copyWith(
items: items,
isScanning: false,
@@ -262,7 +253,6 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
Future<void> clearLibrary() async {
await _db.clearAll();
// Clear lastScannedAt from SharedPreferences
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_lastScannedAtKey);
-1
View File
@@ -292,7 +292,6 @@ void setUseAllFilesAccess(bool enabled) {
_saveSettings();
}
// Local Library Settings
void setLocalLibraryEnabled(bool enabled) {
state = state.copyWith(localLibraryEnabled: enabled);
_saveSettings();
-1
View File
@@ -695,7 +695,6 @@ child: ListTile(
void _handleTap(BuildContext context, WidgetRef ref, {required bool isQueued, required bool isInHistory, required bool isInLocalLibrary}) async {
if (isQueued) return;
// Check if track already exists in local library
if (isInLocalLibrary) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(context.l10n.snackbarAlreadyInLibrary(track.name))));
-20
View File
@@ -103,7 +103,6 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
bool _showTitleInAppBar = false;
final ScrollController _scrollController = ScrollController();
// Selection mode state
bool _isSelectionMode = false;
final Set<String> _selectedAlbumIds = {};
bool _isFetchingDiscography = false;
@@ -112,7 +111,6 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
void initState() {
super.initState();
// Setup scroll listener for sticky title
_scrollController.addListener(_onScroll);
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -322,11 +320,9 @@ return PopScope(
if (compilations.isNotEmpty)
SliverToBoxAdapter(child: _buildAlbumSection(context.l10n.artistCompilations, compilations, colorScheme)),
],
// Add padding at bottom for selection bar
SliverToBoxAdapter(child: SizedBox(height: _isSelectionMode ? 120 : 32)),
],
),
// Selection action bar
if (_isSelectionMode)
_buildSelectionBar(context, colorScheme, albums),
],
@@ -404,14 +400,12 @@ return PopScope(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
// Close button
IconButton(
onPressed: _exitSelectionMode,
icon: const Icon(Icons.close),
tooltip: context.l10n.dialogCancel,
),
const SizedBox(width: 8),
// Selection info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -433,13 +427,11 @@ return PopScope(
],
),
),
// Select all / Deselect button
TextButton(
onPressed: allSelected ? _deselectAll : () => _selectAll(allAlbums),
child: Text(allSelected ? context.l10n.actionDeselect : context.l10n.actionSelectAll),
),
const SizedBox(width: 8),
// Download button
FilledButton.icon(
onPressed: selectedCount > 0 ? () => _downloadSelectedAlbums(context, selectedAlbums) : null,
icon: const Icon(Icons.download, size: 18),
@@ -473,7 +465,6 @@ return PopScope(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Handle bar
Container(
width: 40,
height: 4,
@@ -483,7 +474,6 @@ return PopScope(
borderRadius: BorderRadius.circular(2),
),
),
// Title
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
child: Row(
@@ -577,7 +567,6 @@ return PopScope(
setState(() => _isFetchingDiscography = true);
// Show progress dialog
if (!mounted) {
setState(() => _isFetchingDiscography = false);
return;
@@ -599,7 +588,6 @@ return PopScope(
int fetchedCount = 0;
int failedCount = 0;
// Fetch tracks from each album
for (final album in albums) {
if (!_isFetchingDiscography) break; // Cancelled
@@ -620,12 +608,10 @@ return PopScope(
setState(() => _isFetchingDiscography = false);
// Close progress dialog
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
}
// Show warning if some albums failed
if (failedCount > 0 && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.discographyFailedToFetch)),
@@ -668,14 +654,12 @@ return PopScope(
return;
}
// Add to queue
ref.read(downloadQueueProvider.notifier).addMultipleToQueue(
tracksToQueue,
service,
qualityOverride: qualityOverride,
);
// Show success message
if (mounted) {
final message = skippedCount > 0
? context.l10n.discographySkippedDownloaded(tracksToQueue.length, skippedCount)
@@ -698,14 +682,12 @@ return PopScope(
Future<List<Track>> _fetchAlbumTracks(ArtistAlbum album) async {
if (album.providerId != null && album.providerId!.isNotEmpty) {
// Extension album
final result = await PlatformBridge.getAlbumWithExtension(album.providerId!, album.id);
if (result != null && result['tracks'] != null) {
final tracksList = result['tracks'] as List<dynamic>;
return tracksList.map((t) => _parseTrack(t as Map<String, dynamic>)).toList();
}
} else if (album.id.startsWith('deezer:')) {
// Deezer album
final deezerId = album.id.replaceFirst('deezer:', '');
final metadata = await PlatformBridge.getDeezerMetadata('album', deezerId);
if (metadata['tracks'] != null) {
@@ -713,7 +695,6 @@ return PopScope(
return tracksList.map((t) => _parseTrackFromDeezer(t as Map<String, dynamic>, album)).toList();
}
} else {
// Spotify album
final url = 'https://open.spotify.com/album/${album.id}';
final result = await PlatformBridge.handleURLWithExtension(url);
if (result != null && result['tracks'] != null) {
@@ -1068,7 +1049,6 @@ if (hasValidImage)
void _handlePopularTrackTap(Track track, {required bool isQueued, required bool isInHistory, required bool isInLocalLibrary}) async {
if (isQueued) return;
// Check if track already exists in local library
if (isInLocalLibrary) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
-2
View File
@@ -513,7 +513,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
Extension? currentSearchExtension;
List<SearchFilter> searchFilters = [];
// Check if using extension search provider
final isUsingExtensionSearch = currentSearchProvider != null &&
currentSearchProvider.isNotEmpty &&
extState.extensions.any((e) => e.id == currentSearchProvider && e.enabled);
@@ -2567,7 +2566,6 @@ class _TrackItemWithStatus extends ConsumerWidget {
void _handleTap(BuildContext context, WidgetRef ref, {required bool isQueued, required bool isInHistory, required bool isInLocalLibrary}) async {
if (isQueued) return;
// Check if track already exists in local library
if (isInLocalLibrary) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
-1
View File
@@ -502,7 +502,6 @@ leading: track.coverUrl != null
void _handleTap(BuildContext context, WidgetRef ref, {required bool isQueued, required bool isInHistory, required bool isInLocalLibrary}) async {
if (isQueued) return;
// Check if track already exists in local library
if (isInLocalLibrary) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(context.l10n.snackbarAlreadyInLibrary(track.name))));
+1 -3
View File
@@ -28,13 +28,12 @@ class UnifiedLibraryItem {
final String artistName;
final String albumName;
final String? coverUrl;
final String? localCoverPath; // For local library items with extracted cover
final String? localCoverPath;
final String filePath;
final String? quality;
final DateTime addedAt;
final LibraryItemSource source;
// Original items for navigation
final DownloadHistoryItem? historyItem;
final LocalLibraryItem? localItem;
@@ -258,7 +257,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
'Dec',
];
// Search functionality
final TextEditingController _searchController = TextEditingController();
final FocusNode _searchFocusNode = FocusNode();
String _searchQuery = '';
@@ -117,7 +117,6 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
return;
}
// Check if folder exists
if (!await Directory(libraryPath).exists()) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
-1
View File
@@ -139,7 +139,6 @@ class HistoryDatabase {
final prefs = await _prefs;
final lastContainer = prefs.getString('ios_last_container_path');
// Skip if container hasn't changed
if (lastContainer == _currentContainerPath) {
_log.d('iOS container path unchanged, skipping migration');
return false;
+2 -24
View File
@@ -6,7 +6,6 @@ import 'package:spotiflac_android/utils/logger.dart';
final _log = AppLogger('LibraryDatabase');
/// Represents a track in the user's local music library
class LocalLibraryItem {
final String id;
final String trackName;
@@ -14,7 +13,7 @@ class LocalLibraryItem {
final String albumName;
final String? albumArtist;
final String filePath;
final String? coverPath; // Path to extracted cover art
final String? coverPath;
final DateTime scannedAt;
final String? isrc;
final int? trackNumber;
@@ -92,7 +91,6 @@ class LocalLibraryItem {
String get albumKey => '${albumName.toLowerCase()}|${(albumArtist ?? artistName).toLowerCase()}';
}
/// SQLite database service for local library
class LibraryDatabase {
static final LibraryDatabase instance = LibraryDatabase._init();
static Database? _database;
@@ -144,7 +142,6 @@ class LibraryDatabase {
)
''');
// Indexes for fast lookups
await db.execute('CREATE INDEX idx_library_isrc ON library(isrc)');
await db.execute('CREATE INDEX idx_library_track_artist ON library(track_name, artist_name)');
await db.execute('CREATE INDEX idx_library_album ON library(album_name, album_artist)');
@@ -163,7 +160,6 @@ class LibraryDatabase {
}
}
/// Convert JSON format (camelCase) to DB row (snake_case)
Map<String, dynamic> _jsonToDbRow(Map<String, dynamic> json) {
return {
'id': json['id'],
@@ -186,7 +182,6 @@ class LibraryDatabase {
};
}
/// Convert DB row (snake_case) to JSON format (camelCase)
Map<String, dynamic> _dbRowToJson(Map<String, dynamic> row) {
return {
'id': row['id'],
@@ -209,9 +204,8 @@ class LibraryDatabase {
};
}
// ==================== CRUD Operations ====================
// CRUD Operations
/// Insert or update a library item
Future<void> upsert(Map<String, dynamic> json) async {
final db = await database;
await db.insert(
@@ -221,7 +215,6 @@ class LibraryDatabase {
);
}
/// Batch insert multiple items
Future<void> upsertBatch(List<Map<String, dynamic>> items) async {
final db = await database;
final batch = db.batch();
@@ -238,7 +231,6 @@ class LibraryDatabase {
_log.i('Batch inserted ${items.length} items');
}
/// Get all library items ordered by album/artist
Future<List<Map<String, dynamic>>> getAll({int? limit, int? offset}) async {
final db = await database;
final rows = await db.query(
@@ -250,7 +242,6 @@ class LibraryDatabase {
return rows.map(_dbRowToJson).toList();
}
/// Get item by ID
Future<Map<String, dynamic>?> getById(String id) async {
final db = await database;
final rows = await db.query(
@@ -263,7 +254,6 @@ class LibraryDatabase {
return _dbRowToJson(rows.first);
}
/// Get item by ISRC - O(1) with index
Future<Map<String, dynamic>?> getByIsrc(String isrc) async {
final db = await database;
final rows = await db.query(
@@ -276,7 +266,6 @@ class LibraryDatabase {
return _dbRowToJson(rows.first);
}
/// Check if ISRC exists - O(1) with index
Future<bool> existsByIsrc(String isrc) async {
final db = await database;
final result = await db.rawQuery(
@@ -286,7 +275,6 @@ class LibraryDatabase {
return result.isNotEmpty;
}
/// Find by track name and artist (fuzzy match)
Future<List<Map<String, dynamic>>> findByTrackAndArtist(
String trackName,
String artistName,
@@ -300,7 +288,6 @@ class LibraryDatabase {
return rows.map(_dbRowToJson).toList();
}
/// Check if track exists by name and artist
Future<Map<String, dynamic>?> findExisting({
String? isrc,
String? trackName,
@@ -321,7 +308,6 @@ class LibraryDatabase {
return null;
}
/// Get all ISRCs as Set for fast in-memory lookup
Future<Set<String>> getAllIsrcs() async {
final db = await database;
final rows = await db.rawQuery(
@@ -330,7 +316,6 @@ class LibraryDatabase {
return rows.map((r) => r['isrc'] as String).toSet();
}
/// Get all track keys (name|artist) for matching
Future<Set<String>> getAllTrackKeys() async {
final db = await database;
final rows = await db.rawQuery(
@@ -339,19 +324,16 @@ class LibraryDatabase {
return rows.map((r) => r['match_key'] as String).toSet();
}
/// Delete by file path
Future<void> deleteByPath(String filePath) async {
final db = await database;
await db.delete('library', where: 'file_path = ?', whereArgs: [filePath]);
}
/// Delete by ID
Future<void> delete(String id) async {
final db = await database;
await db.delete('library', where: 'id = ?', whereArgs: [id]);
}
/// Delete items where file no longer exists
Future<int> cleanupMissingFiles() async {
final db = await database;
final rows = await db.query('library', columns: ['id', 'file_path']);
@@ -371,21 +353,18 @@ class LibraryDatabase {
return removed;
}
/// Clear all library data
Future<void> clearAll() async {
final db = await database;
await db.delete('library');
_log.i('Cleared all library data');
}
/// Get total count
Future<int> getCount() async {
final db = await database;
final result = await db.rawQuery('SELECT COUNT(*) as count FROM library');
return Sqflite.firstIntValue(result) ?? 0;
}
/// Search library by query
Future<List<Map<String, dynamic>>> search(String query, {int limit = 50}) async {
final db = await database;
final searchQuery = '%${query.toLowerCase()}%';
@@ -399,7 +378,6 @@ class LibraryDatabase {
return rows.map(_dbRowToJson).toList();
}
/// Close database
Future<void> close() async {
final db = await database;
await db.close();