mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			250 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const Logger = require('../Logger')
 | 
						|
const SocketAuthority = require('../SocketAuthority')
 | 
						|
const Database = require('../Database')
 | 
						|
 | 
						|
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(Database.libraryItems)
 | 
						|
    await Database.createPlaylist(newPlaylist)
 | 
						|
    SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
 | 
						|
    res.json(jsonExpanded)
 | 
						|
  }
 | 
						|
 | 
						|
  // GET: api/playlists
 | 
						|
  findAllForUser(req, res) {
 | 
						|
    res.json({
 | 
						|
      playlists: Database.playlists.filter(p => p.userId === req.user.id).map(p => p.toJSONExpanded(Database.libraryItems))
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  // GET: api/playlists/:id
 | 
						|
  findOne(req, res) {
 | 
						|
    res.json(req.playlist.toJSONExpanded(Database.libraryItems))
 | 
						|
  }
 | 
						|
 | 
						|
  // PATCH: api/playlists/:id
 | 
						|
  async update(req, res) {
 | 
						|
    const playlist = req.playlist
 | 
						|
    let wasUpdated = playlist.update(req.body)
 | 
						|
    const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
 | 
						|
    if (wasUpdated) {
 | 
						|
      await Database.updatePlaylist(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(Database.libraryItems)
 | 
						|
    await Database.removePlaylist(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 = Database.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 playlistMediaItem = {
 | 
						|
      playlistId: playlist.id,
 | 
						|
      mediaItemId: itemToAdd.episodeId || libraryItem.media.id,
 | 
						|
      mediaItemType: itemToAdd.episodeId ? 'podcastEpisode' : 'book',
 | 
						|
      order: playlist.items.length
 | 
						|
    }
 | 
						|
 | 
						|
    const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
 | 
						|
    await Database.createPlaylistMediaItem(playlistMediaItem)
 | 
						|
    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(Database.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 Database.removePlaylist(playlist.id)
 | 
						|
      SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
 | 
						|
    } else {
 | 
						|
      await Database.updatePlaylist(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
 | 
						|
 | 
						|
    let order = playlist.items.length
 | 
						|
    const playlistMediaItems = []
 | 
						|
    for (const item of itemsToAdd) {
 | 
						|
      if (!item.libraryItemId) {
 | 
						|
        return res.status(400).send('Item does not have libraryItemId')
 | 
						|
      }
 | 
						|
 | 
						|
      const libraryItem = Database.getLibraryItem(item.libraryItemId)
 | 
						|
      if (!libraryItem) {
 | 
						|
        return res.status(400).send('Item not found with id ' + item.libraryItemId)
 | 
						|
      }
 | 
						|
 | 
						|
      if (!playlist.containsItem(item)) {
 | 
						|
        playlistMediaItems.push({
 | 
						|
          playlistId: playlist.id,
 | 
						|
          mediaItemId: item.episodeId || libraryItem.media.id, // podcastEpisodeId or bookId
 | 
						|
          mediaItemType: item.episodeId ? 'podcastEpisode' : 'book',
 | 
						|
          order: order++
 | 
						|
        })
 | 
						|
        playlist.addItem(item.libraryItemId, item.episodeId)
 | 
						|
        hasUpdated = true
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
 | 
						|
    if (hasUpdated) {
 | 
						|
      await Database.createBulkPlaylistMediaItems(playlistMediaItems)
 | 
						|
      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(Database.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 Database.removePlaylist(playlist.id)
 | 
						|
        SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
 | 
						|
      } else {
 | 
						|
        await Database.updatePlaylist(playlist)
 | 
						|
        SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
 | 
						|
      }
 | 
						|
    }
 | 
						|
    res.json(jsonExpanded)
 | 
						|
  }
 | 
						|
 | 
						|
  // POST: api/playlists/collection/:collectionId
 | 
						|
  async createFromCollection(req, res) {
 | 
						|
    let collection = Database.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(Database.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(Database.libraryItems)
 | 
						|
    await Database.createPlaylist(newPlaylist)
 | 
						|
    SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
 | 
						|
    res.json(jsonExpanded)
 | 
						|
  }
 | 
						|
 | 
						|
  middleware(req, res, next) {
 | 
						|
    if (req.params.id) {
 | 
						|
      const playlist = Database.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() |