mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-15 08:19:37 +00:00
Update:Experimental metadata embed tool to use tone
This commit is contained in:
parent
b6e3559aba
commit
97da73baf3
10 changed files with 296 additions and 119 deletions
|
|
@ -1,29 +1,31 @@
|
|||
<template>
|
||||
<div id="page-wrapper" class="bg-bg page p-8 overflow-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
||||
<div class="flex justify-center mb-2">
|
||||
<div class="flex justify-center mb-4">
|
||||
<div class="w-full max-w-2xl">
|
||||
<p class="text-xl">Metadata to embed</p>
|
||||
<p class="text-xl mb-2">Metadata to embed</p>
|
||||
<p class="mb-4 text-base text-gray-300">audiobookshelf uses <a href="https://github.com/sandreas/tone" target="_blank" class="hover:underline text-blue-400 hover:text-blue-300">tone</a> to write metadata.</p>
|
||||
</div>
|
||||
<div class="w-full max-w-2xl"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<div class="w-full max-w-2xl border border-opacity-10 bg-bg mx-2">
|
||||
<div class="w-full max-w-2xl border border-white border-opacity-10 bg-bg mx-2">
|
||||
<div class="flex py-2 px-4">
|
||||
<div class="w-1/3 text-xs font-semibold uppercase text-gray-200">Meta Tag</div>
|
||||
<div class="w-2/3 text-xs font-semibold uppercase text-gray-200">Value</div>
|
||||
</div>
|
||||
<div class="w-full max-h-72 overflow-auto">
|
||||
<template v-for="(keyValue, index) in metadataKeyValues">
|
||||
<div :key="keyValue.key" class="flex py-1 px-4 text-sm" :class="index % 2 === 0 ? 'bg-primary bg-opacity-25' : ''">
|
||||
<div class="w-1/3 font-semibold">{{ keyValue.key }}</div>
|
||||
<template v-for="(value, key, index) in toneObject">
|
||||
<div :key="key" class="flex py-1 px-4 text-sm" :class="index % 2 === 0 ? 'bg-primary bg-opacity-25' : ''">
|
||||
<div class="w-1/3 font-semibold">{{ key }}</div>
|
||||
<div class="w-2/3">
|
||||
{{ keyValue.value }}
|
||||
{{ value }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full max-w-2xl border border-opacity-10 bg-bg mx-2">
|
||||
<div class="w-full max-w-2xl border border-white border-opacity-10 bg-bg mx-2">
|
||||
<div class="flex py-2 px-4">
|
||||
<div class="flex-grow text-xs font-semibold uppercase text-gray-200">Chapter Title</div>
|
||||
<div class="w-24 text-xs font-semibold uppercase text-gray-200">Start</div>
|
||||
|
|
@ -34,10 +36,10 @@
|
|||
<div :key="index" class="flex py-1 px-4 text-sm" :class="index % 2 === 0 ? 'bg-primary bg-opacity-25' : ''">
|
||||
<div class="flex-grow font-semibold">{{ chapter.title }}</div>
|
||||
<div class="w-24">
|
||||
{{ chapter.start.toFixed(2) }}
|
||||
{{ $secondsToTimestamp(chapter.start) }}
|
||||
</div>
|
||||
<div class="w-24">
|
||||
{{ chapter.end.toFixed(2) }}
|
||||
{{ $secondsToTimestamp(chapter.end) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -48,12 +50,17 @@
|
|||
<div class="w-full h-px bg-white bg-opacity-10 my-8" />
|
||||
|
||||
<div class="w-full max-w-4xl mx-auto">
|
||||
<div class="w-full flex justify-between items-center mb-4">
|
||||
<div class="w-full flex justify-between items-center mb-2">
|
||||
<p class="text-warning text-lg font-semibold">Warning: Modifies your audio files</p>
|
||||
<ui-btn v-if="!embedFinished" color="primary" :loading="updatingMetadata" @click="updateAudioFileMetadata">Embed Metadata</ui-btn>
|
||||
<ui-btn v-if="!embedFinished" color="primary" :loading="updatingMetadata" @click.stop="embedClick">Embed Metadata</ui-btn>
|
||||
<p v-else class="text-success text-lg font-semibold">Embed Finished!</p>
|
||||
</div>
|
||||
<div class="w-full mx-auto border border-opacity-10 bg-bg">
|
||||
<div class="flex mb-4">
|
||||
<p class="text-gray-200">
|
||||
A backup of your audio files will be stored in <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">/metadata/cache/items/{{ libraryItemId }}/</span> so you can restore the originals if necessary.
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full mx-auto border border-white border-opacity-10 bg-bg">
|
||||
<div class="flex py-2 px-4">
|
||||
<div class="w-10 text-xs font-semibold text-gray-200">#</div>
|
||||
<div class="flex-grow text-xs font-semibold uppercase text-gray-200">Filename</div>
|
||||
|
|
@ -118,7 +125,8 @@ export default {
|
|||
audiofilesEncoding: {},
|
||||
audiofilesFinished: {},
|
||||
updatingMetadata: false,
|
||||
embedFinished: false
|
||||
embedFinished: false,
|
||||
toneObject: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -137,98 +145,35 @@ export default {
|
|||
streamLibraryItem() {
|
||||
return this.$store.state.streamLibraryItem
|
||||
},
|
||||
metadataKeyValues() {
|
||||
const keyValues = [
|
||||
{
|
||||
key: 'title',
|
||||
value: this.mediaMetadata.title
|
||||
},
|
||||
{
|
||||
key: 'artist',
|
||||
value: this.mediaMetadata.authorName
|
||||
},
|
||||
{
|
||||
key: 'album_artist',
|
||||
value: this.mediaMetadata.authorName
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
value: this.mediaMetadata.publishedYear
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
value: this.mediaMetadata.description
|
||||
},
|
||||
{
|
||||
key: 'genre',
|
||||
value: this.mediaMetadata.genres.join(';')
|
||||
},
|
||||
{
|
||||
key: 'performer',
|
||||
value: this.mediaMetadata.narratorName
|
||||
}
|
||||
]
|
||||
|
||||
if (this.mediaMetadata.subtitle) {
|
||||
keyValues.push({
|
||||
key: 'subtitle',
|
||||
value: this.mediaMetadata.subtitle
|
||||
})
|
||||
}
|
||||
|
||||
if (this.mediaMetadata.asin) {
|
||||
keyValues.push({
|
||||
key: 'asin',
|
||||
value: this.mediaMetadata.asin
|
||||
})
|
||||
}
|
||||
if (this.mediaMetadata.isbn) {
|
||||
keyValues.push({
|
||||
key: 'isbn',
|
||||
value: this.mediaMetadata.isbn
|
||||
})
|
||||
}
|
||||
if (this.mediaMetadata.language) {
|
||||
keyValues.push({
|
||||
key: 'language',
|
||||
value: this.mediaMetadata.language
|
||||
})
|
||||
}
|
||||
if (this.mediaMetadata.series.length) {
|
||||
var firstSeries = this.mediaMetadata.series[0]
|
||||
keyValues.push({
|
||||
key: 'series',
|
||||
value: firstSeries.name
|
||||
})
|
||||
if (firstSeries.sequence) {
|
||||
keyValues.push({
|
||||
key: 'series-part',
|
||||
value: firstSeries.sequence
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return keyValues
|
||||
},
|
||||
metadataChapters() {
|
||||
var chapters = this.media.chapters || []
|
||||
return chapters.concat(chapters)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateAudioFileMetadata() {
|
||||
if (confirm(`Warning!\n\nThis will modify the audio files for this audiobook.\nMake sure your audio files are backed up before using this feature.`)) {
|
||||
this.updatingMetadata = true
|
||||
this.$axios
|
||||
.$get(`/api/items/${this.libraryItemId}/audio-metadata`)
|
||||
.then(() => {
|
||||
console.log('Audio metadata encode started')
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Audio metadata encode failed', error)
|
||||
this.updatingMetadata = false
|
||||
})
|
||||
embedClick() {
|
||||
const payload = {
|
||||
message: `Are you sure you want to embed metadata in ${this.audioFiles.length} audio files?`,
|
||||
callback: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this.updateAudioFileMetadata()
|
||||
}
|
||||
},
|
||||
type: 'yesNo'
|
||||
}
|
||||
this.$store.commit('globals/setConfirmPrompt', payload)
|
||||
},
|
||||
updateAudioFileMetadata() {
|
||||
this.updatingMetadata = true
|
||||
this.$axios
|
||||
.$get(`/api/items/${this.libraryItemId}/audio-metadata?tone=1`)
|
||||
.then(() => {
|
||||
console.log('Audio metadata encode started')
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Audio metadata encode failed', error)
|
||||
this.updatingMetadata = false
|
||||
})
|
||||
},
|
||||
audioMetadataStarted(data) {
|
||||
console.log('audio metadata started', data)
|
||||
|
|
@ -252,9 +197,21 @@ export default {
|
|||
if (data.libraryItemId !== this.libraryItemId) return
|
||||
this.$set(this.audiofilesEncoding, data.ino, false)
|
||||
this.$set(this.audiofilesFinished, data.ino, true)
|
||||
},
|
||||
fetchToneObject() {
|
||||
this.$axios
|
||||
.$get(`/api/items/${this.libraryItemId}/tone-object`)
|
||||
.then((toneObject) => {
|
||||
delete toneObject.CoverFile
|
||||
this.toneObject = toneObject
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to fetch tone object', error)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchToneObject()
|
||||
this.$root.socket.on('audio_metadata_started', this.audioMetadataStarted)
|
||||
this.$root.socket.on('audio_metadata_finished', this.audioMetadataFinished)
|
||||
this.$root.socket.on('audiofile_metadata_started', this.audiofileMetadataStarted)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue