From 3f7af8acfb12b4144a1080a823568ddfc848efc6 Mon Sep 17 00:00:00 2001 From: Michael Vincent Date: Wed, 5 Feb 2025 23:12:58 -0600 Subject: [PATCH 1/4] Add Accept-Encoding header to getPodcastFeed() This commit adds the Accept-Encoding header to getPodcastFeed() with gzip, compress, and deflate support. This allows servers to send a compressed response that'll be decompressed by axios transparently. Audiobookshelf is currently using axios v0.27.2, which enables the decompress option by default. The decompress feature supports gzip, compress, and deflate algorithms (see axios/lib/adapters/http.js). axios v0.27.2 does not add the Accept-Encoding header to requests automatically, so that's the responsibility of the caller. --- server/utils/podcastUtils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js index d6983047..485fccfb 100644 --- a/server/utils/podcastUtils.js +++ b/server/utils/podcastUtils.js @@ -311,6 +311,7 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => { responseType: 'arraybuffer', headers: { Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8', + 'Accept-Encoding': 'gzip, compress, deflate', 'User-Agent': userAgent }, httpAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : ssrfFilter(feedUrl), From 0bba7091242718996e4baad43a59b5994f380061 Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 6 Feb 2025 17:27:33 -0600 Subject: [PATCH 2/4] Trim whitespace from book/podcast/episode details text inputs #3943 --- .../modals/podcast/tabs/EpisodeDetails.vue | 8 ++++---- client/components/ui/MultiSelectQueryInput.vue | 16 +++++++++++++--- client/components/ui/TextInput.vue | 7 ++++++- client/components/ui/TextInputWithLabel.vue | 5 +++-- client/components/widgets/BookDetailsEdit.vue | 12 ++++++------ client/components/widgets/PodcastDetailsEdit.vue | 12 ++++++------ 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/client/components/modals/podcast/tabs/EpisodeDetails.vue b/client/components/modals/podcast/tabs/EpisodeDetails.vue index 9a2eb77d..642cd046 100644 --- a/client/components/modals/podcast/tabs/EpisodeDetails.vue +++ b/client/components/modals/podcast/tabs/EpisodeDetails.vue @@ -2,10 +2,10 @@
- +
- +
@@ -14,10 +14,10 @@
- +
- +
diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 25107b79..7c8e7005 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -215,6 +215,10 @@ export default { inputBlur() { if (!this.isFocused) return + if (typeof this.textInput === 'string') { + this.textInput = this.textInput.trim() + } + setTimeout(() => { if (document.activeElement === this.$refs.input) { return @@ -231,6 +235,11 @@ export default { }, forceBlur() { this.isFocused = false + + if (typeof this.textInput === 'string') { + this.textInput = this.textInput.trim() + } + if (this.textInput) this.submitForm() if (this.$refs.input) this.$refs.input.blur() }, @@ -289,11 +298,12 @@ export default { this.selectedMenuItemIndex = null }, submitForm() { - if (!this.textInput) return + if (!this.textInput || !this.textInput.trim?.()) return + + this.textInput = this.textInput.trim() - const cleaned = this.textInput.trim() const matchesItem = this.items.find((i) => { - return i.name === cleaned + return i.name === this.textInput }) if (matchesItem) { diff --git a/client/components/ui/TextInput.vue b/client/components/ui/TextInput.vue index 6e621870..23478dee 100644 --- a/client/components/ui/TextInput.vue +++ b/client/components/ui/TextInput.vue @@ -40,7 +40,8 @@ export default { showCopy: Boolean, step: [String, Number], min: [String, Number], - customInputClass: String + customInputClass: String, + trimWhitespace: Boolean }, data() { return { @@ -101,9 +102,13 @@ export default { this.$emit('focus') }, blurred() { + if (this.trimWhitespace && typeof this.inputValue === 'string') { + this.inputValue = this.inputValue.trim() + } this.isFocused = false this.$emit('blur') }, + change(e) { this.$emit('change', e.target.value) }, diff --git a/client/components/ui/TextInputWithLabel.vue b/client/components/ui/TextInputWithLabel.vue index a10394bd..3d1c8209 100644 --- a/client/components/ui/TextInputWithLabel.vue +++ b/client/components/ui/TextInputWithLabel.vue @@ -6,7 +6,7 @@ {{ note }} - +
@@ -24,7 +24,8 @@ export default { readonly: Boolean, disabled: Boolean, inputClass: String, - showCopy: Boolean + showCopy: Boolean, + trimWhitespace: Boolean }, data() { return {} diff --git a/client/components/widgets/BookDetailsEdit.vue b/client/components/widgets/BookDetailsEdit.vue index fa26bcf5..a7af02c8 100644 --- a/client/components/widgets/BookDetailsEdit.vue +++ b/client/components/widgets/BookDetailsEdit.vue @@ -3,10 +3,10 @@
- +
- +
@@ -42,19 +42,19 @@
- +
- +
- +
- +
diff --git a/client/components/widgets/PodcastDetailsEdit.vue b/client/components/widgets/PodcastDetailsEdit.vue index 389ca894..e665dc4e 100644 --- a/client/components/widgets/PodcastDetailsEdit.vue +++ b/client/components/widgets/PodcastDetailsEdit.vue @@ -3,14 +3,14 @@
- +
- +
- + @@ -25,13 +25,13 @@
- +
- +
- +
From b5e69630def903b7a00504f5eb54bd4fc01c0eea Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 6 Feb 2025 17:29:27 -0600 Subject: [PATCH 3/4] Update batch edit text inputs to trim whitespace --- client/pages/batch/index.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/pages/batch/index.vue b/client/pages/batch/index.vue index 263dee58..575f1db1 100644 --- a/client/pages/batch/index.vue +++ b/client/pages/batch/index.vue @@ -22,7 +22,7 @@
- +
@@ -31,7 +31,7 @@
- +
@@ -51,11 +51,11 @@
- +
- +
From a37fe3c3d2262963831afd8c2863018ae30ab571 Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 7 Feb 2025 17:09:48 -0600 Subject: [PATCH 4/4] Fix: Users with update permission unable to remove books from collection #3947 --- server/controllers/CollectionController.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 00b82ce9..475adfe0 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -251,6 +251,7 @@ class CollectionController { /** * DELETE: /api/collections/:id/book/:bookId * Remove a single book from a collection. Re-order books + * Users with update permission can remove books from collections * TODO: bookId is actually libraryItemId. Clients need updating to use bookId * * @param {CollectionControllerRequest} req @@ -427,7 +428,8 @@ class CollectionController { req.collection = collection } - if (req.method == 'DELETE' && !req.user.canDelete) { + // Users with update permission can remove books from collections + if (req.method == 'DELETE' && !req.params.bookId && !req.user.canDelete) { Logger.warn(`[CollectionController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) {