diff --git a/server/Database.js b/server/Database.js index 2137b3c1..61385981 100644 --- a/server/Database.js +++ b/server/Database.js @@ -401,17 +401,6 @@ class Database { return this.models.setting.updateSettingObj(settings.toJSON()) } - updateBulkBooks(oldBooks) { - if (!this.sequelize) return false - return Promise.all(oldBooks.map((oldBook) => this.models.book.saveFromOld(oldBook))) - } - - async createLibraryItem(oldLibraryItem) { - if (!this.sequelize) return false - await oldLibraryItem.saveMetadata() - await this.models.libraryItem.fullCreateFromOld(oldLibraryItem) - } - /** * Save metadata file and update library item * @@ -429,17 +418,6 @@ class Database { return updated } - async createBulkBookAuthors(bookAuthors) { - if (!this.sequelize) return false - await this.models.bookAuthor.bulkCreate(bookAuthors) - } - - async removeBulkBookAuthors(authorId = null, bookId = null) { - if (!this.sequelize) return false - if (!authorId && !bookId) return - await this.models.bookAuthor.removeByIds(authorId, bookId) - } - getPlaybackSessions(where = null) { if (!this.sequelize) return false return this.models.playbackSession.getOldPlaybackSessions(where) @@ -665,7 +643,7 @@ class Database { /** * Clean invalid records in database * Series should have atleast one Book - * Book and Podcast must have an associated LibraryItem + * Book and Podcast must have an associated LibraryItem (and vice versa) * Remove playback sessions that are 3 seconds or less */ async cleanDatabase() { @@ -695,6 +673,28 @@ class Database { await book.destroy() } + // Remove invalid LibraryItem records + const libraryItemsWithNoMedia = await this.libraryItemModel.findAll({ + include: [ + { + model: this.bookModel, + attributes: ['id'] + }, + { + model: this.podcastModel, + attributes: ['id'] + } + ], + where: { + '$book.id$': null, + '$podcast.id$': null + } + }) + for (const libraryItem of libraryItemsWithNoMedia) { + Logger.warn(`Found libraryItem "${libraryItem.id}" with no media - removing it`) + await libraryItem.destroy() + } + const playlistMediaItemsWithNoMediaItem = await this.playlistMediaItemModel.findAll({ include: [ { diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js index 45bbdf84..31c94307 100644 --- a/server/controllers/AuthorController.js +++ b/server/controllers/AuthorController.js @@ -44,16 +44,21 @@ class AuthorController { // Used on author landing page to include library items and items grouped in series if (include.includes('items')) { - authorJson.libraryItems = await Database.libraryItemModel.getForAuthor(req.author, req.user) + const libraryItems = await Database.libraryItemModel.getForAuthor(req.author, req.user) if (include.includes('series')) { const seriesMap = {} // Group items into series - authorJson.libraryItems.forEach((li) => { - if (li.media.metadata.series) { - li.media.metadata.series.forEach((series) => { - const itemWithSeries = li.toJSONMinified() - itemWithSeries.media.metadata.series = series + libraryItems.forEach((li) => { + if (li.media.series?.length) { + li.media.series.forEach((series) => { + const itemWithSeries = li.toOldJSONMinified() + itemWithSeries.media.metadata.series = { + id: series.id, + name: series.name, + nameIgnorePrefix: series.nameIgnorePrefix, + sequence: series.bookSeries.sequence + } if (seriesMap[series.id]) { seriesMap[series.id].items.push(itemWithSeries) @@ -76,7 +81,7 @@ class AuthorController { } // Minify library items - authorJson.libraryItems = authorJson.libraryItems.map((li) => li.toJSONMinified()) + authorJson.libraryItems = libraryItems.map((li) => li.toOldJSONMinified()) } return res.json(authorJson) @@ -125,7 +130,7 @@ class AuthorController { const bookAuthorsToCreate = [] const allItemsWithAuthor = await Database.authorModel.getAllLibraryItemsForAuthor(req.author.id) - const oldLibraryItems = [] + const libraryItems = [] allItemsWithAuthor.forEach((libraryItem) => { // Replace old author with merging author for each book libraryItem.media.authors = libraryItem.media.authors.filter((au) => au.id !== req.author.id) @@ -134,23 +139,22 @@ class AuthorController { name: existingAuthor.name }) - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - oldLibraryItems.push(oldLibraryItem) + libraryItems.push(libraryItem) bookAuthorsToCreate.push({ bookId: libraryItem.media.id, authorId: existingAuthor.id }) }) - if (oldLibraryItems.length) { - await Database.removeBulkBookAuthors(req.author.id) // Remove all old BookAuthor - await Database.createBulkBookAuthors(bookAuthorsToCreate) // Create all new BookAuthor - for (const libraryItem of allItemsWithAuthor) { + if (libraryItems.length) { + await Database.bookAuthorModel.removeByIds(req.author.id) // Remove all old BookAuthor + await Database.bookAuthorModel.bulkCreate(bookAuthorsToCreate) // Create all new BookAuthor + for (const libraryItem of libraryItems) { await libraryItem.saveMetadataFile() } SocketAuthority.emitter( 'items_updated', - oldLibraryItems.map((li) => li.toJSONExpanded()) + libraryItems.map((li) => li.toOldJSONExpanded()) ) } @@ -190,7 +194,7 @@ class AuthorController { const allItemsWithAuthor = await Database.authorModel.getAllLibraryItemsForAuthor(req.author.id) numBooksForAuthor = allItemsWithAuthor.length - const oldLibraryItems = [] + const libraryItems = [] // Update author name on all books for (const libraryItem of allItemsWithAuthor) { libraryItem.media.authors = libraryItem.media.authors.map((au) => { @@ -199,16 +203,16 @@ class AuthorController { } return au }) - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - oldLibraryItems.push(oldLibraryItem) + + libraryItems.push(libraryItem) await libraryItem.saveMetadataFile() } - if (oldLibraryItems.length) { + if (libraryItems.length) { SocketAuthority.emitter( 'items_updated', - oldLibraryItems.map((li) => li.toJSONExpanded()) + libraryItems.map((li) => li.toOldJSONExpanded()) ) } } else { diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 6986f2b7..00b82ce9 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -221,7 +221,9 @@ class CollectionController { * @param {Response} res */ async addBook(req, res) { - const libraryItem = await Database.libraryItemModel.getOldById(req.body.id) + const libraryItem = await Database.libraryItemModel.findByPk(req.body.id, { + attributes: ['libraryId', 'mediaId'] + }) if (!libraryItem) { return res.status(404).send('Book not found') } @@ -231,14 +233,14 @@ class CollectionController { // Check if book is already in collection const collectionBooks = await req.collection.getCollectionBooks() - if (collectionBooks.some((cb) => cb.bookId === libraryItem.media.id)) { + if (collectionBooks.some((cb) => cb.bookId === libraryItem.mediaId)) { return res.status(400).send('Book already in collection') } // Create collectionBook record await Database.collectionBookModel.create({ collectionId: req.collection.id, - bookId: libraryItem.media.id, + bookId: libraryItem.mediaId, order: collectionBooks.length + 1 }) const jsonExpanded = await req.collection.getOldJsonExpanded() @@ -255,7 +257,9 @@ class CollectionController { * @param {Response} res */ async removeBook(req, res) { - const libraryItem = await Database.libraryItemModel.getOldById(req.params.bookId) + const libraryItem = await Database.libraryItemModel.findByPk(req.params.bookId, { + attributes: ['mediaId'] + }) if (!libraryItem) { return res.sendStatus(404) } @@ -266,7 +270,7 @@ class CollectionController { }) let jsonExpanded = null - const collectionBookToRemove = collectionBooks.find((cb) => cb.bookId === libraryItem.media.id) + const collectionBookToRemove = collectionBooks.find((cb) => cb.bookId === libraryItem.mediaId) if (collectionBookToRemove) { // Remove collection book record await collectionBookToRemove.destroy() @@ -274,7 +278,7 @@ class CollectionController { // Update order on collection books let order = 1 for (const collectionBook of collectionBooks) { - if (collectionBook.bookId === libraryItem.media.id) continue + if (collectionBook.bookId === libraryItem.mediaId) continue if (collectionBook.order !== order) { await collectionBook.update({ order diff --git a/server/controllers/EmailController.js b/server/controllers/EmailController.js index 916b4268..5d433e0a 100644 --- a/server/controllers/EmailController.js +++ b/server/controllers/EmailController.js @@ -106,7 +106,7 @@ class EmailController { return res.sendStatus(403) } - const libraryItem = await Database.libraryItemModel.getOldById(req.body.libraryItemId) + const libraryItem = await Database.libraryItemModel.getExpandedById(req.body.libraryItemId) if (!libraryItem) { return res.status(404).send('Library item not found') } diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index f42a023d..216f7595 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -1145,14 +1145,14 @@ class LibraryController { await libraryItem.media.update({ narrators: libraryItem.media.narrators }) - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - itemsUpdated.push(oldLibraryItem) + + itemsUpdated.push(libraryItem) } if (itemsUpdated.length) { SocketAuthority.emitter( 'items_updated', - itemsUpdated.map((li) => li.toJSONExpanded()) + itemsUpdated.map((li) => li.toOldJSONExpanded()) ) } @@ -1189,14 +1189,14 @@ class LibraryController { await libraryItem.media.update({ narrators: libraryItem.media.narrators }) - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - itemsUpdated.push(oldLibraryItem) + + itemsUpdated.push(libraryItem) } if (itemsUpdated.length) { SocketAuthority.emitter( 'items_updated', - itemsUpdated.map((li) => li.toJSONExpanded()) + itemsUpdated.map((li) => li.toOldJSONExpanded()) ) } diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index 74b8bdfc..14a85f6e 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -551,11 +551,11 @@ class LibraryItemController { const hardDelete = req.query.hard == 1 // Delete files from filesystem const { libraryItemIds } = req.body - if (!libraryItemIds?.length) { + if (!libraryItemIds?.length || !Array.isArray(libraryItemIds)) { return res.status(400).send('Invalid request body') } - const itemsToDelete = await Database.libraryItemModel.getAllOldLibraryItems({ + const itemsToDelete = await Database.libraryItemModel.findAllExpandedWhere({ id: libraryItemIds }) @@ -566,19 +566,19 @@ class LibraryItemController { const libraryId = itemsToDelete[0].libraryId for (const libraryItem of itemsToDelete) { const libraryItemPath = libraryItem.path - Logger.info(`[LibraryItemController] (${hardDelete ? 'Hard' : 'Soft'}) deleting Library Item "${libraryItem.media.metadata.title}" with id "${libraryItem.id}"`) + Logger.info(`[LibraryItemController] (${hardDelete ? 'Hard' : 'Soft'}) deleting Library Item "${libraryItem.media.title}" with id "${libraryItem.id}"`) const mediaItemIds = [] const seriesIds = [] const authorIds = [] if (libraryItem.isPodcast) { - mediaItemIds.push(...libraryItem.media.episodes.map((ep) => ep.id)) + mediaItemIds.push(...libraryItem.media.podcastEpisodes.map((ep) => ep.id)) } else { mediaItemIds.push(libraryItem.media.id) - if (libraryItem.media.metadata.series?.length) { - seriesIds.push(...libraryItem.media.metadata.series.map((se) => se.id)) + if (libraryItem.media.series?.length) { + seriesIds.push(...libraryItem.media.series.map((se) => se.id)) } - if (libraryItem.media.metadata.authors?.length) { - authorIds.push(...libraryItem.media.metadata.authors.map((au) => au.id)) + if (libraryItem.media.authors?.length) { + authorIds.push(...libraryItem.media.authors.map((au) => au.id)) } } await this.handleDeleteLibraryItem(libraryItem.id, mediaItemIds) @@ -623,7 +623,7 @@ class LibraryItemController { } // Get all library items to update - const libraryItems = await Database.libraryItemModel.getAllOldLibraryItems({ + const libraryItems = await Database.libraryItemModel.findAllExpandedWhere({ id: libraryItemIds }) if (updatePayloads.length !== libraryItems.length) { @@ -645,21 +645,23 @@ class LibraryItemController { if (libraryItem.isBook) { if (Array.isArray(mediaPayload.metadata?.series)) { const seriesIdsInUpdate = mediaPayload.metadata.series.map((se) => se.id) - const seriesRemoved = libraryItem.media.metadata.series.filter((se) => !seriesIdsInUpdate.includes(se.id)) + const seriesRemoved = libraryItem.media.series.filter((se) => !seriesIdsInUpdate.includes(se.id)) seriesIdsRemoved.push(...seriesRemoved.map((se) => se.id)) } if (Array.isArray(mediaPayload.metadata?.authors)) { const authorIdsInUpdate = mediaPayload.metadata.authors.map((au) => au.id) - const authorsRemoved = libraryItem.media.metadata.authors.filter((au) => !authorIdsInUpdate.includes(au.id)) + const authorsRemoved = libraryItem.media.authors.filter((au) => !authorIdsInUpdate.includes(au.id)) authorIdsRemoved.push(...authorsRemoved.map((au) => au.id)) } } - if (libraryItem.media.update(mediaPayload)) { - Logger.debug(`[LibraryItemController] Updated library item media ${libraryItem.media.metadata.title}`) + const hasUpdates = await libraryItem.media.updateFromRequest(mediaPayload) + if (hasUpdates) { + libraryItem.changed('updatedAt', true) + await libraryItem.save() - await Database.updateLibraryItem(libraryItem) - SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded()) + Logger.debug(`[LibraryItemController] Updated library item media "${libraryItem.media.title}"`) + SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) itemsUpdated++ } } @@ -688,11 +690,11 @@ class LibraryItemController { if (!libraryItemIds.length) { return res.status(403).send('Invalid payload') } - const libraryItems = await Database.libraryItemModel.getAllOldLibraryItems({ + const libraryItems = await Database.libraryItemModel.findAllExpandedWhere({ id: libraryItemIds }) res.json({ - libraryItems: libraryItems.map((li) => li.toJSONExpanded()) + libraryItems: libraryItems.map((li) => li.toOldJSONExpanded()) }) } @@ -715,7 +717,7 @@ class LibraryItemController { return res.sendStatus(400) } - const libraryItems = await Database.libraryItemModel.getAllOldLibraryItems({ + const libraryItems = await Database.libraryItemModel.findAllExpandedWhere({ id: req.body.libraryItemIds }) if (!libraryItems?.length) { @@ -737,7 +739,8 @@ class LibraryItemController { } for (const libraryItem of libraryItems) { - const matchResult = await Scanner.quickMatchLibraryItem(this, libraryItem, options) + const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) + const matchResult = await Scanner.quickMatchLibraryItem(this, oldLibraryItem, options) if (matchResult.updated) { itemsUpdated++ } else if (matchResult.warning) { diff --git a/server/controllers/MeController.js b/server/controllers/MeController.js index cc67b320..87acd221 100644 --- a/server/controllers/MeController.js +++ b/server/controllers/MeController.js @@ -66,7 +66,7 @@ class MeController { const libraryItem = await Database.libraryItemModel.findByPk(req.params.libraryItemId) const episode = await Database.podcastEpisodeModel.findByPk(req.params.episodeId) - if (!libraryItem || (libraryItem.mediaType === 'podcast' && !episode)) { + if (!libraryItem || (libraryItem.isPodcast && !episode)) { Logger.error(`[MeController] Media item not found for library item id "${req.params.libraryItemId}"`) return res.sendStatus(404) } @@ -296,7 +296,7 @@ class MeController { const mediaProgressesInProgress = req.user.mediaProgresses.filter((mp) => !mp.isFinished && (mp.currentTime > 0 || mp.ebookProgress > 0)) const libraryItemsIds = [...new Set(mediaProgressesInProgress.map((mp) => mp.extraData?.libraryItemId).filter((id) => id))] - const libraryItems = await Database.libraryItemModel.getAllOldLibraryItems({ id: libraryItemsIds }) + const libraryItems = await Database.libraryItemModel.findAllExpandedWhere({ id: libraryItemsIds }) let itemsInProgress = [] @@ -304,19 +304,19 @@ class MeController { const oldMediaProgress = mediaProgress.getOldMediaProgress() const libraryItem = libraryItems.find((li) => li.id === oldMediaProgress.libraryItemId) if (libraryItem) { - if (oldMediaProgress.episodeId && libraryItem.mediaType === 'podcast') { - const episode = libraryItem.media.episodes.find((ep) => ep.id === oldMediaProgress.episodeId) + if (oldMediaProgress.episodeId && libraryItem.isPodcast) { + const episode = libraryItem.media.podcastEpisodes.find((ep) => ep.id === oldMediaProgress.episodeId) if (episode) { const libraryItemWithEpisode = { - ...libraryItem.toJSONMinified(), - recentEpisode: episode.toJSON(), + ...libraryItem.toOldJSONMinified(), + recentEpisode: episode.toOldJSON(libraryItem.id), progressLastUpdate: oldMediaProgress.lastUpdate } itemsInProgress.push(libraryItemWithEpisode) } } else if (!oldMediaProgress.episodeId) { itemsInProgress.push({ - ...libraryItem.toJSONMinified(), + ...libraryItem.toOldJSONMinified(), progressLastUpdate: oldMediaProgress.lastUpdate }) } diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index b35619b7..48eca3f8 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -342,8 +342,8 @@ class MiscController { tags: libraryItem.media.tags }) await libraryItem.saveMetadataFile() - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded()) + + SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) numItemsUpdated++ } } @@ -385,8 +385,8 @@ class MiscController { tags: libraryItem.media.tags }) await libraryItem.saveMetadataFile() - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded()) + + SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) numItemsUpdated++ } @@ -480,8 +480,8 @@ class MiscController { genres: libraryItem.media.genres }) await libraryItem.saveMetadataFile() - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded()) + + SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) numItemsUpdated++ } } @@ -523,8 +523,8 @@ class MiscController { genres: libraryItem.media.genres }) await libraryItem.saveMetadataFile() - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded()) + + SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) numItemsUpdated++ } diff --git a/server/controllers/PlaylistController.js b/server/controllers/PlaylistController.js index 8c13ecb2..972c352a 100644 --- a/server/controllers/PlaylistController.js +++ b/server/controllers/PlaylistController.js @@ -276,7 +276,7 @@ class PlaylistController { return res.status(400).send('Request body has no libraryItemId') } - const libraryItem = await Database.libraryItemModel.getOldById(itemToAdd.libraryItemId) + const libraryItem = await Database.libraryItemModel.getExpandedById(itemToAdd.libraryItemId) if (!libraryItem) { return res.status(400).send('Library item not found') } @@ -286,7 +286,7 @@ class PlaylistController { 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)) { + if (itemToAdd.episodeId && !libraryItem.media.podcastEpisodes.some((pe) => pe.id === itemToAdd.episodeId)) { return res.status(400).send('Episode not found in library item') } @@ -308,17 +308,17 @@ class PlaylistController { // Add the new item to to the old json expanded to prevent having to fully reload the playlist media items if (itemToAdd.episodeId) { - const episode = libraryItem.media.episodes.find((ep) => ep.id === itemToAdd.episodeId) + const episode = libraryItem.media.podcastEpisodes.find((ep) => ep.id === itemToAdd.episodeId) jsonExpanded.items.push({ episodeId: itemToAdd.episodeId, - episode: episode.toJSONExpanded(), + episode: episode.toOldJSONExpanded(libraryItem.id), libraryItemId: libraryItem.id, - libraryItem: libraryItem.toJSONMinified() + libraryItem: libraryItem.toOldJSONMinified() }) } else { jsonExpanded.items.push({ libraryItemId: libraryItem.id, - libraryItem: libraryItem.toJSONExpanded() + libraryItem: libraryItem.toOldJSONExpanded() }) } @@ -388,8 +388,8 @@ class PlaylistController { // Find all library items const libraryItemIds = new Set(req.body.items.map((i) => i.libraryItemId).filter((i) => i)) - const oldLibraryItems = await Database.libraryItemModel.getAllOldLibraryItems({ id: Array.from(libraryItemIds) }) - if (oldLibraryItems.length !== libraryItemIds.size) { + const libraryItems = await Database.libraryItemModel.findAllExpandedWhere({ id: Array.from(libraryItemIds) }) + if (libraryItems.length !== libraryItemIds.size) { return res.status(400).send('Invalid request body items') } @@ -401,7 +401,7 @@ class PlaylistController { // Setup array of playlistMediaItem records to add let order = req.playlist.playlistMediaItems.length + 1 for (const item of req.body.items) { - const libraryItem = oldLibraryItems.find((li) => li.id === item.libraryItemId) + const libraryItem = libraryItems.find((li) => li.id === item.libraryItemId) const mediaItemId = item.episodeId || libraryItem.media.id if (req.playlist.playlistMediaItems.some((pmi) => pmi.mediaItemId === mediaItemId)) { @@ -417,17 +417,17 @@ class PlaylistController { // Add the new item to to the old json expanded to prevent having to fully reload the playlist media items if (item.episodeId) { - const episode = libraryItem.media.episodes.find((ep) => ep.id === item.episodeId) + const episode = libraryItem.media.podcastEpisodes.find((ep) => ep.id === item.episodeId) jsonExpanded.items.push({ episodeId: item.episodeId, - episode: episode.toJSONExpanded(), + episode: episode.toOldJSONExpanded(libraryItem.id), libraryItemId: libraryItem.id, - libraryItem: libraryItem.toJSONMinified() + libraryItem: libraryItem.toOldJSONMinified() }) } else { jsonExpanded.items.push({ libraryItemId: libraryItem.id, - libraryItem: libraryItem.toJSONExpanded() + libraryItem: libraryItem.toOldJSONExpanded() }) } } diff --git a/server/managers/PlaybackSessionManager.js b/server/managers/PlaybackSessionManager.js index 97c87bbe..25992f0a 100644 --- a/server/managers/PlaybackSessionManager.js +++ b/server/managers/PlaybackSessionManager.js @@ -120,8 +120,8 @@ class PlaybackSessionManager { */ async syncLocalSession(user, sessionJson, deviceInfo) { // TODO: Combine libraryItem query with library query - const libraryItem = await Database.libraryItemModel.getOldById(sessionJson.libraryItemId) - const episode = sessionJson.episodeId && libraryItem && libraryItem.isPodcast ? libraryItem.media.getEpisode(sessionJson.episodeId) : null + const libraryItem = await Database.libraryItemModel.getExpandedById(sessionJson.libraryItemId) + const episode = sessionJson.episodeId && libraryItem && libraryItem.isPodcast ? libraryItem.media.podcastEpisodes.find((pe) => pe.id === sessionJson.episodeId) : null if (!libraryItem || (libraryItem.isPodcast && !episode)) { Logger.error(`[PlaybackSessionManager] syncLocalSession: Media item not found for session "${sessionJson.displayTitle}" (${sessionJson.id})`) return { @@ -175,7 +175,8 @@ class PlaybackSessionManager { // New session from local session = new PlaybackSession(sessionJson) session.deviceInfo = deviceInfo - session.setDuration(libraryItem, sessionJson.episodeId) + session.duration = libraryItem.media.getPlaybackDuration(sessionJson.episodeId) + Logger.debug(`[PlaybackSessionManager] Inserting new session for "${session.displayTitle}" (${session.id})`) await Database.createPlaybackSession(session) } else { @@ -346,7 +347,7 @@ class PlaybackSessionManager { */ async syncSession(user, session, syncData) { // TODO: Combine libraryItem query with library query - const libraryItem = await Database.libraryItemModel.getOldById(session.libraryItemId) + const libraryItem = await Database.libraryItemModel.getExpandedById(session.libraryItemId) if (!libraryItem) { Logger.error(`[PlaybackSessionManager] syncSession Library Item not found "${session.libraryItemId}"`) return null @@ -381,9 +382,6 @@ class PlaybackSessionManager { }) } this.saveSession(session) - return { - libraryItem - } } /** diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js index bd42e74b..0b45dfc4 100644 --- a/server/managers/PodcastManager.js +++ b/server/managers/PodcastManager.js @@ -296,9 +296,9 @@ class PodcastManager { Logger.info(`[PodcastManager] runEpisodeCheck: "${libraryItem.media.title}" | Last check: ${new Date(lastEpisodeCheck)} | ${latestEpisodePublishedAt ? `Latest episode pubDate: ${new Date(latestEpisodePublishedAt)}` : 'No latest episode'}`) - // Use latest episode pubDate if exists OR fallback to using lastEpisodeCheckDate - // lastEpisodeCheckDate will be the current time when adding a new podcast - const dateToCheckForEpisodesAfter = latestEpisodePublishedAt || lastEpisodeCheckDate + // Use latest episode pubDate if exists OR fallback to using lastEpisodeCheck + // lastEpisodeCheck will be the current time when adding a new podcast + const dateToCheckForEpisodesAfter = latestEpisodePublishedAt || lastEpisodeCheck Logger.debug(`[PodcastManager] runEpisodeCheck: "${libraryItem.media.title}" checking for episodes after ${new Date(dateToCheckForEpisodesAfter)}`) const newEpisodes = await this.checkPodcastForNewEpisodes(libraryItem, dateToCheckForEpisodesAfter, libraryItem.media.maxNewEpisodesToDownload) diff --git a/server/models/Collection.js b/server/models/Collection.js index c8f62e69..d5f36ba9 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -282,7 +282,7 @@ class Collection extends Model { const libraryItem = book.libraryItem delete book.libraryItem libraryItem.media = book - return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem).toJSONExpanded() + return libraryItem.toOldJSONExpanded() }) return json diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index 3381b94a..31a6a0b4 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -122,45 +122,6 @@ class LibraryItem extends Model { }) } - /** - * - * @param {import('sequelize').WhereOptions} [where] - * @returns {Array} old library items - */ - static async getAllOldLibraryItems(where = null) { - let libraryItems = await this.findAll({ - where, - include: [ - { - model: this.sequelize.models.book, - include: [ - { - model: this.sequelize.models.author, - through: { - attributes: [] - } - }, - { - model: this.sequelize.models.series, - through: { - attributes: ['sequence'] - } - } - ] - }, - { - model: this.sequelize.models.podcast, - include: [ - { - model: this.sequelize.models.podcastEpisode - } - ] - } - ] - }) - return libraryItems.map((ti) => this.getOldLibraryItem(ti)) - } - /** * Convert an expanded LibraryItem into an old library item * @@ -199,40 +160,6 @@ class LibraryItem extends Model { }) } - static async fullCreateFromOld(oldLibraryItem) { - const newLibraryItem = await this.create(this.getFromOld(oldLibraryItem)) - - if (oldLibraryItem.mediaType === 'book') { - const bookObj = this.sequelize.models.book.getFromOld(oldLibraryItem.media) - bookObj.libraryItemId = newLibraryItem.id - const newBook = await this.sequelize.models.book.create(bookObj) - - const oldBookAuthors = oldLibraryItem.media.metadata.authors || [] - const oldBookSeriesAll = oldLibraryItem.media.metadata.series || [] - - for (const oldBookAuthor of oldBookAuthors) { - await this.sequelize.models.bookAuthor.create({ authorId: oldBookAuthor.id, bookId: newBook.id }) - } - for (const oldSeries of oldBookSeriesAll) { - await this.sequelize.models.bookSeries.create({ seriesId: oldSeries.id, bookId: newBook.id, sequence: oldSeries.sequence }) - } - } else if (oldLibraryItem.mediaType === 'podcast') { - const podcastObj = this.sequelize.models.podcast.getFromOld(oldLibraryItem.media) - podcastObj.libraryItemId = newLibraryItem.id - const newPodcast = await this.sequelize.models.podcast.create(podcastObj) - - const oldEpisodes = oldLibraryItem.media.episodes || [] - for (const oldEpisode of oldEpisodes) { - const episodeObj = this.sequelize.models.podcastEpisode.getFromOld(oldEpisode) - episodeObj.libraryItemId = newLibraryItem.id - episodeObj.podcastId = newPodcast.id - await this.sequelize.models.podcastEpisode.create(episodeObj) - } - } - - return newLibraryItem - } - /** * Updates libraryItem, book, authors and series from old library item * @@ -448,6 +375,47 @@ class LibraryItem extends Model { }) } + /** + * + * @param {import('sequelize').WhereOptions} where + * @returns {Promise} + */ + static async findAllExpandedWhere(where = null) { + return this.findAll({ + where, + include: [ + { + model: this.sequelize.models.book, + include: [ + { + model: this.sequelize.models.author, + through: { + attributes: [] + } + }, + { + model: this.sequelize.models.series, + through: { + attributes: ['sequence'] + } + } + ] + }, + { + model: this.sequelize.models.podcast, + include: { + model: this.sequelize.models.podcastEpisode + } + } + ], + order: [ + // Ensure author & series stay in the same order + [this.sequelize.models.book, this.sequelize.models.author, this.sequelize.models.bookAuthor, 'createdAt', 'ASC'], + [this.sequelize.models.book, this.sequelize.models.series, 'bookSeries', 'createdAt', 'ASC'] + ] + }) + } + /** * * @param {string} libraryItemId @@ -611,7 +579,7 @@ class LibraryItem extends Model { return { libraryItems: libraryItems.map((li) => { - const oldLibraryItem = this.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.collapsedSeries) { oldLibraryItem.collapsedSeries = li.collapsedSeries } @@ -817,21 +785,11 @@ class LibraryItem extends Model { * Get book library items for author, optional use user permissions * @param {import('./Author')} author * @param {import('./User')} user - * @returns {Promise} + * @returns {Promise} */ static async getForAuthor(author, user = null) { const { libraryItems } = await libraryFilters.getLibraryItemsForAuthor(author, user, undefined, undefined) - return libraryItems.map((li) => this.getOldLibraryItem(li)) - } - - /** - * Get book library items in a collection - * @param {oldCollection} collection - * @returns {Promise} - */ - static async getForCollection(collection) { - const libraryItems = await libraryFilters.getLibraryItemsForCollection(collection) - return libraryItems.map((li) => this.getOldLibraryItem(li)) + return libraryItems } /** diff --git a/server/models/Playlist.js b/server/models/Playlist.js index ec56248d..35bd6c99 100644 --- a/server/models/Playlist.js +++ b/server/models/Playlist.js @@ -357,7 +357,7 @@ class Playlist extends Model { libraryItem.media = pmi.mediaItem return { libraryItemId: libraryItem.id, - libraryItem: this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem).toJSONExpanded() + libraryItem: libraryItem.toOldJSONExpanded() } } @@ -368,7 +368,7 @@ class Playlist extends Model { episodeId: pmi.mediaItemId, episode: pmi.mediaItem.toOldJSONExpanded(libraryItem.id), libraryItemId: libraryItem.id, - libraryItem: this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem).toJSONMinified() + libraryItem: libraryItem.toOldJSONMinified() } }) diff --git a/server/objects/LibraryItem.js b/server/objects/LibraryItem.js index 17d7484c..d955356e 100644 --- a/server/objects/LibraryItem.js +++ b/server/objects/LibraryItem.js @@ -269,16 +269,5 @@ class LibraryItem { this.isSavingMetadata = false }) } - - removeLibraryFile(ino) { - if (!ino) return false - const libraryFile = this.libraryFiles.find((lf) => lf.ino === ino) - if (libraryFile) { - this.libraryFiles = this.libraryFiles.filter((lf) => lf.ino !== ino) - this.updatedAt = Date.now() - return true - } - return false - } } module.exports = LibraryItem diff --git a/server/objects/mediaTypes/Podcast.js b/server/objects/mediaTypes/Podcast.js index 5f43ebc8..f27f3fa2 100644 --- a/server/objects/mediaTypes/Podcast.js +++ b/server/objects/mediaTypes/Podcast.js @@ -166,10 +166,6 @@ class Podcast { return true } - checkHasEpisode(episodeId) { - return this.episodes.some((ep) => ep.id === episodeId) - } - getEpisode(episodeId) { if (!episodeId) return null diff --git a/server/scanner/LibraryItemScanner.js b/server/scanner/LibraryItemScanner.js index 5edfc2e2..bd99060c 100644 --- a/server/scanner/LibraryItemScanner.js +++ b/server/scanner/LibraryItemScanner.js @@ -64,8 +64,7 @@ class LibraryItemScanner { const { libraryItem: expandedLibraryItem, wasUpdated } = await this.rescanLibraryItemMedia(libraryItem, libraryItemScanData, library.settings, scanLogger) if (libraryItemDataUpdated || wasUpdated) { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(expandedLibraryItem) - SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded()) + SocketAuthority.emitter('item_updated', expandedLibraryItem.toOldJSONExpanded()) await this.checkAuthorsAndSeriesRemovedFromBooks(library.id, scanLogger) diff --git a/server/scanner/LibraryScanner.js b/server/scanner/LibraryScanner.js index a52350f6..c4f6410d 100644 --- a/server/scanner/LibraryScanner.js +++ b/server/scanner/LibraryScanner.js @@ -167,7 +167,7 @@ class LibraryScanner { if (this.shouldCancelScan(libraryScan)) return true const libraryItemIdsMissing = [] - let oldLibraryItemsUpdated = [] + let libraryItemsUpdated = [] 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) @@ -190,11 +190,11 @@ class LibraryScanner { libraryItemIdsMissing.push(existingLibraryItem.id) // TODO: Temporary while using old model to socket emit - const oldLibraryItem = await Database.libraryItemModel.getOldById(existingLibraryItem.id) - if (oldLibraryItem) { - oldLibraryItem.isMissing = true - oldLibraryItem.updatedAt = Date.now() - oldLibraryItemsUpdated.push(oldLibraryItem) + const libraryItem = await Database.libraryItemModel.getExpandedById(existingLibraryItem.id) + if (libraryItem) { + libraryItem.isMissing = true + await libraryItem.save() + libraryItemsUpdated.push(libraryItem) } } } @@ -206,16 +206,15 @@ class LibraryScanner { const { libraryItem, wasUpdated } = await LibraryItemScanner.rescanLibraryItemMedia(existingLibraryItem, libraryItemData, libraryScan.library.settings, libraryScan) if (!forceRescan || wasUpdated) { libraryScan.resultsUpdated++ - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem) - oldLibraryItemsUpdated.push(oldLibraryItem) + libraryItemsUpdated.push(libraryItem) } else { libraryScan.addLog(LogLevel.DEBUG, `Library item "${existingLibraryItem.relPath}" is up-to-date`) } } else { libraryScan.resultsUpdated++ // TODO: Temporary while using old model to socket emit - const oldLibraryItem = await Database.libraryItemModel.getOldById(existingLibraryItem.id) - oldLibraryItemsUpdated.push(oldLibraryItem) + const libraryItem = await Database.libraryItemModel.getExpandedById(existingLibraryItem.id) + libraryItemsUpdated.push(libraryItem) } } else { libraryScan.addLog(LogLevel.DEBUG, `Library item "${existingLibraryItem.relPath}" is up-to-date`) @@ -223,23 +222,23 @@ class LibraryScanner { } // Emit item updates in chunks of 10 to client - if (oldLibraryItemsUpdated.length === 10) { + if (libraryItemsUpdated.length === 10) { // TODO: Should only emit to clients where library item is accessible SocketAuthority.emitter( 'items_updated', - oldLibraryItemsUpdated.map((li) => li.toJSONExpanded()) + libraryItemsUpdated.map((li) => li.toOldJSONExpanded()) ) - oldLibraryItemsUpdated = [] + libraryItemsUpdated = [] } if (this.shouldCancelScan(libraryScan)) return true } // Emit item updates to client - if (oldLibraryItemsUpdated.length) { + if (libraryItemsUpdated.length) { // TODO: Should only emit to clients where library item is accessible SocketAuthority.emitter( 'items_updated', - oldLibraryItemsUpdated.map((li) => li.toJSONExpanded()) + libraryItemsUpdated.map((li) => li.toOldJSONExpanded()) ) } @@ -267,34 +266,33 @@ class LibraryScanner { // Add new library items if (libraryItemDataFound.length) { - let newOldLibraryItems = [] + let newLibraryItems = [] for (const libraryItemData of libraryItemDataFound) { const newLibraryItem = await LibraryItemScanner.scanNewLibraryItem(libraryItemData, libraryScan.library.settings, libraryScan) if (newLibraryItem) { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(newLibraryItem) - newOldLibraryItems.push(oldLibraryItem) + newLibraryItems.push(newLibraryItem) libraryScan.resultsAdded++ } // Emit new items in chunks of 10 to client - if (newOldLibraryItems.length === 10) { + if (newLibraryItems.length === 10) { // TODO: Should only emit to clients where library item is accessible SocketAuthority.emitter( 'items_added', - newOldLibraryItems.map((li) => li.toJSONExpanded()) + newLibraryItems.map((li) => li.toOldJSONExpanded()) ) - newOldLibraryItems = [] + newLibraryItems = [] } if (this.shouldCancelScan(libraryScan)) return true } // Emit new items to client - if (newOldLibraryItems.length) { + if (newLibraryItems.length) { // TODO: Should only emit to clients where library item is accessible SocketAuthority.emitter( 'items_added', - newOldLibraryItems.map((li) => li.toJSONExpanded()) + newLibraryItems.map((li) => li.toOldJSONExpanded()) ) } } @@ -645,8 +643,7 @@ class LibraryScanner { const isSingleMediaItem = isSingleMediaFile(fileUpdateGroup, itemDir) const newLibraryItem = await LibraryItemScanner.scanPotentialNewLibraryItem(fullPath, library, folder, isSingleMediaItem) if (newLibraryItem) { - const oldNewLibraryItem = Database.libraryItemModel.getOldLibraryItem(newLibraryItem) - SocketAuthority.emitter('item_added', oldNewLibraryItem.toJSONExpanded()) + SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded()) } itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING } diff --git a/server/utils/migrations/dbMigration.js b/server/utils/migrations/dbMigration.js index 8337f5aa..eb42c81c 100644 --- a/server/utils/migrations/dbMigration.js +++ b/server/utils/migrations/dbMigration.js @@ -1200,7 +1200,7 @@ async function migrationPatchNewColumns(queryInterface) { */ async function handleOldLibraryItems(ctx) { const oldLibraryItems = await oldDbFiles.loadOldData('libraryItems') - const libraryItems = await ctx.models.libraryItem.getAllOldLibraryItems() + const libraryItems = (await ctx.models.libraryItem.findAllExpandedWhere()).map((li) => ctx.models.libraryItem.getOldLibraryItem(li)) const bulkUpdateItems = [] const bulkUpdateEpisodes = [] diff --git a/server/utils/queries/libraryFilters.js b/server/utils/queries/libraryFilters.js index 57ca48ba..60c07805 100644 --- a/server/utils/queries/libraryFilters.js +++ b/server/utils/queries/libraryFilters.js @@ -18,7 +18,7 @@ module.exports = { * @param {string} libraryId * @param {import('../../models/User')} user * @param {object} options - * @returns {object} { libraryItems:LibraryItem[], count:number } + * @returns {Promise<{ libraryItems:import('../../models/LibraryItem')[], count:number }>} */ async getFilteredLibraryItems(libraryId, user, options) { const { filterBy, sortBy, sortDesc, limit, offset, collapseseries, include, mediaType } = options @@ -52,7 +52,7 @@ module.exports = { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'progress', 'in-progress', 'progress', true, false, include, limit, 0, true) return { items: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.rssFeed) { oldLibraryItem.rssFeed = li.rssFeed.toOldJSONMinified() } @@ -68,7 +68,7 @@ module.exports = { return { count, items: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() oldLibraryItem.recentEpisode = li.recentEpisode return oldLibraryItem }) @@ -89,7 +89,7 @@ module.exports = { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'recent', null, 'addedAt', true, false, include, limit, 0) return { libraryItems: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.rssFeed) { oldLibraryItem.rssFeed = li.rssFeed.toOldJSONMinified() } @@ -107,7 +107,7 @@ module.exports = { const { libraryItems, count } = await libraryItemsPodcastFilters.getFilteredLibraryItems(library.id, user, 'recent', null, 'addedAt', true, include, limit, 0) return { libraryItems: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.rssFeed) { oldLibraryItem.rssFeed = li.rssFeed.toOldJSONMinified() } @@ -136,7 +136,7 @@ module.exports = { const { libraryItems, count } = await libraryItemsBookFilters.getContinueSeriesLibraryItems(library, user, include, limit, 0) return { libraryItems: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.rssFeed) { oldLibraryItem.rssFeed = li.rssFeed.toOldJSONMinified() } @@ -166,7 +166,7 @@ module.exports = { const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'progress', 'finished', 'progress', true, false, include, limit, 0) return { items: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.rssFeed) { oldLibraryItem.rssFeed = li.rssFeed.toOldJSONMinified() } @@ -182,7 +182,7 @@ module.exports = { return { count, items: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() oldLibraryItem.recentEpisode = li.recentEpisode return oldLibraryItem }) @@ -293,15 +293,17 @@ module.exports = { }) oldSeries.books = s.bookSeries .map((bs) => { - const libraryItem = bs.book.libraryItem?.toJSON() + const libraryItem = bs.book.libraryItem if (!libraryItem) { Logger.warn(`Book series book has no libraryItem`, bs, bs.book, 'series=', series) return null } delete bs.book.libraryItem + bs.book.authors = [] // Not needed + bs.book.series = [] // Not needed libraryItem.media = bs.book - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem).toJSONMinified() + const oldLibraryItem = libraryItem.toOldJSONMinified() return oldLibraryItem }) .filter((b) => b) @@ -373,7 +375,7 @@ module.exports = { const { libraryItems, count } = await libraryItemsBookFilters.getDiscoverLibraryItems(library.id, user, include, limit) return { libraryItems: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() if (li.rssFeed) { oldLibraryItem.rssFeed = li.rssFeed.toOldJSONMinified() } @@ -400,7 +402,7 @@ module.exports = { return { count, libraryItems: libraryItems.map((li) => { - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified() + const oldLibraryItem = li.toOldJSONMinified() oldLibraryItem.recentEpisode = li.recentEpisode return oldLibraryItem }) diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index ccce5304..9e74276a 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -349,7 +349,7 @@ module.exports = { * @param {number} limit * @param {number} offset * @param {boolean} isHomePage for home page shelves - * @returns {object} { libraryItems:LibraryItem[], count:number } + * @returns {{ libraryItems: import('../../models/LibraryItem')[], count: number }} */ async getFilteredLibraryItems(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, collapseseries, include, limit, offset, isHomePage = false) { // TODO: Handle collapse sub-series @@ -583,8 +583,8 @@ module.exports = { }) const libraryItems = books.map((bookExpanded) => { - const libraryItem = bookExpanded.libraryItem.toJSON() - const book = bookExpanded.toJSON() + const libraryItem = bookExpanded.libraryItem + const book = bookExpanded if (filterGroup === 'series' && book.series?.length) { // For showing sequence on book cover when filtering for series @@ -596,27 +596,37 @@ module.exports = { } delete book.libraryItem - delete book.authors - delete book.series + + book.series = + book.bookSeries?.map((bs) => { + const series = bs.series + delete bs.series + series.bookSeries = bs + return series + }) || [] + delete book.bookSeries + + book.authors = book.bookAuthors?.map((ba) => ba.author) || [] + delete book.bookAuthors // For showing details of collapsed series - if (collapseseries && book.bookSeries?.length) { - const collapsedSeries = book.bookSeries.find((bs) => collapseSeriesBookSeries.some((cbs) => cbs.id === bs.id)) + if (collapseseries && book.series?.length) { + const collapsedSeries = book.series.find((bs) => collapseSeriesBookSeries.some((cbs) => cbs.id === bs.bookSeries.id)) if (collapsedSeries) { - const collapseSeriesObj = collapseSeriesBookSeries.find((csbs) => csbs.id === collapsedSeries.id) + const collapseSeriesObj = collapseSeriesBookSeries.find((csbs) => csbs.id === collapsedSeries.bookSeries.id) libraryItem.collapsedSeries = { - id: collapsedSeries.series.id, - name: collapsedSeries.series.name, - nameIgnorePrefix: collapsedSeries.series.nameIgnorePrefix, - sequence: collapsedSeries.sequence, + id: collapsedSeries.id, + name: collapsedSeries.name, + nameIgnorePrefix: collapsedSeries.nameIgnorePrefix, + sequence: collapsedSeries.bookSeries.sequence, numBooks: collapseSeriesObj?.numBooks || 0, libraryItemIds: collapseSeriesObj?.libraryItemIds || [] } } } - if (bookExpanded.libraryItem.feeds?.length) { - libraryItem.rssFeed = bookExpanded.libraryItem.feeds[0] + if (libraryItem.feeds?.length) { + libraryItem.rssFeed = libraryItem.feeds[0] } if (includeMediaItemShare) { @@ -646,7 +656,7 @@ module.exports = { * @param {string[]} include * @param {number} limit * @param {number} offset - * @returns {{ libraryItems:import('../../models/LibraryItem')[], count:number }} + * @returns {Promise<{ libraryItems:import('../../models/LibraryItem')[], count:number }>} */ async getContinueSeriesLibraryItems(library, user, include, limit, offset) { const libraryId = library.id @@ -758,16 +768,19 @@ module.exports = { } } - const libraryItem = s.bookSeries[bookIndex].book.libraryItem.toJSON() - const book = s.bookSeries[bookIndex].book.toJSON() + const libraryItem = s.bookSeries[bookIndex].book.libraryItem + const book = s.bookSeries[bookIndex].book delete book.libraryItem + + book.series = [] + libraryItem.series = { id: s.id, name: s.name, sequence: s.bookSeries[bookIndex].sequence } - if (s.bookSeries[bookIndex].book.libraryItem.feeds?.length) { - libraryItem.rssFeed = s.bookSeries[bookIndex].book.libraryItem.feeds[0] + if (libraryItem.feeds?.length) { + libraryItem.rssFeed = libraryItem.feeds[0] } libraryItem.media = book return libraryItem @@ -788,7 +801,7 @@ module.exports = { * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit - * @returns {object} {libraryItems:LibraryItem, count:number} + * @returns {Promise<{ libraryItems: import('../../models/LibraryItem')[], count: number }>} */ async getDiscoverLibraryItems(libraryId, user, include, limit) { const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(user) @@ -895,13 +908,26 @@ module.exports = { // Step 3: Map books to library items const libraryItems = books.map((bookExpanded) => { - const libraryItem = bookExpanded.libraryItem.toJSON() - const book = bookExpanded.toJSON() + const libraryItem = bookExpanded.libraryItem + const book = bookExpanded delete book.libraryItem + + book.series = + book.bookSeries?.map((bs) => { + const series = bs.series + delete bs.series + series.bookSeries = bs + return series + }) || [] + delete book.bookSeries + + book.authors = book.bookAuthors?.map((ba) => ba.author) || [] + delete book.bookAuthors + libraryItem.media = book - if (bookExpanded.libraryItem.feeds?.length) { - libraryItem.rssFeed = bookExpanded.libraryItem.feeds[0] + if (libraryItem.feeds?.length) { + libraryItem.rssFeed = libraryItem.feeds[0] } return libraryItem @@ -961,11 +987,11 @@ module.exports = { * Get library items for series * @param {import('../../models/Series')} series * @param {import('../../models/User')} [user] - * @returns {Promise} + * @returns {Promise} */ async getLibraryItemsForSeries(series, user) { const { libraryItems } = await this.getFilteredLibraryItems(series.libraryId, user, 'series', series.id, null, null, false, [], null, null) - return libraryItems.map((li) => Database.libraryItemModel.getOldLibraryItem(li)) + return libraryItems }, /** @@ -1040,9 +1066,21 @@ module.exports = { for (const book of books) { const libraryItem = book.libraryItem delete book.libraryItem + + book.series = book.bookSeries.map((bs) => { + const series = bs.series + delete bs.series + series.bookSeries = bs + return series + }) + delete book.bookSeries + + book.authors = book.bookAuthors.map((ba) => ba.author) + delete book.bookAuthors + libraryItem.media = book itemMatches.push({ - libraryItem: Database.libraryItemModel.getOldLibraryItem(libraryItem).toJSONExpanded() + libraryItem: libraryItem.toOldJSONExpanded() }) } @@ -1132,7 +1170,9 @@ module.exports = { const books = series.bookSeries.map((bs) => { const libraryItem = bs.book.libraryItem libraryItem.media = bs.book - return Database.libraryItemModel.getOldLibraryItem(libraryItem).toJSON() + libraryItem.media.authors = [] + libraryItem.media.series = [] + return libraryItem.toOldJSON() }) seriesMatches.push({ series: series.toOldJSON(), diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js index c7c0914b..0aaf6f4b 100644 --- a/server/utils/queries/libraryItemsPodcastFilters.js +++ b/server/utils/queries/libraryItemsPodcastFilters.js @@ -107,7 +107,7 @@ module.exports = { * @param {string[]} include * @param {number} limit * @param {number} offset - * @returns {object} { libraryItems:LibraryItem[], count:number } + * @returns {Promise<{ libraryItems: import('../../models/LibraryItem')[], count: number }>} */ async getFilteredLibraryItems(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, include, limit, offset) { const includeRSSFeed = include.includes('rssfeed') @@ -175,16 +175,19 @@ module.exports = { }) const libraryItems = podcasts.map((podcastExpanded) => { - const libraryItem = podcastExpanded.libraryItem.toJSON() - const podcast = podcastExpanded.toJSON() + const libraryItem = podcastExpanded.libraryItem + const podcast = podcastExpanded delete podcast.libraryItem - if (podcastExpanded.libraryItem.feeds?.length) { - libraryItem.rssFeed = podcastExpanded.libraryItem.feeds[0] + if (libraryItem.feeds?.length) { + libraryItem.rssFeed = libraryItem.feeds[0] } - if (podcast.numEpisodesIncomplete) { - libraryItem.numEpisodesIncomplete = podcast.numEpisodesIncomplete + if (podcast.dataValues.numEpisodesIncomplete) { + libraryItem.numEpisodesIncomplete = podcast.dataValues.numEpisodesIncomplete + } + if (podcast.dataValues.numEpisodes) { + podcast.numEpisodes = podcast.dataValues.numEpisodes } libraryItem.media = podcast @@ -209,7 +212,7 @@ module.exports = { * @param {number} limit * @param {number} offset * @param {boolean} isHomePage for home page shelves - * @returns {object} {libraryItems:LibraryItem[], count:number} + * @returns {Promise<{ libraryItems: import('../../models/LibraryItem')[], count: number }>} */ async getFilteredPodcastEpisodes(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, limit, offset, isHomePage = false) { if (sortBy === 'progress' && filterGroup !== 'progress') { @@ -289,10 +292,11 @@ module.exports = { }) const libraryItems = podcastEpisodes.map((ep) => { - const libraryItem = ep.podcast.libraryItem.toJSON() - const podcast = ep.podcast.toJSON() + const libraryItem = ep.podcast.libraryItem + const podcast = ep.podcast delete podcast.libraryItem libraryItem.media = podcast + libraryItem.recentEpisode = ep.getOldPodcastEpisode(libraryItem.id).toJSON() return libraryItem }) @@ -362,8 +366,9 @@ module.exports = { const libraryItem = podcast.libraryItem delete podcast.libraryItem libraryItem.media = podcast + libraryItem.media.podcastEpisodes = [] itemMatches.push({ - libraryItem: Database.libraryItemModel.getOldLibraryItem(libraryItem).toJSONExpanded() + libraryItem: libraryItem.toOldJSONExpanded() }) } diff --git a/server/utils/queries/seriesFilters.js b/server/utils/queries/seriesFilters.js index 2e0e2346..ed71e5b3 100644 --- a/server/utils/queries/seriesFilters.js +++ b/server/utils/queries/seriesFilters.js @@ -162,6 +162,12 @@ module.exports = { include: [ { model: Database.libraryItemModel + }, + { + model: Database.authorModel + }, + { + model: Database.seriesModel } ] }, @@ -195,10 +201,10 @@ module.exports = { }) }) oldSeries.books = s.bookSeries.map((bs) => { - const libraryItem = bs.book.libraryItem.toJSON() + const libraryItem = bs.book.libraryItem delete bs.book.libraryItem libraryItem.media = bs.book - const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem).toJSONMinified() + const oldLibraryItem = libraryItem.toOldJSONMinified() return oldLibraryItem }) allOldSeries.push(oldSeries)