Skip to content

Commit

Permalink
perf: improvements for deleting cached data
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed May 13, 2024
1 parent b51c302 commit e4c4583
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 60 deletions.
2 changes: 1 addition & 1 deletion lib/controller/backup_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class BackupController {
}
}

Indexer.inst.updateImageSizeInStorage();
Indexer.inst.calculateAllImageSizesInStorage();
Indexer.inst.updateColorPalettesSizeInStorage();
Indexer.inst.updateVideosSizeInStorage();
await _readNewFiles();
Expand Down
64 changes: 50 additions & 14 deletions lib/controller/edit_delete_controller.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:isolate';

import 'package:namida/class/track.dart';
import 'package:namida/class/video.dart';
Expand Down Expand Up @@ -28,26 +29,60 @@ class EditDeleteController {
await Indexer.inst.clearVideoCache(videosToDelete);
}

Future<void> deleteLyrics(List<Selectable> tracks) async {
await tracks.loopFuture((track, index) async {
await File("${AppDirs.LYRICS}${track.track.filename}.txt").deleteIfExists();
});
Future<void> deleteTXTLyrics(List<Selectable> tracks) async {
await _deleteAll(AppDirs.LYRICS, 'txt', tracks);
}

Future<void> deleteArtwork(List<Selectable> tracks) async {
await tracks.loopFuture((track, index) async {
final file = File(track.track.pathToImage);
await Indexer.inst.updateImageSizeInStorage(oldDeletedFile: file);
await file.deleteIfExists();
});
Future<void> deleteLRCLyrics(List<Selectable> tracks) async {
await _deleteAll(AppDirs.LYRICS, 'lrc', tracks);
}

Future<void> deleteArtwork(List<Selectable> tracks) async {
final files = tracks.map((e) => e.track.pathToImage).toList();
final details = await Isolate.run(() => _deleteAllWithDetailsIsolate(files));
Indexer.inst.updateImageSizesInStorage(removedCount: details.deletedCount, removedSize: details.sizeOfDeleted);
await deleteExtractedColor(tracks);
}

Future<void> deleteExtractedColor(List<Selectable> tracks) async {
await tracks.loopFuture((track, index) async {
await File("${AppDirs.PALETTES}${track.track.filename}.palette").deleteIfExists();
await _deleteAll(AppDirs.PALETTES, 'palette', tracks);
}

Future<void> _deleteAll(String dir, String extension, List<Selectable> tracks) async {
if (!dir.endsWith('/')) dir += '/';
final files = tracks.map((e) => "$dir${e.track.filename}.$extension").toList();
await Isolate.run(() => _deleteAllIsolate(files));
}

/// returns failed deletes.
static int _deleteAllIsolate(List<String> files) {
int failed = 0;
files.loop((e, index) {
try {
File(e).deleteSync();
} catch (_) {
failed++;
}
});
return failed;
}

/// returns size & count of deleted file.
static ({int deletedCount, int sizeOfDeleted}) _deleteAllWithDetailsIsolate(List<String> files) {
int deleted = 0;
int size = 0;
files.loop((e, index) {
int s = 0;
try {
s = File(e).lengthSync();
} catch (_) {}
try {
File(e).deleteSync();
deleted++;
size += s;
} catch (_) {}
});
return (deletedCount: deleted, sizeOfDeleted: size);
}

/// returns save directory path if saved successfully
Expand Down Expand Up @@ -161,7 +196,8 @@ extension HasCachedFiles on List<Selectable> {
// we use [pathToImage] to ensure when [settings.groupArtworksByAlbum] is enabled
bool get hasArtworkCached => _doesAnyPathExist(AppDirs.ARTWORKS, 'png', fullPath: (tr) => tr.track.pathToImage);

bool get hasLyricsCached => _doesAnyPathExist(AppDirs.LYRICS, 'txt');
bool get hasTXTLyricsCached => _doesAnyPathExist(AppDirs.LYRICS, 'txt');
bool get hasLRCLyricsCached => _doesAnyPathExist(AppDirs.LYRICS, 'lrc');
bool get hasColorCached => _doesAnyPathExist(AppDirs.PALETTES, 'palette');
bool get hasVideoCached {
for (int i = 0; i < length; i++) {
Expand All @@ -173,7 +209,7 @@ extension HasCachedFiles on List<Selectable> {
return false;
}

bool get hasAnythingCached => hasArtworkCached || hasLyricsCached /* || hasColorCached */;
bool get hasAnythingCached => hasArtworkCached || hasTXTLyricsCached || hasLRCLyricsCached /* || hasColorCached */;

bool _doesAnyPathExist(String directory, String extension, {String Function(Selectable tr)? fullPath}) {
for (int i = 0; i < length; i++) {
Expand Down
56 changes: 15 additions & 41 deletions lib/controller/indexer_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1204,22 +1204,12 @@ class Indexer {
return tracks;
}

Future<void> updateImageSizeInStorage({String? newImagePath, File? oldDeletedFile}) async {
if (newImagePath != null || oldDeletedFile != null) {
if (oldDeletedFile != null) {
if (oldDeletedFile.existsSync()) {
artworksInStorage.value--;
artworksSizeInStorage.value -= oldDeletedFile.fileSizeSync() ?? 0;
}
}
if (newImagePath != null) {
artworksInStorage.value++;
artworksSizeInStorage.value += File(newImagePath).fileSizeSync() ?? 0;
}

return;
}
void updateImageSizesInStorage({required int removedCount, required int removedSize}) {
artworksInStorage.value -= removedCount;
artworksSizeInStorage.value -= removedSize;
}

Future<void> calculateAllImageSizesInStorage() async {
final stats = await updateImageSizeInStorageIsolate.thready({
"dirPath": AppDirs.ARTWORKS,
"token": RootIsolateToken.instance,
Expand All @@ -1230,37 +1220,21 @@ class Indexer {

static (int, int) updateImageSizeInStorageIsolate(Map p) {
final dirPath = p["dirPath"] as String;
final newImagePath = p["newImagePath"] as String?;
final oldDeletedFile = p["oldDeletedFile"] as File?;
final token = p["token"] as RootIsolateToken;
BackgroundIsolateBinaryMessenger.ensureInitialized(token);

int initialCount = p["initialCount"] as int? ?? 0;
int initialSize = p["initialSize"] as int? ?? 0;

if (newImagePath != null || oldDeletedFile != null) {
if (oldDeletedFile != null) {
if (oldDeletedFile.existsSync()) {
initialCount--;
initialSize -= oldDeletedFile.fileSizeSync() ?? 0;
}
}
if (newImagePath != null) {
initialCount++;
initialSize += File(newImagePath).fileSizeSync() ?? 0;
}
} else {
final dir = Directory(dirPath);
int totalCount = 0;
int totalSize = 0;
final dir = Directory(dirPath);

for (final f in dir.listSyncSafe()) {
if (f is File) {
initialCount++;
initialSize += f.fileSizeSync() ?? 0;
}
}
for (final f in dir.listSyncSafe()) {
try {
totalSize += (f as File).lengthSync();
totalCount++;
} catch (_) {}
}

return (initialCount, initialSize);
return (totalCount, totalSize);
}

Future<void> updateColorPalettesSizeInStorage({String? newPalettePath}) async {
Expand Down Expand Up @@ -1295,7 +1269,7 @@ class Indexer {
await Directory(AppDirs.ARTWORKS).delete(recursive: true);
await Directory(AppDirs.ARTWORKS).create();
await _createDefaultNamidaArtwork();
updateImageSizeInStorage();
calculateAllImageSizesInStorage();
}

/// Deletes specific videos or the whole cache.
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ void mainInitialization() async {
NamidaChannel.inst.setCanEnterPip(settings.enablePip.value);

/// updates values on startup
Indexer.inst.updateImageSizeInStorage();
Indexer.inst.calculateAllImageSizesInStorage();
Indexer.inst.updateColorPalettesSizeInStorage();
Indexer.inst.updateVideosSizeInStorage();
if (!shouldShowOnBoarding && settings.refreshOnStartup.value) {
Expand Down
11 changes: 9 additions & 2 deletions lib/ui/dialogs/track_clear_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import 'package:namida/ui/widgets/custom_widgets.dart';
void showTrackClearDialog(List<Selectable> tracksPre, Color colorScheme) {
final tracks = tracksPre.uniqued((element) => element.track);
final isSingle = tracks.length == 1;

final hasLRCLyricsCached = tracks.hasLRCLyricsCached;
final hasTXTLyricsCached = tracks.hasTXTLyricsCached;
NamidaNavigator.inst.navigateDialog(
dialogBuilder: (theme) => CustomBlurryDialog(
theme: theme,
Expand All @@ -29,13 +32,17 @@ void showTrackClearDialog(List<Selectable> tracksPre, Color colorScheme) {
NamidaNavigator.inst.closeDialog();
},
),
if (tracks.hasLyricsCached)
if (hasLRCLyricsCached || hasTXTLyricsCached)
CustomListTile(
passedColor: colorScheme,
title: lang.LYRICS,
icon: Broken.document,
onTap: () async {
await EditDeleteController.inst.deleteLyrics(tracks);
if (hasLRCLyricsCached) {
await EditDeleteController.inst.deleteLRCLyrics(tracks);
} else if (hasTXTLyricsCached) {
await EditDeleteController.inst.deleteTXTLyrics(tracks);
}
NamidaNavigator.inst.closeDialog();
},
),
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: namida
description: A Beautiful and Feature-rich Music Player, With YouTube & Video Support Built in Flutter
publish_to: "none"
version: 2.2.5-beta+240512204
version: 2.2.6-beta+240513130

environment:
sdk: ">=3.1.4 <4.0.0"
Expand Down

0 comments on commit e4c4583

Please sign in to comment.