diff --git a/server/Database.js b/server/Database.js index bd14fbd5a1..2137b3c1f2 100644 --- a/server/Database.js +++ b/server/Database.js @@ -695,6 +695,27 @@ class Database { await book.destroy() } + const playlistMediaItemsWithNoMediaItem = await this.playlistMediaItemModel.findAll({ + include: [ + { + model: this.bookModel, + attributes: ['id'] + }, + { + model: this.podcastEpisodeModel, + attributes: ['id'] + } + ], + where: { + '$book.id$': null, + '$podcastEpisode.id$': null + } + }) + for (const playlistMediaItem of playlistMediaItemsWithNoMediaItem) { + Logger.warn(`Found playlistMediaItem with no book or podcastEpisode - removing it`) + await playlistMediaItem.destroy() + } + // Remove empty series const emptySeries = await this.seriesModel.findAll({ include: { diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index f08a60115b..4da68866da 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -971,6 +971,20 @@ class LibraryItemController { } } else if (req.libraryItem.media.podcastEpisodes.some((ep) => ep.audioFile.ino === req.params.fileid)) { const episodeToRemove = req.libraryItem.media.podcastEpisodes.find((ep) => ep.audioFile.ino === req.params.fileid) + // Remove episode from all playlists + await Database.playlistModel.removeMediaItemsFromPlaylists([episodeToRemove.id]) + + // Remove episode media progress + const numProgressRemoved = await Database.mediaProgressModel.destroy({ + where: { + mediaItemId: episodeToRemove.id + } + }) + if (numProgressRemoved > 0) { + Logger.info(`[LibraryItemController] Removed media progress for episode ${episodeToRemove.id}`) + } + + // Remove episode await episodeToRemove.destroy() req.libraryItem.media.podcastEpisodes = req.libraryItem.media.podcastEpisodes.filter((ep) => ep.audioFile.ino !== req.params.fileid) diff --git a/server/models/Playlist.js b/server/models/Playlist.js index 7817211f3a..ec56248db8 100644 --- a/server/models/Playlist.js +++ b/server/models/Playlist.js @@ -1,5 +1,6 @@ const { DataTypes, Model, Op } = require('sequelize') const Logger = require('../Logger') +const SocketAuthority = require('../SocketAuthority') class Playlist extends Model { constructor(values, options) { @@ -163,6 +164,49 @@ class Playlist extends Model { return playlists } + /** + * Removes media items and re-orders playlists + * + * @param {string[]} mediaItemIds + */ + static async removeMediaItemsFromPlaylists(mediaItemIds) { + if (!mediaItemIds?.length) return + + const playlistsWithItem = await this.getPlaylistsForMediaItemIds(mediaItemIds) + + if (!playlistsWithItem.length) return + + for (const playlist of playlistsWithItem) { + 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 + const jsonExpanded = await playlist.getOldJsonExpanded() + if (!numMediaItems) { + Logger.info(`[ApiRouter] Playlist "${playlist.name}" has no more items - removing it`) + await playlist.destroy() + SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded) + } else { + SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded) + } + } + } + /** * Initialize model * @param {import('../Database').sequelize} sequelize diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index 235d25cd5f..4402fd04be 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -361,36 +361,7 @@ class ApiRouter { } // remove item from playlists - const playlistsWithItem = await Database.playlistModel.getPlaylistsForMediaItemIds(mediaItemIds) - for (const playlist of playlistsWithItem) { - 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 - const jsonExpanded = await playlist.getOldJsonExpanded() - if (!numMediaItems) { - Logger.info(`[ApiRouter] Playlist "${playlist.name}" has no more items - removing it`) - await playlist.destroy() - SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', jsonExpanded) - } else { - SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', jsonExpanded) - } - } + await Database.playlistModel.removeMediaItemsFromPlaylists(mediaItemIds) // Close rss feed - remove from db and emit socket event await RssFeedManager.closeFeedForEntityId(libraryItemId)