mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-11-04 03:17:00 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			124 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const Path = require('path')
 | 
						|
 | 
						|
const SocketAuthority = require('../SocketAuthority')
 | 
						|
const Logger = require('../Logger')
 | 
						|
 | 
						|
const fs = require('../libs/fsExtra')
 | 
						|
 | 
						|
const { secondsToTimestamp } = require('../utils/index')
 | 
						|
const toneHelpers = require('../utils/toneHelpers')
 | 
						|
const filePerms = require('../utils/filePerms')
 | 
						|
 | 
						|
class AudioMetadataMangaer {
 | 
						|
  constructor(db, taskManager) {
 | 
						|
    this.db = db
 | 
						|
    this.taskManager = taskManager
 | 
						|
  }
 | 
						|
 | 
						|
  getToneMetadataObjectForApi(libraryItem) {
 | 
						|
    return toneHelpers.getToneMetadataObject(libraryItem)
 | 
						|
  }
 | 
						|
 | 
						|
  async updateMetadataForItem(user, libraryItem, options = {}) {
 | 
						|
    const forceEmbedChapters = !!options.forceEmbedChapters
 | 
						|
    const backupFiles = !!options.backup
 | 
						|
 | 
						|
    const audioFiles = libraryItem.media.includedAudioFiles
 | 
						|
 | 
						|
    const itemAudioMetadataPayload = {
 | 
						|
      userId: user.id,
 | 
						|
      libraryItemId: libraryItem.id,
 | 
						|
      startedAt: Date.now(),
 | 
						|
      audioFiles: audioFiles.map(af => ({ index: af.index, ino: af.ino, filename: af.metadata.filename }))
 | 
						|
    }
 | 
						|
 | 
						|
    SocketAuthority.emitter('audio_metadata_started', itemAudioMetadataPayload)
 | 
						|
 | 
						|
    // Ensure folder for backup files
 | 
						|
    const itemCacheDir = Path.join(global.MetadataPath, `cache/items/${libraryItem.id}`)
 | 
						|
    let cacheDirCreated = false
 | 
						|
    if (!await fs.pathExists(itemCacheDir)) {
 | 
						|
      await fs.mkdir(itemCacheDir)
 | 
						|
      await filePerms.setDefault(itemCacheDir, true)
 | 
						|
      cacheDirCreated = true
 | 
						|
    }
 | 
						|
 | 
						|
    // Write chapters file
 | 
						|
    const toneJsonPath = Path.join(itemCacheDir, 'metadata.json')
 | 
						|
 | 
						|
    try {
 | 
						|
      const chapters = (audioFiles.length == 1 || forceEmbedChapters) ? libraryItem.media.chapters : null
 | 
						|
      await toneHelpers.writeToneMetadataJsonFile(libraryItem, chapters, toneJsonPath, audioFiles.length)
 | 
						|
    } catch (error) {
 | 
						|
      Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error)
 | 
						|
 | 
						|
      itemAudioMetadataPayload.failed = true
 | 
						|
      itemAudioMetadataPayload.error = 'Failed to write metadata.json'
 | 
						|
      SocketAuthority.emitter('audio_metadata_finished', itemAudioMetadataPayload)
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    const results = []
 | 
						|
    for (const af of audioFiles) {
 | 
						|
      const result = await this.updateAudioFileMetadataWithTone(libraryItem, af, toneJsonPath, itemCacheDir, backupFiles)
 | 
						|
      results.push(result)
 | 
						|
    }
 | 
						|
 | 
						|
    // Remove temp cache file/folder if not backing up
 | 
						|
    if (!backupFiles) {
 | 
						|
      // If cache dir was created from this then remove it
 | 
						|
      if (cacheDirCreated) {
 | 
						|
        await fs.remove(itemCacheDir)
 | 
						|
      } else {
 | 
						|
        await fs.remove(toneJsonPath)
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    const elapsed = Date.now() - itemAudioMetadataPayload.startedAt
 | 
						|
    Logger.debug(`[AudioMetadataManager] Elapsed ${secondsToTimestamp(elapsed / 1000, true)}`)
 | 
						|
    itemAudioMetadataPayload.results = results
 | 
						|
    itemAudioMetadataPayload.elapsed = elapsed
 | 
						|
    itemAudioMetadataPayload.finishedAt = Date.now()
 | 
						|
    SocketAuthority.emitter('audio_metadata_finished', itemAudioMetadataPayload)
 | 
						|
  }
 | 
						|
 | 
						|
  async updateAudioFileMetadataWithTone(libraryItem, audioFile, toneJsonPath, itemCacheDir, backupFiles) {
 | 
						|
    const resultPayload = {
 | 
						|
      libraryItemId: libraryItem.id,
 | 
						|
      index: audioFile.index,
 | 
						|
      ino: audioFile.ino,
 | 
						|
      filename: audioFile.metadata.filename
 | 
						|
    }
 | 
						|
    SocketAuthority.emitter('audiofile_metadata_started', resultPayload)
 | 
						|
 | 
						|
    // Backup audio file
 | 
						|
    if (backupFiles) {
 | 
						|
      try {
 | 
						|
        const backupFilePath = Path.join(itemCacheDir, audioFile.metadata.filename)
 | 
						|
        await fs.copy(audioFile.metadata.path, backupFilePath)
 | 
						|
        Logger.debug(`[AudioMetadataManager] Backed up audio file at "${backupFilePath}"`)
 | 
						|
      } catch (err) {
 | 
						|
        Logger.error(`[AudioMetadataManager] Failed to backup audio file "${audioFile.metadata.path}"`, err)
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    const _toneMetadataObject = {
 | 
						|
      'ToneJsonFile': toneJsonPath,
 | 
						|
      'TrackNumber': audioFile.index,
 | 
						|
    }
 | 
						|
 | 
						|
    if (libraryItem.media.coverPath) {
 | 
						|
      _toneMetadataObject['CoverFile'] = libraryItem.media.coverPath
 | 
						|
    }
 | 
						|
 | 
						|
    resultPayload.success = await toneHelpers.tagAudioFile(audioFile.metadata.path, _toneMetadataObject)
 | 
						|
    if (resultPayload.success) {
 | 
						|
      Logger.info(`[AudioMetadataManager] Successfully tagged audio file "${audioFile.metadata.path}"`)
 | 
						|
    }
 | 
						|
 | 
						|
    SocketAuthority.emitter('audiofile_metadata_finished', resultPayload)
 | 
						|
    return resultPayload
 | 
						|
  }
 | 
						|
}
 | 
						|
module.exports = AudioMetadataMangaer
 |