mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-07 16:44:16 +01:00
Update playlist API endpoints to load library items from DB
This commit is contained in:
parent
6056c14926
commit
fc44c801f2
@ -254,83 +254,11 @@ class Database {
|
||||
await this.models.library.removeById(libraryId)
|
||||
}
|
||||
|
||||
async createCollection(oldCollection) {
|
||||
if (!this.sequelize) return false
|
||||
const newCollection = await this.models.collection.createFromOld(oldCollection)
|
||||
// Create CollectionBooks
|
||||
if (newCollection) {
|
||||
const collectionBooks = []
|
||||
oldCollection.books.forEach((libraryItemId) => {
|
||||
const libraryItem = this.libraryItems.find(li => li.id === libraryItemId)
|
||||
if (libraryItem) {
|
||||
collectionBooks.push({
|
||||
collectionId: newCollection.id,
|
||||
bookId: libraryItem.media.id
|
||||
})
|
||||
}
|
||||
})
|
||||
if (collectionBooks.length) {
|
||||
await this.createBulkCollectionBooks(collectionBooks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createBulkCollectionBooks(collectionBooks) {
|
||||
if (!this.sequelize) return false
|
||||
return this.models.collectionBook.bulkCreate(collectionBooks)
|
||||
}
|
||||
|
||||
async createPlaylist(oldPlaylist) {
|
||||
if (!this.sequelize) return false
|
||||
const newPlaylist = await this.models.playlist.createFromOld(oldPlaylist)
|
||||
if (newPlaylist) {
|
||||
const playlistMediaItems = []
|
||||
let order = 1
|
||||
for (const mediaItemObj of oldPlaylist.items) {
|
||||
const libraryItem = this.libraryItems.find(li => li.id === mediaItemObj.libraryItemId)
|
||||
if (!libraryItem) continue
|
||||
|
||||
let mediaItemId = libraryItem.media.id // bookId
|
||||
let mediaItemType = 'book'
|
||||
if (mediaItemObj.episodeId) {
|
||||
mediaItemType = 'podcastEpisode'
|
||||
mediaItemId = mediaItemObj.episodeId
|
||||
}
|
||||
playlistMediaItems.push({
|
||||
playlistId: newPlaylist.id,
|
||||
mediaItemId,
|
||||
mediaItemType,
|
||||
order: order++
|
||||
})
|
||||
}
|
||||
if (playlistMediaItems.length) {
|
||||
await this.createBulkPlaylistMediaItems(playlistMediaItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePlaylist(oldPlaylist) {
|
||||
if (!this.sequelize) return false
|
||||
const playlistMediaItems = []
|
||||
let order = 1
|
||||
oldPlaylist.items.forEach((item) => {
|
||||
const libraryItem = this.getLibraryItem(item.libraryItemId)
|
||||
if (!libraryItem) return
|
||||
playlistMediaItems.push({
|
||||
playlistId: oldPlaylist.id,
|
||||
mediaItemId: item.episodeId || libraryItem.media.id,
|
||||
mediaItemType: item.episodeId ? 'podcastEpisode' : 'book',
|
||||
order: order++
|
||||
})
|
||||
})
|
||||
return this.models.playlist.fullUpdateFromOld(oldPlaylist, playlistMediaItems)
|
||||
}
|
||||
|
||||
async removePlaylist(playlistId) {
|
||||
if (!this.sequelize) return false
|
||||
await this.models.playlist.removeById(playlistId)
|
||||
}
|
||||
|
||||
createPlaylistMediaItem(playlistMediaItem) {
|
||||
if (!this.sequelize) return false
|
||||
return this.models.playlistMediaItem.create(playlistMediaItem)
|
||||
@ -341,11 +269,6 @@ class Database {
|
||||
return this.models.playlistMediaItem.bulkCreate(playlistMediaItems)
|
||||
}
|
||||
|
||||
removePlaylistMediaItem(playlistId, mediaItemId) {
|
||||
if (!this.sequelize) return false
|
||||
return this.models.playlistMediaItem.removeByIds(playlistId, mediaItemId)
|
||||
}
|
||||
|
||||
getLibraryItem(libraryItemId) {
|
||||
if (!this.sequelize || !libraryItemId) return false
|
||||
|
||||
|
@ -7,71 +7,187 @@ const Playlist = require('../objects/Playlist')
|
||||
class PlaylistController {
|
||||
constructor() { }
|
||||
|
||||
// POST: api/playlists
|
||||
/**
|
||||
* POST: /api/playlists
|
||||
* Create playlist
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async create(req, res) {
|
||||
const newPlaylist = new Playlist()
|
||||
const oldPlaylist = new Playlist()
|
||||
req.body.userId = req.user.id
|
||||
const success = newPlaylist.setData(req.body)
|
||||
const success = oldPlaylist.setData(req.body)
|
||||
if (!success) {
|
||||
return res.status(400).send('Invalid playlist request data')
|
||||
}
|
||||
const jsonExpanded = newPlaylist.toJSONExpanded(Database.libraryItems)
|
||||
await Database.createPlaylist(newPlaylist)
|
||||
|
||||
// Create Playlist record
|
||||
const newPlaylist = await Database.models.playlist.createFromOld(oldPlaylist)
|
||||
|
||||
// Lookup all library items in playlist
|
||||
const libraryItemIds = oldPlaylist.items.map(i => i.libraryItemId).filter(i => i)
|
||||
const libraryItemsInPlaylist = await Database.models.libraryItem.findAll({
|
||||
where: {
|
||||
id: libraryItemIds
|
||||
}
|
||||
})
|
||||
|
||||
// Create playlistMediaItem records
|
||||
const mediaItemsToAdd = []
|
||||
let order = 1
|
||||
for (const mediaItemObj of oldPlaylist.items) {
|
||||
const libraryItem = libraryItemsInPlaylist.find(li => li.id === mediaItemObj.libraryItemId)
|
||||
if (!libraryItem) continue
|
||||
|
||||
mediaItemsToAdd.push({
|
||||
mediaItemId: mediaItemObj.episodeId || libraryItem.mediaId,
|
||||
mediaItemType: mediaItemObj.episodeId ? 'podcastEpisode' : 'book',
|
||||
playlistId: oldPlaylist.id,
|
||||
order: order++
|
||||
})
|
||||
}
|
||||
if (mediaItemsToAdd.length) {
|
||||
await Database.createBulkPlaylistMediaItems(mediaItemsToAdd)
|
||||
}
|
||||
|
||||
const jsonExpanded = await newPlaylist.getOldJsonExpanded()
|
||||
SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
// GET: api/playlists
|
||||
/**
|
||||
* GET: /api/playlists
|
||||
* Get all playlists for user
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async findAllForUser(req, res) {
|
||||
const playlistsForUser = await Database.models.playlist.getPlaylistsForUserAndLibrary(req.user.id)
|
||||
const playlistsForUser = await Database.models.playlist.findAll({
|
||||
where: {
|
||||
userId: req.user.id
|
||||
}
|
||||
})
|
||||
const playlists = []
|
||||
for (const playlist of playlistsForUser) {
|
||||
const jsonExpanded = await playlist.getOldJsonExpanded()
|
||||
playlists.push(jsonExpanded)
|
||||
}
|
||||
res.json({
|
||||
playlists: playlistsForUser.map(p => p.toJSONExpanded(Database.libraryItems))
|
||||
playlists
|
||||
})
|
||||
}
|
||||
|
||||
// GET: api/playlists/:id
|
||||
findOne(req, res) {
|
||||
res.json(req.playlist.toJSONExpanded(Database.libraryItems))
|
||||
/**
|
||||
* GET: /api/playlists/:id
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async findOne(req, res) {
|
||||
const jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
// PATCH: api/playlists/:id
|
||||
/**
|
||||
* PATCH: /api/playlists/:id
|
||||
* Update playlist
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async update(req, res) {
|
||||
const playlist = req.playlist
|
||||
let wasUpdated = playlist.update(req.body)
|
||||
const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
|
||||
const updatedPlaylist = req.playlist.set(req.body)
|
||||
let wasUpdated = false
|
||||
const changed = updatedPlaylist.changed()
|
||||
if (changed?.length) {
|
||||
await req.playlist.save()
|
||||
Logger.debug(`[PlaylistController] Updated playlist ${req.playlist.id} keys [${changed.join(',')}]`)
|
||||
wasUpdated = true
|
||||
}
|
||||
|
||||
// If array of items is passed in then update order of playlist media items
|
||||
const libraryItemIds = req.body.items?.map(i => i.libraryItemId).filter(i => i) || []
|
||||
if (libraryItemIds.length) {
|
||||
const libraryItems = await Database.models.libraryItem.findAll({
|
||||
where: {
|
||||
id: libraryItemIds
|
||||
}
|
||||
})
|
||||
const existingPlaylistMediaItems = await updatedPlaylist.getPlaylistMediaItems({
|
||||
order: [['order', 'ASC']]
|
||||
})
|
||||
|
||||
// Set an array of mediaItemId
|
||||
const newMediaItemIdOrder = []
|
||||
for (const item of req.body.items) {
|
||||
const libraryItem = libraryItems.find(li => li.id === item.libraryItemId)
|
||||
if (!libraryItem) {
|
||||
continue
|
||||
}
|
||||
const mediaItemId = item.episodeId || libraryItem.mediaId
|
||||
newMediaItemIdOrder.push(mediaItemId)
|
||||
}
|
||||
|
||||
// Sort existing playlist media items into new order
|
||||
existingPlaylistMediaItems.sort((a, b) => {
|
||||
const aIndex = newMediaItemIdOrder.findIndex(i => i === a.mediaItemId)
|
||||
const bIndex = newMediaItemIdOrder.findIndex(i => i === b.mediaItemId)
|
||||
return aIndex - bIndex
|
||||
})
|
||||
|
||||
// Update order on playlistMediaItem records
|
||||
let order = 1
|
||||
for (const playlistMediaItem of existingPlaylistMediaItems) {
|
||||
if (playlistMediaItem.order !== order) {
|
||||
await playlistMediaItem.update({
|
||||
order
|
||||
})
|
||||
wasUpdated = true
|
||||
}
|
||||
order++
|
||||
}
|
||||
}
|
||||
|
||||
const jsonExpanded = await updatedPlaylist.getOldJsonExpanded()
|
||||
if (wasUpdated) {
|
||||
await Database.updatePlaylist(playlist)
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
||||
SocketAuthority.clientEmitter(updatedPlaylist.userId, 'playlist_updated', jsonExpanded)
|
||||
}
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
// DELETE: api/playlists/:id
|
||||
/**
|
||||
* DELETE: /api/playlists/:id
|
||||
* Remove playlist
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
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)
|
||||
const jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
await req.playlist.destroy()
|
||||
SocketAuthority.clientEmitter(jsonExpanded.userId, 'playlist_removed', jsonExpanded)
|
||||
res.sendStatus(200)
|
||||
}
|
||||
|
||||
// POST: api/playlists/:id/item
|
||||
/**
|
||||
* POST: /api/playlists/:id/item
|
||||
* Add item to playlist
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async addItem(req, res) {
|
||||
const playlist = req.playlist
|
||||
const oldPlaylist = await Database.models.playlist.getById(req.playlist.id)
|
||||
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)
|
||||
const libraryItem = await Database.models.libraryItem.getOldById(itemToAdd.libraryItemId)
|
||||
if (!libraryItem) {
|
||||
return res.status(400).send('Library item not found')
|
||||
}
|
||||
if (libraryItem.libraryId !== playlist.libraryId) {
|
||||
if (libraryItem.libraryId !== oldPlaylist.libraryId) {
|
||||
return res.status(400).send('Library item in different library')
|
||||
}
|
||||
if (playlist.containsItem(itemToAdd)) {
|
||||
if (oldPlaylist.containsItem(itemToAdd)) {
|
||||
return res.status(400).send('Item already in playlist')
|
||||
}
|
||||
if ((itemToAdd.episodeId && !libraryItem.isPodcast) || (libraryItem.isPodcast && !itemToAdd.episodeId)) {
|
||||
@ -81,160 +197,248 @@ class PlaylistController {
|
||||
return res.status(400).send('Episode not found in library item')
|
||||
}
|
||||
|
||||
playlist.addItem(itemToAdd.libraryItemId, itemToAdd.episodeId)
|
||||
|
||||
const playlistMediaItem = {
|
||||
playlistId: playlist.id,
|
||||
playlistId: oldPlaylist.id,
|
||||
mediaItemId: itemToAdd.episodeId || libraryItem.media.id,
|
||||
mediaItemType: itemToAdd.episodeId ? 'podcastEpisode' : 'book',
|
||||
order: playlist.items.length
|
||||
order: oldPlaylist.items.length + 1
|
||||
}
|
||||
|
||||
const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
|
||||
await Database.createPlaylistMediaItem(playlistMediaItem)
|
||||
const jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
// DELETE: api/playlists/:id/item/:libraryItemId/:episodeId?
|
||||
/**
|
||||
* DELETE: /api/playlists/:id/item/:libraryItemId/:episodeId?
|
||||
* Remove item from playlist
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
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)
|
||||
const oldLibraryItem = await Database.models.libraryItem.getOldById(req.params.libraryItemId)
|
||||
if (!oldLibraryItem) {
|
||||
return res.status(404).send('Library item not found')
|
||||
}
|
||||
|
||||
playlist.removeItem(itemToRemove.libraryItemId, itemToRemove.episodeId)
|
||||
// Get playlist media items
|
||||
const mediaItemId = req.params.episodeId || oldLibraryItem.media.id
|
||||
const playlistMediaItems = await req.playlist.getPlaylistMediaItems({
|
||||
order: [['order', 'ASC']]
|
||||
})
|
||||
|
||||
const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
|
||||
// Check if media item to delete is in playlist
|
||||
const mediaItemToRemove = playlistMediaItems.find(pmi => pmi.mediaItemId === mediaItemId)
|
||||
if (!mediaItemToRemove) {
|
||||
return res.status(404).send('Media item not found in playlist')
|
||||
}
|
||||
|
||||
// Remove record
|
||||
await mediaItemToRemove.destroy()
|
||||
|
||||
// Update playlist media items order
|
||||
let order = 1
|
||||
for (const mediaItem of playlistMediaItems) {
|
||||
if (mediaItem.mediaItemId === mediaItemId) continue
|
||||
if (mediaItem.order !== order) {
|
||||
await mediaItem.update({
|
||||
order
|
||||
})
|
||||
}
|
||||
order++
|
||||
}
|
||||
|
||||
const jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
|
||||
// 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)
|
||||
if (!jsonExpanded.items.length) {
|
||||
Logger.info(`[PlaylistController] Playlist "${jsonExpanded.name}" has no more items - removing it`)
|
||||
await req.playlist.destroy()
|
||||
SocketAuthority.clientEmitter(jsonExpanded.userId, 'playlist_removed', jsonExpanded)
|
||||
} else {
|
||||
await Database.updatePlaylist(playlist)
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
||||
SocketAuthority.clientEmitter(jsonExpanded.userId, 'playlist_updated', jsonExpanded)
|
||||
}
|
||||
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
// POST: api/playlists/:id/batch/add
|
||||
/**
|
||||
* POST: /api/playlists/:id/batch/add
|
||||
* Batch add playlist items
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async addBatch(req, res) {
|
||||
const playlist = req.playlist
|
||||
if (!req.body.items || !req.body.items.length) {
|
||||
return res.status(500).send('Invalid request body')
|
||||
if (!req.body.items?.length) {
|
||||
return res.status(400).send('Invalid request body')
|
||||
}
|
||||
const itemsToAdd = req.body.items
|
||||
let hasUpdated = false
|
||||
|
||||
let order = playlist.items.length + 1
|
||||
const playlistMediaItems = []
|
||||
const libraryItemIds = itemsToAdd.map(i => i.libraryItemId).filter(i => i)
|
||||
if (!libraryItemIds.length) {
|
||||
return res.status(400).send('Invalid request body')
|
||||
}
|
||||
|
||||
// Find all library items
|
||||
const libraryItems = await Database.models.libraryItem.findAll({
|
||||
where: {
|
||||
id: libraryItemIds
|
||||
}
|
||||
})
|
||||
|
||||
// Get all existing playlist media items
|
||||
const existingPlaylistMediaItems = await req.playlist.getPlaylistMediaItems({
|
||||
order: [['order', 'ASC']]
|
||||
})
|
||||
|
||||
const mediaItemsToAdd = []
|
||||
|
||||
// Setup array of playlistMediaItem records to add
|
||||
let order = existingPlaylistMediaItems.length + 1
|
||||
for (const item of itemsToAdd) {
|
||||
if (!item.libraryItemId) {
|
||||
return res.status(400).send('Item does not have libraryItemId')
|
||||
}
|
||||
|
||||
const libraryItem = Database.getLibraryItem(item.libraryItemId)
|
||||
const libraryItem = libraryItems.find(li => li.id === 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
|
||||
return res.status(404).send('Item not found with id ' + item.libraryItemId)
|
||||
} else {
|
||||
const mediaItemId = item.episodeId || libraryItem.mediaId
|
||||
if (existingPlaylistMediaItems.some(pmi => pmi.mediaItemId === mediaItemId)) {
|
||||
// Already exists in playlist
|
||||
continue
|
||||
} else {
|
||||
mediaItemsToAdd.push({
|
||||
playlistId: req.playlist.id,
|
||||
mediaItemId,
|
||||
mediaItemType: item.episodeId ? 'podcastEpisode' : 'book',
|
||||
order: order++
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
|
||||
if (hasUpdated) {
|
||||
await Database.createBulkPlaylistMediaItems(playlistMediaItems)
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
||||
let jsonExpanded = null
|
||||
if (mediaItemsToAdd.length) {
|
||||
await Database.createBulkPlaylistMediaItems(mediaItemsToAdd)
|
||||
jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
SocketAuthority.clientEmitter(req.playlist.userId, 'playlist_updated', jsonExpanded)
|
||||
} else {
|
||||
jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
}
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
// POST: api/playlists/:id/batch/remove
|
||||
/**
|
||||
* POST: /api/playlists/:id/batch/remove
|
||||
* Batch remove playlist items
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async removeBatch(req, res) {
|
||||
const playlist = req.playlist
|
||||
if (!req.body.items || !req.body.items.length) {
|
||||
return res.status(500).send('Invalid request body')
|
||||
if (!req.body.items?.length) {
|
||||
return res.status(400).send('Invalid request body')
|
||||
}
|
||||
|
||||
const itemsToRemove = req.body.items
|
||||
const libraryItemIds = itemsToRemove.map(i => i.libraryItemId).filter(i => i)
|
||||
if (!libraryItemIds.length) {
|
||||
return res.status(400).send('Invalid request body')
|
||||
}
|
||||
|
||||
// Find all library items
|
||||
const libraryItems = await Database.models.libraryItem.findAll({
|
||||
where: {
|
||||
id: libraryItemIds
|
||||
}
|
||||
})
|
||||
|
||||
// Get all existing playlist media items for playlist
|
||||
const existingPlaylistMediaItems = await req.playlist.getPlaylistMediaItems({
|
||||
order: [['order', 'ASC']]
|
||||
})
|
||||
let numMediaItems = existingPlaylistMediaItems.length
|
||||
|
||||
// Remove playlist media 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 libraryItem = libraryItems.find(li => li.id === item.libraryItemId)
|
||||
if (!libraryItem) continue
|
||||
const mediaItemId = item.episodeId || libraryItem.mediaId
|
||||
const existingMediaItem = existingPlaylistMediaItems.find(pmi => pmi.mediaItemId === mediaItemId)
|
||||
if (!existingMediaItem) continue
|
||||
await existingMediaItem.destroy()
|
||||
hasUpdated = true
|
||||
numMediaItems--
|
||||
}
|
||||
|
||||
const jsonExpanded = playlist.toJSONExpanded(Database.libraryItems)
|
||||
const jsonExpanded = await req.playlist.getOldJsonExpanded()
|
||||
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)
|
||||
if (!numMediaItems) {
|
||||
Logger.info(`[PlaylistController] Playlist "${req.playlist.name}" has no more items - removing it`)
|
||||
await req.playlist.destroy()
|
||||
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
|
||||
/**
|
||||
* POST: /api/playlists/collection/:collectionId
|
||||
* Create a playlist from a collection
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async createFromCollection(req, res) {
|
||||
let collection = await Database.models.collection.getOldById(req.params.collectionId)
|
||||
const collection = await Database.models.collection.findByPk(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 collectionExpanded = await collection.getOldJsonExpanded(req.user)
|
||||
if (!collectionExpanded) {
|
||||
// This can happen if the user has no access to all items in collection
|
||||
return res.status(404).send('Collection not found')
|
||||
}
|
||||
|
||||
const newPlaylist = new Playlist()
|
||||
// Playlists cannot be empty
|
||||
if (!collectionExpanded.books.length) {
|
||||
return res.status(400).send('Collection has no books')
|
||||
}
|
||||
|
||||
const newPlaylistData = {
|
||||
const oldPlaylist = new Playlist()
|
||||
oldPlaylist.setData({
|
||||
userId: req.user.id,
|
||||
libraryId: collection.libraryId,
|
||||
name: collection.name,
|
||||
description: collection.description || null,
|
||||
items: libraryItems.map(li => ({ libraryItemId: li.id }))
|
||||
}
|
||||
newPlaylist.setData(newPlaylistData)
|
||||
description: collection.description || null
|
||||
})
|
||||
|
||||
const jsonExpanded = newPlaylist.toJSONExpanded(Database.libraryItems)
|
||||
await Database.createPlaylist(newPlaylist)
|
||||
// Create Playlist record
|
||||
const newPlaylist = await Database.models.playlist.createFromOld(oldPlaylist)
|
||||
|
||||
// Create PlaylistMediaItem records
|
||||
const mediaItemsToAdd = []
|
||||
let order = 1
|
||||
for (const libraryItem of collectionExpanded.books) {
|
||||
mediaItemsToAdd.push({
|
||||
playlistId: newPlaylist.id,
|
||||
mediaItemId: libraryItem.media.id,
|
||||
mediaItemType: 'book',
|
||||
order: order++
|
||||
})
|
||||
}
|
||||
await Database.createBulkPlaylistMediaItems(mediaItemsToAdd)
|
||||
|
||||
const jsonExpanded = await newPlaylist.getOldJsonExpanded()
|
||||
SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
async middleware(req, res, next) {
|
||||
if (req.params.id) {
|
||||
const playlist = await Database.models.playlist.getById(req.params.id)
|
||||
const playlist = await Database.models.playlist.findByPk(req.params.id)
|
||||
if (!playlist) {
|
||||
return res.status(404).send('Playlist not found')
|
||||
}
|
||||
|
@ -122,9 +122,13 @@ class UserController {
|
||||
// Todo: check if user is logged in and cancel streams
|
||||
|
||||
// Remove user playlists
|
||||
const userPlaylists = await Database.models.playlist.getPlaylistsForUserAndLibrary(user.id)
|
||||
const userPlaylists = await Database.models.playlist.findAll({
|
||||
where: {
|
||||
userId: user.id
|
||||
}
|
||||
})
|
||||
for (const playlist of userPlaylists) {
|
||||
await Database.removePlaylist(playlist.id)
|
||||
await playlist.destroy()
|
||||
}
|
||||
|
||||
const userJson = user.toJSONForBrowser()
|
||||
|
@ -84,7 +84,7 @@ module.exports = (sequelize) => {
|
||||
id: libraryItemIds
|
||||
})
|
||||
|
||||
const playlistExpanded = oldCollection.toJSONExpanded(libraryItems)
|
||||
const playlistExpanded = oldPlaylist.toJSONExpanded(libraryItems)
|
||||
|
||||
if (include?.includes('rssfeed')) {
|
||||
const feeds = await this.getFeeds()
|
||||
@ -248,7 +248,7 @@ module.exports = (sequelize) => {
|
||||
/**
|
||||
* Get all playlists for mediaItemIds
|
||||
* @param {string[]} mediaItemIds
|
||||
* @returns {Promise<oldPlaylist[]>}
|
||||
* @returns {Promise<Playlist[]>}
|
||||
*/
|
||||
static async getPlaylistsForMediaItemIds(mediaItemIds) {
|
||||
if (!mediaItemIds?.length) return []
|
||||
@ -282,8 +282,13 @@ module.exports = (sequelize) => {
|
||||
],
|
||||
order: [['playlist', 'playlistMediaItems', 'order', 'ASC']]
|
||||
})
|
||||
return playlistMediaItemsExpanded.map(pmie => {
|
||||
pmie.playlist.playlistMediaItems = pmie.playlist.playlistMediaItems.map(pmi => {
|
||||
|
||||
const playlists = []
|
||||
for (const playlistMediaItem of playlistMediaItemsExpanded) {
|
||||
const playlist = playlistMediaItem.playlist
|
||||
if (playlists.some(p => p.id === playlist.id)) continue
|
||||
|
||||
playlist.playlistMediaItems = playlist.playlistMediaItems.map(pmi => {
|
||||
if (pmi.mediaItemType === 'book' && pmi.book !== undefined) {
|
||||
pmi.mediaItem = pmi.book
|
||||
pmi.dataValues.mediaItem = pmi.dataValues.book
|
||||
@ -297,9 +302,9 @@ module.exports = (sequelize) => {
|
||||
delete pmi.dataValues.podcastEpisode
|
||||
return pmi
|
||||
})
|
||||
|
||||
return this.getOldPlaylist(pmie.playlist)
|
||||
})
|
||||
playlists.push(playlist)
|
||||
}
|
||||
return playlists
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,7 +325,9 @@ module.exports = (sequelize) => {
|
||||
library.hasMany(Playlist)
|
||||
Playlist.belongsTo(library)
|
||||
|
||||
user.hasMany(Playlist)
|
||||
user.hasMany(Playlist, {
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
Playlist.belongsTo(user)
|
||||
|
||||
Playlist.addHook('afterFind', findResult => {
|
||||
|
@ -403,17 +403,32 @@ class ApiRouter {
|
||||
// remove item from playlists
|
||||
const playlistsWithItem = await Database.models.playlist.getPlaylistsForMediaItemIds(mediaItemIds)
|
||||
for (const playlist of playlistsWithItem) {
|
||||
playlist.removeItemsForLibraryItem(libraryItem.id)
|
||||
let numMediaItems = playlist.playlistMediaItems.length
|
||||
|
||||
let order = 1
|
||||
// Remove items in playlist and re-order
|
||||
for (const playlistMediaItem of playlist.playlistMediaItems) {
|
||||
if (mediaItemIds.includes(playlistMediaItem.mediaItemId)) {
|
||||
await playlistMediaItem.destroy()
|
||||
numMediaItems--
|
||||
} else {
|
||||
if (playlistMediaItem.order !== order) {
|
||||
playlistMediaItem.update({
|
||||
order
|
||||
})
|
||||
}
|
||||
order++
|
||||
}
|
||||
}
|
||||
|
||||
// If playlist is now empty then remove it
|
||||
if (!playlist.items.length) {
|
||||
const jsonExpanded = await playlist.getOldJsonExpanded()
|
||||
if (!numMediaItems) {
|
||||
Logger.info(`[ApiRouter] Playlist "${playlist.name}" has no more items - removing it`)
|
||||
await Database.removePlaylist(playlist.id)
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', playlist.toJSONExpanded(Database.libraryItems))
|
||||
await playlist.destroy()
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
|
||||
} else {
|
||||
await Database.updatePlaylist(playlist)
|
||||
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', playlist.toJSONExpanded(Database.libraryItems))
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user