mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Fix:Update metadata.json when using item metadata utils #2837
This commit is contained in:
		
							parent
							
								
									29fc503503
								
							
						
					
					
						commit
						c4fd4ff9de
					
				@ -329,6 +329,7 @@ class MiscController {
 | 
				
			|||||||
        await libraryItem.media.update({
 | 
					        await libraryItem.media.update({
 | 
				
			||||||
          tags: libraryItem.media.tags
 | 
					          tags: libraryItem.media.tags
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					        await libraryItem.saveMetadataFile()
 | 
				
			||||||
        const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
					        const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
				
			||||||
        SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
					        SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
				
			||||||
        numItemsUpdated++
 | 
					        numItemsUpdated++
 | 
				
			||||||
@ -370,6 +371,7 @@ class MiscController {
 | 
				
			|||||||
      await libraryItem.media.update({
 | 
					      await libraryItem.media.update({
 | 
				
			||||||
        tags: libraryItem.media.tags
 | 
					        tags: libraryItem.media.tags
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					      await libraryItem.saveMetadataFile()
 | 
				
			||||||
      const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
					      const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
				
			||||||
      SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
					      SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
				
			||||||
      numItemsUpdated++
 | 
					      numItemsUpdated++
 | 
				
			||||||
@ -462,6 +464,7 @@ class MiscController {
 | 
				
			|||||||
        await libraryItem.media.update({
 | 
					        await libraryItem.media.update({
 | 
				
			||||||
          genres: libraryItem.media.genres
 | 
					          genres: libraryItem.media.genres
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					        await libraryItem.saveMetadataFile()
 | 
				
			||||||
        const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
					        const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
				
			||||||
        SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
					        SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
				
			||||||
        numItemsUpdated++
 | 
					        numItemsUpdated++
 | 
				
			||||||
@ -503,6 +506,7 @@ class MiscController {
 | 
				
			|||||||
      await libraryItem.media.update({
 | 
					      await libraryItem.media.update({
 | 
				
			||||||
        genres: libraryItem.media.genres
 | 
					        genres: libraryItem.media.genres
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					      await libraryItem.saveMetadataFile()
 | 
				
			||||||
      const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
					      const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
 | 
				
			||||||
      SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
					      SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
 | 
				
			||||||
      numItemsUpdated++
 | 
					      numItemsUpdated++
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,12 @@
 | 
				
			|||||||
 | 
					const Path = require('path')
 | 
				
			||||||
const { DataTypes, Model } = require('sequelize')
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					const fsExtra = require('../libs/fsExtra')
 | 
				
			||||||
const Logger = require('../Logger')
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
const oldLibraryItem = require('../objects/LibraryItem')
 | 
					const oldLibraryItem = require('../objects/LibraryItem')
 | 
				
			||||||
const libraryFilters = require('../utils/queries/libraryFilters')
 | 
					const libraryFilters = require('../utils/queries/libraryFilters')
 | 
				
			||||||
const { areEquivalent } = require('../utils/index')
 | 
					const { areEquivalent } = require('../utils/index')
 | 
				
			||||||
 | 
					const { filePathToPOSIX, getFileTimestampsWithIno } = require('../utils/fileUtils')
 | 
				
			||||||
 | 
					const LibraryFile = require('../objects/files/LibraryFile')
 | 
				
			||||||
const Book = require('./Book')
 | 
					const Book = require('./Book')
 | 
				
			||||||
const Podcast = require('./Podcast')
 | 
					const Podcast = require('./Podcast')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -828,6 +832,147 @@ class LibraryItem extends Model {
 | 
				
			|||||||
    return this[mixinMethodName](options)
 | 
					    return this[mixinMethodName](options)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * @returns {Promise<Book|Podcast>}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  getMediaExpanded() {
 | 
				
			||||||
 | 
					    if (this.mediaType === 'podcast') {
 | 
				
			||||||
 | 
					      return this.getMedia({
 | 
				
			||||||
 | 
					        include: [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            model: this.sequelize.models.podcastEpisode
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return this.getMedia({
 | 
				
			||||||
 | 
					        include: [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            model: this.sequelize.models.author,
 | 
				
			||||||
 | 
					            through: {
 | 
				
			||||||
 | 
					              attributes: []
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            model: this.sequelize.models.series,
 | 
				
			||||||
 | 
					            through: {
 | 
				
			||||||
 | 
					              attributes: ['sequence']
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        order: [
 | 
				
			||||||
 | 
					          [this.sequelize.models.author, this.sequelize.models.bookAuthor, 'createdAt', 'ASC'],
 | 
				
			||||||
 | 
					          [this.sequelize.models.series, 'bookSeries', 'createdAt', 'ASC']
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * @returns {Promise}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async saveMetadataFile() {
 | 
				
			||||||
 | 
					    let metadataPath = Path.join(global.MetadataPath, 'items', this.id)
 | 
				
			||||||
 | 
					    let storeMetadataWithItem = global.ServerSettings.storeMetadataWithItem
 | 
				
			||||||
 | 
					    if (storeMetadataWithItem && !this.isFile) {
 | 
				
			||||||
 | 
					      metadataPath = this.path
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // Make sure metadata book dir exists
 | 
				
			||||||
 | 
					      storeMetadataWithItem = false
 | 
				
			||||||
 | 
					      await fsExtra.ensureDir(metadataPath)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const metadataFilePath = Path.join(metadataPath, `metadata.${global.ServerSettings.metadataFileFormat}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Expanded with series, authors, podcastEpisodes
 | 
				
			||||||
 | 
					    const mediaExpanded = this.media || await this.getMediaExpanded()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let jsonObject = {}
 | 
				
			||||||
 | 
					    if (this.mediaType === 'book') {
 | 
				
			||||||
 | 
					      jsonObject = {
 | 
				
			||||||
 | 
					        tags: mediaExpanded.tags || [],
 | 
				
			||||||
 | 
					        chapters: mediaExpanded.chapters?.map(c => ({ ...c })) || [],
 | 
				
			||||||
 | 
					        title: mediaExpanded.title,
 | 
				
			||||||
 | 
					        subtitle: mediaExpanded.subtitle,
 | 
				
			||||||
 | 
					        authors: mediaExpanded.authors.map(a => a.name),
 | 
				
			||||||
 | 
					        narrators: mediaExpanded.narrators,
 | 
				
			||||||
 | 
					        series: mediaExpanded.series.map(se => {
 | 
				
			||||||
 | 
					          const sequence = se.bookSeries?.sequence || ''
 | 
				
			||||||
 | 
					          if (!sequence) return se.name
 | 
				
			||||||
 | 
					          return `${se.name} #${sequence}`
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        genres: mediaExpanded.genres || [],
 | 
				
			||||||
 | 
					        publishedYear: mediaExpanded.publishedYear,
 | 
				
			||||||
 | 
					        publishedDate: mediaExpanded.publishedDate,
 | 
				
			||||||
 | 
					        publisher: mediaExpanded.publisher,
 | 
				
			||||||
 | 
					        description: mediaExpanded.description,
 | 
				
			||||||
 | 
					        isbn: mediaExpanded.isbn,
 | 
				
			||||||
 | 
					        asin: mediaExpanded.asin,
 | 
				
			||||||
 | 
					        language: mediaExpanded.language,
 | 
				
			||||||
 | 
					        explicit: !!mediaExpanded.explicit,
 | 
				
			||||||
 | 
					        abridged: !!mediaExpanded.abridged
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      jsonObject = {
 | 
				
			||||||
 | 
					        tags: mediaExpanded.tags || [],
 | 
				
			||||||
 | 
					        title: mediaExpanded.title,
 | 
				
			||||||
 | 
					        author: mediaExpanded.author,
 | 
				
			||||||
 | 
					        description: mediaExpanded.description,
 | 
				
			||||||
 | 
					        releaseDate: mediaExpanded.releaseDate,
 | 
				
			||||||
 | 
					        genres: mediaExpanded.genres || [],
 | 
				
			||||||
 | 
					        feedURL: mediaExpanded.feedURL,
 | 
				
			||||||
 | 
					        imageURL: mediaExpanded.imageURL,
 | 
				
			||||||
 | 
					        itunesPageURL: mediaExpanded.itunesPageURL,
 | 
				
			||||||
 | 
					        itunesId: mediaExpanded.itunesId,
 | 
				
			||||||
 | 
					        itunesArtistId: mediaExpanded.itunesArtistId,
 | 
				
			||||||
 | 
					        asin: mediaExpanded.asin,
 | 
				
			||||||
 | 
					        language: mediaExpanded.language,
 | 
				
			||||||
 | 
					        explicit: !!mediaExpanded.explicit,
 | 
				
			||||||
 | 
					        podcastType: mediaExpanded.podcastType
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return fsExtra.writeFile(metadataFilePath, JSON.stringify(jsonObject, null, 2)).then(async () => {
 | 
				
			||||||
 | 
					      // Add metadata.json to libraryFiles array if it is new
 | 
				
			||||||
 | 
					      let metadataLibraryFile = this.libraryFiles.find(lf => lf.metadata.path === filePathToPOSIX(metadataFilePath))
 | 
				
			||||||
 | 
					      if (storeMetadataWithItem) {
 | 
				
			||||||
 | 
					        if (!metadataLibraryFile) {
 | 
				
			||||||
 | 
					          const newLibraryFile = new LibraryFile()
 | 
				
			||||||
 | 
					          await newLibraryFile.setDataFromPath(metadataFilePath, `metadata.json`)
 | 
				
			||||||
 | 
					          metadataLibraryFile = newLibraryFile.toJSON()
 | 
				
			||||||
 | 
					          this.libraryFiles.push(metadataLibraryFile)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          const fileTimestamps = await getFileTimestampsWithIno(metadataFilePath)
 | 
				
			||||||
 | 
					          if (fileTimestamps) {
 | 
				
			||||||
 | 
					            metadataLibraryFile.metadata.mtimeMs = fileTimestamps.mtimeMs
 | 
				
			||||||
 | 
					            metadataLibraryFile.metadata.ctimeMs = fileTimestamps.ctimeMs
 | 
				
			||||||
 | 
					            metadataLibraryFile.metadata.size = fileTimestamps.size
 | 
				
			||||||
 | 
					            metadataLibraryFile.ino = fileTimestamps.ino
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const libraryItemDirTimestamps = await getFileTimestampsWithIno(this.path)
 | 
				
			||||||
 | 
					        if (libraryItemDirTimestamps) {
 | 
				
			||||||
 | 
					          this.mtime = libraryItemDirTimestamps.mtimeMs
 | 
				
			||||||
 | 
					          this.ctime = libraryItemDirTimestamps.ctimeMs
 | 
				
			||||||
 | 
					          let size = 0
 | 
				
			||||||
 | 
					          this.libraryFiles.forEach((lf) => size += (!isNaN(lf.metadata.size) ? Number(lf.metadata.size) : 0))
 | 
				
			||||||
 | 
					          this.size = size
 | 
				
			||||||
 | 
					          await this.save()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Logger.debug(`Success saving abmetadata to "${metadataFilePath}"`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return metadataLibraryFile
 | 
				
			||||||
 | 
					    }).catch((error) => {
 | 
				
			||||||
 | 
					      Logger.error(`Failed to save json file at "${metadataFilePath}"`, error)
 | 
				
			||||||
 | 
					      return null
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Initialize model
 | 
					   * Initialize model
 | 
				
			||||||
   * @param {import('../Database').sequelize} sequelize 
 | 
					   * @param {import('../Database').sequelize} sequelize 
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,10 @@ module.exports = {
 | 
				
			|||||||
            attributes: ['sequence']
 | 
					            attributes: ['sequence']
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      order: [
 | 
				
			||||||
 | 
					        [Database.authorModel, Database.bookAuthorModel, 'createdAt', 'ASC'],
 | 
				
			||||||
 | 
					        [Database.seriesModel, 'bookSeries', 'createdAt', 'ASC']
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    for (const book of booksWithTag) {
 | 
					    for (const book of booksWithTag) {
 | 
				
			||||||
@ -68,7 +72,7 @@ module.exports = {
 | 
				
			|||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Get all library items that have genres
 | 
					   * Get all library items that have genres
 | 
				
			||||||
   * @param {string[]} genres 
 | 
					   * @param {string[]} genres 
 | 
				
			||||||
   * @returns {Promise<LibraryItem[]>}
 | 
					   * @returns {Promise<import('../../models/LibraryItem')[]>}
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  async getAllLibraryItemsWithGenres(genres) {
 | 
					  async getAllLibraryItemsWithGenres(genres) {
 | 
				
			||||||
    const libraryItems = []
 | 
					    const libraryItems = []
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user