mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	New data model scanner update and change scan chunks to be based on total file size
This commit is contained in:
		
							parent
							
								
									14a8f84446
								
							
						
					
					
						commit
						f00b120e96
					
				| @ -141,6 +141,11 @@ class LibraryItem { | |||||||
|     this.libraryFiles.forEach((lf) => total += lf.metadata.size) |     this.libraryFiles.forEach((lf) => total += lf.metadata.size) | ||||||
|     return total |     return total | ||||||
|   } |   } | ||||||
|  |   get audioFileTotalSize() { | ||||||
|  |     var total = 0 | ||||||
|  |     this.libraryFiles.filter(lf => lf.fileType == 'audio').forEach((lf) => total += lf.metadata.size) | ||||||
|  |     return total | ||||||
|  |   } | ||||||
|   get hasAudioFiles() { |   get hasAudioFiles() { | ||||||
|     return this.libraryFiles.some(lf => lf.fileType === 'audio') |     return this.libraryFiles.some(lf => lf.fileType === 'audio') | ||||||
|   } |   } | ||||||
| @ -347,7 +352,9 @@ class LibraryItem { | |||||||
|       return true |       return true | ||||||
|     }) |     }) | ||||||
|     if (filesRemoved.length) { |     if (filesRemoved.length) { | ||||||
|       this.media.checkUpdateMissingTracks() |       if (this.media.audiobooks && this.media.audiobooks.length) { | ||||||
|  |         this.media.audiobooks.forEach(ab => ab.checkUpdateMissingTracks()) | ||||||
|  |       } | ||||||
|       hasUpdated = true |       hasUpdated = true | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| const Path = require('path') | const Path = require('path') | ||||||
| const AudioFile = require('../files/AudioFile') | const AudioFile = require('../files/AudioFile') | ||||||
| const { areEquivalent, copyValue } = require('../../utils/index') | const { areEquivalent, copyValue, getId } = require('../../utils/index') | ||||||
| const AudioTrack = require('../files/AudioTrack') | const AudioTrack = require('../files/AudioTrack') | ||||||
| 
 | 
 | ||||||
| class Audiobook { | class Audiobook { | ||||||
| @ -93,6 +93,14 @@ class Audiobook { | |||||||
|     return this.audioFiles.some(af => af.embeddedCoverArt) |     return this.audioFiles.some(af => af.embeddedCoverArt) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   setData(name, index) { | ||||||
|  |     this.id = getId('ab') | ||||||
|  |     this.name = name | ||||||
|  |     this.index = index | ||||||
|  |     this.addedAt = Date.now() | ||||||
|  |     this.updatedAt = Date.now() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   update(payload) { |   update(payload) { | ||||||
|     var json = this.toJSON() |     var json = this.toJSON() | ||||||
|     var hasUpdates = false |     var hasUpdates = false | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| const EBookFile = require('../files/EBookFile') | const EBookFile = require('../files/EBookFile') | ||||||
| const { areEquivalent, copyValue } = require('../../utils/index') | const { areEquivalent, copyValue, getId } = require('../../utils/index') | ||||||
| 
 | 
 | ||||||
| class EBook { | class EBook { | ||||||
|   constructor(ebook) { |   constructor(ebook) { | ||||||
| @ -64,6 +64,15 @@ class EBook { | |||||||
|     return this.ebookFile.metadata.size |     return this.ebookFile.metadata.size | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   setData(ebookFile, index) { | ||||||
|  |     this.id = getId('eb') | ||||||
|  |     this.name = ebookFile.metadata.filename | ||||||
|  |     this.index = index | ||||||
|  |     this.ebookFile = ebookFile | ||||||
|  |     this.addedAt = Date.now() | ||||||
|  |     this.updatedAt = Date.now() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   findFileWithInode(inode) { |   findFileWithInode(inode) { | ||||||
|     return this.ebookFile.ino === inode |     return this.ebookFile.ino === inode | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ const { areEquivalent, copyValue } = require('../../utils/index') | |||||||
| const { parseOpfMetadataXML } = require('../../utils/parseOpfMetadata') | const { parseOpfMetadataXML } = require('../../utils/parseOpfMetadata') | ||||||
| const { readTextFile } = require('../../utils/fileUtils') | const { readTextFile } = require('../../utils/fileUtils') | ||||||
| 
 | 
 | ||||||
|  | const EBookFile = require('../files/EBookFile') | ||||||
| const Audiobook = require('../entities/Audiobook') | const Audiobook = require('../entities/Audiobook') | ||||||
| const EBook = require('../entities/EBook') | const EBook = require('../entities/EBook') | ||||||
| 
 | 
 | ||||||
| @ -267,9 +268,30 @@ class Book { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   addEbookFile(libraryFile) { |   addEbookFile(libraryFile) { | ||||||
|     // var newEbook = new EBookFile()
 |     var ebookFile = new EBookFile() | ||||||
|     // newEbook.setData(libraryFile)
 |     ebookFile.setData(libraryFile) | ||||||
|     // this.ebookFiles.push(newEbook)
 | 
 | ||||||
|  |     var ebookIndex = this.ebooks.length + 1 | ||||||
|  |     var newEBook = new EBook() | ||||||
|  |     newEBook.setData(ebookFile, ebookIndex) | ||||||
|  |     this.ebooks.push(newEBook) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getCreateAudiobookVariant(variant) { | ||||||
|  |     if (this.audiobooks.length) { | ||||||
|  |       var ab = this.audiobooks.find(ab => ab.name == variantName) | ||||||
|  |       if (ab) return ab | ||||||
|  |     } | ||||||
|  |     var abIndex = this.audiobooks.length + 1 | ||||||
|  |     var newAb = new Audiobook() | ||||||
|  |     newAb.setData(variant, abIndex) | ||||||
|  |     this.audiobooks.push(newAb) | ||||||
|  |     return newAb | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   addAudioFileToAudiobook(audioFile, variant = 'default') { // Create if none
 | ||||||
|  |     var audiobook = this.getCreateAudiobookVariant(variant) | ||||||
|  |     audiobook.audioFiles.push(audioFile) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| module.exports = Book | module.exports = Book | ||||||
| @ -169,11 +169,12 @@ class AudioFileScanner { | |||||||
|       if (existingAF) { |       if (existingAF) { | ||||||
|         if (existingAF.updateFromScan) existingAF.updateFromScan(audioFiles[i]) |         if (existingAF.updateFromScan) existingAF.updateFromScan(audioFiles[i]) | ||||||
|       } else { |       } else { | ||||||
|         libraryItem.media.audioFiles.push(audioFiles[i]) |         libraryItem.media.addAudioFileToAudiobook(audioFiles[i]) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // TODO: support for multiple audiobooks in a book item will need to pass an audiobook variant name here
 | ||||||
|   async scanAudioFiles(audioLibraryFiles, scanData, libraryItem, preferAudioMetadata, libraryScan = null) { |   async scanAudioFiles(audioLibraryFiles, scanData, libraryItem, preferAudioMetadata, libraryScan = null) { | ||||||
|     var hasUpdated = false |     var hasUpdated = false | ||||||
| 
 | 
 | ||||||
| @ -195,14 +196,13 @@ class AudioFileScanner { | |||||||
|           if (totalAudioFilesToInclude === 1) { |           if (totalAudioFilesToInclude === 1) { | ||||||
|             var af = audioScanResult.audioFiles[0] |             var af = audioScanResult.audioFiles[0] | ||||||
|             af.index = 1 |             af.index = 1 | ||||||
|             libraryItem.media.audioFiles.push(af) |             libraryItem.media.addAudioFileToAudiobook(af) | ||||||
|             hasUpdated = true |             hasUpdated = true | ||||||
|           } else { |           } else { | ||||||
|             this.runSmartTrackOrder(libraryItem, audioScanResult.audioFiles) |             this.runSmartTrackOrder(libraryItem, audioScanResult.audioFiles) | ||||||
|             hasUpdated = true |             hasUpdated = true | ||||||
|           } |           } | ||||||
|         } else { |         } else { | ||||||
|           Logger.debug(`[AudioFileScanner] No audio track re-order required`) |  | ||||||
|           // Only update metadata not index
 |           // Only update metadata not index
 | ||||||
|           audioScanResult.audioFiles.forEach((af) => { |           audioScanResult.audioFiles.forEach((af) => { | ||||||
|             var existingAF = libraryItem.media.findFileWithInode(af.ino) |             var existingAF = libraryItem.media.findFileWithInode(af.ino) | ||||||
| @ -221,7 +221,12 @@ class AudioFileScanner { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (hasUpdated) { |         if (hasUpdated) { | ||||||
|           libraryItem.media.rebuildTracks() |           if (!libraryItem.media.audiobooks.length) { | ||||||
|  |             Logger.error(`[AudioFileScanner] Updates were made but library item has no audiobooks`, libraryItem) | ||||||
|  |           } else { | ||||||
|  |             var audiobook = libraryItem.media.audiobooks[0] | ||||||
|  |             audiobook.rebuildTracks() | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       } // End Book media type
 |       } // End Book media type
 | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -3,14 +3,12 @@ const Path = require('path') | |||||||
| 
 | 
 | ||||||
| // Utils
 | // Utils
 | ||||||
| const Logger = require('../Logger') | const Logger = require('../Logger') | ||||||
| const { version } = require('../../package.json') |  | ||||||
| const { groupFilesIntoLibraryItemPaths, getLibraryItemFileData, scanFolder } = require('../utils/scandir') | const { groupFilesIntoLibraryItemPaths, getLibraryItemFileData, scanFolder } = require('../utils/scandir') | ||||||
| const { comparePaths, getId } = require('../utils/index') | const { comparePaths } = require('../utils/index') | ||||||
| const { ScanResult, LogLevel } = require('../utils/constants') | const { ScanResult, LogLevel } = require('../utils/constants') | ||||||
| 
 | 
 | ||||||
| const AudioFileScanner = require('./AudioFileScanner') | const AudioFileScanner = require('./AudioFileScanner') | ||||||
| const BookFinder = require('../finders/BookFinder') | const BookFinder = require('../finders/BookFinder') | ||||||
| const Audiobook = require('../objects/legacy/Audiobook') |  | ||||||
| const LibraryItem = require('../objects/LibraryItem') | const LibraryItem = require('../objects/LibraryItem') | ||||||
| const LibraryScan = require('./LibraryScan') | const LibraryScan = require('./LibraryScan') | ||||||
| const ScanOptions = require('./ScanOptions') | const ScanOptions = require('./ScanOptions') | ||||||
| @ -181,13 +179,14 @@ class Scanner { | |||||||
|     libraryItemDataFound = libraryItemDataFound.filter(lid => lid.ino) |     libraryItemDataFound = libraryItemDataFound.filter(lid => lid.ino) | ||||||
|     var libraryItemsInLibrary = this.db.libraryItems.filter(li => li.libraryId === libraryScan.libraryId) |     var libraryItemsInLibrary = this.db.libraryItems.filter(li => li.libraryId === libraryScan.libraryId) | ||||||
| 
 | 
 | ||||||
|     const NumScansPerChunk = 25 |     const MaxSizePerChunk = 2.5e9 | ||||||
|     const itemsToUpdateChunks = [] |  | ||||||
|     const itemDataToRescanChunks = [] |     const itemDataToRescanChunks = [] | ||||||
|     const newItemDataToScanChunks = [] |     const newItemDataToScanChunks = [] | ||||||
|     var itemsToUpdate = [] |     var itemsToUpdate = [] | ||||||
|     var itemDataToRescan = [] |     var itemDataToRescan = [] | ||||||
|  |     var itemDataToRescanSize = 0 | ||||||
|     var newItemDataToScan = [] |     var newItemDataToScan = [] | ||||||
|  |     var newItemDataToScanSize = 0 | ||||||
|     var itemsToFindCovers = [] |     var itemsToFindCovers = [] | ||||||
| 
 | 
 | ||||||
|     // Check for existing & removed library items
 |     // Check for existing & removed library items
 | ||||||
| @ -200,40 +199,37 @@ class Scanner { | |||||||
|         libraryScan.resultsMissing++ |         libraryScan.resultsMissing++ | ||||||
|         libraryItem.setMissing() |         libraryItem.setMissing() | ||||||
|         itemsToUpdate.push(libraryItem) |         itemsToUpdate.push(libraryItem) | ||||||
|         if (itemsToUpdate.length === NumScansPerChunk) { |  | ||||||
|           itemsToUpdateChunks.push(itemsToUpdate) |  | ||||||
|           itemsToUpdate = [] |  | ||||||
|         } |  | ||||||
|       } else { |       } else { | ||||||
|         var checkRes = libraryItem.checkScanData(dataFound) |         var checkRes = libraryItem.checkScanData(dataFound) | ||||||
|         if (checkRes.newLibraryFiles.length || libraryScan.scanOptions.forceRescan) { // Item has new files
 |         if (checkRes.newLibraryFiles.length || libraryScan.scanOptions.forceRescan) { // Item has new files
 | ||||||
|           checkRes.libraryItem = libraryItem |           checkRes.libraryItem = libraryItem | ||||||
|           checkRes.scanData = dataFound |           checkRes.scanData = dataFound | ||||||
|           itemDataToRescan.push(checkRes) | 
 | ||||||
|           if (itemDataToRescan.length === NumScansPerChunk) { |           // If this item will go over max size then push current chunk
 | ||||||
|  |           if (libraryItem.audioFileTotalSize + itemDataToRescanSize > MaxSizePerChunk && itemDataToRescan.length > 0) { | ||||||
|             itemDataToRescanChunks.push(itemDataToRescan) |             itemDataToRescanChunks.push(itemDataToRescan) | ||||||
|  |             itemDataToRescanSize = 0 | ||||||
|             itemDataToRescan = [] |             itemDataToRescan = [] | ||||||
|           } |           } | ||||||
|         } else if (libraryScan.findCovers && libraryItem.media.shouldSearchForCover) { | 
 | ||||||
|  |           itemDataToRescan.push(checkRes) | ||||||
|  |           itemDataToRescanSize += libraryItem.audioFileTotalSize | ||||||
|  |           if (itemDataToRescanSize >= MaxSizePerChunk) { | ||||||
|  |             itemDataToRescanChunks.push(itemDataToRescan) | ||||||
|  |             itemDataToRescanSize = 0 | ||||||
|  |             itemDataToRescan = [] | ||||||
|  |           } | ||||||
|  |         } else if (libraryScan.findCovers && libraryItem.media.shouldSearchForCover) { // Search cover
 | ||||||
|           libraryScan.resultsUpdated++ |           libraryScan.resultsUpdated++ | ||||||
|           itemsToFindCovers.push(libraryItem) |           itemsToFindCovers.push(libraryItem) | ||||||
|           itemsToUpdate.push(libraryItem) |           itemsToUpdate.push(libraryItem) | ||||||
|           if (itemsToUpdate.length === NumScansPerChunk) { |  | ||||||
|             itemsToUpdateChunks.push(itemsToUpdate) |  | ||||||
|             itemsToUpdate = [] |  | ||||||
|           } |  | ||||||
|         } else if (checkRes.updated) { // Updated but no scan required
 |         } else if (checkRes.updated) { // Updated but no scan required
 | ||||||
|           libraryScan.resultsUpdated++ |           libraryScan.resultsUpdated++ | ||||||
|           itemsToUpdate.push(libraryItem) |           itemsToUpdate.push(libraryItem) | ||||||
|           if (itemsToUpdate.length === NumScansPerChunk) { |  | ||||||
|             itemsToUpdateChunks.push(itemsToUpdate) |  | ||||||
|             itemsToUpdate = [] |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|         libraryItemDataFound = libraryItemDataFound.filter(lid => lid.ino !== dataFound.ino) |         libraryItemDataFound = libraryItemDataFound.filter(lid => lid.ino !== dataFound.ino) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (itemsToUpdate.length) itemsToUpdateChunks.push(itemsToUpdate) |  | ||||||
|     if (itemDataToRescan.length) itemDataToRescanChunks.push(itemDataToRescan) |     if (itemDataToRescan.length) itemDataToRescanChunks.push(itemDataToRescan) | ||||||
| 
 | 
 | ||||||
|     // Potential NEW Library Items
 |     // Potential NEW Library Items
 | ||||||
| @ -244,9 +240,21 @@ class Scanner { | |||||||
|       if (!hasMediaFile) { |       if (!hasMediaFile) { | ||||||
|         libraryScan.addLog(LogLevel.WARN, `Directory found "${libraryItemDataFound.path}" has no media files`) |         libraryScan.addLog(LogLevel.WARN, `Directory found "${libraryItemDataFound.path}" has no media files`) | ||||||
|       } else { |       } else { | ||||||
|         newItemDataToScan.push(dataFound) |         var audioFileSize = 0 | ||||||
|         if (newItemDataToScan.length === NumScansPerChunk) { |         dataFound.libraryFiles.filter(lf => lf.fileType == 'audio').forEach(lf => audioFileSize += lf.metadata.size) | ||||||
|  | 
 | ||||||
|  |         // If this item will go over max size then push current chunk
 | ||||||
|  |         if (audioFileSize + newItemDataToScanSize > MaxSizePerChunk && newItemDataToScan.length > 0) { | ||||||
|           newItemDataToScanChunks.push(newItemDataToScan) |           newItemDataToScanChunks.push(newItemDataToScan) | ||||||
|  |           newItemDataToScanSize = 0 | ||||||
|  |           newItemDataToScan = [] | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         newItemDataToScan.push(dataFound) | ||||||
|  |         newItemDataToScanSize += audioFileSize | ||||||
|  |         if (newItemDataToScanSize >= MaxSizePerChunk) { | ||||||
|  |           newItemDataToScanChunks.push(newItemDataToScan) | ||||||
|  |           newItemDataToScanSize = 0 | ||||||
|           newItemDataToScan = [] |           newItemDataToScan = [] | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -260,10 +268,9 @@ class Scanner { | |||||||
|       libraryItem.media.updateLastCoverSearch(updatedCover) |       libraryItem.media.updateLastCoverSearch(updatedCover) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (let i = 0; i < itemsToUpdateChunks.length; i++) { |     if (itemsToUpdate.length) { | ||||||
|       await this.updateLibraryItemChunk(itemsToUpdateChunks[i]) |       await this.updateLibraryItemChunk(itemsToUpdate) | ||||||
|       if (this.cancelLibraryScan[libraryScan.libraryId]) return true |       if (this.cancelLibraryScan[libraryScan.libraryId]) return true | ||||||
|       // console.log('Update chunk done', i, 'of', itemsToUpdateChunks.length)
 |  | ||||||
|     } |     } | ||||||
|     for (let i = 0; i < itemDataToRescanChunks.length; i++) { |     for (let i = 0; i < itemDataToRescanChunks.length; i++) { | ||||||
|       await this.rescanLibraryItemDataChunk(itemDataToRescanChunks[i], libraryScan) |       await this.rescanLibraryItemDataChunk(itemDataToRescanChunks[i], libraryScan) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user