From a55c167ddebbca3cee60880784dec9add5d06ec0 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 3 Aug 2024 17:09:17 -0500 Subject: [PATCH] Fix:Cleanup media progress when deleting podcasts, remove usage of old user model --- server/Database.js | 5 --- server/controllers/LibraryItemController.js | 34 ++++++++++++++++++--- server/models/LibraryItem.js | 6 ++++ server/routers/ApiRouter.js | 15 ++++----- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/server/Database.js b/server/Database.js index 935ddc20..6fe058c6 100644 --- a/server/Database.js +++ b/server/Database.js @@ -469,11 +469,6 @@ class Database { return updated } - async removeLibraryItem(libraryItemId) { - if (!this.sequelize) return false - await this.models.libraryItem.removeById(libraryItemId) - } - async createFeed(oldFeed) { if (!this.sequelize) return false await this.models.feed.fullCreateFromOld(oldFeed) diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index af264154..ba0e1050 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -76,10 +76,21 @@ class LibraryItemController { res.json(libraryItem.toJSON()) } + /** + * DELETE: /api/items/:id + * Delete library item. Will delete from database and file system if hard delete is requested. + * Optional query params: + * ?hard=1 + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ async delete(req, res) { const hardDelete = req.query.hard == 1 // Delete from file system const libraryItemPath = req.libraryItem.path - await this.handleDeleteLibraryItem(req.libraryItem.mediaType, req.libraryItem.id, [req.libraryItem.media.id]) + + const mediaItemIds = req.libraryItem.mediaType === 'podcast' ? req.libraryItem.media.episodes.map((ep) => ep.id) : [req.libraryItem.media.id] + await this.handleDeleteLibraryItem(req.libraryItem.mediaType, req.libraryItem.id, mediaItemIds) if (hardDelete) { Logger.info(`[LibraryItemController] Deleting library item from file system at "${libraryItemPath}"`) await fs.remove(libraryItemPath).catch((error) => { @@ -366,7 +377,15 @@ class LibraryItemController { res.json(matchResult) } - // POST: api/items/batch/delete + /** + * POST: /api/items/batch/delete + * Batch delete library items. Will delete from database and file system if hard delete is requested. + * Optional query params: + * ?hard=1 + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ async batchDelete(req, res) { if (!req.user.canDelete) { Logger.warn(`[LibraryItemController] User attempted to delete without permission`, req.user) @@ -391,7 +410,8 @@ class LibraryItemController { for (const libraryItem of itemsToDelete) { const libraryItemPath = libraryItem.path Logger.info(`[LibraryItemController] Deleting Library Item "${libraryItem.media.metadata.title}"`) - await this.handleDeleteLibraryItem(libraryItem.mediaType, libraryItem.id, [libraryItem.media.id]) + const mediaItemIds = libraryItem.mediaType === 'podcast' ? libraryItem.media.episodes.map((ep) => ep.id) : [libraryItem.media.id] + await this.handleDeleteLibraryItem(libraryItem.mediaType, libraryItem.id, mediaItemIds) if (hardDelete) { Logger.info(`[LibraryItemController] Deleting library item from file system at "${libraryItemPath}"`) await fs.remove(libraryItemPath).catch((error) => { @@ -738,7 +758,7 @@ class LibraryItemController { return res.sendStatus(404) } const ebookFilePath = ebookFile.metadata.path - + Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${req.libraryItem.media.metadata.title}" ebook at "${ebookFilePath}"`) if (global.XAccel) { @@ -781,6 +801,12 @@ class LibraryItemController { res.sendStatus(200) } + /** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next + */ async middleware(req, res, next) { req.libraryItem = await Database.libraryItemModel.getOldById(req.params.id) if (!req.libraryItem?.media) return res.sendStatus(404) diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index 1b56a23f..bf7e26a0 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -427,6 +427,12 @@ class LibraryItem extends Model { } } + /** + * Remove library item by id + * + * @param {string} libraryItemId + * @returns {Promise} The number of destroyed rows + */ static removeById(libraryItemId) { return this.destroy({ where: { diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index c22f24ff..98a42163 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -353,12 +353,13 @@ class ApiRouter { * @param {string[]} mediaItemIds array of bookId or podcastEpisodeId */ async handleDeleteLibraryItem(mediaType, libraryItemId, mediaItemIds) { - // Remove media progress for this library item from all users - const users = await Database.userModel.getOldUsers() - for (const user of users) { - for (const mediaProgress of user.getAllMediaProgressForLibraryItem(libraryItemId)) { - await Database.removeMediaProgress(mediaProgress.id) + const numProgressRemoved = await Database.mediaProgressModel.destroy({ + where: { + mediaItemId: mediaItemIds } + }) + if (numProgressRemoved > 0) { + Logger.info(`[ApiRouter] Removed ${numProgressRemoved} media progress entries for library item "${libraryItemId}"`) } // TODO: Remove open sessions for library item @@ -424,11 +425,11 @@ class ApiRouter { const itemMetadataPath = Path.join(global.MetadataPath, 'items', libraryItemId) if (await fs.pathExists(itemMetadataPath)) { - Logger.debug(`[ApiRouter] Removing item metadata path "${itemMetadataPath}"`) + Logger.info(`[ApiRouter] Removing item metadata at "${itemMetadataPath}"`) await fs.remove(itemMetadataPath) } - await Database.removeLibraryItem(libraryItemId) + await Database.libraryItemModel.removeById(libraryItemId) SocketAuthority.emitter('item_removed', { id: libraryItemId