mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-17 00:08:55 +01:00
Updates to metadata file format changing, use chapters from metadata file
This commit is contained in:
parent
81d4ac3ed2
commit
3c406c12b4
@ -45,7 +45,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-44 mb-2">
|
<div class="w-44 mb-2">
|
||||||
<ui-dropdown v-model="newServerSettings.metadataFileFormat" small :items="metadataFileFormats" label="Metadata File Format" @input="(val) => updateSettingsKey('metadataFileFormat', val)" :disabled="updatingServerSettings" />
|
<ui-dropdown v-model="newServerSettings.metadataFileFormat" small :items="metadataFileFormats" label="Metadata File Format" @input="updateMetadataFileFormat" :disabled="updatingServerSettings" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
@ -355,6 +355,10 @@ export default {
|
|||||||
updateServerLanguage(val) {
|
updateServerLanguage(val) {
|
||||||
this.updateSettingsKey('language', val)
|
this.updateSettingsKey('language', val)
|
||||||
},
|
},
|
||||||
|
updateMetadataFileFormat(val) {
|
||||||
|
if (this.serverSettings.metadataFileFormat === val) return
|
||||||
|
this.updateSettingsKey('metadataFileFormat', val)
|
||||||
|
},
|
||||||
updateSettingsKey(key, val) {
|
updateSettingsKey(key, val) {
|
||||||
this.updateServerSettings({
|
this.updateServerSettings({
|
||||||
[key]: val
|
[key]: val
|
||||||
@ -364,8 +368,7 @@ export default {
|
|||||||
this.updatingServerSettings = true
|
this.updatingServerSettings = true
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('updateServerSettings', payload)
|
.dispatch('updateServerSettings', payload)
|
||||||
.then((success) => {
|
.then(() => {
|
||||||
console.log('Updated Server Settings', success)
|
|
||||||
this.updatingServerSettings = false
|
this.updatingServerSettings = false
|
||||||
this.$toast.success('Server settings updated')
|
this.$toast.success('Server settings updated')
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ const Podcast = require('./mediaTypes/Podcast')
|
|||||||
const Video = require('./mediaTypes/Video')
|
const Video = require('./mediaTypes/Video')
|
||||||
const Music = require('./mediaTypes/Music')
|
const Music = require('./mediaTypes/Music')
|
||||||
const { areEquivalent, copyValue, getId, cleanStringForSearch } = require('../utils/index')
|
const { areEquivalent, copyValue, getId, cleanStringForSearch } = require('../utils/index')
|
||||||
|
const { filePathToPOSIX } = require('../utils/fileUtils')
|
||||||
|
|
||||||
class LibraryItem {
|
class LibraryItem {
|
||||||
constructor(libraryItem = null) {
|
constructor(libraryItem = null) {
|
||||||
@ -368,7 +369,7 @@ class LibraryItem {
|
|||||||
const fileFoundCheck = this.checkFileFound(lf, true)
|
const fileFoundCheck = this.checkFileFound(lf, true)
|
||||||
if (fileFoundCheck === null) {
|
if (fileFoundCheck === null) {
|
||||||
newLibraryFiles.push(lf)
|
newLibraryFiles.push(lf)
|
||||||
} else if (fileFoundCheck && lf.metadata.format !== 'abs') { // Ignore abs file updates
|
} else if (fileFoundCheck && lf.metadata.format !== 'abs' && lf.metadata.filename !== 'metadata.json') { // Ignore abs file updates
|
||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
existingLibraryFiles.push(lf)
|
existingLibraryFiles.push(lf)
|
||||||
} else {
|
} else {
|
||||||
@ -502,17 +503,26 @@ class LibraryItem {
|
|||||||
|
|
||||||
const metadataFileFormat = global.ServerSettings.metadataFileFormat
|
const metadataFileFormat = global.ServerSettings.metadataFileFormat
|
||||||
const metadataFilePath = Path.join(metadataPath, `metadata.${metadataFileFormat}`)
|
const metadataFilePath = Path.join(metadataPath, `metadata.${metadataFileFormat}`)
|
||||||
|
|
||||||
if (metadataFileFormat === 'json') {
|
if (metadataFileFormat === 'json') {
|
||||||
// Remove metadata.abs if it exists
|
// Remove metadata.abs if it exists
|
||||||
if (await fs.pathExists(Path.join(metadataPath, `metadata.abs`))) {
|
if (await fs.pathExists(Path.join(metadataPath, `metadata.abs`))) {
|
||||||
Logger.debug(`[LibraryItem] Removing metadata.abs for item "${this.media.metadata.title}"`)
|
Logger.debug(`[LibraryItem] Removing metadata.abs for item "${this.media.metadata.title}"`)
|
||||||
await fs.remove(Path.join(metadataPath, `metadata.abs`))
|
await fs.remove(Path.join(metadataPath, `metadata.abs`))
|
||||||
|
this.libraryFiles = this.libraryFiles.filter(lf => lf.metadata.path !== filePathToPOSIX(Path.join(metadataPath, `metadata.abs`)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.writeFile(metadataFilePath, JSON.stringify(this.media.toJSONForMetadataFile(), null, 2)).then(async () => {
|
||||||
|
this.isSavingMetadata = false
|
||||||
|
// Add metadata.json to libraryFiles array if it is new
|
||||||
|
if (global.ServerSettings.storeMetadataWithItem && !this.libraryFiles.some(lf => lf.metadata.path === filePathToPOSIX(metadataFilePath))) {
|
||||||
|
const newLibraryFile = new LibraryFile()
|
||||||
|
await newLibraryFile.setDataFromPath(metadataFilePath, `metadata.json`)
|
||||||
|
this.libraryFiles.push(newLibraryFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.writeFile(metadataFilePath, JSON.stringify(this.media.toJSONForMetadataFile(), null, 2)).then(() => {
|
|
||||||
return true
|
return true
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
this.isSavingMetadata = false
|
||||||
Logger.error(`[LibraryItem] Failed to save json file at "${metadataFilePath}"`, error)
|
Logger.error(`[LibraryItem] Failed to save json file at "${metadataFilePath}"`, error)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@ -521,12 +531,22 @@ class LibraryItem {
|
|||||||
if (await fs.pathExists(Path.join(metadataPath, `metadata.json`))) {
|
if (await fs.pathExists(Path.join(metadataPath, `metadata.json`))) {
|
||||||
Logger.debug(`[LibraryItem] Removing metadata.json for item "${this.media.metadata.title}"`)
|
Logger.debug(`[LibraryItem] Removing metadata.json for item "${this.media.metadata.title}"`)
|
||||||
await fs.remove(Path.join(metadataPath, `metadata.json`))
|
await fs.remove(Path.join(metadataPath, `metadata.json`))
|
||||||
|
this.libraryFiles = this.libraryFiles.filter(lf => lf.metadata.path !== filePathToPOSIX(Path.join(metadataPath, `metadata.json`)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return abmetadataGenerator.generate(this, metadataFilePath).then((success) => {
|
return abmetadataGenerator.generate(this, metadataFilePath).then(async (success) => {
|
||||||
this.isSavingMetadata = false
|
this.isSavingMetadata = false
|
||||||
if (!success) Logger.error(`[LibraryItem] Failed saving abmetadata to "${metadataFilePath}"`)
|
if (!success) Logger.error(`[LibraryItem] Failed saving abmetadata to "${metadataFilePath}"`)
|
||||||
else Logger.debug(`[LibraryItem] Success saving abmetadata to "${metadataFilePath}"`)
|
else {
|
||||||
|
// Add metadata.abs to libraryFiles array if it is new
|
||||||
|
if (global.ServerSettings.storeMetadataWithItem && !this.libraryFiles.some(lf => lf.metadata.path === filePathToPOSIX(metadataFilePath))) {
|
||||||
|
const newLibraryFile = new LibraryFile()
|
||||||
|
await newLibraryFile.setDataFromPath(metadataFilePath, `metadata.abs`)
|
||||||
|
this.libraryFiles.push(newLibraryFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.debug(`[LibraryItem] Success saving abmetadata to "${metadataFilePath}"`)
|
||||||
|
}
|
||||||
return success
|
return success
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const { getFileTimestampsWithIno } = require('../../utils/fileUtils')
|
const { getFileTimestampsWithIno, filePathToPOSIX } = require('../../utils/fileUtils')
|
||||||
const globals = require('../../utils/globals')
|
const globals = require('../../utils/globals')
|
||||||
const FileMetadata = require('../metadata/FileMetadata')
|
const FileMetadata = require('../metadata/FileMetadata')
|
||||||
|
|
||||||
@ -59,8 +59,8 @@ class LibraryFile {
|
|||||||
var fileMetadata = new FileMetadata()
|
var fileMetadata = new FileMetadata()
|
||||||
fileMetadata.setData(fileTsData)
|
fileMetadata.setData(fileTsData)
|
||||||
fileMetadata.filename = Path.basename(relPath)
|
fileMetadata.filename = Path.basename(relPath)
|
||||||
fileMetadata.path = path
|
fileMetadata.path = filePathToPOSIX(path)
|
||||||
fileMetadata.relPath = relPath
|
fileMetadata.relPath = filePathToPOSIX(relPath)
|
||||||
fileMetadata.ext = Path.extname(relPath)
|
fileMetadata.ext = Path.extname(relPath)
|
||||||
this.ino = fileTsData.ino
|
this.ino = fileTsData.ino
|
||||||
this.metadata = fileMetadata
|
this.metadata = fileMetadata
|
||||||
|
@ -237,7 +237,7 @@ class Book {
|
|||||||
// Look for desc.txt, reader.txt, metadata.abs and opf file then update details if found
|
// Look for desc.txt, reader.txt, metadata.abs and opf file then update details if found
|
||||||
async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) {
|
async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) {
|
||||||
let metadataUpdatePayload = {}
|
let metadataUpdatePayload = {}
|
||||||
let tagsUpdated = false
|
let hasUpdated = false
|
||||||
|
|
||||||
const descTxt = textMetadataFiles.find(lf => lf.metadata.filename === 'desc.txt')
|
const descTxt = textMetadataFiles.find(lf => lf.metadata.filename === 'desc.txt')
|
||||||
if (descTxt) {
|
if (descTxt) {
|
||||||
@ -256,18 +256,25 @@ class Book {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadataAbs = textMetadataFiles.find(lf => lf.metadata.filename === 'metadata.abs' || lf.metadata.filename === 'metadata.json')
|
const metadataIsJSON = global.ServerSettings.metadataFileFormat === 'json'
|
||||||
if (metadataAbs) {
|
const metadataAbs = textMetadataFiles.find(lf => lf.metadata.filename === 'metadata.abs')
|
||||||
const isJSON = metadataAbs.metadata.filename === 'metadata.json'
|
const metadataJson = textMetadataFiles.find(lf => lf.metadata.filename === 'metadata.json')
|
||||||
Logger.debug(`[Book] Found ${metadataAbs.metadata.filename} file for "${this.metadata.title}"`)
|
|
||||||
const metadataText = await readTextFile(metadataAbs.metadata.path)
|
const metadataFile = metadataIsJSON ? metadataJson : metadataAbs
|
||||||
const abmetadataUpdates = abmetadataGenerator.parseAndCheckForUpdates(metadataText, this, 'book', isJSON)
|
if (metadataFile) {
|
||||||
|
Logger.debug(`[Book] Found ${metadataFile.metadata.filename} file for "${this.metadata.title}"`)
|
||||||
|
const metadataText = await readTextFile(metadataFile.metadata.path)
|
||||||
|
const abmetadataUpdates = abmetadataGenerator.parseAndCheckForUpdates(metadataText, this, 'book', metadataIsJSON)
|
||||||
if (abmetadataUpdates && Object.keys(abmetadataUpdates).length) {
|
if (abmetadataUpdates && Object.keys(abmetadataUpdates).length) {
|
||||||
Logger.debug(`[Book] "${this.metadata.title}" changes found in metadata.abs file`, abmetadataUpdates)
|
Logger.debug(`[Book] "${this.metadata.title}" changes found in metadata.abs file`, abmetadataUpdates)
|
||||||
|
|
||||||
if (abmetadataUpdates.tags) { // Set media tags if updated
|
if (abmetadataUpdates.tags) { // Set media tags if updated
|
||||||
this.tags = abmetadataUpdates.tags
|
this.tags = abmetadataUpdates.tags
|
||||||
tagsUpdated = true
|
hasUpdated = true
|
||||||
|
}
|
||||||
|
if (abmetadataUpdates.chapters) { // Set chapters if updated
|
||||||
|
this.chapters = abmetadataUpdates.chapters
|
||||||
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
if (abmetadataUpdates.metadata) {
|
if (abmetadataUpdates.metadata) {
|
||||||
metadataUpdatePayload = {
|
metadataUpdatePayload = {
|
||||||
@ -276,6 +283,9 @@ class Book {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (metadataAbs || metadataJson) { // Has different metadata file format so mark as updated
|
||||||
|
Logger.debug(`[Book] Found different format metadata file ${(metadataAbs || metadataJson).metadata.filename}, expecting .${global.ServerSettings.metadataFileFormat} for "${this.metadata.title}"`)
|
||||||
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadataOpf = textMetadataFiles.find(lf => lf.isOPFFile || lf.metadata.filename === 'metadata.xml')
|
const metadataOpf = textMetadataFiles.find(lf => lf.isOPFFile || lf.metadata.filename === 'metadata.xml')
|
||||||
@ -289,7 +299,7 @@ class Book {
|
|||||||
if (key === 'tags') { // Add tags only if tags are empty
|
if (key === 'tags') { // Add tags only if tags are empty
|
||||||
if (opfMetadata.tags.length && (!this.tags.length || opfMetadataOverrideDetails)) {
|
if (opfMetadata.tags.length && (!this.tags.length || opfMetadataOverrideDetails)) {
|
||||||
this.tags = opfMetadata.tags
|
this.tags = opfMetadata.tags
|
||||||
tagsUpdated = true
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
} else if (key === 'genres') { // Add genres only if genres are empty
|
} else if (key === 'genres') { // Add genres only if genres are empty
|
||||||
if (opfMetadata.genres.length && (!this.metadata.genres.length || opfMetadataOverrideDetails)) {
|
if (opfMetadata.genres.length && (!this.metadata.genres.length || opfMetadataOverrideDetails)) {
|
||||||
@ -321,9 +331,9 @@ class Book {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(metadataUpdatePayload).length) {
|
if (Object.keys(metadataUpdatePayload).length) {
|
||||||
return this.metadata.update(metadataUpdatePayload) || tagsUpdated
|
return this.metadata.update(metadataUpdatePayload) || hasUpdated
|
||||||
}
|
}
|
||||||
return tagsUpdated
|
return hasUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
searchQuery(query) {
|
searchQuery(query) {
|
||||||
|
@ -111,8 +111,8 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasUpdated) {
|
if (hasUpdated) {
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
|
|
||||||
await this.db.updateLibraryItem(libraryItem)
|
await this.db.updateLibraryItem(libraryItem)
|
||||||
|
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
|
||||||
return ScanResult.UPDATED
|
return ScanResult.UPDATED
|
||||||
}
|
}
|
||||||
return ScanResult.UPTODATE
|
return ScanResult.UPTODATE
|
||||||
|
@ -3,6 +3,7 @@ const filePerms = require('./filePerms')
|
|||||||
const package = require('../../package.json')
|
const package = require('../../package.json')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { getId } = require('./index')
|
const { getId } = require('./index')
|
||||||
|
const areEquivalent = require('../utils/areEquivalent')
|
||||||
|
|
||||||
|
|
||||||
const CurrentAbMetadataVersion = 2
|
const CurrentAbMetadataVersion = 2
|
||||||
@ -424,6 +425,33 @@ function parseJsonMetadataText(text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanChaptersArray(chaptersArray, mediaTitle) {
|
||||||
|
const chapters = []
|
||||||
|
let index = 0
|
||||||
|
for (const chap of chaptersArray) {
|
||||||
|
if (chap.start === null || isNaN(chap.start)) {
|
||||||
|
Logger.error(`[abmetadataGenerator] Invalid chapter start time ${chap.start} for "${mediaTitle}" metadata file`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (chap.end === null || isNaN(chap.end)) {
|
||||||
|
Logger.error(`[abmetadataGenerator] Invalid chapter end time ${chap.end} for "${mediaTitle}" metadata file`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!chap.title || typeof chap.title !== 'string') {
|
||||||
|
Logger.error(`[abmetadataGenerator] Invalid chapter title ${chap.title} for "${mediaTitle}" metadata file`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
chapters.push({
|
||||||
|
id: index++,
|
||||||
|
start: chap.start,
|
||||||
|
end: chap.end,
|
||||||
|
title: chap.title
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return chapters
|
||||||
|
}
|
||||||
|
|
||||||
// Input text from abmetadata file and return object of media changes
|
// Input text from abmetadata file and return object of media changes
|
||||||
// only returns object of changes. empty object means no changes
|
// only returns object of changes. empty object means no changes
|
||||||
function parseAndCheckForUpdates(text, media, mediaType, isJSON) {
|
function parseAndCheckForUpdates(text, media, mediaType, isJSON) {
|
||||||
@ -477,6 +505,15 @@ function parseAndCheckForUpdates(text, media, mediaType, isJSON) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (abmetadataData.chapters && mediaType === 'book') {
|
||||||
|
const abmetadataChaptersCleaned = cleanChaptersArray(abmetadataData.chapters)
|
||||||
|
if (abmetadataChaptersCleaned) {
|
||||||
|
if (!areEquivalent(abmetadataChaptersCleaned, media.chapters)) {
|
||||||
|
updatePayload.chapters = abmetadataChaptersCleaned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(metadataUpdatePayload).length) {
|
if (Object.keys(metadataUpdatePayload).length) {
|
||||||
updatePayload.metadata = metadataUpdatePayload
|
updatePayload.metadata = metadataUpdatePayload
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user