mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	New data model fix scan for creating series/authors and mapping ebooks
This commit is contained in:
		
							parent
							
								
									ea9ec13845
								
							
						
					
					
						commit
						6597fca576
					
				| @ -3,7 +3,7 @@ const Logger = require('../Logger') | ||||
| const LibraryFile = require('./files/LibraryFile') | ||||
| const Book = require('./entities/Book') | ||||
| const Podcast = require('./entities/Podcast') | ||||
| const { areEquivalent, copyValue } = require('../utils/index') | ||||
| const { areEquivalent, copyValue, getId } = require('../utils/index') | ||||
| 
 | ||||
| class LibraryItem { | ||||
|   constructor(libraryItem = null) { | ||||
| @ -149,6 +149,7 @@ class LibraryItem { | ||||
| 
 | ||||
|   // Data comes from scandir library item data
 | ||||
|   setData(libraryMediaType, payload) { | ||||
|     this.id = getId('li') | ||||
|     if (libraryMediaType === 'podcast') { | ||||
|       this.mediaType = 'podcast' | ||||
|       this.media = new Podcast() | ||||
| @ -319,7 +320,6 @@ class LibraryItem { | ||||
| 
 | ||||
|     dataFound.libraryFiles.forEach((lf) => { | ||||
|       var fileFoundCheck = this.checkFileFound(lf, true) | ||||
|       console.log('Check library file', fileFoundCheck, lf.metadata.filename) | ||||
|       if (fileFoundCheck === null) { | ||||
|         newLibraryFiles.push(lf) | ||||
|       } else if (fileFoundCheck) { | ||||
| @ -381,21 +381,47 @@ class LibraryItem { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   findLibraryFileWithIno(inode) { | ||||
|     return this.libraryFiles.find(lf => lf.ino === inode) | ||||
|   } | ||||
| 
 | ||||
|   // Set metadata from files
 | ||||
|   async syncFiles(preferOpfMetadata) { | ||||
|     var hasUpdated = false | ||||
| 
 | ||||
|     if (this.mediaType === 'book') { | ||||
|       // Add/update ebook files (ebooks that were removed are removed in checkScanData)
 | ||||
|       this.libraryFiles.forEach((lf) => { | ||||
|         if (lf.fileType === 'ebook') { | ||||
|           var existingFile = this.media.findFileWithInode(lf.ino) | ||||
|           if (!existingFile) { | ||||
|             this.media.addEbookFile(lf) | ||||
|             hasUpdated = true | ||||
|           } else if (existingFile.ebookFormat) { | ||||
|             if (existingFile.updateFromLibraryFile(lf)) {// EBookFile.js
 | ||||
|               hasUpdated = true | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
| 
 | ||||
|     // Set cover image if not set
 | ||||
|     var imageFiles = this.libraryFiles.filter(lf => lf.fileType === 'image') | ||||
|     console.log('image files', imageFiles.length, 'has cover', this.media.coverPath) | ||||
|     if (imageFiles.length && !this.media.coverPath) { | ||||
|       this.media.coverPath = imageFiles[0].metadata.path | ||||
|       Logger.debug('[LibraryItem] Set media cover path', this.media.coverPath) | ||||
|       hasUpdated = true | ||||
|     } | ||||
| 
 | ||||
|     // Parse metadata files
 | ||||
|     var textMetadataFiles = this.libraryFiles.filter(lf => lf.fileType === 'metadata' || lf.fileType === 'text') | ||||
|     if (!textMetadataFiles.length) { | ||||
|       return false | ||||
|     if (textMetadataFiles.length) { | ||||
|       if (await this.media.syncMetadataFiles(textMetadataFiles, preferOpfMetadata)) { | ||||
|         hasUpdated = true | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     var hasUpdated = await this.media.syncMetadataFiles(textMetadataFiles, preferOpfMetadata) | ||||
|     if (hasUpdated) { | ||||
|       this.updatedAt = Date.now() | ||||
|     } | ||||
|  | ||||
| @ -341,5 +341,11 @@ class Book { | ||||
|     } | ||||
|     return payload | ||||
|   } | ||||
| 
 | ||||
|   addEbookFile(libraryFile) { | ||||
|     var newEbook = new EBookFile() | ||||
|     newEbook.setData(libraryFile) | ||||
|     this.ebookFiles.push(newEbook) | ||||
|   } | ||||
| } | ||||
| module.exports = Book | ||||
| @ -30,5 +30,28 @@ class EBookFile { | ||||
|       updatedAt: this.updatedAt | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   setData(libraryFile) { | ||||
|     this.ino = libraryFile.ino | ||||
|     this.metadata = libraryFile.metadata.clone() | ||||
|     this.ebookFormat = libraryFile.metadata.format | ||||
|     this.addedAt = Date.now() | ||||
|     this.updatedAt = Date.now() | ||||
|   } | ||||
| 
 | ||||
|   updateFromLibraryFile(libraryFile) { | ||||
|     var hasUpdated = false | ||||
| 
 | ||||
|     if (this.metadata.update(libraryFile.metadata)) { | ||||
|       hasUpdated = true | ||||
|     } | ||||
| 
 | ||||
|     if (this.ebookFormat !== libraryFile.metadata.format) { | ||||
|       this.ebookFormat = libraryFile.metadata.format | ||||
|       hasUpdated = true | ||||
|     } | ||||
| 
 | ||||
|     return hasUpdated | ||||
|   } | ||||
| } | ||||
| module.exports = EBookFile | ||||
| @ -247,7 +247,7 @@ class BookMetadata { | ||||
|   parseAuthorsTag(authorsTag) { | ||||
|     var parsed = parseNameString(authorsTag) | ||||
|     if (!parsed) return [] | ||||
|     return parsed.map((au) => { | ||||
|     return (parsed.names || []).map((au) => { | ||||
|       return { | ||||
|         id: `new-${Math.floor(Math.random() * 1000000)}`, | ||||
|         name: au | ||||
|  | ||||
| @ -185,7 +185,7 @@ class AudioFileScanner { | ||||
| 
 | ||||
|       var totalAudioFilesToInclude = audioScanResult.audioFiles.length | ||||
|       var newAudioFiles = audioScanResult.audioFiles.filter(af => { | ||||
|         return !libraryItem.libraryFiles.find(lf => lf.ino === af.ino) | ||||
|         return !libraryItem.media.findFileWithInode(af.ino) | ||||
|       }) | ||||
| 
 | ||||
|       // Adding audio files to book media
 | ||||
|  | ||||
| @ -354,6 +354,9 @@ class Scanner { | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Temp authors & series are inserted - create them if found
 | ||||
|     await this.createNewAuthorsAndSeries(libraryItem) | ||||
| 
 | ||||
|     if (!libraryItem.media.hasMediaFiles) { // Library item is invalid
 | ||||
|       libraryItem.setInvalid() | ||||
|       hasUpdated = true | ||||
| @ -407,51 +410,58 @@ class Scanner { | ||||
|         libraryItem.media.updateLastCoverSearch(updatedCover) | ||||
|       } | ||||
| 
 | ||||
|       // Create or match all new authors and series
 | ||||
|       if (libraryItem.media.metadata.authors.some(au => au.id.startsWith('new'))) { | ||||
|         var newAuthors = [] | ||||
|         libraryItem.media.metadata.authors = libraryItem.media.metadata.authors.map((tempMinAuthor) => { | ||||
|           var _author = this.db.authors.find(au => au.checkNameEquals(tempMinAuthor.name)) | ||||
|           if (!_author) { | ||||
|             _author = new Author() | ||||
|             _author.setData(tempMinAuthor) | ||||
|             newAuthors.push(_author) | ||||
|           } | ||||
|           return { | ||||
|             id: _author.id, | ||||
|             name: _author.name | ||||
|           } | ||||
|         }) | ||||
|         if (newAuthors.length) { | ||||
|           await this.db.insertEntities('author', newAuthors) | ||||
|           this.emitter('authors_added', newAuthors.map(au => au.toJSON())) | ||||
|         } | ||||
|       } | ||||
|       if (libraryItem.media.metadata.series.some(se => se.id.startsWith('new'))) { | ||||
|         var newSeries = [] | ||||
|         libraryItem.media.metadata.series = libraryItem.media.metadata.series.map((tempMinSeries) => { | ||||
|           var _series = this.db.series.find(se => se.checkNameEquals(tempMinSeries.name)) | ||||
|           if (!_series) { | ||||
|             _series = new Series() | ||||
|             _series.setData(tempMinSeries) | ||||
|             newSeries.push(_series) | ||||
|           } | ||||
|           return { | ||||
|             id: _series.id, | ||||
|             name: _series.name, | ||||
|             sequence: tempMinSeries.sequence | ||||
|           } | ||||
|         }) | ||||
|         if (newSeries.length) { | ||||
|           await this.db.insertEntities('series', newSeries) | ||||
|           this.emitter('series_added', newSeries.map(se => se.toJSON())) | ||||
|         } | ||||
|       } | ||||
|       // Temp authors & series are inserted - create them if found
 | ||||
|       await this.createNewAuthorsAndSeries(libraryItem) | ||||
|     } | ||||
| 
 | ||||
|     return libraryItem | ||||
|   } | ||||
| 
 | ||||
|   async createNewAuthorsAndSeries(libraryItem) { | ||||
|     // Create or match all new authors and series
 | ||||
|     if (libraryItem.media.metadata.authors.some(au => au.id.startsWith('new'))) { | ||||
|       var newAuthors = [] | ||||
|       libraryItem.media.metadata.authors = libraryItem.media.metadata.authors.map((tempMinAuthor) => { | ||||
|         var _author = this.db.authors.find(au => au.checkNameEquals(tempMinAuthor.name)) | ||||
|         if (!_author) _author = newAuthors.find(au => au.checkNameEquals(tempMinAuthor.name)) // Check new unsaved authors
 | ||||
|         if (!_author) { | ||||
|           _author = new Author() | ||||
|           _author.setData(tempMinAuthor) | ||||
|           newAuthors.push(_author) | ||||
|         } | ||||
|         return { | ||||
|           id: _author.id, | ||||
|           name: _author.name | ||||
|         } | ||||
|       }) | ||||
|       if (newAuthors.length) { | ||||
|         await this.db.insertEntities('author', newAuthors) | ||||
|         this.emitter('authors_added', newAuthors.map(au => au.toJSON())) | ||||
|       } | ||||
|     } | ||||
|     if (libraryItem.media.metadata.series.some(se => se.id.startsWith('new'))) { | ||||
|       var newSeries = [] | ||||
|       libraryItem.media.metadata.series = libraryItem.media.metadata.series.map((tempMinSeries) => { | ||||
|         var _series = this.db.series.find(se => se.checkNameEquals(tempMinSeries.name)) | ||||
|         if (!_series) _series = newSeries.find(se => se.checkNameEquals(tempMinSeries.name)) // Check new unsaved series
 | ||||
|         if (!_series) { | ||||
|           _series = new Series() | ||||
|           _series.setData(tempMinSeries) | ||||
|           newSeries.push(_series) | ||||
|         } | ||||
|         return { | ||||
|           id: _series.id, | ||||
|           name: _series.name, | ||||
|           sequence: tempMinSeries.sequence | ||||
|         } | ||||
|       }) | ||||
|       if (newSeries.length) { | ||||
|         await this.db.insertEntities('series', newSeries) | ||||
|         this.emitter('series_added', newSeries.map(se => se.toJSON())) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getFileUpdatesGrouped(fileUpdates) { | ||||
|     var folderGroups = {} | ||||
|     fileUpdates.forEach((file) => { | ||||
| @ -529,7 +539,6 @@ class Scanner { | ||||
|       // Check if book dir group is already an item
 | ||||
|       var existingLibraryItem = this.db.libraryItems.find(li => fullPath.startsWith(li.path)) | ||||
|       if (existingLibraryItem) { | ||||
| 
 | ||||
|         // Is the item exactly - check if was deleted
 | ||||
|         if (existingLibraryItem.path === fullPath) { | ||||
|           var exists = await fs.pathExists(fullPath) | ||||
|  | ||||
| @ -18,7 +18,9 @@ const FileMetadata = require('../objects/metadata/FileMetadata') | ||||
| const AudioMetaTags = require('../objects/metadata/AudioMetaTags') | ||||
| 
 | ||||
| var authorsToAdd = [] | ||||
| var existingDbAuthors = [] | ||||
| var seriesToAdd = [] | ||||
| var existingDbSeries = [] | ||||
| 
 | ||||
| // Load old audiobooks
 | ||||
| async function loadAudiobooks() { | ||||
| @ -41,6 +43,10 @@ function makeAuthorsFromOldAb(authorsList) { | ||||
|     if (existingAuthor) { | ||||
|       return existingAuthor.toJSONMinimal() | ||||
|     } | ||||
|     var existingDbAuthor = existingDbAuthors.find(a => a.name.toLowerCase() === authorName.toLowerCase()) | ||||
|     if (existingDbAuthor) { | ||||
|       return existingDbAuthor.toJSONMinimal() | ||||
|     } | ||||
| 
 | ||||
|     var newAuthor = new Author() | ||||
|     newAuthor.setData({ name: authorName }) | ||||
| @ -55,6 +61,10 @@ function makeSeriesFromOldAb({ series, volumeNumber }) { | ||||
|   if (existingSeries) { | ||||
|     return [existingSeries.toJSONMinimal(volumeNumber)] | ||||
|   } | ||||
|   var existingDbSeriesItem = existingDbSeries.find(s => s.name.toLowerCase() === series.toLowerCase()) | ||||
|   if (existingDbSeriesItem) { | ||||
|     return [existingDbSeriesItem.toJSONMinimal(volumeNumber)] | ||||
|   } | ||||
|   var newSeries = new Series() | ||||
|   newSeries.setData({ name: series }) | ||||
|   seriesToAdd.push(newSeries) | ||||
| @ -190,6 +200,13 @@ async function migrateDb(db) { | ||||
|     return | ||||
|   } | ||||
| 
 | ||||
|   if (db.authors && db.authors.length) { | ||||
|     existingDbAuthors = db.authors | ||||
|   } | ||||
|   if (db.series && db.series.length) { | ||||
|     existingDbSeries = db.series | ||||
|   } | ||||
| 
 | ||||
|   var libraryItems = audiobooks.map((ab) => makeLibraryItemFromOldAb(ab)) | ||||
| 
 | ||||
|   Logger.info(`>>> ${libraryItems.length} Library Items made`) | ||||
| @ -202,7 +219,10 @@ async function migrateDb(db) { | ||||
|     Logger.info(`>>> ${seriesToAdd.length} Series made`) | ||||
|     await db.insertEntities('series', seriesToAdd) | ||||
|   } | ||||
| 
 | ||||
|   existingDbSeries = [] | ||||
|   existingDbAuthors = [] | ||||
|   authorsToAdd = [] | ||||
|   seriesToAdd = [] | ||||
|   Logger.info(`==== DB Migration Complete ====`) | ||||
| } | ||||
| module.exports = migrateDb | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user