mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			226 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const Logger = require('../Logger')
 | |
| const SocketAuthority = require('../SocketAuthority')
 | |
| 
 | |
| const Playlist = require('../objects/Playlist')
 | |
| 
 | |
| class PlaylistController {
 | |
|   constructor() { }
 | |
| 
 | |
|   // POST: api/playlists
 | |
|   async create(req, res) {
 | |
|     const newPlaylist = new Playlist()
 | |
|     req.body.userId = req.user.id
 | |
|     const success = newPlaylist.setData(req.body)
 | |
|     if (!success) {
 | |
|       return res.status(400).send('Invalid playlist request data')
 | |
|     }
 | |
|     const jsonExpanded = newPlaylist.toJSONExpanded(this.db.libraryItems)
 | |
|     await this.db.insertEntity('playlist', newPlaylist)
 | |
|     SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
 | |
|     res.json(jsonExpanded)
 | |
|   }
 | |
| 
 | |
|   // GET: api/playlists
 | |
|   findAllForUser(req, res) {
 | |
|     res.json({
 | |
|       playlists: this.db.playlists.filter(p => p.userId === req.user.id).map(p => p.toJSONExpanded(this.db.libraryItems))
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   // GET: api/playlists/:id
 | |
|   findOne(req, res) {
 | |
|     res.json(req.playlist.toJSONExpanded(this.db.libraryItems))
 | |
|   }
 | |
| 
 | |
|   // PATCH: api/playlists/:id
 | |
|   async update(req, res) {
 | |
|     const playlist = req.playlist
 | |
|     let wasUpdated = playlist.update(req.body)
 | |
|     const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
 | |
|     if (wasUpdated) {
 | |
|       await this.db.updateEntity('playlist', playlist)
 | |
|       SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
 | |
|     }
 | |
|     res.json(jsonExpanded)
 | |
|   }
 | |
| 
 | |
|   // DELETE: api/playlists/:id
 | |
|   async delete(req, res) {
 | |
|     const playlist = req.playlist
 | |
|     const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
 | |
|     await this.db.removeEntity('playlist', playlist.id)
 | |
|     SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
 | |
|     res.sendStatus(200)
 | |
|   }
 | |
| 
 | |
|   // POST: api/playlists/:id/item
 | |
|   async addItem(req, res) {
 | |
|     const playlist = req.playlist
 | |
|     const itemToAdd = req.body
 | |
| 
 | |
|     if (!itemToAdd.libraryItemId) {
 | |
|       return res.status(400).send('Request body has no libraryItemId')
 | |
|     }
 | |
| 
 | |
|     const libraryItem = this.db.libraryItems.find(li => li.id === itemToAdd.libraryItemId)
 | |
|     if (!libraryItem) {
 | |
|       return res.status(400).send('Library item not found')
 | |
|     }
 | |
|     if (libraryItem.libraryId !== playlist.libraryId) {
 | |
|       return res.status(400).send('Library item in different library')
 | |
|     }
 | |
|     if (playlist.containsItem(itemToAdd)) {
 | |
|       return res.status(400).send('Item already in playlist')
 | |
|     }
 | |
|     if ((itemToAdd.episodeId && !libraryItem.isPodcast) || (libraryItem.isPodcast && !itemToAdd.episodeId)) {
 | |
|       return res.status(400).send('Invalid item to add for this library type')
 | |
|     }
 | |
|     if (itemToAdd.episodeId && !libraryItem.media.checkHasEpisode(itemToAdd.episodeId)) {
 | |
|       return res.status(400).send('Episode not found in library item')
 | |
|     }
 | |
| 
 | |
|     playlist.addItem(itemToAdd.libraryItemId, itemToAdd.episodeId)
 | |
|     const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
 | |
|     await this.db.updateEntity('playlist', playlist)
 | |
|     SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
 | |
|     res.json(jsonExpanded)
 | |
|   }
 | |
| 
 | |
|   // DELETE: api/playlists/:id/item/:libraryItemId/:episodeId?
 | |
|   async removeItem(req, res) {
 | |
|     const playlist = req.playlist
 | |
|     const itemToRemove = {
 | |
|       libraryItemId: req.params.libraryItemId,
 | |
|       episodeId: req.params.episodeId || null
 | |
|     }
 | |
|     if (!playlist.containsItem(itemToRemove)) {
 | |
|       return res.sendStatus(404)
 | |
|     }
 | |
| 
 | |
|     playlist.removeItem(itemToRemove.libraryItemId, itemToRemove.episodeId)
 | |
| 
 | |
|     const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
 | |
| 
 | |
|     // Playlist is removed when there are no items
 | |
|     if (!playlist.items.length) {
 | |
|       Logger.info(`[PlaylistController] Playlist "${playlist.name}" has no more items - removing it`)
 | |
|       await this.db.removeEntity('playlist', playlist.id)
 | |
|       SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
 | |
|     } else {
 | |
|       await this.db.updateEntity('playlist', playlist)
 | |
|       SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
 | |
|     }
 | |
| 
 | |
|     res.json(jsonExpanded)
 | |
|   }
 | |
| 
 | |
|   // POST: api/playlists/:id/batch/add
 | |
|   async addBatch(req, res) {
 | |
|     const playlist = req.playlist
 | |
|     if (!req.body.items || !req.body.items.length) {
 | |
|       return res.status(500).send('Invalid request body')
 | |
|     }
 | |
|     const itemsToAdd = req.body.items
 | |
|     let hasUpdated = false
 | |
|     for (const item of itemsToAdd) {
 | |
|       if (!item.libraryItemId) {
 | |
|         return res.status(400).send('Item does not have libraryItemId')
 | |
|       }
 | |
| 
 | |
|       if (!playlist.containsItem(item)) {
 | |
|         playlist.addItem(item.libraryItemId, item.episodeId)
 | |
|         hasUpdated = true
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
 | |
|     if (hasUpdated) {
 | |
|       await this.db.updateEntity('playlist', playlist)
 | |
|       SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
 | |
|     }
 | |
|     res.json(jsonExpanded)
 | |
|   }
 | |
| 
 | |
|   // POST: api/playlists/:id/batch/remove
 | |
|   async removeBatch(req, res) {
 | |
|     const playlist = req.playlist
 | |
|     if (!req.body.items || !req.body.items.length) {
 | |
|       return res.status(500).send('Invalid request body')
 | |
|     }
 | |
|     const itemsToRemove = req.body.items
 | |
|     let hasUpdated = false
 | |
|     for (const item of itemsToRemove) {
 | |
|       if (!item.libraryItemId) {
 | |
|         return res.status(400).send('Item does not have libraryItemId')
 | |
|       }
 | |
|       if (playlist.containsItem(item)) {
 | |
|         playlist.removeItem(item.libraryItemId, item.episodeId)
 | |
|         hasUpdated = true
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
 | |
|     if (hasUpdated) {
 | |
|       // Playlist is removed when there are no items
 | |
|       if (!playlist.items.length) {
 | |
|         Logger.info(`[PlaylistController] Playlist "${playlist.name}" has no more items - removing it`)
 | |
|         await this.db.removeEntity('playlist', playlist.id)
 | |
|         SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
 | |
|       } else {
 | |
|         await this.db.updateEntity('playlist', playlist)
 | |
|         SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
 | |
|       }
 | |
|     }
 | |
|     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) {
 | |
|       const playlist = this.db.playlists.find(p => p.id === req.params.id)
 | |
|       if (!playlist) {
 | |
|         return res.status(404).send('Playlist not found')
 | |
|       }
 | |
|       if (playlist.userId !== req.user.id) {
 | |
|         Logger.warn(`[PlaylistController] Playlist ${req.params.id} requested by user ${req.user.id} that is not the owner`)
 | |
|         return res.sendStatus(403)
 | |
|       }
 | |
|       req.playlist = playlist
 | |
|     }
 | |
| 
 | |
|     next()
 | |
|   }
 | |
| }
 | |
| module.exports = new PlaylistController() |