mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add startup function to remove invalid records from DB
This commit is contained in:
		
							parent
							
								
									a44ee913c4
								
							
						
					
					
						commit
						10011bd6a3
					
				| @ -156,6 +156,7 @@ class Database { | ||||
|     await this.buildModels(force) | ||||
|     Logger.info(`[Database] Db initialized with models:`, Object.keys(this.sequelize.models).join(', ')) | ||||
| 
 | ||||
|     await this.cleanDatabase() | ||||
|     await this.loadData() | ||||
|   } | ||||
| 
 | ||||
| @ -380,27 +381,6 @@ class Database { | ||||
|     return this.models.libraryItem.fullUpdateFromOld(oldLibraryItem) | ||||
|   } | ||||
| 
 | ||||
|   async updateBulkLibraryItems(oldLibraryItems) { | ||||
|     if (!this.sequelize) return false | ||||
|     let updatesMade = 0 | ||||
|     for (const oldLibraryItem of oldLibraryItems) { | ||||
|       await oldLibraryItem.saveMetadata() | ||||
|       const hasUpdates = await this.models.libraryItem.fullUpdateFromOld(oldLibraryItem) | ||||
|       if (hasUpdates) { | ||||
|         updatesMade++ | ||||
|       } | ||||
|     } | ||||
|     return updatesMade | ||||
|   } | ||||
| 
 | ||||
|   async createBulkLibraryItems(oldLibraryItems) { | ||||
|     if (!this.sequelize) return false | ||||
|     for (const oldLibraryItem of oldLibraryItems) { | ||||
|       await oldLibraryItem.saveMetadata() | ||||
|       await this.models.libraryItem.fullCreateFromOld(oldLibraryItem) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async removeLibraryItem(libraryItemId) { | ||||
|     if (!this.sequelize) return false | ||||
|     await this.models.libraryItem.removeById(libraryItemId) | ||||
| @ -675,6 +655,40 @@ class Database { | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Clean invalid records in database | ||||
|    * Series should have atleast one Book | ||||
|    * Book and Podcast must have an associated LibraryItem | ||||
|    */ | ||||
|   async cleanDatabase() { | ||||
|     // Remove invalid Podcast records
 | ||||
|     const podcastsWithNoLibraryItem = await this.podcastModel.findAll({ | ||||
|       where: Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM libraryItems li WHERE li.mediaId = podcast.id)`), 0) | ||||
|     }) | ||||
|     for (const podcast of podcastsWithNoLibraryItem) { | ||||
|       Logger.warn(`Found podcast "${podcast.title}" with no libraryItem - removing it`) | ||||
|       await podcast.destroy() | ||||
|     } | ||||
| 
 | ||||
|     // Remove invalid Book records
 | ||||
|     const booksWithNoLibraryItem = await this.bookModel.findAll({ | ||||
|       where: Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM libraryItems li WHERE li.mediaId = book.id)`), 0) | ||||
|     }) | ||||
|     for (const book of booksWithNoLibraryItem) { | ||||
|       Logger.warn(`Found book "${book.title}" with no libraryItem - removing it`) | ||||
|       await book.destroy() | ||||
|     } | ||||
| 
 | ||||
|     // Remove empty series
 | ||||
|     const emptySeries = await this.seriesModel.findAll({ | ||||
|       where: Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM bookSeries bs WHERE bs.seriesId = series.id)`), 0) | ||||
|     }) | ||||
|     for (const series of emptySeries) { | ||||
|       Logger.warn(`Found series "${series.name}" with no books - removing it`) | ||||
|       await series.destroy() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| module.exports = new Database() | ||||
| @ -50,10 +50,14 @@ class BookSeries extends Model { | ||||
|     book.belongsToMany(series, { through: BookSeries }) | ||||
|     series.belongsToMany(book, { through: BookSeries }) | ||||
| 
 | ||||
|     book.hasMany(BookSeries) | ||||
|     book.hasMany(BookSeries, { | ||||
|       onDelete: 'CASCADE' | ||||
|     }) | ||||
|     BookSeries.belongsTo(book) | ||||
| 
 | ||||
|     series.hasMany(BookSeries) | ||||
|     series.hasMany(BookSeries, { | ||||
|       onDelete: 'CASCADE' | ||||
|     }) | ||||
|     BookSeries.belongsTo(series) | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -63,53 +63,6 @@ class LibraryItem extends Model { | ||||
|     this.updatedAt | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Loads all podcast episodes, all library items in chunks of 500, then maps them to old library items | ||||
|    * @todo this is a temporary solution until we can use the sqlite without loading all the library items on init | ||||
|    *  | ||||
|    * @returns {Promise<objects.LibraryItem[]>} old library items | ||||
|    */ | ||||
|   static async loadAllLibraryItems() { | ||||
|     let start = Date.now() | ||||
|     Logger.info(`[LibraryItem] Loading podcast episodes...`) | ||||
|     const podcastEpisodes = await this.sequelize.models.podcastEpisode.findAll() | ||||
|     Logger.info(`[LibraryItem] Finished loading ${podcastEpisodes.length} podcast episodes in ${((Date.now() - start) / 1000).toFixed(2)}s`) | ||||
| 
 | ||||
|     start = Date.now() | ||||
|     Logger.info(`[LibraryItem] Loading library items...`) | ||||
|     let libraryItems = await this.getAllOldLibraryItemsIncremental() | ||||
|     Logger.info(`[LibraryItem] Finished loading ${libraryItems.length} library items in ${((Date.now() - start) / 1000).toFixed(2)}s`) | ||||
| 
 | ||||
|     // Map LibraryItem to old library item
 | ||||
|     libraryItems = libraryItems.map(li => { | ||||
|       if (li.mediaType === 'podcast') { | ||||
|         li.media.podcastEpisodes = podcastEpisodes.filter(pe => pe.podcastId === li.media.id) | ||||
|       } | ||||
|       return this.getOldLibraryItem(li) | ||||
|     }) | ||||
| 
 | ||||
|     return libraryItems | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Loads all LibraryItem in batches of 500 | ||||
|    * @todo temporary solution | ||||
|    *  | ||||
|    * @param {Model<LibraryItem>[]} libraryItems  | ||||
|    * @param {number} offset  | ||||
|    * @returns {Promise<Model<LibraryItem>[]>} | ||||
|    */ | ||||
|   static async getAllOldLibraryItemsIncremental(libraryItems = [], offset = 0) { | ||||
|     const limit = 500 | ||||
|     const rows = await this.getLibraryItemsIncrement(offset, limit) | ||||
|     libraryItems.push(...rows) | ||||
|     if (!rows.length || rows.length < limit) { | ||||
|       return libraryItems | ||||
|     } | ||||
|     Logger.info(`[LibraryItem] Loaded ${rows.length} library items. ${libraryItems.length} loaded so far.`) | ||||
|     return this.getAllOldLibraryItemsIncremental(libraryItems, offset + rows.length) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets library items partially expanded, not including podcast episodes | ||||
|    * @todo temporary solution | ||||
| @ -120,10 +73,6 @@ class LibraryItem extends Model { | ||||
|    */ | ||||
|   static getLibraryItemsIncrement(offset, limit, where = null) { | ||||
|     return this.findAll({ | ||||
|       benchmark: true, | ||||
|       logging: (sql, timeMs) => { | ||||
|         console.log(`[Query] Elapsed ${timeMs}ms.`) | ||||
|       }, | ||||
|       where, | ||||
|       include: [ | ||||
|         { | ||||
|  | ||||
| @ -547,10 +547,6 @@ module.exports = { | ||||
|       distinct: true, | ||||
|       attributes: bookAttributes, | ||||
|       replacements, | ||||
|       benchmark: true, | ||||
|       logging: (sql, timeMs) => { | ||||
|         console.log(`[Query] Elapsed ${timeMs}ms`) | ||||
|       }, | ||||
|       include: [ | ||||
|         { | ||||
|           model: Database.libraryItemModel, | ||||
|  | ||||
| @ -141,10 +141,6 @@ module.exports = { | ||||
|       offset, | ||||
|       distinct: true, | ||||
|       subQuery: false, | ||||
|       benchmark: true, | ||||
|       logging: (sql, timeMs) => { | ||||
|         console.log(`[Query] Series filter/sort. Elapsed ${timeMs}ms`) | ||||
|       }, | ||||
|       attributes: seriesAttributes, | ||||
|       replacements: userPermissionBookWhere.replacements, | ||||
|       include: [ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user