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