Adds fetching book data on upload

This commit is contained in:
Kieran Eglin 2023-11-20 08:51:00 -08:00
parent aa933df525
commit 3cc900ffbf
No known key found for this signature in database
GPG Key ID: 193984967FCF432D
3 changed files with 119 additions and 20 deletions

View File

@ -8,6 +8,12 @@
<span class="text-base text-white text-opacity-80 font-mono material-icons">close</span> <span class="text-base text-white text-opacity-80 font-mono material-icons">close</span>
</div> </div>
<div v-if="!isPodcast"
class="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">refresh</span>
</div>
<template v-if="!uploadSuccess && !uploadFailed"> <template v-if="!uploadSuccess && !uploadFailed">
<widgets-alert v-if="error" type="error"> <widgets-alert v-if="error" type="error">
<p class="text-base">{{ error }}</p> <p class="text-base">{{ error }}</p>
@ -48,8 +54,8 @@
<p class="text-base">{{ $strings.MessageUploaderItemFailed }}</p> <p class="text-base">{{ $strings.MessageUploaderItemFailed }}</p>
</widgets-alert> </widgets-alert>
<div v-if="isUploading" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-20"> <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="$strings.MessageUploading" /> <ui-loading-indicator :text="nonInteractionLabel" />
</div> </div>
</div> </div>
</template> </template>
@ -61,10 +67,11 @@ export default {
props: { props: {
item: { item: {
type: Object, type: Object,
default: () => {} default: () => { }
}, },
mediaType: String, mediaType: String,
processing: Boolean processing: Boolean,
provider: String
}, },
data() { data() {
return { return {
@ -76,7 +83,8 @@ export default {
error: '', error: '',
isUploading: false, isUploading: false,
uploadFailed: false, uploadFailed: false,
uploadSuccess: false uploadSuccess: false,
isFetchingMetadata: false
} }
}, },
computed: { computed: {
@ -94,6 +102,16 @@ export default {
} else { } else {
return this.itemData.title return this.itemData.title
} }
},
isNonInteractable() {
return this.isUploading || this.isFetchingMetadata
},
nonInteractionLabel() {
if (this.isUploading) {
return this.$strings.MessageUploading
} else if (this.isFetchingMetadata) {
return this.$strings.LabelFetchingMetadata
}
} }
}, },
methods: { methods: {
@ -105,6 +123,30 @@ export default {
titleUpdated() { titleUpdated() {
this.error = '' this.error = ''
}, },
async fetchMetadata() {
if (!this.itemData.title.trim().length) {
return
}
this.isFetchingMetadata = true
try {
const searchQueryString = `title=${this.itemData.title}&author=${this.itemData.author}&provider=${this.provider}`
const [bestCandidate, ..._rest] = await this.$axios.$get(`/api/search/books?${searchQueryString}`)
this.itemData = {
...this.itemData,
title: bestCandidate?.title,
author: bestCandidate?.author,
series: (bestCandidate?.series || [])[0]?.series
}
} catch (e) {
console.error('Failed', e)
// TODO: do something with the error?
} finally {
this.isFetchingMetadata = false
}
},
getData() { getData() {
if (!this.itemData.title) { if (!this.itemData.title) {
this.error = 'Must have a title' this.error = 'Must have a title'
@ -128,4 +170,4 @@ export default {
} }
} }
} }
</script> </script>

View File

@ -14,6 +14,14 @@
</div> </div>
</div> </div>
<div v-if="!selectedLibraryIsPodcast" class="flex items-center py-2">
<ui-toggle-switch v-model="fetchMetadata.enabled" />
<p class="pl-4 text-base">{{ $strings.LabelAutoFetchMetadata }}</p>
<div class="flex-grow ml-4">
<ui-dropdown v-model="fetchMetadata.provider" :items="providers" :label="$strings.LabelProvider" :disabled="!fetchMetadata.enabled" />
</div>
</div>
<widgets-alert v-if="error" type="error"> <widgets-alert v-if="error" type="error">
<p class="text-lg">{{ error }}</p> <p class="text-lg">{{ error }}</p>
</widgets-alert> </widgets-alert>
@ -61,9 +69,16 @@
</widgets-alert> </widgets-alert>
<!-- Item Upload cards --> <!-- Item Upload cards -->
<template v-for="item in items"> <cards-item-upload-card
<cards-item-upload-card :ref="`itemCard-${item.index}`" :key="item.index" :media-type="selectedLibraryMediaType" :item="item" :processing="processing" @remove="removeItem(item)" /> v-for="item in items"
</template> :key="item.index"
:ref="`itemCard-${item.index}`"
:media-type="selectedLibraryMediaType"
:item="item"
:provider="fetchMetadata.provider"
:processing="processing"
@remove="removeItem(item)"
/>
<!-- Upload/Reset btns --> <!-- Upload/Reset btns -->
<div v-show="items.length" class="flex justify-end pb-8 pt-4"> <div v-show="items.length" class="flex justify-end pb-8 pt-4">
@ -92,13 +107,18 @@ export default {
selectedLibraryId: null, selectedLibraryId: null,
selectedFolderId: null, selectedFolderId: null,
processing: false, processing: false,
uploadFinished: false uploadFinished: false,
fetchMetadata: {
enabled: false,
provider: 'google'
}
} }
}, },
watch: { watch: {
selectedLibrary(newVal) { selectedLibrary(newVal) {
if (newVal && !this.selectedFolderId) { if (newVal && !this.selectedFolderId) {
this.setDefaultFolder() this.setDefaultFolder()
this.setMetadataProvider()
} }
} }
}, },
@ -133,6 +153,13 @@ export default {
selectedLibraryIsPodcast() { selectedLibraryIsPodcast() {
return this.selectedLibraryMediaType === 'podcast' return this.selectedLibraryMediaType === 'podcast'
}, },
providers() {
if (this.selectedLibraryIsPodcast) return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
},
canFetchMetadata() {
return !this.selectedLibraryIsPodcast && this.fetchMetadata.enabled
},
selectedFolder() { selectedFolder() {
if (!this.selectedLibrary) return null if (!this.selectedLibrary) return null
return this.selectedLibrary.folders.find((fold) => fold.id === this.selectedFolderId) return this.selectedLibrary.folders.find((fold) => fold.id === this.selectedFolderId)
@ -160,12 +187,16 @@ export default {
} }
} }
this.setDefaultFolder() this.setDefaultFolder()
this.setMetadataProvider()
}, },
setDefaultFolder() { setDefaultFolder() {
if (!this.selectedFolderId && this.selectedLibrary && this.selectedLibrary.folders.length) { if (!this.selectedFolderId && this.selectedLibrary && this.selectedLibrary.folders.length) {
this.selectedFolderId = this.selectedLibrary.folders[0].id this.selectedFolderId = this.selectedLibrary.folders[0].id
} }
}, },
setMetadataProvider() {
this.fetchMetadata.provider = this.$store.getters['libraries/getLibraryProvider'](this.selectedLibraryId)
},
removeItem(item) { removeItem(item) {
this.items = this.items.filter((b) => b.index !== item.index) this.items = this.items.filter((b) => b.index !== item.index)
if (!this.items.length) { if (!this.items.length) {
@ -213,27 +244,49 @@ export default {
var items = e.dataTransfer.items || [] var items = e.dataTransfer.items || []
var itemResults = await this.uploadHelpers.getItemsFromDrop(items, this.selectedLibraryMediaType) var itemResults = await this.uploadHelpers.getItemsFromDrop(items, this.selectedLibraryMediaType)
this.setResults(itemResults) this.onItemsSelected(itemResults)
}, },
inputChanged(e) { inputChanged(e) {
if (!e.target || !e.target.files) return if (!e.target || !e.target.files) return
var _files = Array.from(e.target.files) var _files = Array.from(e.target.files)
if (_files && _files.length) { if (_files && _files.length) {
var itemResults = this.uploadHelpers.getItemsFromPicker(_files, this.selectedLibraryMediaType) var itemResults = this.uploadHelpers.getItemsFromPicker(_files, this.selectedLibraryMediaType)
this.setResults(itemResults) this.onItemsSelected(itemResults)
} }
}, },
setResults(itemResults) { onItemsSelected(itemResults) {
if (this.itemSelectionSuccessful(itemResults)) {
// setTimeout ensures the new item ref is attached before this method is called
setTimeout(this.attemptMetadataFetch, 0)
}
},
itemSelectionSuccessful(itemResults) {
console.log('Upload results', itemResults)
if (itemResults.error) { if (itemResults.error) {
this.error = itemResults.error this.error = itemResults.error
this.items = [] this.items = []
this.ignoredFiles = [] this.ignoredFiles = []
} else { return false
this.error = ''
this.items = itemResults.items
this.ignoredFiles = itemResults.ignoredFiles
} }
console.log('Upload results', itemResults)
this.error = ''
this.items = itemResults.items
this.ignoredFiles = itemResults.ignoredFiles
return true
},
attemptMetadataFetch() {
if (!this.canFetchMetadata) {
return false
}
this.items.forEach((item) => {
let itemRef = this.$refs[`itemCard-${item.index}`]
if (itemRef?.length) {
itemRef[0].fetchMetadata(this.fetchMetadata.provider)
}
})
}, },
updateItemCardStatus(index, status) { updateItemCardStatus(index, status) {
var ref = this.$refs[`itemCard-${index}`] var ref = this.$refs[`itemCard-${index}`]
@ -346,6 +399,8 @@ export default {
}, },
mounted() { mounted() {
this.selectedLibraryId = this.$store.state.libraries.currentLibraryId this.selectedLibraryId = this.$store.state.libraries.currentLibraryId
this.setMetadataProvider()
this.setDefaultFolder() this.setDefaultFolder()
window.addEventListener('dragenter', this.dragenter) window.addEventListener('dragenter', this.dragenter)
window.addEventListener('dragleave', this.dragleave) window.addEventListener('dragleave', this.dragleave)
@ -359,4 +414,4 @@ export default {
window.removeEventListener('drop', this.drop) window.removeEventListener('drop', this.drop)
} }
} }
</script> </script>

View File

@ -194,6 +194,7 @@
"LabelAuthorLastFirst": "Author (Last, First)", "LabelAuthorLastFirst": "Author (Last, First)",
"LabelAuthors": "Authors", "LabelAuthors": "Authors",
"LabelAutoDownloadEpisodes": "Auto Download Episodes", "LabelAutoDownloadEpisodes": "Auto Download Episodes",
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
"LabelBackToUser": "Back to User", "LabelBackToUser": "Back to User",
"LabelBackupLocation": "Backup Location", "LabelBackupLocation": "Backup Location",
"LabelBackupsEnableAutomaticBackups": "Enable automatic backups", "LabelBackupsEnableAutomaticBackups": "Enable automatic backups",
@ -259,6 +260,7 @@
"LabelExample": "Example", "LabelExample": "Example",
"LabelExplicit": "Explicit", "LabelExplicit": "Explicit",
"LabelFeedURL": "Feed URL", "LabelFeedURL": "Feed URL",
"LabelFetchingMetadata": "Fetching Metadata",
"LabelFile": "File", "LabelFile": "File",
"LabelFileBirthtime": "File Birthtime", "LabelFileBirthtime": "File Birthtime",
"LabelFileModified": "File Modified", "LabelFileModified": "File Modified",
@ -727,4 +729,4 @@
"ToastSocketFailedToConnect": "Socket failed to connect", "ToastSocketFailedToConnect": "Socket failed to connect",
"ToastUserDeleteFailed": "Failed to delete user", "ToastUserDeleteFailed": "Failed to delete user",
"ToastUserDeleteSuccess": "User deleted" "ToastUserDeleteSuccess": "User deleted"
} }