175 lines
5.7 KiB
Vue
175 lines
5.7 KiB
Vue
<template>
|
|
<modals-modal v-model="show" name="audiofile-data-modal" :width="700" :height="'unset'">
|
|
<div v-if="audioFile" ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden p-6" style="max-height: 80vh">
|
|
<div class="flex items-center justify-between">
|
|
<p class="text-base text-gray-200 truncate">{{ metadata.filename }}</p>
|
|
<ui-btn v-if="ffprobeData" small class="ml-2" @click="ffprobeData = null">{{ $strings.ButtonReset }}</ui-btn>
|
|
<ui-btn v-else-if="userIsAdminOrUp" small :loading="probingFile" class="ml-2" @click="getFFProbeData">{{ $strings.ButtonProbeAudioFile }}</ui-btn>
|
|
</div>
|
|
|
|
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
|
|
|
<template v-if="!ffprobeData">
|
|
<ui-text-input-with-label :value="metadata.path" readonly :label="$strings.LabelPath" class="mb-4 text-sm" />
|
|
|
|
<div class="flex flex-col sm:flex-row text-sm">
|
|
<div class="w-full sm:w-1/2">
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelSize }}
|
|
</p>
|
|
<p>{{ $bytesPretty(metadata.size) }}</p>
|
|
</div>
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelDuration }}
|
|
</p>
|
|
<p>{{ $secondsToTimestamp(audioFile.duration) }}</p>
|
|
</div>
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">{{ $strings.LabelFormat }}</p>
|
|
<p>{{ audioFile.format }}</p>
|
|
</div>
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelChapters }}
|
|
</p>
|
|
<p>{{ audioFile.chapters?.length || 0 }}</p>
|
|
</div>
|
|
<div v-if="audioFile.embeddedCoverArt" class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelEmbeddedCover }}
|
|
</p>
|
|
<p>{{ audioFile.embeddedCoverArt || '' }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="w-full sm:w-1/2">
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelCodec }}
|
|
</p>
|
|
<p>{{ audioFile.codec }}</p>
|
|
</div>
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelChannels }}
|
|
</p>
|
|
<p>{{ audioFile.channels }} ({{ audioFile.channelLayout }})</p>
|
|
</div>
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelBitrate }}
|
|
</p>
|
|
<p>{{ $bytesPretty(audioFile.bitRate || 0, 0) }}</p>
|
|
</div>
|
|
<div class="flex mb-1">
|
|
<p class="w-32 text-black-50">{{ $strings.LabelTimeBase }}</p>
|
|
<p>{{ audioFile.timeBase }}</p>
|
|
</div>
|
|
<div v-if="audioFile.language" class="flex mb-1">
|
|
<p class="w-32 text-black-50">
|
|
{{ $strings.LabelLanguage }}
|
|
</p>
|
|
<p>{{ audioFile.language || '' }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
|
|
|
<p class="font-bold mb-2">{{ $strings.LabelMetaTags }}</p>
|
|
|
|
<div v-for="(value, key) in metaTags" :key="key" class="flex mb-1 text-sm">
|
|
<p class="w-32 min-w-32 text-black-50 mb-1">
|
|
{{ key.replace('tag', '') }}
|
|
</p>
|
|
<p>{{ value }}</p>
|
|
</div>
|
|
</template>
|
|
<div v-else class="w-full">
|
|
<div class="relative">
|
|
<ui-textarea-with-label :value="prettyFfprobeData" readonly :rows="30" class="text-xs" />
|
|
|
|
<button class="absolute top-4 right-4" :class="copiedToClipboard ? 'text-success' : 'text-white/50 hover:text-white/80'" @click.stop="copyFfprobeData">
|
|
<span class="material-symbols">{{ copiedToClipboard ? 'check' : 'content_copy' }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</modals-modal>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
props: {
|
|
value: Boolean,
|
|
audioFile: {
|
|
type: Object,
|
|
default: () => {}
|
|
},
|
|
libraryItemId: String
|
|
},
|
|
data() {
|
|
return {
|
|
probingFile: false,
|
|
ffprobeData: null,
|
|
copiedToClipboard: false
|
|
}
|
|
},
|
|
watch: {
|
|
show(newVal) {
|
|
if (newVal) {
|
|
this.ffprobeData = null
|
|
this.copiedToClipboard = false
|
|
this.probingFile = false
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
show: {
|
|
get() {
|
|
return this.value
|
|
},
|
|
set(val) {
|
|
this.$emit('input', val)
|
|
}
|
|
},
|
|
metadata() {
|
|
return this.audioFile?.metadata || {}
|
|
},
|
|
metaTags() {
|
|
return this.audioFile?.metaTags || {}
|
|
},
|
|
userIsAdminOrUp() {
|
|
return this.$store.getters['user/getIsAdminOrUp']
|
|
},
|
|
prettyFfprobeData() {
|
|
if (!this.ffprobeData) return ''
|
|
return JSON.stringify(this.ffprobeData, null, 2)
|
|
}
|
|
},
|
|
methods: {
|
|
getFFProbeData() {
|
|
this.probingFile = true
|
|
this.$axios
|
|
.$get(`/api/items/${this.libraryItemId}/ffprobe/${this.audioFile.ino}`)
|
|
.then((data) => {
|
|
console.log('Got ffprobe data', data)
|
|
this.ffprobeData = data
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to get ffprobe data', error)
|
|
this.$toast.error(this.$strings.ToastFailedToLoadData)
|
|
})
|
|
.finally(() => {
|
|
this.probingFile = false
|
|
})
|
|
},
|
|
async copyFfprobeData() {
|
|
this.copiedToClipboard = await this.$copyToClipboard(this.prettyFfprobeData)
|
|
}
|
|
},
|
|
mounted() {}
|
|
}
|
|
</script>
|