diff --git a/lib/youtube/controller/youtube_controller.dart b/lib/youtube/controller/youtube_controller.dart index 468fd842..83f5a889 100644 --- a/lib/youtube/controller/youtube_controller.dart +++ b/lib/youtube/controller/youtube_controller.dart @@ -114,6 +114,8 @@ class YoutubeController { /// {groupName: {filename: YoutubeItemDownloadConfig}} final youtubeDownloadTasksMap = >{}.obs; + final youtubeDownloadTasksTempList = <(String, YoutubeItemDownloadConfig)>[]; + /// Used to keep track of existing downloaded files, more performant than real-time checking. /// /// {groupName: {filename: File}} @@ -703,6 +705,7 @@ class YoutubeController { _downloadClientsMap[groupName]?[c.filename]?.close(force: true); _downloadClientsMap[groupName]?.remove(c.filename); youtubeDownloadTasksMap[groupName]?.remove(c.filename); + print('KKKKKKKK ${File("$directory/${c.filename}").existsSync()}'); await File("$directory/${c.filename}").deleteIfExists(); downloadedFilesMap[groupName]?[c.filename] = null; }); diff --git a/lib/youtube/pages/youtube_home_view.dart b/lib/youtube/pages/youtube_home_view.dart index 3d1385f7..9690cf91 100644 --- a/lib/youtube/pages/youtube_home_view.dart +++ b/lib/youtube/pages/youtube_home_view.dart @@ -4,6 +4,7 @@ import 'package:namida/core/dimensions.dart'; import 'package:namida/core/translations/language.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/youtube/pages/youtube_page.dart'; +import 'package:namida/youtube/pages/yt_downloads_page.dart'; import 'package:namida/youtube/youtube_playlists_view.dart'; class YouTubeHomeView extends StatelessWidget { @@ -11,7 +12,7 @@ class YouTubeHomeView extends StatelessWidget { @override Widget build(BuildContext context) { - final tabs = [lang.HOME, lang.PLAYLISTS]; + final tabs = [lang.HOME, lang.PLAYLISTS, lang.DOWNLOADS]; const historyIndex = 1; return BackgroundWrapper( child: NamidaTabView( @@ -21,6 +22,7 @@ class YouTubeHomeView extends StatelessWidget { children: const [ YoutubePage(), YoutubePlaylistsView(bottomPadding: kBottomPadding, scrollable: false), + YTDownloadsPage(), ], ), ); diff --git a/lib/youtube/pages/yt_downloads_page.dart b/lib/youtube/pages/yt_downloads_page.dart index c76044ab..d11e5f03 100644 --- a/lib/youtube/pages/yt_downloads_page.dart +++ b/lib/youtube/pages/yt_downloads_page.dart @@ -2,86 +2,246 @@ import 'package:flutter/material.dart'; import 'package:flutter_scrollbar_modified/flutter_scrollbar_modified.dart'; import 'package:get/get.dart'; +import 'package:namida/controller/current_color.dart'; import 'package:namida/core/dimensions.dart'; +import 'package:namida/core/extensions.dart'; import 'package:namida/core/icon_fonts/broken_icons.dart'; +import 'package:namida/core/translations/language.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/youtube/controller/youtube_controller.dart'; import 'package:namida/youtube/widgets/yt_download_task_item_card.dart'; +final _isOnGoingSelected = Rxn(); + class YTDownloadsPage extends StatelessWidget { const YTDownloadsPage({super.key}); + Widget _getFilterChip({ + required BuildContext context, + required String title, + required IconData icon, + required void Function() onTap, + required bool? isOnGoing, + }) { + return Obx( + () { + final enabled = isOnGoing == _isOnGoingSelected.value; + final color = enabled ? Colors.white.withOpacity(0.7) : null; + return NamidaInkWell( + bgColor: enabled ? CurrentColor.inst.color : context.theme.cardColor, + borderRadius: 6.0, + margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + animationDurationMS: 300, + onTap: onTap, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 18.0, color: color), + const SizedBox(width: 4.0), + Text( + title, + style: context.textTheme.displayMedium?.copyWith(color: color), + ), + ], + ), + ); + }, + ); + } + + void _updateTempList(bool? forIsGoing) { + final itemsList = YoutubeController.inst.youtubeDownloadTasksTempList; + itemsList.clear(); + if (forIsGoing == null) return; + + // -- separate same functions bcz dont wanna check in each loop. + // -- reverseLoop to insert newer first. + if (forIsGoing) { + YoutubeController.inst.youtubeDownloadTasksMap.keys.toList().reverseLoop((key, index) { + final smallList = YoutubeController.inst.youtubeDownloadTasksMap[key]?.values.toList(); + smallList?.reverseLoop((v, index) { + final match = YoutubeController.inst.downloadedFilesMap[key]?[v.filename] == null; + if (match) itemsList.add((key, v)); + }); + }); + } else { + YoutubeController.inst.youtubeDownloadTasksMap.keys.toList().reverseLoop((key, index) { + final smallList = YoutubeController.inst.youtubeDownloadTasksMap[key]?.values.toList(); + smallList?.reverseLoop((v, index) { + final match = YoutubeController.inst.downloadedFilesMap[key]?[v.filename] != null; + if (match) itemsList.add((key, v)); + }); + }); + } + } + @override Widget build(BuildContext context) { return BackgroundWrapper( - child: Obx(() { - final keys = YoutubeController.inst.youtubeDownloadTasksMap.keys.toList(); - return CupertinoScrollbar( - child: CustomScrollView( - slivers: [ - SliverList.builder( - itemCount: keys.length, - itemBuilder: (context, index) { - final groupName = keys[index]; - final list = YoutubeController.inst.youtubeDownloadTasksMap[groupName]?.values.toList() ?? []; - return NamidaExpansionTile( - initiallyExpanded: true, - titleText: groupName, - trailing: Row( - mainAxisSize: MainAxisSize.min, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(4.0), + child: Wrap( + children: [ + _getFilterChip( + context: context, + title: lang.ALL, + icon: Broken.task, + onTap: () { + _updateTempList(null); + _isOnGoingSelected.value = null; + }, + isOnGoing: null, + ), + _getFilterChip( + context: context, + title: lang.ONGOING, + icon: Broken.import, + onTap: () { + _updateTempList(true); + _isOnGoingSelected.value = true; + }, + isOnGoing: true, + ), + _getFilterChip( + context: context, + title: lang.FINISHED, + icon: Broken.tick_circle, + onTap: () { + _updateTempList(false); + _isOnGoingSelected.value = false; + }, + isOnGoing: false, + ), + ], + ), + ), + Obx( + () => _isOnGoingSelected.value != null + ? Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Row( children: [ + const SizedBox(width: 24.0), + Text( + YoutubeController.inst.youtubeDownloadTasksTempList.length.displayVideoKeyword, + style: context.textTheme.displayMedium?.copyWith(fontSize: 20.0.multipliedFontScale), + ), + const Spacer(), IconButton.filledTonal( padding: EdgeInsets.zero, - visualDensity: VisualDensity.compact, onPressed: () { - YoutubeController.inst.resumeDownloadTasks(groupName: groupName); + YoutubeController.inst.youtubeDownloadTasksTempList.loop((e, index) { + YoutubeController.inst.resumeDownloadTasks(groupName: e.$1); + }); }, - icon: const Icon(Broken.play, size: 18.0), + icon: const Icon(Broken.play, size: 20.0), ), IconButton.filledTonal( padding: EdgeInsets.zero, - visualDensity: VisualDensity.compact, onPressed: () { - YoutubeController.inst.pauseDownloadTask( - itemsConfig: [], - groupName: groupName, - allInGroupName: true, - ); + YoutubeController.inst.youtubeDownloadTasksTempList.loop((e, index) { + YoutubeController.inst.pauseDownloadTask( + itemsConfig: [], + groupName: e.$1, + allInGroupName: true, + ); + }); }, - icon: const Icon(Broken.pause, size: 18.0), + icon: const Icon(Broken.pause, size: 20.0), ), - const SizedBox(width: 4.0), - const Icon( - Broken.arrow_down_2, - size: 20.0, - ), - const SizedBox(width: 4.0), + const SizedBox(width: 12.0), ], ), - leading: NamidaInkWell( - borderRadius: 8.0, - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - bgColor: context.theme.cardColor, - child: Text( - "${list.length}", - style: context.textTheme.displayLarge, - ), - ), - children: list - .asMap() - .keys - .map( - (key) => YTDownloadTaskItemCard(videos: list, index: key, groupName: groupName), - ) - .toList(), - ); - }, - ), - const SliverPadding(padding: EdgeInsets.only(bottom: kBottomPadding)), - ], + ) + : const SizedBox(), ), - ); - }), + Expanded( + child: Obx(() { + final keys = YoutubeController.inst.youtubeDownloadTasksMap.keys.toList(); + return CupertinoScrollbar( + child: CustomScrollView( + slivers: [ + _isOnGoingSelected.value == null + ? SliverList.builder( + itemCount: keys.length, + itemBuilder: (context, index) { + final groupName = keys[index]; + final list = YoutubeController.inst.youtubeDownloadTasksMap[groupName]?.values.toList() ?? []; + return NamidaExpansionTile( + initiallyExpanded: true, + titleText: groupName, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton.filledTonal( + padding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + onPressed: () { + YoutubeController.inst.resumeDownloadTasks(groupName: groupName); + }, + icon: const Icon(Broken.play, size: 18.0), + ), + IconButton.filledTonal( + padding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + onPressed: () { + YoutubeController.inst.pauseDownloadTask( + itemsConfig: [], + groupName: groupName, + allInGroupName: true, + ); + }, + icon: const Icon(Broken.pause, size: 18.0), + ), + const SizedBox(width: 4.0), + const Icon( + Broken.arrow_down_2, + size: 20.0, + ), + const SizedBox(width: 4.0), + ], + ), + leading: NamidaInkWell( + borderRadius: 8.0, + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + bgColor: context.theme.cardColor, + child: Text( + "${list.length}", + style: context.textTheme.displayLarge, + ), + ), + children: list + .asMap() + .keys + .map( + (key) => YTDownloadTaskItemCard(videos: list, index: key, groupName: groupName), + ) + .toList(), + ); + }, + ) + : SliverList.builder( + itemCount: YoutubeController.inst.youtubeDownloadTasksTempList.length, + itemBuilder: (context, index) { + final groupNameAndItem = YoutubeController.inst.youtubeDownloadTasksTempList[index]; + return YTDownloadTaskItemCard( + videos: YoutubeController.inst.youtubeDownloadTasksTempList.map((e) => e.$2).toList(), + index: index, + groupName: groupNameAndItem.$1, + ); + }, + ), + const SliverPadding(padding: EdgeInsets.only(bottom: kBottomPadding)), + ], + ), + ); + }), + ), + ], + ), ); } } diff --git a/lib/youtube/youtube_playlists_view.dart b/lib/youtube/youtube_playlists_view.dart index 78c32f06..49810c70 100644 --- a/lib/youtube/youtube_playlists_view.dart +++ b/lib/youtube/youtube_playlists_view.dart @@ -214,7 +214,7 @@ class YoutubePlaylistsView extends StatelessWidget { () { YoutubePlaylistController.inst.favouritesPlaylist.value; return _getHorizontalSliverList( - title: lang.FAVOURITES, + title: lang.LIKED, icon: Broken.like_1, viewAllPage: const YTLikedVideosPage(), videos: getFavouriteVideos,