From 3cc900ffbfc3c3c6d2f08e64a717e73c62c2d82b Mon Sep 17 00:00:00 2001 From: Kieran Eglin Date: Mon, 20 Nov 2023 08:51:00 -0800 Subject: [PATCH 1/7] Adds fetching book data on upload --- client/components/cards/ItemUploadCard.vue | 54 +++++++++++++-- client/pages/upload/index.vue | 81 ++++++++++++++++++---- client/strings/en-us.json | 4 +- 3 files changed, 119 insertions(+), 20 deletions(-) diff --git a/client/components/cards/ItemUploadCard.vue b/client/components/cards/ItemUploadCard.vue index 21d97b20..68b77d56 100644 --- a/client/components/cards/ItemUploadCard.vue +++ b/client/components/cards/ItemUploadCard.vue @@ -8,6 +8,12 @@ close +
+ refresh +
+ @@ -61,10 +67,11 @@ export default { props: { item: { type: Object, - default: () => {} + default: () => { } }, mediaType: String, - processing: Boolean + processing: Boolean, + provider: String }, data() { return { @@ -76,7 +83,8 @@ export default { error: '', isUploading: false, uploadFailed: false, - uploadSuccess: false + uploadSuccess: false, + isFetchingMetadata: false } }, computed: { @@ -94,6 +102,16 @@ export default { } else { 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: { @@ -105,6 +123,30 @@ export default { titleUpdated() { 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() { if (!this.itemData.title) { this.error = 'Must have a title' @@ -128,4 +170,4 @@ export default { } } } - \ No newline at end of file + diff --git a/client/pages/upload/index.vue b/client/pages/upload/index.vue index 09a9008b..bf3cf584 100644 --- a/client/pages/upload/index.vue +++ b/client/pages/upload/index.vue @@ -14,6 +14,14 @@ +
+ +

{{ $strings.LabelAutoFetchMetadata }}

+
+ +
+
+

{{ error }}

@@ -61,9 +69,16 @@ - +
@@ -92,13 +107,18 @@ export default { selectedLibraryId: null, selectedFolderId: null, processing: false, - uploadFinished: false + uploadFinished: false, + fetchMetadata: { + enabled: false, + provider: 'google' + } } }, watch: { selectedLibrary(newVal) { if (newVal && !this.selectedFolderId) { this.setDefaultFolder() + this.setMetadataProvider() } } }, @@ -133,6 +153,13 @@ export default { selectedLibraryIsPodcast() { 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() { if (!this.selectedLibrary) return null return this.selectedLibrary.folders.find((fold) => fold.id === this.selectedFolderId) @@ -160,12 +187,16 @@ export default { } } this.setDefaultFolder() + this.setMetadataProvider() }, setDefaultFolder() { if (!this.selectedFolderId && this.selectedLibrary && this.selectedLibrary.folders.length) { this.selectedFolderId = this.selectedLibrary.folders[0].id } }, + setMetadataProvider() { + this.fetchMetadata.provider = this.$store.getters['libraries/getLibraryProvider'](this.selectedLibraryId) + }, removeItem(item) { this.items = this.items.filter((b) => b.index !== item.index) if (!this.items.length) { @@ -213,27 +244,49 @@ export default { var items = e.dataTransfer.items || [] var itemResults = await this.uploadHelpers.getItemsFromDrop(items, this.selectedLibraryMediaType) - this.setResults(itemResults) + this.onItemsSelected(itemResults) }, inputChanged(e) { if (!e.target || !e.target.files) return var _files = Array.from(e.target.files) if (_files && _files.length) { 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) { this.error = itemResults.error this.items = [] this.ignoredFiles = [] - } else { - this.error = '' - this.items = itemResults.items - this.ignoredFiles = itemResults.ignoredFiles + return false } - 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) { var ref = this.$refs[`itemCard-${index}`] @@ -346,6 +399,8 @@ export default { }, mounted() { this.selectedLibraryId = this.$store.state.libraries.currentLibraryId + this.setMetadataProvider() + this.setDefaultFolder() window.addEventListener('dragenter', this.dragenter) window.addEventListener('dragleave', this.dragleave) @@ -359,4 +414,4 @@ export default { window.removeEventListener('drop', this.drop) } } - \ No newline at end of file + diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 6f06ca77..9c608e48 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -194,6 +194,7 @@ "LabelAuthorLastFirst": "Author (Last, First)", "LabelAuthors": "Authors", "LabelAutoDownloadEpisodes": "Auto Download Episodes", + "LabelAutoFetchMetadata": "Auto Fetch Metadata", "LabelBackToUser": "Back to User", "LabelBackupLocation": "Backup Location", "LabelBackupsEnableAutomaticBackups": "Enable automatic backups", @@ -259,6 +260,7 @@ "LabelExample": "Example", "LabelExplicit": "Explicit", "LabelFeedURL": "Feed URL", + "LabelFetchingMetadata": "Fetching Metadata", "LabelFile": "File", "LabelFileBirthtime": "File Birthtime", "LabelFileModified": "File Modified", @@ -727,4 +729,4 @@ "ToastSocketFailedToConnect": "Socket failed to connect", "ToastUserDeleteFailed": "Failed to delete user", "ToastUserDeleteSuccess": "User deleted" -} \ No newline at end of file +} From 8c434703fb5b24afde800cd00de62bb235f9090b Mon Sep 17 00:00:00 2001 From: Kieran Eglin Date: Mon, 20 Nov 2023 09:18:50 -0800 Subject: [PATCH 2/7] Added computed metadata check to UI dropdown --- client/pages/upload/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pages/upload/index.vue b/client/pages/upload/index.vue index bf3cf584..8dd13990 100644 --- a/client/pages/upload/index.vue +++ b/client/pages/upload/index.vue @@ -18,7 +18,7 @@

{{ $strings.LabelAutoFetchMetadata }}

- +
From e5579b2c3346c816856e7a9f47740661c6c66699 Mon Sep 17 00:00:00 2001 From: Kieran Eglin Date: Tue, 28 Nov 2023 11:45:44 -0800 Subject: [PATCH 3/7] Improved UI; Added tooltips; Fixed unrelated layout issues --- client/components/cards/ItemUploadCard.vue | 27 ++++++++++++---------- client/pages/upload/index.vue | 16 +++++++++---- client/strings/en-us.json | 2 ++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/client/components/cards/ItemUploadCard.vue b/client/components/cards/ItemUploadCard.vue index 68b77d56..a393a170 100644 --- a/client/components/cards/ItemUploadCard.vue +++ b/client/components/cards/ItemUploadCard.vue @@ -8,12 +8,6 @@ close -
- refresh -
-