From 159ccd807fd97c50d8cc9aa2f2a484c64ba33d0e Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 24 Aug 2024 16:09:54 -0500 Subject: [PATCH] Updates to migrate off of old library model --- server/controllers/LibraryController.js | 20 +- server/controllers/MiscController.js | 6 +- server/controllers/PodcastController.js | 4 +- server/managers/NotificationManager.js | 2 +- server/models/LibraryItem.js | 8 +- server/scanner/LibraryScanner.js | 176 ++++++++++-------- server/utils/libraryHelpers.js | 2 +- server/utils/queries/libraryFilters.js | 44 ++--- .../utils/queries/libraryItemsBookFilters.js | 4 +- .../queries/libraryItemsPodcastFilters.js | 6 +- server/utils/queries/seriesFilters.js | 2 +- 11 files changed, 141 insertions(+), 133 deletions(-) diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index cc9e6e2e55..9d9ed2eecb 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -507,15 +507,13 @@ class LibraryController { payload.offset = payload.page * payload.limit // TODO: Temporary way of handling collapse sub-series. Either remove feature or handle through sql queries - // TODO: Update to new library model - const oldLibrary = Database.libraryModel.getOldLibrary(req.library) const filterByGroup = payload.filterBy?.split('.').shift() const filterByValue = filterByGroup ? libraryFilters.decode(payload.filterBy.replace(`${filterByGroup}.`, '')) : null if (filterByGroup === 'series' && filterByValue !== 'no-series' && payload.collapseseries) { const seriesId = libraryFilters.decode(payload.filterBy.split('.')[1]) - payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, oldLibrary) + payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, req.library) } else { - const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(oldLibrary, req.user, payload) + const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.user, payload) payload.results = libraryItems payload.total = count } @@ -589,9 +587,6 @@ class LibraryController { * @param {Response} res */ async getAllSeriesForLibrary(req, res) { - // TODO: Update to new library model - const oldLibrary = Database.libraryModel.getOldLibrary(req.library) - const include = (req.query.include || '') .split(',') .map((v) => v.trim().toLowerCase()) @@ -610,7 +605,7 @@ class LibraryController { } const offset = payload.page * payload.limit - const { series, count } = await seriesFilters.getFilteredSeries(oldLibrary, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset) + const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset) payload.total = count payload.results = series @@ -741,14 +736,12 @@ class LibraryController { * @param {Response} res */ async getUserPersonalizedShelves(req, res) { - // TODO: Update to new library model - const oldLibrary = Database.libraryModel.getOldLibrary(req.library) const limitPerShelf = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) || 10 : 10 const include = (req.query.include || '') .split(',') .map((v) => v.trim().toLowerCase()) .filter((v) => !!v) - const shelves = await Database.libraryItemModel.getPersonalizedShelves(oldLibrary, req.user, include, limitPerShelf) + const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.user, include, limitPerShelf) res.json(shelves) } @@ -1098,8 +1091,7 @@ class LibraryController { if (req.library.mediaType !== 'podcast') { return res.sendStatus(404) } - // TODO: Update to new library model - const oldLibrary = Database.libraryModel.getOldLibrary(req.library) + const payload = { episodes: [], limit: req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 0, @@ -1107,7 +1099,7 @@ class LibraryController { } const offset = payload.page * payload.limit - payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, oldLibrary, payload.limit, offset) + payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, req.library, payload.limit, offset) res.json(payload) } diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index ac6afff727..d9c1c445e6 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -44,11 +44,11 @@ class MiscController { const files = Object.values(req.files) const { title, author, series, folder: folderId, library: libraryId } = req.body - const library = await Database.libraryModel.getOldById(libraryId) + const library = await Database.libraryModel.findByPk(libraryId) if (!library) { return res.status(404).send(`Library not found with id ${libraryId}`) } - const folder = library.folders.find((fold) => fold.id === folderId) + const folder = library.libraryFolders.find((fold) => fold.id === folderId) if (!folder) { return res.status(404).send(`Folder not found with id ${folderId} in library ${library.name}`) } @@ -63,7 +63,7 @@ class MiscController { // before sanitizing all the directory parts to remove illegal chars and finally prepending // the base folder path const cleanedOutputDirectoryParts = outputDirectoryParts.filter(Boolean).map((part) => sanitizeFilename(part)) - const outputDirectory = Path.join(...[folder.fullPath, ...cleanedOutputDirectoryParts]) + const outputDirectory = Path.join(...[folder.path, ...cleanedOutputDirectoryParts]) await fs.ensureDir(outputDirectory) diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js index 30688c7687..976e1ac917 100644 --- a/server/controllers/PodcastController.js +++ b/server/controllers/PodcastController.js @@ -38,13 +38,13 @@ class PodcastController { } const payload = req.body - const library = await Database.libraryModel.getOldById(payload.libraryId) + const library = await Database.libraryModel.findByPk(payload.libraryId) if (!library) { Logger.error(`[PodcastController] Create: Library not found "${payload.libraryId}"`) return res.status(404).send('Library not found') } - const folder = library.folders.find((fold) => fold.id === payload.folderId) + const folder = library.libraryFolders.find((fold) => fold.id === payload.folderId) if (!folder) { Logger.error(`[PodcastController] Create: Folder not found "${payload.folderId}"`) return res.status(404).send('Folder not found') diff --git a/server/managers/NotificationManager.js b/server/managers/NotificationManager.js index f8bd75114a..a59c128154 100644 --- a/server/managers/NotificationManager.js +++ b/server/managers/NotificationManager.js @@ -23,7 +23,7 @@ class NotificationManager { } Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: Episode "${episode.title}" for podcast ${libraryItem.media.metadata.title}`) - const library = await Database.libraryModel.getOldById(libraryItem.libraryId) + const library = await Database.libraryModel.findByPk(libraryItem.libraryId) const eventData = { libraryItemId: libraryItem.id, libraryId: libraryItem.libraryId, diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index b986ed54bd..54f85aded9 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -11,8 +11,6 @@ const LibraryFile = require('../objects/files/LibraryFile') const Book = require('./Book') const Podcast = require('./Podcast') -const ShareManager = require('../managers/ShareManager') - /** * @typedef LibraryFileObject * @property {string} ino @@ -559,14 +557,14 @@ class LibraryItem extends Model { /** * Get library items using filter and sort - * @param {oldLibrary} library + * @param {import('./Library')} library * @param {import('./User')} user * @param {object} options * @returns {{ libraryItems:oldLibraryItem[], count:number }} */ static async getByFilterAndSort(library, user, options) { let start = Date.now() - const { libraryItems, count } = await libraryFilters.getFilteredLibraryItems(library, user, options) + const { libraryItems, count } = await libraryFilters.getFilteredLibraryItems(library.id, user, options) Logger.debug(`Loaded ${libraryItems.length} of ${count} items for libary page in ${((Date.now() - start) / 1000).toFixed(2)}s`) return { @@ -602,7 +600,7 @@ class LibraryItem extends Model { /** * Get home page data personalized shelves - * @param {oldLibrary} library + * @param {import('./Library')} library * @param {import('./User')} user * @param {string[]} include * @param {number} limit diff --git a/server/scanner/LibraryScanner.js b/server/scanner/LibraryScanner.js index 6d72e8704c..bbdde32833 100644 --- a/server/scanner/LibraryScanner.js +++ b/server/scanner/LibraryScanner.js @@ -26,27 +26,27 @@ class LibraryScanner { } /** - * @param {string} libraryId + * @param {string} libraryId * @returns {boolean} */ isLibraryScanning(libraryId) { - return this.librariesScanning.some(ls => ls.id === libraryId) + return this.librariesScanning.some((ls) => ls.id === libraryId) } /** - * - * @param {string} libraryId + * + * @param {string} libraryId */ setCancelLibraryScan(libraryId) { - const libraryScanning = this.librariesScanning.find(ls => ls.id === libraryId) + const libraryScanning = this.librariesScanning.find((ls) => ls.id === libraryId) if (!libraryScanning) return this.cancelLibraryScan[libraryId] = true } /** - * - * @param {import('../objects/Library')} library - * @param {boolean} [forceRescan] + * + * @param {import('../objects/Library')} library + * @param {boolean} [forceRescan] */ async scan(library, forceRescan = false) { if (this.isLibraryScanning(library.id)) { @@ -89,7 +89,7 @@ class LibraryScanner { libraryScan.setComplete() Logger.info(`[LibraryScanner] Library scan ${libraryScan.id} completed in ${libraryScan.elapsedTimestamp} | ${libraryScan.resultStats}`) - this.librariesScanning = this.librariesScanning.filter(ls => ls.id !== library.id) + this.librariesScanning = this.librariesScanning.filter((ls) => ls.id !== library.id) if (canceled && !libraryScan.totalResults) { task.setFinished('Scan canceled') @@ -116,8 +116,8 @@ class LibraryScanner { } /** - * - * @param {import('./LibraryScan')} libraryScan + * + * @param {import('./LibraryScan')} libraryScan * @param {boolean} forceRescan * @returns {Promise} true if scan canceled */ @@ -151,14 +151,10 @@ class LibraryScanner { let oldLibraryItemsUpdated = [] for (const existingLibraryItem of existingLibraryItems) { // First try to find matching library item with exact file path - let libraryItemData = libraryItemDataFound.find(lid => lid.path === existingLibraryItem.path) + let libraryItemData = libraryItemDataFound.find((lid) => lid.path === existingLibraryItem.path) if (!libraryItemData) { // Fallback to finding matching library item with matching inode value - libraryItemData = libraryItemDataFound.find(lid => - ItemToItemInoMatch(lid, existingLibraryItem) || - ItemToFileInoMatch(lid, existingLibraryItem) || - ItemToFileInoMatch(existingLibraryItem, lid) - ) + libraryItemData = libraryItemDataFound.find((lid) => ItemToItemInoMatch(lid, existingLibraryItem) || ItemToFileInoMatch(lid, existingLibraryItem) || ItemToFileInoMatch(existingLibraryItem, lid)) if (libraryItemData) { libraryScan.addLog(LogLevel.INFO, `Library item with path "${existingLibraryItem.path}" was not found, but library item inode "${existingLibraryItem.ino}" was found at path "${libraryItemData.path}"`) } @@ -166,7 +162,7 @@ class LibraryScanner { if (!libraryItemData) { // Podcast folder can have no episodes and still be valid - if (libraryScan.libraryMediaType === 'podcast' && await fs.pathExists(existingLibraryItem.path)) { + if (libraryScan.libraryMediaType === 'podcast' && (await fs.pathExists(existingLibraryItem.path))) { libraryScan.addLog(LogLevel.INFO, `Library item "${existingLibraryItem.relPath}" folder exists but has no episodes`) } else { libraryScan.addLog(LogLevel.WARN, `Library Item "${existingLibraryItem.path}" (inode: ${existingLibraryItem.ino}) is missing`) @@ -184,7 +180,7 @@ class LibraryScanner { } } } else { - libraryItemDataFound = libraryItemDataFound.filter(lidf => lidf !== libraryItemData) + libraryItemDataFound = libraryItemDataFound.filter((lidf) => lidf !== libraryItemData) let libraryItemDataUpdated = await libraryItemData.checkLibraryItemData(existingLibraryItem, libraryScan) if (libraryItemDataUpdated || forceRescan) { if (forceRescan || libraryItemData.hasLibraryFileChanges || libraryItemData.hasPathChange) { @@ -210,7 +206,10 @@ class LibraryScanner { // Emit item updates in chunks of 10 to client if (oldLibraryItemsUpdated.length === 10) { // TODO: Should only emit to clients where library item is accessible - SocketAuthority.emitter('items_updated', oldLibraryItemsUpdated.map(li => li.toJSONExpanded())) + SocketAuthority.emitter( + 'items_updated', + oldLibraryItemsUpdated.map((li) => li.toJSONExpanded()) + ) oldLibraryItemsUpdated = [] } @@ -219,7 +218,10 @@ class LibraryScanner { // Emit item updates to client if (oldLibraryItemsUpdated.length) { // TODO: Should only emit to clients where library item is accessible - SocketAuthority.emitter('items_updated', oldLibraryItemsUpdated.map(li => li.toJSONExpanded())) + SocketAuthority.emitter( + 'items_updated', + oldLibraryItemsUpdated.map((li) => li.toJSONExpanded()) + ) } // Authors and series that were removed from books should be removed if they are now empty @@ -228,15 +230,18 @@ class LibraryScanner { // Update missing library items if (libraryItemIdsMissing.length) { libraryScan.addLog(LogLevel.INFO, `Updating ${libraryItemIdsMissing.length} library items missing`) - await Database.libraryItemModel.update({ - isMissing: true, - lastScan: Date.now(), - lastScanVersion: packageJson.version - }, { - where: { - id: libraryItemIdsMissing + await Database.libraryItemModel.update( + { + isMissing: true, + lastScan: Date.now(), + lastScanVersion: packageJson.version + }, + { + where: { + id: libraryItemIdsMissing + } } - }) + ) } if (this.cancelLibraryScan[libraryScan.libraryId]) return true @@ -256,7 +261,10 @@ class LibraryScanner { // Emit new items in chunks of 10 to client if (newOldLibraryItems.length === 10) { // TODO: Should only emit to clients where library item is accessible - SocketAuthority.emitter('items_added', newOldLibraryItems.map(li => li.toJSONExpanded())) + SocketAuthority.emitter( + 'items_added', + newOldLibraryItems.map((li) => li.toJSONExpanded()) + ) newOldLibraryItems = [] } @@ -265,15 +273,18 @@ class LibraryScanner { // Emit new items to client if (newOldLibraryItems.length) { // TODO: Should only emit to clients where library item is accessible - SocketAuthority.emitter('items_added', newOldLibraryItems.map(li => li.toJSONExpanded())) + SocketAuthority.emitter( + 'items_added', + newOldLibraryItems.map((li) => li.toJSONExpanded()) + ) } } } /** * Get scan data for library folder - * @param {import('../objects/Library')} library - * @param {import('../objects/Folder')} folder + * @param {import('../objects/Library')} library + * @param {import('../objects/Folder')} folder * @returns {LibraryItemScanData[]} */ async scanFolder(library, folder) { @@ -321,27 +332,29 @@ class LibraryScanner { continue } - items.push(new LibraryItemScanData({ - libraryFolderId: folder.id, - libraryId: folder.libraryId, - mediaType: library.mediaType, - ino: libraryItemFolderStats.ino, - mtimeMs: libraryItemFolderStats.mtimeMs || 0, - ctimeMs: libraryItemFolderStats.ctimeMs || 0, - birthtimeMs: libraryItemFolderStats.birthtimeMs || 0, - path: libraryItemData.path, - relPath: libraryItemData.relPath, - isFile, - mediaMetadata: libraryItemData.mediaMetadata || null, - libraryFiles: fileObjs - })) + items.push( + new LibraryItemScanData({ + libraryFolderId: folder.id, + libraryId: folder.libraryId, + mediaType: library.mediaType, + ino: libraryItemFolderStats.ino, + mtimeMs: libraryItemFolderStats.mtimeMs || 0, + ctimeMs: libraryItemFolderStats.ctimeMs || 0, + birthtimeMs: libraryItemFolderStats.birthtimeMs || 0, + path: libraryItemData.path, + relPath: libraryItemData.relPath, + isFile, + mediaMetadata: libraryItemData.mediaMetadata || null, + libraryFiles: fileObjs + }) + ) } return items } /** * Scan files changed from Watcher - * @param {import('../Watcher').PendingFileUpdate[]} fileUpdates + * @param {import('../Watcher').PendingFileUpdate[]} fileUpdates * @param {Task} pendingTask */ async scanFilesChanged(fileUpdates, pendingTask) { @@ -366,7 +379,7 @@ class LibraryScanner { for (const folderId in folderGroups) { const libraryId = folderGroups[folderId].libraryId - // const library = await Database.libraryModel.getOldById(libraryId) + const library = await Database.libraryModel.findByPk(libraryId, { include: { model: Database.libraryFolderModel, @@ -381,7 +394,7 @@ class LibraryScanner { } const folder = library.libraryFolders[0] - const relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath) + const relFilePaths = folderGroups[folderId].fileUpdates.map((fileUpdate) => fileUpdate.relPath) const fileUpdateGroup = scanUtils.groupFilesIntoLibraryItemPaths(library.mediaType, relFilePaths) if (!Object.keys(fileUpdateGroup).length) { @@ -432,7 +445,7 @@ class LibraryScanner { /** * Group array of PendingFileUpdate from Watcher by folder - * @param {import('../Watcher').PendingFileUpdate[]} fileUpdates + * @param {import('../Watcher').PendingFileUpdate[]} fileUpdates * @returns {Record} */ getFileUpdatesGrouped(fileUpdates) { @@ -453,9 +466,9 @@ class LibraryScanner { /** * Scan grouped paths for library folder coming from Watcher - * @param {import('../models/Library')} library - * @param {import('../models/LibraryFolder')} folder - * @param {Record} fileUpdateGroup + * @param {import('../models/Library')} library + * @param {import('../models/LibraryFolder')} folder + * @param {Record} fileUpdateGroup * @returns {Promise>} */ async scanFolderUpdates(library, folder, fileUpdateGroup) { @@ -471,7 +484,7 @@ class LibraryScanner { for (const itemDir in updateGroup) { if (isSingleMediaFile(fileUpdateGroup, itemDir)) continue // Media in root path - const itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/')) + const itemDirNestedFiles = fileUpdateGroup[itemDir].filter((b) => b.includes('/')) if (!itemDirNestedFiles.length) continue const firstNest = itemDirNestedFiles[0].split('/').shift() @@ -523,7 +536,15 @@ class LibraryScanner { const potentialChildDirs = [fullPath] for (let i = 0; i < itemDirParts.length; i++) { - potentialChildDirs.push(Path.posix.join(fileUtils.filePathToPOSIX(folder.path), itemDir.split('/').slice(0, -1 - i).join('/'))) + potentialChildDirs.push( + Path.posix.join( + fileUtils.filePathToPOSIX(folder.path), + itemDir + .split('/') + .slice(0, -1 - i) + .join('/') + ) + ) } // Check if book dir group is already an item @@ -535,10 +556,7 @@ class LibraryScanner { let updatedLibraryItemDetails = {} if (!existingLibraryItem) { const isSingleMedia = isSingleMediaFile(fileUpdateGroup, itemDir) - existingLibraryItem = - await findLibraryItemByItemToItemInoMatch(library.id, fullPath) || - await findLibraryItemByItemToFileInoMatch(library.id, fullPath, isSingleMedia) || - await findLibraryItemByFileToItemInoMatch(library.id, fullPath, isSingleMedia, fileUpdateGroup[itemDir]) + existingLibraryItem = (await findLibraryItemByItemToItemInoMatch(library.id, fullPath)) || (await findLibraryItemByItemToFileInoMatch(library.id, fullPath, isSingleMedia)) || (await findLibraryItemByFileToItemInoMatch(library.id, fullPath, isSingleMedia, fileUpdateGroup[itemDir])) if (existingLibraryItem) { // Update library item paths for scan existingLibraryItem.path = fullPath @@ -603,7 +621,7 @@ class LibraryScanner { module.exports = new LibraryScanner() function ItemToFileInoMatch(libraryItem1, libraryItem2) { - return libraryItem1.isFile && libraryItem2.libraryFiles.some(lf => lf.ino === libraryItem1.ino) + return libraryItem1.isFile && libraryItem2.libraryFiles.some((lf) => lf.ino === libraryItem1.ino) } function ItemToItemInoMatch(libraryItem1, libraryItem2) { @@ -611,9 +629,7 @@ function ItemToItemInoMatch(libraryItem1, libraryItem2) { } function hasAudioFiles(fileUpdateGroup, itemDir) { - return isSingleMediaFile(fileUpdateGroup, itemDir) ? - scanUtils.checkFilepathIsAudioFile(fileUpdateGroup[itemDir]) : - fileUpdateGroup[itemDir].some(scanUtils.checkFilepathIsAudioFile) + return isSingleMediaFile(fileUpdateGroup, itemDir) ? scanUtils.checkFilepathIsAudioFile(fileUpdateGroup[itemDir]) : fileUpdateGroup[itemDir].some(scanUtils.checkFilepathIsAudioFile) } function isSingleMediaFile(fileUpdateGroup, itemDir) { @@ -627,8 +643,7 @@ async function findLibraryItemByItemToItemInoMatch(libraryId, fullPath) { libraryId: libraryId, ino: ino }) - if (existingLibraryItem) - Logger.debug(`[LibraryScanner] Found library item with matching inode "${ino}" at path "${existingLibraryItem.path}"`) + if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with matching inode "${ino}" at path "${existingLibraryItem.path}"`) return existingLibraryItem } @@ -637,18 +652,20 @@ async function findLibraryItemByItemToFileInoMatch(libraryId, fullPath, isSingle // check if it was moved from another folder by comparing the ino to the library files const ino = await fileUtils.getIno(fullPath) if (!ino) return null - const existingLibraryItem = await Database.libraryItemModel.findOneOld([ + const existingLibraryItem = await Database.libraryItemModel.findOneOld( + [ + { + libraryId: libraryId + }, + sequelize.where(sequelize.literal('(SELECT count(*) FROM json_each(libraryFiles) WHERE json_valid(json_each.value) AND json_each.value->>"$.ino" = :inode)'), { + [sequelize.Op.gt]: 0 + }) + ], { - libraryId: libraryId - }, - sequelize.where(sequelize.literal('(SELECT count(*) FROM json_each(libraryFiles) WHERE json_valid(json_each.value) AND json_each.value->>"$.ino" = :inode)'), { - [sequelize.Op.gt]: 0 - }) - ], { - inode: ino - }) - if (existingLibraryItem) - Logger.debug(`[LibraryScanner] Found library item with a library file matching inode "${ino}" at path "${existingLibraryItem.path}"`) + inode: ino + } + ) + if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with a library file matching inode "${ino}" at path "${existingLibraryItem.path}"`) return existingLibraryItem } @@ -667,7 +684,6 @@ async function findLibraryItemByFileToItemInoMatch(libraryId, fullPath, isSingle [sequelize.Op.in]: itemFileInos } }) - if (existingLibraryItem) - Logger.debug(`[LibraryScanner] Found library item with inode matching one of "${itemFileInos.join(',')}" at path "${existingLibraryItem.path}"`) + if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with inode matching one of "${itemFileInos.join(',')}" at path "${existingLibraryItem.path}"`) return existingLibraryItem -} \ No newline at end of file +} diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js index ad71ee3fad..664bd6e301 100644 --- a/server/utils/libraryHelpers.js +++ b/server/utils/libraryHelpers.js @@ -83,7 +83,7 @@ module.exports = { * @param {*} payload * @param {string} seriesId * @param {import('../models/User')} user - * @param {import('../objects/Library')} library + * @param {import('../models/Library')} library * @returns {Object[]} */ async handleCollapseSubseries(payload, seriesId, user, library) { diff --git a/server/utils/queries/libraryFilters.js b/server/utils/queries/libraryFilters.js index 471a1c0b48..2268ef2111 100644 --- a/server/utils/queries/libraryFilters.js +++ b/server/utils/queries/libraryFilters.js @@ -15,12 +15,12 @@ module.exports = { /** * Get library items using filter and sort - * @param {import('../../objects/Library')} library + * @param {string} libraryId * @param {import('../../models/User')} user * @param {object} options * @returns {object} { libraryItems:LibraryItem[], count:number } */ - async getFilteredLibraryItems(library, user, options) { + async getFilteredLibraryItems(libraryId, user, options) { const { filterBy, sortBy, sortDesc, limit, offset, collapseseries, include, mediaType } = options let filterValue = null @@ -33,22 +33,22 @@ module.exports = { } if (mediaType === 'book') { - return libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, filterGroup, filterValue, sortBy, sortDesc, collapseseries, include, limit, offset) + return libraryItemsBookFilters.getFilteredLibraryItems(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, collapseseries, include, limit, offset) } else { - return libraryItemsPodcastFilters.getFilteredLibraryItems(library.id, user, filterGroup, filterValue, sortBy, sortDesc, include, limit, offset) + return libraryItemsPodcastFilters.getFilteredLibraryItems(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, include, limit, offset) } }, /** * Get library items for continue listening & continue reading shelves - * @param {import('../../objects/Library')} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {Promise<{ items:import('../../models/LibraryItem')[], count:number }>} */ async getMediaItemsInProgress(library, user, include, limit) { - if (library.mediaType === 'book') { + if (library.isBook) { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'progress', 'in-progress', 'progress', true, false, include, limit, 0, true) return { items: libraryItems.map((li) => { @@ -78,14 +78,14 @@ module.exports = { /** * Get library items for most recently added shelf - * @param {import('../../objects/Library')} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {object} { libraryItems:LibraryItem[], count:number } */ async getLibraryItemsMostRecentlyAdded(library, user, include, limit) { - if (library.mediaType === 'book') { + if (library.isBook) { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'recent', null, 'addedAt', true, false, include, limit, 0) return { libraryItems: libraryItems.map((li) => { @@ -126,7 +126,7 @@ module.exports = { /** * Get library items for continue series shelf - * @param {import('../../objects/Library')} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit @@ -154,14 +154,15 @@ module.exports = { /** * Get library items or podcast episodes for the "Listen Again" and "Read Again" shelf - * @param {import('../../objects/Library')} library + * + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit - * @returns {object} { items:object[], count:number } + * @returns {Promise<{ items:oldLibraryItem[], count:number }>} */ async getMediaFinished(library, user, include, limit) { - if (library.mediaType === 'book') { + if (library.isBook) { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'progress', 'finished', 'progress', true, false, include, limit, 0) return { items: libraryItems.map((li) => { @@ -191,7 +192,7 @@ module.exports = { /** * Get series for recent series shelf - * @param {import('../../objects/Library')} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit @@ -316,10 +317,11 @@ module.exports = { /** * Get most recently created authors for "Newest Authors" shelf * Author must be linked to at least 1 book - * @param {oldLibrary} library + * + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {number} limit - * @returns {object} { authors:oldAuthor[], count:number } + * @returns {Promise<{ authors:oldAuthor[], count:number }>} */ async getNewestAuthors(library, user, limit) { if (library.mediaType !== 'book') return { authors: [], count: 0 } @@ -359,11 +361,11 @@ module.exports = { /** * Get book library items for the "Discover" shelf - * @param {oldLibrary} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit - * @returns {object} {libraryItems:oldLibraryItem[], count:number} + * @returns {Promise<{libraryItems:oldLibraryItem[], count:number}>} */ async getLibraryItemsToDiscover(library, user, include, limit) { if (library.mediaType !== 'book') return { libraryItems: [], count: 0 } @@ -386,10 +388,10 @@ module.exports = { /** * Get podcast episodes most recently added - * @param {oldLibrary} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {number} limit - * @returns {object} {libraryItems:oldLibraryItem[], count:number} + * @returns {Promise<{libraryItems:oldLibraryItem[], count:number}>} */ async getNewestPodcastEpisodes(library, user, limit) { if (library.mediaType !== 'podcast') return { libraryItems: [], count: 0 } @@ -411,7 +413,7 @@ module.exports = { * @param {import('../../models/User')} user * @param {number} limit * @param {number} offset - * @returns {Promise} { libraryItems:LibraryItem[], count:number } + * @returns {Promise<{ libraryItems:import('../../objects/LibraryItem')[], count:number }>} */ async getLibraryItemsForAuthor(author, user, limit, offset) { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(author.libraryId, user, 'authors', author.id, 'addedAt', true, false, [], limit, offset) @@ -424,7 +426,7 @@ module.exports = { /** * Get book library items in a collection * @param {oldCollection} collection - * @returns {Promise} + * @returns {Promise} */ getLibraryItemsForCollection(collection) { return libraryItemsBookFilters.getLibraryItemsForCollection(collection) diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index 4ea28b4df3..094095ce7c 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -636,7 +636,7 @@ module.exports = { * 2. Has no books in progress * 3. Has at least 1 unfinished book * TODO: Reduce queries - * @param {import('../../objects/Library')} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit @@ -911,7 +911,7 @@ module.exports = { /** * Get book library items in a collection * @param {oldCollection} collection - * @returns {Promise} + * @returns {Promise} */ async getLibraryItemsForCollection(collection) { if (!collection?.books?.length) { diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js index 77eb50120b..50163edfbd 100644 --- a/server/utils/queries/libraryItemsPodcastFilters.js +++ b/server/utils/queries/libraryItemsPodcastFilters.js @@ -412,12 +412,12 @@ module.exports = { /** * Most recent podcast episodes not finished * @param {import('../../models/User')} user - * @param {import('../../objects/Library')} oldLibrary + * @param {import('../../models/Library')} library * @param {number} limit * @param {number} offset * @returns {Promise} */ - async getRecentEpisodes(user, oldLibrary, limit, offset) { + async getRecentEpisodes(user, library, limit, offset) { const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(user) const episodes = await Database.podcastEpisodeModel.findAll({ @@ -435,7 +435,7 @@ module.exports = { include: { model: Database.libraryItemModel, where: { - libraryId: oldLibrary.id + libraryId: library.id } } }, diff --git a/server/utils/queries/seriesFilters.js b/server/utils/queries/seriesFilters.js index c03c13bff2..b7afcf85f9 100644 --- a/server/utils/queries/seriesFilters.js +++ b/server/utils/queries/seriesFilters.js @@ -11,7 +11,7 @@ module.exports = { /** * Get series filtered and sorted * - * @param {import('../../objects/Library')} library + * @param {import('../../models/Library')} library * @param {import('../../models/User')} user * @param {string} filterBy * @param {string} sortBy