mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Remove NFO metadata and save metadata button
This commit is contained in:
		
							parent
							
								
									3f6ed6dbf9
								
							
						
					
					
						commit
						79a82df914
					
				@ -9,10 +9,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <div class="flex-grow" />
 | 
					        <div class="flex-grow" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ui-tooltip v-if="!isMissing && mediaType == 'book'" text="(Root User Only) Save a NFO metadata file in your audiobooks directory" direction="bottom" class="mr-4 hidden sm:block">
 | 
					 | 
				
			||||||
          <ui-btn v-if="isRootUser" :loading="savingMetadata" color="bg" type="button" class="h-full" small @click.stop.prevent="saveMetadata">Save Metadata</ui-btn>
 | 
					 | 
				
			||||||
        </ui-tooltip>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <ui-tooltip v-if="mediaType == 'book'" :disabled="!!quickMatching" :text="`(Root User Only) Populate empty book details & cover with first book result from '${libraryProvider}'. Does not overwrite details.`" direction="bottom" class="mr-4">
 | 
					        <ui-tooltip v-if="mediaType == 'book'" :disabled="!!quickMatching" :text="`(Root User Only) Populate empty book details & cover with first book result from '${libraryProvider}'. Does not overwrite details.`" direction="bottom" class="mr-4">
 | 
				
			||||||
          <ui-btn v-if="isRootUser" :loading="quickMatching" color="bg" type="button" class="h-full" small @click.stop.prevent="quickMatch">Quick Match</ui-btn>
 | 
					          <ui-btn v-if="isRootUser" :loading="quickMatching" color="bg" type="button" class="h-full" small @click.stop.prevent="quickMatch">Quick Match</ui-btn>
 | 
				
			||||||
        </ui-tooltip>
 | 
					        </ui-tooltip>
 | 
				
			||||||
@ -40,7 +36,6 @@ export default {
 | 
				
			|||||||
    return {
 | 
					    return {
 | 
				
			||||||
      resettingProgress: false,
 | 
					      resettingProgress: false,
 | 
				
			||||||
      isScrollable: false,
 | 
					      isScrollable: false,
 | 
				
			||||||
      savingMetadata: false,
 | 
					 | 
				
			||||||
      rescanning: false,
 | 
					      rescanning: false,
 | 
				
			||||||
      quickMatching: false
 | 
					      quickMatching: false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -144,24 +139,6 @@ export default {
 | 
				
			|||||||
          this.rescanning = false
 | 
					          this.rescanning = false
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    saveMetadataComplete(result) {
 | 
					 | 
				
			||||||
      this.savingMetadata = false
 | 
					 | 
				
			||||||
      if (result.error) {
 | 
					 | 
				
			||||||
        this.$toast.error(result.error)
 | 
					 | 
				
			||||||
      } else if (result.audiobookId) {
 | 
					 | 
				
			||||||
        var { savedPath } = result
 | 
					 | 
				
			||||||
        if (!savedPath) {
 | 
					 | 
				
			||||||
          this.$toast.error(`Failed to save metadata file (${result.audiobookId})`)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          this.$toast.success(`Metadata file saved "${result.audiobookTitle}"`)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    saveMetadata() {
 | 
					 | 
				
			||||||
      this.savingMetadata = true
 | 
					 | 
				
			||||||
      this.$root.socket.once('save_metadata_complete', this.saveMetadataComplete)
 | 
					 | 
				
			||||||
      this.$root.socket.emit('save_metadata', this.libraryItemId)
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    submitForm() {
 | 
					    submitForm() {
 | 
				
			||||||
      if (this.isProcessing) {
 | 
					      if (this.isProcessing) {
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
				
			|||||||
@ -225,11 +225,6 @@ class Server {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Scanning
 | 
					      // Scanning
 | 
				
			||||||
      socket.on('cancel_scan', this.cancelScan.bind(this))
 | 
					      socket.on('cancel_scan', this.cancelScan.bind(this))
 | 
				
			||||||
      socket.on('save_metadata', (libraryItemId) => this.saveMetadata(socket, libraryItemId))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Downloading
 | 
					 | 
				
			||||||
      socket.on('download', (payload) => this.downloadManager.downloadSocketRequest(socket, payload))
 | 
					 | 
				
			||||||
      socket.on('remove_download', (downloadId) => this.downloadManager.removeSocketRequest(socket, downloadId))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Logs
 | 
					      // Logs
 | 
				
			||||||
      socket.on('set_log_listener', (level) => Logger.addSocketListener(socket, level))
 | 
					      socket.on('set_log_listener', (level) => Logger.addSocketListener(socket, level))
 | 
				
			||||||
@ -273,14 +268,6 @@ class Server {
 | 
				
			|||||||
    this.scanner.setCancelLibraryScan(id)
 | 
					    this.scanner.setCancelLibraryScan(id)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Generates an NFO metadata file, if no audiobookId is passed then all audiobooks are done
 | 
					 | 
				
			||||||
  async saveMetadata(socket, audiobookId = null) {
 | 
					 | 
				
			||||||
    Logger.info('[Server] Starting save metadata files')
 | 
					 | 
				
			||||||
    var response = await this.scanner.saveMetadata(audiobookId)
 | 
					 | 
				
			||||||
    Logger.info(`[Server] Finished saving metadata files Successful: ${response.success}, Failed: ${response.failed}`)
 | 
					 | 
				
			||||||
    socket.emit('save_metadata_complete', response)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Remove unused /metadata/items/{id} folders
 | 
					  // Remove unused /metadata/items/{id} folders
 | 
				
			||||||
  async purgeMetadata() {
 | 
					  async purgeMetadata() {
 | 
				
			||||||
    var itemsMetadata = Path.join(global.MetadataPath, 'items')
 | 
					    var itemsMetadata = Path.join(global.MetadataPath, 'items')
 | 
				
			||||||
 | 
				
			|||||||
@ -243,8 +243,6 @@ class LibraryItem {
 | 
				
			|||||||
    this.scanVersion = version
 | 
					    this.scanVersion = version
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveMetadata() { }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Returns null if file not found, true if file was updated, false if up to date
 | 
					  // Returns null if file not found, true if file was updated, false if up to date
 | 
				
			||||||
  //  updates existing LibraryFile, AudioFile, EBookFile's
 | 
					  //  updates existing LibraryFile, AudioFile, EBookFile's
 | 
				
			||||||
  checkFileFound(fileFound) {
 | 
					  checkFileFound(fileFound) {
 | 
				
			||||||
 | 
				
			|||||||
@ -616,40 +616,6 @@ class Scanner {
 | 
				
			|||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: Redo metadata
 | 
					 | 
				
			||||||
  async saveMetadata(audiobookId) {
 | 
					 | 
				
			||||||
    // if (audiobookId) {
 | 
					 | 
				
			||||||
    //   var audiobook = this.db.audiobooks.find(ab => ab.id === audiobookId)
 | 
					 | 
				
			||||||
    //   if (!audiobook) {
 | 
					 | 
				
			||||||
    //     return {
 | 
					 | 
				
			||||||
    //       error: 'Audiobook not found'
 | 
					 | 
				
			||||||
    //     }
 | 
					 | 
				
			||||||
    //   }
 | 
					 | 
				
			||||||
    //   var savedPath = await audiobook.writeNfoFile()
 | 
					 | 
				
			||||||
    //   return {
 | 
					 | 
				
			||||||
    //     audiobookId,
 | 
					 | 
				
			||||||
    //     audiobookTitle: audiobook.title,
 | 
					 | 
				
			||||||
    //     savedPath
 | 
					 | 
				
			||||||
    //   }
 | 
					 | 
				
			||||||
    // } else {
 | 
					 | 
				
			||||||
    //   var response = {
 | 
					 | 
				
			||||||
    //     success: 0,
 | 
					 | 
				
			||||||
    //     failed: 0
 | 
					 | 
				
			||||||
    //   }
 | 
					 | 
				
			||||||
    //   for (let i = 0; i < this.db.audiobooks.length; i++) {
 | 
					 | 
				
			||||||
    //     var audiobook = this.db.audiobooks[i]
 | 
					 | 
				
			||||||
    //     var savedPath = await audiobook.writeNfoFile()
 | 
					 | 
				
			||||||
    //     if (savedPath) {
 | 
					 | 
				
			||||||
    //       Logger.info(`[Scanner] Saved metadata nfo ${savedPath}`)
 | 
					 | 
				
			||||||
    //       response.success++
 | 
					 | 
				
			||||||
    //     } else {
 | 
					 | 
				
			||||||
    //       response.failed++
 | 
					 | 
				
			||||||
    //     }
 | 
					 | 
				
			||||||
    //   }
 | 
					 | 
				
			||||||
    //   return response
 | 
					 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async quickMatchBook(libraryItem, options = {}) {
 | 
					  async quickMatchBook(libraryItem, options = {}) {
 | 
				
			||||||
    var provider = options.provider || 'google'
 | 
					    var provider = options.provider || 'google'
 | 
				
			||||||
    var searchTitle = options.title || libraryItem.media.metadata.title
 | 
					    var searchTitle = options.title || libraryItem.media.metadata.title
 | 
				
			||||||
 | 
				
			|||||||
@ -1,91 +0,0 @@
 | 
				
			|||||||
const fs = require('fs-extra')
 | 
					 | 
				
			||||||
const Path = require('path')
 | 
					 | 
				
			||||||
const { bytesPretty } = require('./fileUtils')
 | 
					 | 
				
			||||||
const Logger = require('../Logger')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const LEFT_COL_LEN = 25
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function sectionHeaderLines(title) {
 | 
					 | 
				
			||||||
  return [title, ''.padEnd(10, '=')]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function generateSection(sectionTitle, sectionData) {
 | 
					 | 
				
			||||||
  var lines = sectionHeaderLines(sectionTitle)
 | 
					 | 
				
			||||||
  for (const key in sectionData) {
 | 
					 | 
				
			||||||
    var line = key.padEnd(LEFT_COL_LEN) + (sectionData[key] || '')
 | 
					 | 
				
			||||||
    lines.push(line)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return lines
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function generate(audiobook, nfoFilename = 'metadata.nfo') {
 | 
					 | 
				
			||||||
  var jsonObj = audiobook.toJSON()
 | 
					 | 
				
			||||||
  var book = jsonObj.book
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var generalSectionData = {
 | 
					 | 
				
			||||||
    'Title': book.title,
 | 
					 | 
				
			||||||
    'Subtitle': book.subtitle,
 | 
					 | 
				
			||||||
    'Author': book.author,
 | 
					 | 
				
			||||||
    'Narrator': book.narrator,
 | 
					 | 
				
			||||||
    'Series': book.series,
 | 
					 | 
				
			||||||
    'Volume Number': book.volumeNumber,
 | 
					 | 
				
			||||||
    'Publish Year': book.publishedYear,
 | 
					 | 
				
			||||||
    'Genre': book.genres ? book.genres.join(', ') : '',
 | 
					 | 
				
			||||||
    'Duration': audiobook.durationPretty,
 | 
					 | 
				
			||||||
    'Chapters': jsonObj.chapters.length
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!book.subtitle) {
 | 
					 | 
				
			||||||
    delete generalSectionData['Subtitle']
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!book.series) {
 | 
					 | 
				
			||||||
    delete generalSectionData['Series']
 | 
					 | 
				
			||||||
    delete generalSectionData['Volume Number']
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var tracks = audiobook.tracks
 | 
					 | 
				
			||||||
  var audioTrack = tracks.length ? audiobook.tracks[0] : {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var totalBitrate = 0
 | 
					 | 
				
			||||||
  var numBitrates = 0
 | 
					 | 
				
			||||||
  for (let i = 0; i < tracks.length; i++) {
 | 
					 | 
				
			||||||
    if (tracks[i].bitRate) {
 | 
					 | 
				
			||||||
      totalBitrate += tracks[i].bitRate
 | 
					 | 
				
			||||||
      numBitrates++
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  var averageBitrate = numBitrates ? totalBitrate / numBitrates : 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var mediaSectionData = {
 | 
					 | 
				
			||||||
    'Tracks': jsonObj.tracks.length,
 | 
					 | 
				
			||||||
    'Size': audiobook.sizePretty,
 | 
					 | 
				
			||||||
    'Codec': audioTrack.codec,
 | 
					 | 
				
			||||||
    'Ext': audioTrack.ext,
 | 
					 | 
				
			||||||
    'Channels': audioTrack.channels,
 | 
					 | 
				
			||||||
    'Channel Layout': audioTrack.channelLayout,
 | 
					 | 
				
			||||||
    'Average Bitrate': bytesPretty(averageBitrate)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var bookSection = generateSection('Book Info', generalSectionData)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var descriptionSection = null
 | 
					 | 
				
			||||||
  if (book.description) {
 | 
					 | 
				
			||||||
    descriptionSection = sectionHeaderLines('Book Description')
 | 
					 | 
				
			||||||
    descriptionSection.push(book.description)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var mediaSection = generateSection('Media Info', mediaSectionData)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var fullFile = bookSection.join('\n') + '\n\n'
 | 
					 | 
				
			||||||
  if (descriptionSection) fullFile += descriptionSection.join('\n') + '\n\n'
 | 
					 | 
				
			||||||
  fullFile += mediaSection.join('\n')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var nfoPath = Path.join(audiobook.fullPath, nfoFilename)
 | 
					 | 
				
			||||||
  var relativePath = Path.join(audiobook.path, nfoFilename)
 | 
					 | 
				
			||||||
  return fs.writeFile(nfoPath, fullFile).then(() => relativePath).catch((error) => {
 | 
					 | 
				
			||||||
    Logger.error(`Failed to write nfo file ${error}`)
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
module.exports = generate
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user