From 5165f11460de5453a456c5c10d7e1e7076328496 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 17 Dec 2022 17:31:19 -0600 Subject: [PATCH] Add:Create playlist from a collection #1226 --- client/components/ui/ContextMenuDropdown.vue | 55 ++++++++++++++++++++ client/pages/collection/_id.vue | 54 ++++++++++++++++--- server/controllers/CollectionController.js | 2 +- server/controllers/PlaylistController.js | 35 ++++++++++++- server/objects/Collection.js | 6 +-- server/routers/ApiRouter.js | 3 +- 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 client/components/ui/ContextMenuDropdown.vue diff --git a/client/components/ui/ContextMenuDropdown.vue b/client/components/ui/ContextMenuDropdown.vue new file mode 100644 index 00000000..988f7469 --- /dev/null +++ b/client/components/ui/ContextMenuDropdown.vue @@ -0,0 +1,55 @@ + + + \ No newline at end of file diff --git a/client/pages/collection/_id.vue b/client/pages/collection/_id.vue index 70b446bd..0aa313ea 100644 --- a/client/pages/collection/_id.vue +++ b/client/pages/collection/_id.vue @@ -19,9 +19,11 @@ {{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }} - + - +
@@ -32,7 +34,7 @@
-
+
@@ -64,7 +66,7 @@ export default { }, data() { return { - processingRemove: false + processing: false } }, computed: { @@ -102,15 +104,55 @@ export default { }, userCanDelete() { return this.$store.getters['user/getUserCanDelete'] + }, + contextMenuItems() { + const items = [ + { + text: 'Create playlist from collection', + action: 'create-playlist' + } + ] + if (this.userCanDelete) { + items.push({ + text: 'Delete collection', + action: 'delete' + }) + } + return items } }, methods: { + contextMenuAction(action) { + if (action === 'delete') { + this.removeClick() + } else if (action === 'create-playlist') { + this.createPlaylistFromCollection() + } + }, + createPlaylistFromCollection() { + this.processing = true + this.$axios + .$post(`/api/playlists/collection/${this.collectionId}`) + .then((playlist) => { + if (playlist) { + this.$toast.success('Playlist created') + this.$router.push(`/playlist/${playlist.id}`) + } + }) + .catch((error) => { + const errMsg = error.response ? error.response.data || '' : '' + this.$toast.error(errMsg || 'Failed to create playlist') + }) + .finally(() => { + this.processing = false + }) + }, editClick() { this.$store.commit('globals/setEditCollection', this.collection) }, removeClick() { if (confirm(this.$getString('MessageConfirmRemoveCollection', [this.collectionName]))) { - this.processingRemove = true + this.processing = true this.$axios .$delete(`/api/collections/${this.collection.id}`) .then(() => { @@ -121,7 +163,7 @@ export default { this.$toast.error(this.$strings.ToastCollectionRemoveFailed) }) .finally(() => { - this.processingRemove = false + this.processing = false }) } }, diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index a00ae867..f4161f37 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -123,7 +123,7 @@ class CollectionController { middleware(req, res, next) { if (req.params.id) { - var collection = this.db.collections.find(c => c.id === req.params.id) + const collection = this.db.collections.find(c => c.id === req.params.id) if (!collection) { return res.status(404).send('Collection not found') } diff --git a/server/controllers/PlaylistController.js b/server/controllers/PlaylistController.js index ecabb686..e923cf82 100644 --- a/server/controllers/PlaylistController.js +++ b/server/controllers/PlaylistController.js @@ -174,9 +174,42 @@ class PlaylistController { res.json(jsonExpanded) } + // POST: api/playlists/collection/:collectionId + async createFromCollection(req, res) { + let collection = this.db.collections.find(c => c.id === req.params.collectionId) + if (!collection) { + return res.status(404).send('Collection not found') + } + // Expand collection to get library items + collection = collection.toJSONExpanded(this.db.libraryItems) + + // Filter out library items not accessible to user + const libraryItems = collection.books.filter(item => req.user.checkCanAccessLibraryItem(item)) + + if (!libraryItems.length) { + return res.status(400).send('Collection has no books accessible to user') + } + + const newPlaylist = new Playlist() + + const newPlaylistData = { + userId: req.user.id, + libraryId: collection.libraryId, + name: collection.name, + description: collection.description || null, + items: libraryItems.map(li => ({ libraryItemId: li.id })) + } + newPlaylist.setData(newPlaylistData) + + const jsonExpanded = newPlaylist.toJSONExpanded(this.db.libraryItems) + await this.db.insertEntity('playlist', newPlaylist) + SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded) + res.json(jsonExpanded) + } + middleware(req, res, next) { if (req.params.id) { - var playlist = this.db.playlists.find(p => p.id === req.params.id) + const playlist = this.db.playlists.find(p => p.id === req.params.id) if (!playlist) { return res.status(404).send('Playlist not found') } diff --git a/server/objects/Collection.js b/server/objects/Collection.js index cab8348d..813ced21 100644 --- a/server/objects/Collection.js +++ b/server/objects/Collection.js @@ -38,10 +38,10 @@ class Collection { } toJSONExpanded(libraryItems, minifiedBooks = false) { - var json = this.toJSON() + const json = this.toJSON() json.books = json.books.map(bookId => { - var _ab = libraryItems.find(li => li.id === bookId) - return _ab ? minifiedBooks ? _ab.toJSONMinified() : _ab.toJSONExpanded() : null + const book = libraryItems.find(li => li.id === bookId) + return book ? minifiedBooks ? book.toJSONMinified() : book.toJSONExpanded() : null }).filter(b => !!b) return json } diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index b219763a..88129244 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -143,7 +143,7 @@ class ApiRouter { // // Playlist Routes // - this.router.post('/playlists', PlaylistController.middleware.bind(this), PlaylistController.create.bind(this)) + this.router.post('/playlists', PlaylistController.create.bind(this)) this.router.get('/playlists', PlaylistController.findAllForUser.bind(this)) this.router.get('/playlists/:id', PlaylistController.middleware.bind(this), PlaylistController.findOne.bind(this)) this.router.patch('/playlists/:id', PlaylistController.middleware.bind(this), PlaylistController.update.bind(this)) @@ -152,6 +152,7 @@ class ApiRouter { this.router.delete('/playlists/:id/item/:libraryItemId/:episodeId?', PlaylistController.middleware.bind(this), PlaylistController.removeItem.bind(this)) this.router.post('/playlists/:id/batch/add', PlaylistController.middleware.bind(this), PlaylistController.addBatch.bind(this)) this.router.post('/playlists/:id/batch/remove', PlaylistController.middleware.bind(this), PlaylistController.removeBatch.bind(this)) + this.router.post('/playlists/collection/:collectionId', PlaylistController.createFromCollection.bind(this)) // // Current User Routes (Me)