diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue
index 93a4f803..2ff59422 100644
--- a/client/pages/config/index.vue
+++ b/client/pages/config/index.vue
@@ -45,7 +45,7 @@
- updateSettingsKey('metadataFileFormat', val)" :disabled="updatingServerSettings" />
+
@@ -355,6 +355,10 @@ export default {
updateServerLanguage(val) {
this.updateSettingsKey('language', val)
},
+ updateMetadataFileFormat(val) {
+ if (this.serverSettings.metadataFileFormat === val) return
+ this.updateSettingsKey('metadataFileFormat', val)
+ },
updateSettingsKey(key, val) {
this.updateServerSettings({
[key]: val
@@ -364,8 +368,7 @@ export default {
this.updatingServerSettings = true
this.$store
.dispatch('updateServerSettings', payload)
- .then((success) => {
- console.log('Updated Server Settings', success)
+ .then(() => {
this.updatingServerSettings = false
this.$toast.success('Server settings updated')
diff --git a/server/objects/LibraryItem.js b/server/objects/LibraryItem.js
index 0d001799..b95559f8 100644
--- a/server/objects/LibraryItem.js
+++ b/server/objects/LibraryItem.js
@@ -9,6 +9,7 @@ const Podcast = require('./mediaTypes/Podcast')
const Video = require('./mediaTypes/Video')
const Music = require('./mediaTypes/Music')
const { areEquivalent, copyValue, getId, cleanStringForSearch } = require('../utils/index')
+const { filePathToPOSIX } = require('../utils/fileUtils')
class LibraryItem {
constructor(libraryItem = null) {
@@ -368,7 +369,7 @@ class LibraryItem {
const fileFoundCheck = this.checkFileFound(lf, true)
if (fileFoundCheck === null) {
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
existingLibraryFiles.push(lf)
} else {
@@ -502,17 +503,26 @@ class LibraryItem {
const metadataFileFormat = global.ServerSettings.metadataFileFormat
const metadataFilePath = Path.join(metadataPath, `metadata.${metadataFileFormat}`)
-
if (metadataFileFormat === 'json') {
// Remove metadata.abs if it exists
if (await fs.pathExists(Path.join(metadataPath, `metadata.abs`))) {
Logger.debug(`[LibraryItem] Removing metadata.abs for item "${this.media.metadata.title}"`)
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(() => {
+ 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 true
}).catch((error) => {
+ this.isSavingMetadata = false
Logger.error(`[LibraryItem] Failed to save json file at "${metadataFilePath}"`, error)
return false
})
@@ -521,12 +531,22 @@ class LibraryItem {
if (await fs.pathExists(Path.join(metadataPath, `metadata.json`))) {
Logger.debug(`[LibraryItem] Removing metadata.json for item "${this.media.metadata.title}"`)
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
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
})
}
diff --git a/server/objects/files/LibraryFile.js b/server/objects/files/LibraryFile.js
index 72c39c37..d832a135 100644
--- a/server/objects/files/LibraryFile.js
+++ b/server/objects/files/LibraryFile.js
@@ -1,5 +1,5 @@
const Path = require('path')
-const { getFileTimestampsWithIno } = require('../../utils/fileUtils')
+const { getFileTimestampsWithIno, filePathToPOSIX } = require('../../utils/fileUtils')
const globals = require('../../utils/globals')
const FileMetadata = require('../metadata/FileMetadata')
@@ -59,8 +59,8 @@ class LibraryFile {
var fileMetadata = new FileMetadata()
fileMetadata.setData(fileTsData)
fileMetadata.filename = Path.basename(relPath)
- fileMetadata.path = path
- fileMetadata.relPath = relPath
+ fileMetadata.path = filePathToPOSIX(path)
+ fileMetadata.relPath = filePathToPOSIX(relPath)
fileMetadata.ext = Path.extname(relPath)
this.ino = fileTsData.ino
this.metadata = fileMetadata
diff --git a/server/objects/mediaTypes/Book.js b/server/objects/mediaTypes/Book.js
index d0cfab93..5d549970 100644
--- a/server/objects/mediaTypes/Book.js
+++ b/server/objects/mediaTypes/Book.js
@@ -237,7 +237,7 @@ class Book {
// Look for desc.txt, reader.txt, metadata.abs and opf file then update details if found
async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) {
let metadataUpdatePayload = {}
- let tagsUpdated = false
+ let hasUpdated = false
const descTxt = textMetadataFiles.find(lf => lf.metadata.filename === 'desc.txt')
if (descTxt) {
@@ -256,18 +256,25 @@ class Book {
}
}
- const metadataAbs = textMetadataFiles.find(lf => lf.metadata.filename === 'metadata.abs' || lf.metadata.filename === 'metadata.json')
- if (metadataAbs) {
- const isJSON = metadataAbs.metadata.filename === 'metadata.json'
- Logger.debug(`[Book] Found ${metadataAbs.metadata.filename} file for "${this.metadata.title}"`)
- const metadataText = await readTextFile(metadataAbs.metadata.path)
- const abmetadataUpdates = abmetadataGenerator.parseAndCheckForUpdates(metadataText, this, 'book', isJSON)
+ const metadataIsJSON = global.ServerSettings.metadataFileFormat === 'json'
+ const metadataAbs = textMetadataFiles.find(lf => lf.metadata.filename === 'metadata.abs')
+ const metadataJson = textMetadataFiles.find(lf => lf.metadata.filename === 'metadata.json')
+
+ const metadataFile = metadataIsJSON ? metadataJson : metadataAbs
+ 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) {
Logger.debug(`[Book] "${this.metadata.title}" changes found in metadata.abs file`, abmetadataUpdates)
if (abmetadataUpdates.tags) { // Set media tags if updated
this.tags = abmetadataUpdates.tags
- tagsUpdated = true
+ hasUpdated = true
+ }
+ if (abmetadataUpdates.chapters) { // Set chapters if updated
+ this.chapters = abmetadataUpdates.chapters
+ hasUpdated = true
}
if (abmetadataUpdates.metadata) {
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')
@@ -289,7 +299,7 @@ class Book {
if (key === 'tags') { // Add tags only if tags are empty
if (opfMetadata.tags.length && (!this.tags.length || opfMetadataOverrideDetails)) {
this.tags = opfMetadata.tags
- tagsUpdated = true
+ hasUpdated = true
}
} else if (key === 'genres') { // Add genres only if genres are empty
if (opfMetadata.genres.length && (!this.metadata.genres.length || opfMetadataOverrideDetails)) {
@@ -321,9 +331,9 @@ class Book {
}
if (Object.keys(metadataUpdatePayload).length) {
- return this.metadata.update(metadataUpdatePayload) || tagsUpdated
+ return this.metadata.update(metadataUpdatePayload) || hasUpdated
}
- return tagsUpdated
+ return hasUpdated
}
searchQuery(query) {
diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js
index d6f20930..3a0f75f1 100644
--- a/server/scanner/Scanner.js
+++ b/server/scanner/Scanner.js
@@ -111,8 +111,8 @@ class Scanner {
}
if (hasUpdated) {
- SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
await this.db.updateLibraryItem(libraryItem)
+ SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
return ScanResult.UPDATED
}
return ScanResult.UPTODATE
diff --git a/server/utils/abmetadataGenerator.js b/server/utils/abmetadataGenerator.js
index 26cffbae..b0415aea 100644
--- a/server/utils/abmetadataGenerator.js
+++ b/server/utils/abmetadataGenerator.js
@@ -3,6 +3,7 @@ const filePerms = require('./filePerms')
const package = require('../../package.json')
const Logger = require('../Logger')
const { getId } = require('./index')
+const areEquivalent = require('../utils/areEquivalent')
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
// only returns object of changes. empty object means no changes
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) {
updatePayload.metadata = metadataUpdatePayload
}