Skip to content

Commit

Permalink
feat: clear audio/video cache for playing yt item
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Feb 13, 2024
1 parent 3aa0956 commit a6e767e
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 24 deletions.
28 changes: 10 additions & 18 deletions lib/controller/indexer_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1427,21 +1427,7 @@ class Indexer {
await _updateDirectoryStats(AppDirs.PALETTES, colorPalettesInStorage, null);
}

Future<void> updateVideosSizeInStorage({String? newVideoPath, File? oldDeletedFile}) async {
if (newVideoPath != null || oldDeletedFile != null) {
if (oldDeletedFile != null) {
if (await oldDeletedFile.exists()) {
videosInStorage.value--;
videosInStorage.value -= await oldDeletedFile.length();
}
}
if (newVideoPath != null) {
videosInStorage.value++;
videosInStorage.value += await File(newVideoPath).length();
}

return;
}
Future<void> updateVideosSizeInStorage() async {
await _updateDirectoryStats(AppDirs.VIDEOS_CACHE, videosInStorage, videosSizeInStorage);
}

Expand Down Expand Up @@ -1471,13 +1457,19 @@ class Indexer {
/// Deletes specific videos or the whole cache.
Future<void> clearVideoCache([List<NamidaVideo>? videosToDelete]) async {
if (videosToDelete != null) {
await videosToDelete.loopFuture((v, index) async => await File(v.path).delete());
for (final v in videosToDelete) {
final deleted = await File(v.path).tryDeleting();
if (deleted) {
videosInStorage.value--;
videosSizeInStorage.value -= v.sizeInBytes;
}
}
} else {
await Directory(AppDirs.VIDEOS_CACHE).delete(recursive: true);
await Directory(AppDirs.VIDEOS_CACHE).create();
videosInStorage.value = 0;
videosSizeInStorage.value = 0;
}

updateVideosSizeInStorage();
}

Future<void> _createDefaultNamidaArtwork() async {
Expand Down
2 changes: 1 addition & 1 deletion lib/controller/queue_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ class QueueController {
}

Future<void> _deleteQueueFromStorage(Queue queue) async {
await File('${AppDirs.QUEUES}${queue.date}.json').delete();
await File('${AppDirs.QUEUES}${queue.date}.json').tryDeleting();
}

/// Used to add Queues that were rejected by [addNewQueue] after full loading of queues.
Expand Down
11 changes: 11 additions & 0 deletions lib/controller/video_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,17 @@ class VideoController {
return videos;
}

void removeNVFromCacheMap(String youtubeId, String path) {
_videoCacheIDMap[youtubeId]?.removeWhere((element) {
if (element.path == path) {
Indexer.inst.videosInStorage.value--;
Indexer.inst.videosSizeInStorage.value -= element.sizeInBytes;
return true;
}
return false;
});
}

Future<NamidaVideo?> updateCurrentVideo(Track track, {bool returnEarly = false}) async {
isNoVideosAvailable.value = false;
currentDownloadedBytes.value = null;
Expand Down
4 changes: 2 additions & 2 deletions lib/packages/miniplayer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ class _NamidaMiniPlayerState extends State<NamidaMiniPlayer> {
bgColor: isCurrent ? CurrentColor.inst.miniplayerColor.withAlpha(20) : null,
icon: Broken.video,
title: [
"${element.height}p${element.framerateText()}",
"${element.resolution}p${element.framerateText()}",
localOrCache,
].join(' • '),
subtitle: [
Expand Down Expand Up @@ -800,7 +800,7 @@ class _NamidaMiniPlayerState extends State<NamidaMiniPlayer> {
final currentVideo = VideoController.inst.currentVideo.value;
final downloadedBytes = VideoController.inst.currentDownloadedBytes.value;
final videoTotalSize = currentVideo?.sizeInBytes ?? 0;
final videoQuality = currentVideo?.height ?? 0;
final videoQuality = currentVideo?.resolution ?? 0;
final videoFramerate = currentVideo?.framerateText(30);
final markText = VideoController.inst.isNoVideosAvailable.value ? 'x' : '?';
final fallbackQualityLabel = currentVideo?.nameInCache?.split('_').last;
Expand Down
5 changes: 4 additions & 1 deletion lib/ui/widgets/custom_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,8 @@ class SmallListTile extends StatelessWidget {
final double? titleGap;
final double borderRadius;
final Widget? leading;
final VisualDensity? visualDensity;

const SmallListTile({
super.key,
required this.title,
Expand All @@ -694,6 +696,7 @@ class SmallListTile extends StatelessWidget {
this.titleGap,
this.borderRadius = 0.0,
this.leading,
this.visualDensity,
});

@override
Expand All @@ -716,7 +719,7 @@ class SmallListTile extends StatelessWidget {
size: 18.0,
),
),
visualDensity: compact ? const VisualDensity(horizontal: -2.0, vertical: -2.0) : const VisualDensity(horizontal: -1.0, vertical: -1.0),
visualDensity: visualDensity ?? (compact ? const VisualDensity(horizontal: -2.0, vertical: -2.0) : const VisualDensity(horizontal: -1.0, vertical: -1.0)),
title: Text(
title,
style: context.textTheme.displayMedium?.copyWith(
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/widgets/settings/advanced_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ class AdvancedSettings extends SettingSubpageProvider {
style: context.textTheme.displayMedium,
),
Text(
"${video.height}p • ${video.framerate}fps - ${video.sizeInBytes.fileSizeFormatted}",
"${video.resolution}p • ${video.framerate}fps - ${video.sizeInBytes.fileSizeFormatted}",
style: context.textTheme.displaySmall,
),
],
Expand Down
4 changes: 3 additions & 1 deletion lib/ui/widgets/video_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -982,14 +982,16 @@ class NamidaVideoControlsState extends State<NamidaVideoControls> with TickerPro
),
),
...ytQualities.map((element) {
final sizeInBytes = element.sizeInBytes;
return Obx(
() {
final isSelected = element.height == Player.inst.currentVideoStream?.height;
final id = Player.inst.nowPlayingVideoID?.id;
final cachedFile = id == null ? null : element.getCachedFile(id);

return _getQualityChip(
title: element.resolution ?? '',
subtitle: " • ${element.sizeInBytes?.fileSizeFormatted ?? ''}",
subtitle: sizeInBytes == null ? '' : " • ${sizeInBytes.fileSizeFormatted}",
onPlay: (isSelected) {
if (!isSelected) {
Player.inst.onItemPlayYoutubeIDSetQuality(
Expand Down
9 changes: 9 additions & 0 deletions lib/youtube/youtube_miniplayer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,15 @@ class YoutubeMiniPlayer extends StatelessWidget {
);
items.add(repeatForWidget);
}
items.add(
NamidaPopupItem(
icon: Broken.trash,
title: lang.CLEAR,
onTap: () {
YTUtils().showVideoClearDialog(context, videoId, CurrentColor.inst.miniplayerColor);
},
),
);
return items;
},
child: const Icon(
Expand Down
197 changes: 197 additions & 0 deletions lib/youtube/yt_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:newpipeextractor_dart/newpipeextractor_dart.dart';
import 'package:playlist_manager/module/playlist_id.dart';
import 'package:share_plus/share_plus.dart';

import 'package:namida/class/video.dart';
import 'package:namida/controller/ffmpeg_controller.dart';
import 'package:namida/controller/miniplayer_controller.dart';
import 'package:namida/controller/navigator_controller.dart';
Expand Down Expand Up @@ -377,4 +378,200 @@ class YTUtils {
);
}
}

void showVideoClearDialog(BuildContext context, String videoId, Color colorScheme) {
final videosCached = VideoController.inst.getNVFromID(videoId);
final audiosCached = Player.inst.audioCacheMap[videoId]?.where((element) => element.file.existsSync()).toList() ?? [];

final fileSizeLookup = <String, int>{};
final fileTypeLookup = <String, int>{};

int videosSize = 0;
int audiosSize = 0;

audiosCached.loop((e, _) {
final s = e.file.sizeInBytesSync();
audiosSize += s;
fileSizeLookup[e.file.path] = s;
fileTypeLookup[e.file.path] = 0;
});
videosCached.loop((e, _) {
final s = e.sizeInBytes;
videosSize += s;
fileSizeLookup[e.path] = s;
fileTypeLookup[e.path] = 1;
});

final pathsToDelete = <String, bool>{}.obs;
final allSelected = false.obs;
final totalSizeToDelete = 0.obs;

Future<void> deleteItems(Iterable<String> paths) async {
for (final path in paths) {
await File(path).tryDeleting();

final type = fileTypeLookup[path];
if (type == 1) {
VideoController.inst.removeNVFromCacheMap(videoId, path);
} else if (type == 0) {
Player.inst.audioCacheMap[videoId]?.removeWhere((element) => element.file.path == path);
}
}
}

Widget getExpansionTileWidget<T>({
required String title,
required String subtitle,
required IconData icon,
required List<T> items,
required ({String title, String subtitle, String path}) Function(T item) itemBuilder,
required int Function(T item) itemSize,
}) {
return NamidaExpansionTile(
initiallyExpanded: true,
titleText: title,
subtitleText: subtitle,
icon: icon,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
DecoratedBox(
decoration: BoxDecoration(
color: context.theme.cardColor,
borderRadius: BorderRadius.circular(6.0.multipliedRadius),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 3.0),
child: Text("${items.length}"),
),
),
const SizedBox(width: 6.0),
const Icon(Broken.arrow_down_2, size: 20.0),
],
),
childrenPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
children: items.map(
(item) {
final data = itemBuilder(item);
return SmallListTile(
borderRadius: 12.0,
color: context.theme.cardColor,
visualDensity: const VisualDensity(horizontal: -3.0, vertical: -3.0),
title: data.title,
subtitle: data.subtitle,
active: false,
onTap: () {
final wasTrue = pathsToDelete[data.path] == true;
final willEnable = !wasTrue;
pathsToDelete[data.path] = willEnable;
if (willEnable) {
totalSizeToDelete.value += itemSize(item);
} else {
totalSizeToDelete.value -= itemSize(item);
}
},
trailing: Obx(
() => NamidaCheckMark(
size: 16.0,
active: allSelected.value || pathsToDelete[data.path] == true,
),
),
);
},
).toList(),
);
}

NamidaNavigator.inst.navigateDialog(
onDisposing: () {
pathsToDelete.close();
allSelected.close();
totalSizeToDelete.close();
},
dialogBuilder: (theme) => CustomBlurryDialog(
theme: theme,
normalTitleStyle: true,
icon: Broken.trash,
title: lang.CLEAR,
trailingWidgets: [
Obx(
() => Checkbox.adaptive(
splashRadius: 28.0,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.0.multipliedRadius),
),
value: allSelected.value,
onChanged: (value) {
allSelected.value = !allSelected.value;
if (allSelected.value) {
totalSizeToDelete.value = audiosSize + videosSize;
} else {
int newVal = 0;
for (final k in pathsToDelete.keys) {
if (pathsToDelete[k] == true) newVal += fileSizeLookup[k] ?? 0;
}
totalSizeToDelete.value = newVal;
}
},
),
),
],
actions: [
const CancelButton(),
Obx(
() => NamidaButton(
enabled: pathsToDelete.values.any((element) => element) || allSelected.value,
text: "${lang.DELETE} (${totalSizeToDelete.value.fileSizeFormatted})",
onPressed: () async {
if (allSelected.value) {
await Future.wait([
deleteItems(videosCached.map((e) => e.path)),
deleteItems(audiosCached.map((e) => e.file.path)),
]);
} else {
await deleteItems(pathsToDelete.keys.where((element) => pathsToDelete[element] == true));
}
NamidaNavigator.inst.closeDialog();
},
),
),
],
child: Column(
children: [
getExpansionTileWidget(
title: lang.VIDEO_CACHE,
subtitle: videosSize.fileSizeFormatted,
icon: Broken.video,
items: videosCached,
itemSize: (item) => item.sizeInBytes,
itemBuilder: (v) {
return (
title: "${v.resolution}p • ${v.framerate}fps ",
subtitle: v.sizeInBytes.fileSizeFormatted,
path: v.path,
);
},
),
getExpansionTileWidget(
title: lang.AUDIO_CACHE,
subtitle: audiosSize.fileSizeFormatted,
icon: Broken.musicnote,
items: audiosCached,
itemSize: (item) => fileSizeLookup[item.file.path] ?? item.file.sizeInBytesSync(),
itemBuilder: (a) {
final bitrateText = a.bitrate == null ? null : "${a.bitrate! ~/ 1000}kb/s";
final langText = a.langaugeName == null ? '' : " • ${a.langaugeName}";
return (
title: "${bitrateText ?? lang.AUDIO}$langText",
subtitle: a.file.fileSizeFormatted() ?? '',
path: a.file.path,
);
},
),
],
),
),
);
}
}

0 comments on commit a6e767e

Please sign in to comment.