Skip to content

Commit

Permalink
added feature to rearrange local playlist, add multiple songs to plal…
Browse files Browse the repository at this point in the history
…ist, delete multiple songs
  • Loading branch information
anandnet committed Feb 10, 2024
1 parent bfe83e4 commit 8b9f646
Show file tree
Hide file tree
Showing 14 changed files with 871 additions and 197 deletions.
4 changes: 2 additions & 2 deletions lib/services/piped_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ class PipedServices extends GetxService {
data: {"playlistId": plalistId});
}

Future<Res> addToPlaylist(String plalistId, String videoId) async {
Future<Res> addToPlaylist(String plalistId, List<String> videosId) async {
return await _sendRequest("/user/playlists/add", data: {
"playlistId": plalistId,
"videoIds": [videoId]
"videoIds": videosId
});
}

Expand Down
2 changes: 1 addition & 1 deletion lib/ui/player/mini_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class MiniPlayer extends StatelessWidget {
showDialog(
context: context,
builder: (context) =>
AddToPlaylist(currentSong),
AddToPlaylist([currentSong]),
).whenComplete(() => Get.delete<
AddToPlaylistController>());
}
Expand Down
72 changes: 72 additions & 0 deletions lib/ui/screens/Artists/artist_screen_controller.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';

import '../../widgets/add_to_playlist.dart';
import '/ui/widgets/sort_widget.dart';
import '../../../models/artist.dart';
import '../../../utils/helper.dart';
import '../Library/library_controller.dart';
Expand All @@ -21,6 +24,8 @@ class ArtistScreenController extends GetxController
final isAddedToLibrary = false.obs;
final songScrollController = ScrollController();
final videoScrollController = ScrollController();
SortWidgetController? sortWidgetController;
final additionalOperationMode = OperationMode.none.obs;
bool continuationInProgress = false;
late Artist artist_;
Map<String, List> tempListContainer = {};
Expand Down Expand Up @@ -98,6 +103,12 @@ class ArtistScreenController extends GetxController
navigationRailCurrentIndex.value = val;
final tabName = ["About", "Songs", "Videos", "Albums", "Singles"][val];

//cancel additional operations in case of tab change
if(sortWidgetController!=null){
sortWidgetController?.setActiveMode(OperationMode.none);
cancelAdditionalOperation();
}

//skip for about page
if (val == 0 || sepataredContent.containsKey(tabName)) return;
if (artistData[tabName] == null) {
Expand Down Expand Up @@ -189,6 +200,67 @@ class ArtistScreenController extends GetxController
(tempListContainer[title]!).clear();
}

//Additional operations
final additionalOperationTempList = <MediaItem>[].obs;
final additionalOperationTempMap = <int, bool>{}.obs;

void startAdditionalOperation(
SortWidgetController sortWidgetController_, OperationMode mode) {
sortWidgetController = sortWidgetController_;
final tabName = ["About", "Songs", "Videos", "Albums", "Singles"][navigationRailCurrentIndex.value];
additionalOperationTempList.value = sepataredContent[tabName]['results'].toList();
if (mode == OperationMode.addToPlaylist || mode == OperationMode.delete) {
for (int i = 0; i < additionalOperationTempList.length; i++) {
additionalOperationTempMap[i] = false;
}
}
additionalOperationMode.value = mode;
}

void checkIfAllSelected() {
sortWidgetController!.isAllSelected.value =
!additionalOperationTempMap.containsValue(false);
}

void selectAll(bool selected) {
for (int i = 0; i < additionalOperationTempList.length; i++) {
additionalOperationTempMap[i] = selected;
}
}

void performAdditionalOperation() {
final currMode = additionalOperationMode.value;
if (currMode == OperationMode.addToPlaylist) {
showDialog(
context: Get.context!,
builder: (context) => AddToPlaylist(selectedSongs()),
).whenComplete(() {
Get.delete<AddToPlaylistController>();
sortWidgetController?.setActiveMode(OperationMode.none);
cancelAdditionalOperation();
});
}
}

List<MediaItem> selectedSongs() {
return additionalOperationTempMap.entries
.map((item) {
if (item.value) {
return additionalOperationTempList[item.key];
}
})
.whereType<MediaItem>()
.toList();
}

void cancelAdditionalOperation() {
sortWidgetController!.isAllSelected.value = false;
sortWidgetController = null;
additionalOperationMode.value = OperationMode.none;
additionalOperationTempList.clear();
additionalOperationTempMap.clear();
}

@override
void onClose() {
tempListContainer.clear();
Expand Down
43 changes: 30 additions & 13 deletions lib/ui/screens/Library/library.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '/ui/widgets/modification_list.dart';
import '../../../models/playlist.dart';
import '../../widgets/piped_sync_widget.dart';
import 'library_controller.dart';
Expand Down Expand Up @@ -37,34 +38,47 @@ class SongsLibraryWidget extends StatelessWidget {
final libSongsController = Get.find<LibrarySongsController>();
return SortWidget(
tag: "LibSongSort",
itemCountTitle:
"${libSongsController.librarySongsList.length}",
itemCountTitle: "${libSongsController.librarySongsList.length}",
itemIcon: Icons.music_note_rounded,
titleLeftPadding: 9,
isDateOptionRequired: true,
isDurationOptionRequired: true,
isSearchFeatureRequired: true,
isSongDeletetioFeatureRequired: true,
onSort: (p0, p1, p2, p3) {
libSongsController.onSort(p0, p1, p2, p3);
},
onSearch: libSongsController.onSearch,
onSearchClose: libSongsController.onSearchClose,
onSearchStart: libSongsController.onSearchStart,
startAdditionalOperation:
libSongsController.startAdditionalOperation,
selectAll: libSongsController.selectAll,
performAdditionalOperation:
libSongsController.performAdditionalOperation,
cancelAdditionalOperation:
libSongsController.cancelAdditionalOperation,
);
}),
GetX<LibrarySongsController>(builder: (controller) {
return controller.librarySongsList.isNotEmpty
? ListWidget(
controller.librarySongsList,
"library Songs",
true,
isPlaylist: true,
playlist: Playlist(
title: "Library Songs",
playlistId: "SongsCache",
thumbnailUrl: "",
isCloudPlaylist: false),
)
? (controller.additionalOperationMode.value ==
OperationMode.none
? ListWidget(
controller.librarySongsList,
"library Songs",
true,
isPlaylist: true,
playlist: Playlist(
title: "Library Songs",
playlistId: "SongsCache",
thumbnailUrl: "",
isCloudPlaylist: false),
)
: ModificationList(
mode: controller.additionalOperationMode.value,
librarySongsController: controller,
))
: Expanded(
child: Center(
child: Text(
Expand Down Expand Up @@ -131,6 +145,7 @@ class PlaylistNAlbumLibraryWidget extends StatelessWidget {
() => isAlbumContent
? SortWidget(
tag: "LibAlbumSort",
isAdditionalOperationRequired: false,
isSearchFeatureRequired: true,
itemCountTitle:
"${libralbumCntrller.libraryAlbums.length} ${"items".tr}",
Expand All @@ -144,6 +159,7 @@ class PlaylistNAlbumLibraryWidget extends StatelessWidget {
)
: SortWidget(
tag: "LibPlaylistSort",
isAdditionalOperationRequired: false,
isSearchFeatureRequired: true,
itemCountTitle:
"${librplstCntrller.libraryPlaylists.length} ${"items".tr}",
Expand Down Expand Up @@ -222,6 +238,7 @@ class LibraryArtistWidget extends StatelessWidget {
Obx(
() => SortWidget(
tag: "LibArtistSort",
isAdditionalOperationRequired: false,
isSearchFeatureRequired: true,
itemCountTitle: "${cntrller.libraryArtists.length} ${"items".tr}",
onSort: (sortByName, sortByDate, sortByDuration, isAscending) {
Expand Down
101 changes: 94 additions & 7 deletions lib/ui/screens/Library/library_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';

import '../../widgets/add_to_playlist.dart';
import '/ui/widgets/sort_widget.dart';
import '../Settings/settings_screen_controller.dart';
import '/services/piped_service.dart';
import '../../../utils/helper.dart';
Expand All @@ -17,6 +19,8 @@ class LibrarySongsController extends GetxController {
late RxList<MediaItem> librarySongsList = RxList();
final isSongFetched = false.obs;
List<MediaItem> tempListContainer = [];
SortWidgetController? sortWidgetController;
final additionalOperationMode = OperationMode.none.obs;

@override
void onInit() {
Expand Down Expand Up @@ -92,15 +96,15 @@ class LibrarySongsController extends GetxController {
tempListContainer.clear();
}

Future<void> removeSong(MediaItem item, bool isDownloaded,{String? url}) async {
Future<void> removeSong(MediaItem item, bool isDownloaded,
{String? url}) async {
if (tempListContainer.isNotEmpty) {
tempListContainer.remove(item);
}
librarySongsList.remove(item);
String filePath = "";
if (isDownloaded) {
filePath =
item.extras!['url'] ?? url;
filePath = item.extras!['url'] ?? url;
} else {
final cacheDir = (await getTemporaryDirectory()).path;
filePath = "$cacheDir/cachedSongs/${item.id}.mp3";
Expand All @@ -116,6 +120,85 @@ class LibrarySongsController extends GetxController {
await thumbFile.delete();
}
}

//Additional operations
final additionalOperationTempList = [].obs;
final additionalOperationTempMap = <int, bool>{}.obs;

void startAdditionalOperation(
SortWidgetController sortWidgetController_, OperationMode mode) {
sortWidgetController = sortWidgetController_;
additionalOperationTempList.value = librarySongsList.toList();
if (mode == OperationMode.addToPlaylist || mode == OperationMode.delete) {
for (int i = 0; i < additionalOperationTempList.length; i++) {
additionalOperationTempMap[i] = false;
}
}
additionalOperationMode.value = mode;
}

void checkIfAllSelected() {
sortWidgetController!.isAllSelected.value =
!additionalOperationTempMap.containsValue(false);
}

void selectAll(bool selected) {
for (int i = 0; i < additionalOperationTempList.length; i++) {
additionalOperationTempMap[i] = selected;
}
}

void performAdditionalOperation() {
final currMode = additionalOperationMode.value;
if (currMode == OperationMode.delete) {
deleteMultipleSongs(selectedSongs()).then((value) {
sortWidgetController?.setActiveMode(OperationMode.none);
cancelAdditionalOperation();
});
} else if (currMode == OperationMode.addToPlaylist) {
showDialog(
context: Get.context!,
builder: (context) => AddToPlaylist(selectedSongs()),
).whenComplete(() {
Get.delete<AddToPlaylistController>();
sortWidgetController?.setActiveMode(OperationMode.none);
cancelAdditionalOperation();
});
}
}

Future<void> deleteMultipleSongs(List<MediaItem> songs) async {
final downloadsBox = await Hive.openBox("SongDownloads");
final cacheBox = await Hive.openBox("SongsCache");
for (MediaItem element in songs) {
if(downloadsBox.containsKey(element.id)){
await downloadsBox.delete(element.id);
removeSong(element, true);
}else{
await cacheBox.delete(element.id);
removeSong(element, false);
}
}
}

List<MediaItem> selectedSongs() {
return additionalOperationTempMap.entries
.map((item) {
if (item.value) {
return additionalOperationTempList[item.key];
}
})
.whereType<MediaItem>()
.toList();
}

void cancelAdditionalOperation() {
sortWidgetController!.isAllSelected.value = false;
sortWidgetController = null;
additionalOperationMode.value = OperationMode.none;
additionalOperationTempList.clear();
additionalOperationTempMap.clear();
}
}

class LibraryPlaylistsController extends GetxController
Expand Down Expand Up @@ -259,7 +342,7 @@ class LibraryPlaylistsController extends GetxController
}

Future<bool> createNewPlaylist(
{bool createPlaylistNaddSong = false, MediaItem? songItem}) async {
{bool createPlaylistNaddSong = false, List<MediaItem>? songItems}) async {
String title = textInputController.text;
if (title != "") {
title = "${title[0].toUpperCase()}${title.substring(1).toLowerCase()}";
Expand All @@ -272,7 +355,8 @@ class LibraryPlaylistsController extends GetxController
newplst = Playlist(
title: title,
playlistId: "${res.response['playlistId']}",
thumbnailUrl: songItem != null ? songItem.artUri.toString() : "",
thumbnailUrl:
songItems != null ? songItems[0].artUri.toString() : "",
description: "Piped Playlist",
isCloudPlaylist: true,
isPipedPlaylist: true);
Expand All @@ -296,12 +380,15 @@ class LibraryPlaylistsController extends GetxController

if (createPlaylistNaddSong && playlistCreationMode.value == "local") {
final plastbox = await Hive.openBox(newplst.playlistId);
plastbox.put(songItem?.id, MediaItemBuilder.toJson(songItem!));
for (MediaItem item in songItems!) {
plastbox.add(MediaItemBuilder.toJson(item));
}
plastbox.close();
} else if ((createPlaylistNaddSong &&
playlistCreationMode.value == "piped")) {
final songIds = songItems!.map((e) => e.id).toList();
await Get.find<PipedServices>()
.addToPlaylist(newplst.playlistId, songItem!.id);
.addToPlaylist(newplst.playlistId, songIds);
}
creationInProgress.value = false;
return true;
Expand Down
Loading

1 comment on commit 8b9f646

@anandnet
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to #153

Please sign in to comment.