audiobookshelf/client/components/cards/ItemUploadCard.vue

183 lines
6.5 KiB
Vue
Raw Normal View History

2022-02-26 23:19:22 +01:00
<template>
<div class="relative w-full py-4 px-6 border border-white border-opacity-10 shadow-lg rounded-md my-6">
<div class="absolute -top-3 -left-3 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full">
<p class="text-base text-white text-opacity-80 font-mono">#{{ item.index }}</p>
2022-02-26 23:19:22 +01:00
</div>
<div v-if="!processing && !uploadFailed && !uploadSuccess" class="absolute -top-3 -right-3 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full hover:bg-error cursor-pointer" @click="$emit('remove')">
<span class="text-base text-white text-opacity-80 font-mono material-icons">close</span>
</div>
<template v-if="!uploadSuccess && !uploadFailed">
<widgets-alert v-if="error" type="error">
<p class="text-base">{{ error }}</p>
</widgets-alert>
<div class="flex my-2 -mx-2">
<div class="w-1/2 px-2">
<ui-text-input-with-label v-model.trim="itemData.title" :disabled="processing" :label="$strings.LabelTitle" @input="titleUpdated" />
2022-02-26 23:19:22 +01:00
</div>
<div class="w-1/2 px-2">
<div v-if="!isPodcast" class="flex items-end">
<ui-text-input-with-label v-model.trim="itemData.author" :disabled="processing" :label="$strings.LabelAuthor" />
<ui-tooltip :text="$strings.LabelUploaderItemFetchMetadataHelp">
<div
class="ml-2 mb-1 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full hover:bg-primary cursor-pointer"
@click="fetchMetadata">
<span class="text-base text-white text-opacity-80 font-mono material-icons">sync</span>
</div>
</ui-tooltip>
</div>
<div v-else class="w-full">
2022-11-08 01:27:17 +01:00
<p class="px-1 text-sm font-semibold">{{ $strings.LabelDirectory }} <em class="font-normal text-xs pl-2">(auto)</em></p>
<ui-text-input :value="directory" disabled class="w-full font-mono text-xs" />
</div>
2022-02-26 23:19:22 +01:00
</div>
</div>
<div v-if="!isPodcast" class="flex my-2 -mx-2">
2022-02-26 23:19:22 +01:00
<div class="w-1/2 px-2">
<ui-text-input-with-label v-model.trim="itemData.series" :disabled="processing" :label="$strings.LabelSeries" note="(optional)" inputClass="h-10" />
2022-02-26 23:19:22 +01:00
</div>
<div class="w-1/2 px-2">
<div class="w-full">
<label class="px-1 text-sm font-semibold">{{ $strings.LabelDirectory }} <em class="font-normal text-xs pl-2">(auto)</em></label>
<ui-text-input :value="directory" disabled class="w-full font-mono text-xs h-10" />
2022-02-26 23:19:22 +01:00
</div>
</div>
</div>
2022-11-08 01:27:17 +01:00
<tables-uploaded-files-table :files="item.itemFiles" :title="$strings.HeaderItemFiles" class="mt-8" />
<tables-uploaded-files-table v-if="item.otherFiles.length" :title="$strings.HeaderOtherFiles" :files="item.otherFiles" />
<tables-uploaded-files-table v-if="item.ignoredFiles.length" :title="$strings.HeaderIgnoredFiles" :files="item.ignoredFiles" />
2022-02-26 23:19:22 +01:00
</template>
<widgets-alert v-if="uploadSuccess" type="success">
2022-11-08 01:27:17 +01:00
<p class="text-base">{{ $strings.MessageUploaderItemSuccess }}</p>
2022-02-26 23:19:22 +01:00
</widgets-alert>
<widgets-alert v-if="uploadFailed" type="error">
2022-11-08 01:27:17 +01:00
<p class="text-base">{{ $strings.MessageUploaderItemFailed }}</p>
2022-02-26 23:19:22 +01:00
</widgets-alert>
2023-11-20 17:51:00 +01:00
<div v-if="isNonInteractable" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-20">
<ui-loading-indicator :text="nonInteractionLabel" />
2022-02-26 23:19:22 +01:00
</div>
</div>
</template>
<script>
import Path from 'path'
export default {
props: {
item: {
2022-02-26 23:19:22 +01:00
type: Object,
2023-11-20 17:51:00 +01:00
default: () => { }
2022-02-26 23:19:22 +01:00
},
mediaType: String,
2023-11-20 17:51:00 +01:00
processing: Boolean,
provider: String
2022-02-26 23:19:22 +01:00
},
data() {
return {
itemData: {
2022-02-26 23:19:22 +01:00
title: '',
author: '',
series: ''
},
error: '',
isUploading: false,
uploadFailed: false,
2023-11-20 17:51:00 +01:00
uploadSuccess: false,
isFetchingMetadata: false
2022-02-26 23:19:22 +01:00
}
},
computed: {
isPodcast() {
return this.mediaType === 'podcast'
},
2022-02-26 23:19:22 +01:00
directory() {
if (!this.itemData.title) return ''
if (this.isPodcast) return this.itemData.title
2023-12-02 06:42:54 +01:00
const outputPathParts = [this.itemData.author, this.itemData.series, this.itemData.title]
const cleanedOutputPathParts = outputPathParts.filter(Boolean).map(part => this.$sanitizeFilename(part))
return Path.join(...cleanedOutputPathParts)
2023-11-20 17:51:00 +01:00
},
isNonInteractable() {
return this.isUploading || this.isFetchingMetadata
},
nonInteractionLabel() {
if (this.isUploading) {
return this.$strings.MessageUploading
} else if (this.isFetchingMetadata) {
return this.$strings.LabelFetchingMetadata
}
2022-02-26 23:19:22 +01:00
}
},
methods: {
setUploadStatus(status) {
this.isUploading = status === 'uploading'
this.uploadFailed = status === 'failed'
this.uploadSuccess = status === 'success'
},
titleUpdated() {
this.error = ''
},
2023-11-20 17:51:00 +01:00
async fetchMetadata() {
if (!this.itemData.title.trim().length) {
return
}
this.isFetchingMetadata = true
this.error = ''
2023-11-20 17:51:00 +01:00
try {
2023-12-02 00:19:50 +01:00
const searchQueryString = new URLSearchParams({
title: this.itemData.title,
author: this.itemData.author,
provider: this.provider
})
2023-11-20 17:51:00 +01:00
const [bestCandidate, ..._rest] = await this.$axios.$get(`/api/search/books?${searchQueryString}`)
if (bestCandidate) {
this.itemData = {
...this.itemData,
2023-12-02 00:19:50 +01:00
title: bestCandidate.title,
author: bestCandidate.author,
series: (bestCandidate.series || [])[0]?.series
}
} else {
this.error = this.$strings.ErrorUploadFetchMetadataNoResults
2023-11-20 17:51:00 +01:00
}
} catch (e) {
console.error('Failed', e)
this.error = this.$strings.ErrorUploadFetchMetadataAPI
2023-11-20 17:51:00 +01:00
} finally {
this.isFetchingMetadata = false
}
},
2022-02-26 23:19:22 +01:00
getData() {
if (!this.itemData.title) {
this.error = this.$strings.ErrorUploadLacksTitle
2022-02-26 23:19:22 +01:00
return null
}
this.error = ''
var files = this.item.itemFiles.concat(this.item.otherFiles)
2022-02-26 23:19:22 +01:00
return {
index: this.item.index,
directory: this.directory,
...this.itemData,
2022-02-26 23:19:22 +01:00
files
}
}
},
mounted() {
if (this.item) {
this.itemData.title = this.item.title
this.itemData.author = this.item.author
this.itemData.series = this.item.series
2022-02-26 23:19:22 +01:00
}
}
}
2023-11-20 17:51:00 +01:00
</script>