2022-11-26 22:14:45 +01:00
|
|
|
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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
|
2022-11-26 22:14:45 +01:00
|
|
|
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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
2022-11-26 22:14:45 +01:00
|
|
|
}
|
|
|
|
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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
|
2022-11-26 22:14:45 +01:00
|
|
|
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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
2022-11-26 22:14:45 +01:00
|
|
|
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)
|
2022-11-27 19:04:49 +01:00
|
|
|
|
|
|
|
// 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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
|
2022-11-27 19:04:49 +01:00
|
|
|
} else {
|
|
|
|
await this.db.updateEntity('playlist', playlist)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
2022-11-27 19:04:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
res.json(jsonExpanded)
|
2022-11-26 22:14:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
2022-11-26 22:14:45 +01:00
|
|
|
}
|
|
|
|
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)) {
|
2022-11-26 23:45:54 +01:00
|
|
|
playlist.removeItem(item.libraryItemId, item.episodeId)
|
2022-11-26 22:14:45 +01:00
|
|
|
hasUpdated = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const jsonExpanded = playlist.toJSONExpanded(this.db.libraryItems)
|
|
|
|
if (hasUpdated) {
|
2022-11-27 19:04:49 +01:00
|
|
|
// 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)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded)
|
2022-11-27 19:04:49 +01:00
|
|
|
} else {
|
|
|
|
await this.db.updateEntity('playlist', playlist)
|
2022-11-27 21:49:21 +01:00
|
|
|
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded)
|
2022-11-27 19:04:49 +01:00
|
|
|
}
|
2022-11-26 22:14:45 +01:00
|
|
|
}
|
|
|
|
res.json(jsonExpanded)
|
|
|
|
}
|
|
|
|
|
2022-12-18 00:31:19 +01:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2022-11-26 22:14:45 +01:00
|
|
|
middleware(req, res, next) {
|
|
|
|
if (req.params.id) {
|
2022-12-18 00:31:19 +01:00
|
|
|
const playlist = this.db.playlists.find(p => p.id === req.params.id)
|
2022-11-26 22:14:45 +01:00
|
|
|
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()
|