diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2e5f4bced..809563018 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -60,7 +60,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -73,6 +73,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: '/language:${{matrix.language}}' diff --git a/build/debian/DEBIAN/preinst b/build/debian/DEBIAN/preinst index e30bc490c..241a47010 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -22,7 +22,7 @@ add_user() { declare -r descr="${4:-No description}" declare -r shell="${5:-/bin/false}" - if ! getent passwd | grep -q "^$user:"; then + if ! getent passwd "$user" 2>&1 >/dev/null; then echo "Creating system user: $user in $group with $descr and shell $shell" useradd $uid_flags --gid $group --no-create-home --system --shell $shell -c "$descr" $user fi @@ -39,7 +39,7 @@ add_group() { declare -r gid_flags="--gid $gid" fi - if ! getent group | grep -q "^$group:" ; then + if ! getent group "$group" 2>&1 >/dev/null; then echo "Creating system group: $group" groupadd $gid_flags --system $group fi diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index f74134041..428ae6ebf 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -196,6 +196,7 @@ export default { requestBatchQuickEmbed() { const payload = { message: this.$strings.MessageConfirmQuickEmbed, + allowHtml: true, callback: (confirmed) => { if (confirmed) { this.$axios diff --git a/client/components/app/BookShelfCategorized.vue b/client/components/app/BookShelfCategorized.vue index 4bf8cfbbf..a8e8ae469 100644 --- a/client/components/app/BookShelfCategorized.vue +++ b/client/components/app/BookShelfCategorized.vue @@ -300,6 +300,8 @@ export default { }) }, userUpdated(user) { + if (user.id !== this.$store.state.user.user.id) return + if (user.seriesHideFromContinueListening && user.seriesHideFromContinueListening.length) { this.removeAllSeriesFromContinueSeries(user.seriesHideFromContinueListening) } diff --git a/client/components/app/BookShelfRow.vue b/client/components/app/BookShelfRow.vue index 082f9fe37..fac89a70b 100644 --- a/client/components/app/BookShelfRow.vue +++ b/client/components/app/BookShelfRow.vue @@ -93,10 +93,10 @@ export default { editAuthor(author) { this.$store.commit('globals/showEditAuthorModal', author) }, - editItem(libraryItem) { + editItem(libraryItem, tab = 'details') { var itemIds = this.shelf.entities.map((e) => e.id) this.$store.commit('setBookshelfBookIds', itemIds) - this.$store.commit('showEditModal', libraryItem) + this.$store.commit('showEditModalOnTab', { libraryItem, tab: tab || 'details' }) }, editEpisode({ libraryItem, episode }) { this.$store.commit('setEpisodeTableEpisodeIds', [episode.id]) diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue index 854b61b24..9ec492a8f 100644 --- a/client/components/app/LazyBookshelf.vue +++ b/client/components/app/LazyBookshelf.vue @@ -22,7 +22,7 @@
{{ emptyMessageHelp }} - + help_outline @@ -232,11 +232,11 @@ export default { clearFilter() { this.$store.dispatch('user/updateUserSettings', { filterBy: 'all' }) }, - editEntity(entity) { + editEntity(entity, tab = 'details') { if (this.entityName === 'items' || this.entityName === 'series-books') { const bookIds = this.entities.map((e) => e.id) this.$store.commit('setBookshelfBookIds', bookIds) - this.$store.commit('showEditModal', entity) + this.$store.commit('showEditModalOnTab', { libraryItem: entity, tab: tab || 'details' }) } else if (this.entityName === 'collections') { this.$store.commit('globals/setEditCollection', entity) } else if (this.entityName === 'playlists') { diff --git a/client/components/cards/ItemUploadCard.vue b/client/components/cards/ItemUploadCard.vue index 40836b8e4..adbf3adbe 100644 --- a/client/components/cards/ItemUploadCard.vue +++ b/client/components/cards/ItemUploadCard.vue @@ -62,7 +62,24 @@
- + +
+ + {{ nonInteractionLabel }} + +
+ +
+
+ + {{ uploadProgressText }} + +
+
+
+
+
+
@@ -91,7 +108,11 @@ export default { isUploading: false, uploadFailed: false, uploadSuccess: false, - isFetchingMetadata: false + isFetchingMetadata: false, + uploadProgress: { + loaded: 0, + total: 0 + } } }, computed: { @@ -116,6 +137,15 @@ export default { } else if (this.isFetchingMetadata) { return this.$strings.LabelFetchingMetadata } + }, + uploadProgressPercent() { + if (this.uploadProgress.total === 0) return 0 + return Math.min(100, Math.round((this.uploadProgress.loaded / this.uploadProgress.total) * 100)) + }, + uploadProgressText() { + const loaded = this.$bytesPretty(this.uploadProgress.loaded) + const total = this.$bytesPretty(this.uploadProgress.total) + return `${this.uploadProgressPercent}% (${loaded} / ${total})` } }, methods: { @@ -123,6 +153,21 @@ export default { this.isUploading = status === 'uploading' this.uploadFailed = status === 'failed' this.uploadSuccess = status === 'success' + + if (status !== 'uploading') { + this.uploadProgress = { + loaded: 0, + total: 0 + } + } + }, + setUploadProgress(progress) { + if (this.isUploading && progress) { + this.uploadProgress = { + loaded: progress.loaded || 0, + total: progress.total || 0 + } + } }, titleUpdated() { this.error = '' diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index fbb50bb14..51f657dbc 100644 --- a/client/components/cards/LazyBookCard.vue +++ b/client/components/cards/LazyBookCard.vue @@ -78,7 +78,7 @@ - +
priority_high
@@ -121,12 +121,12 @@
- +

{{ displayTitle }}

- +

{{ displaySubtitle }}

{{ displayLineTwo || ' ' }}

@@ -788,11 +788,11 @@ export default { }, showEditModalFiles() { // More menu func - this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'files' }) + this.$emit('edit', this.libraryItem, 'files') }, showEditModalMatch() { // More menu func - this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'match' }) + this.$emit('edit', this.libraryItem, 'match') }, sendToDevice(deviceName) { // More menu func diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue index 62a9b8037..4834a1a25 100644 --- a/client/components/controls/LibraryFilterSelect.vue +++ b/client/components/controls/LibraryFilterSelect.vue @@ -338,6 +338,18 @@ export default { const series = this.series.find((se) => se.id == decoded) if (series) filterValue = series.name } + } else if (parts[0] === 'progress') { + const item = this.progress.find((p) => p.id == decoded) + if (item) filterValue = item.name + } else if (parts[0] === 'tracks') { + const item = this.tracks.find((t) => t.id == decoded) + if (item) filterValue = item.name + } else if (parts[0] === 'ebooks') { + const item = this.ebooks.find((e) => e.id == decoded) + if (item) filterValue = item.name + } else if (parts[0] === 'missing') { + const item = this.missing.find((m) => m.id == decoded) + if (item) filterValue = item.name } else { filterValue = decoded } diff --git a/client/components/modals/BatchQuickMatchModel.vue b/client/components/modals/BatchQuickMatchModel.vue index 8bea68faf..f6bcd9728 100644 --- a/client/components/modals/BatchQuickMatchModel.vue +++ b/client/components/modals/BatchQuickMatchModel.vue @@ -88,7 +88,7 @@ export default { }, providers() { if (this.isPodcast) return this.$store.state.scanners.podcastProviders - return this.$store.state.scanners.providers + return this.$store.state.scanners.bookProviders }, libraryProvider() { return this.$store.getters['libraries/getLibraryProvider'](this.currentLibraryId) || 'google' @@ -96,6 +96,9 @@ export default { }, methods: { init() { + // Fetch providers when modal is shown + this.$store.dispatch('scanners/fetchProviders') + // If we don't have a set provider (first open of dialog) or we've switched library, set // the selected provider to the current library default provider if (!this.options.provider || this.lastUsedLibrary != this.currentLibraryId) { @@ -127,8 +130,7 @@ export default { this.show = false }) } - }, - mounted() {} + } } diff --git a/client/components/modals/ShareModal.vue b/client/components/modals/ShareModal.vue index bd0c9acfb..3dc1d8057 100644 --- a/client/components/modals/ShareModal.vue +++ b/client/components/modals/ShareModal.vue @@ -8,7 +8,7 @@
- + help_outline diff --git a/client/components/modals/collections/AddCreateModal.vue b/client/components/modals/collections/AddCreateModal.vue index 24e8695d6..5cec2255a 100644 --- a/client/components/modals/collections/AddCreateModal.vue +++ b/client/components/modals/collections/AddCreateModal.vue @@ -25,7 +25,7 @@

{{ $strings.MessageBookshelfNoCollectionsHelp }}

- + help_outline @@ -227,7 +227,7 @@ export default { .catch((error) => { console.error('Failed to create collection', error) var errMsg = error.response ? error.response.data || '' : '' - this.$toast.error(this.$strings.ToastCollectionCreateFailed + ': ' + errMsg) + this.$toast.error(errMsg) this.processing = false }) } diff --git a/client/components/modals/item/tabs/Cover.vue b/client/components/modals/item/tabs/Cover.vue index b404a9abb..be17f9636 100644 --- a/client/components/modals/item/tabs/Cover.vue +++ b/client/components/modals/item/tabs/Cover.vue @@ -133,8 +133,8 @@ export default { } }, providers() { - if (this.isPodcast) return this.$store.state.scanners.podcastProviders - return [{ text: 'Best', value: 'best' }, ...this.$store.state.scanners.providers, ...this.$store.state.scanners.coverOnlyProviders, { text: 'All', value: 'all' }] + if (this.isPodcast) return this.$store.state.scanners.podcastCoverProviders + return this.$store.state.scanners.bookCoverProviders }, searchTitleLabel() { if (this.provider.startsWith('audible')) return this.$strings.LabelSearchTitleOrASIN @@ -438,6 +438,8 @@ export default { mounted() { // Setup socket listeners when component is mounted this.addSocketListeners() + // Fetch providers if not already loaded + this.$store.dispatch('scanners/fetchProviders') }, beforeDestroy() { // Cancel any ongoing search when component is destroyed diff --git a/client/components/modals/item/tabs/Match.vue b/client/components/modals/item/tabs/Match.vue index 3faa26b27..a1fc41091 100644 --- a/client/components/modals/item/tabs/Match.vue +++ b/client/components/modals/item/tabs/Match.vue @@ -2,7 +2,7 @@
@@ -105,7 +105,7 @@

- {{ $strings.LabelCurrently }} {{ mediaMetadata.publisher }} + {{ $strings.LabelCurrently }} {{ mediaMetadata.publisher }}

@@ -114,7 +114,7 @@

- {{ $strings.LabelCurrently }} {{ mediaMetadata.publishedYear }} + {{ $strings.LabelCurrently }} {{ mediaMetadata.publishedYear }}

@@ -253,6 +253,7 @@ export default { hasSearched: false, selectedMatch: null, selectedMatchOrig: null, + waitingForProviders: false, selectedMatchUsage: { title: true, subtitle: true, @@ -285,9 +286,19 @@ export default { handler(newVal) { if (newVal) this.init() } + }, + providersLoaded(isLoaded) { + // Complete initialization once providers are loaded + if (isLoaded && this.waitingForProviders) { + this.waitingForProviders = false + this.initProviderAndSearch() + } } }, computed: { + providersLoaded() { + return this.$store.getters['scanners/areProvidersLoaded'] + }, isProcessing: { get() { return this.processing @@ -319,7 +330,7 @@ export default { }, providers() { if (this.isPodcast) return this.$store.state.scanners.podcastProviders - return this.$store.state.scanners.providers + return this.$store.state.scanners.bookProviders }, searchTitleLabel() { if (this.provider.startsWith('audible')) return this.$strings.LabelSearchTitleOrASIN @@ -478,6 +489,24 @@ export default { this.checkboxToggled() }, + initProviderAndSearch() { + // Set provider based on media type + if (this.isPodcast) { + this.provider = 'itunes' + } else { + this.provider = this.getDefaultBookProvider() + } + + // Prefer using ASIN if set and using audible provider + if (this.provider.startsWith('audible') && this.libraryItem.media.metadata.asin) { + this.searchTitle = this.libraryItem.media.metadata.asin + this.searchAuthor = '' + } + + if (this.searchTitle) { + this.submitSearch() + } + }, init() { this.clearSelectedMatch() this.initSelectedMatchUsage() @@ -495,19 +524,13 @@ export default { } this.searchTitle = this.libraryItem.media.metadata.title this.searchAuthor = this.libraryItem.media.metadata.authorName || '' - if (this.isPodcast) this.provider = 'itunes' - else { - this.provider = this.getDefaultBookProvider() - } - // Prefer using ASIN if set and using audible provider - if (this.provider.startsWith('audible') && this.libraryItem.media.metadata.asin) { - this.searchTitle = this.libraryItem.media.metadata.asin - this.searchAuthor = '' - } - - if (this.searchTitle) { - this.submitSearch() + // Wait for providers to be loaded before setting provider and searching + if (this.providersLoaded || this.isPodcast) { + this.waitingForProviders = false + this.initProviderAndSearch() + } else { + this.waitingForProviders = true } }, selectMatch(match) { @@ -637,6 +660,10 @@ export default { this.selectedMatch = null this.selectedMatchOrig = null } + }, + mounted() { + // Fetch providers if not already loaded + this.$store.dispatch('scanners/fetchProviders') } } diff --git a/client/components/modals/item/tabs/Schedule.vue b/client/components/modals/item/tabs/Schedule.vue index 2cd3af514..0faa29630 100644 --- a/client/components/modals/item/tabs/Schedule.vue +++ b/client/components/modals/item/tabs/Schedule.vue @@ -158,6 +158,8 @@ export default { this.isProcessing = true var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, updatePayload).catch((error) => { console.error('Failed to update', error) + const errorMessage = typeof error?.response?.data === 'string' ? error?.response?.data : null + this.$toast.error(errorMessage || this.$strings.ToastFailedToUpdate) return false }) this.isProcessing = false diff --git a/client/components/modals/item/tabs/Tools.vue b/client/components/modals/item/tabs/Tools.vue index d96550887..8e40ca3c0 100644 --- a/client/components/modals/item/tabs/Tools.vue +++ b/client/components/modals/item/tabs/Tools.vue @@ -107,6 +107,7 @@ export default { quickEmbed() { const payload = { message: this.$strings.MessageConfirmQuickEmbed, + allowHtml: true, callback: (confirmed) => { if (confirmed) { this.$axios diff --git a/client/components/modals/libraries/EditLibrary.vue b/client/components/modals/libraries/EditLibrary.vue index 083fc5766..c805f79b0 100644 --- a/client/components/modals/libraries/EditLibrary.vue +++ b/client/components/modals/libraries/EditLibrary.vue @@ -74,7 +74,7 @@ export default { }, providers() { if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders - return this.$store.state.scanners.providers + return this.$store.state.scanners.bookProviders } }, methods: { @@ -156,6 +156,8 @@ export default { }, mounted() { this.init() + // Fetch providers if not already loaded + this.$store.dispatch('scanners/fetchProviders') } } diff --git a/client/components/modals/libraries/LibraryScannerSettings.vue b/client/components/modals/libraries/LibraryScannerSettings.vue index b27925ce4..6a8856a0e 100644 --- a/client/components/modals/libraries/LibraryScannerSettings.vue +++ b/client/components/modals/libraries/LibraryScannerSettings.vue @@ -8,7 +8,7 @@

{{ $strings.LabelMetadataOrderOfPrecedenceDescription }}

- + help_outline diff --git a/client/components/modals/libraries/LibrarySettings.vue b/client/components/modals/libraries/LibrarySettings.vue index d3b40de95..7cfc22017 100644 --- a/client/components/modals/libraries/LibrarySettings.vue +++ b/client/components/modals/libraries/LibrarySettings.vue @@ -104,7 +104,6 @@ export default { }, data() { return { - provider: null, useSquareBookCovers: false, enableWatcher: false, skipMatchingMediaWithAsin: false, @@ -134,10 +133,6 @@ export default { isPodcastLibrary() { return this.mediaType === 'podcast' }, - providers() { - if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders - return this.$store.state.scanners.providers - }, maskAsFinishedWhenItems() { return [ { diff --git a/client/components/modals/playlists/AddCreateModal.vue b/client/components/modals/playlists/AddCreateModal.vue index e695ccb0c..9aa68bd80 100644 --- a/client/components/modals/playlists/AddCreateModal.vue +++ b/client/components/modals/playlists/AddCreateModal.vue @@ -25,7 +25,7 @@

{{ $strings.MessageNoUserPlaylistsHelp }}

- + help_outline @@ -97,7 +97,10 @@ export default { ...playlist } }) - .sort((a, b) => (a.isItemIncluded ? -1 : 1)) + .sort((a, b) => { + if (a.isItemIncluded !== b.isItemIncluded) return a.isItemIncluded ? -1 : 1 + return a.name.localeCompare(b.name) + }) }, isBatch() { return this.selectedPlaylistItems.length > 1 diff --git a/client/components/player/PlayerPlaybackControls.vue b/client/components/player/PlayerPlaybackControls.vue index 368e3f35b..c7cbba63c 100644 --- a/client/components/player/PlayerPlaybackControls.vue +++ b/client/components/player/PlayerPlaybackControls.vue @@ -8,7 +8,7 @@ - diff --git a/client/components/prompt/Confirm.vue b/client/components/prompt/Confirm.vue index 361765e2d..d0828c7d1 100644 --- a/client/components/prompt/Confirm.vue +++ b/client/components/prompt/Confirm.vue @@ -3,7 +3,8 @@
-

+

+

{{ message }}

@@ -52,6 +53,17 @@ export default { message() { return this.confirmPromptOptions.message || '' }, + allowHtmlMessage() { + return !!this.confirmPromptOptions.allowHtml + }, + sanitizedMessage() { + if (!this.allowHtmlMessage) return this.message + + return this.escapeHtml(this.message) + .replace(/<br\s*\/?>/gi, '
') + .replace(/<code>/gi, '') + .replace(/<\/code>/gi, '') + }, callback() { return this.confirmPromptOptions.callback }, @@ -103,6 +115,14 @@ export default { if (this.callback) this.callback(true, this.checkboxValue) this.show = false }, + escapeHtml(value) { + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + }, setShow() { this.checkboxValue = this.checkboxDefaultValue this.$eventBus.$emit('showing-prompt', true) diff --git a/client/components/stats/DailyListeningChart.vue b/client/components/stats/DailyListeningChart.vue index c0f3acb32..55752fc5d 100644 --- a/client/components/stats/DailyListeningChart.vue +++ b/client/components/stats/DailyListeningChart.vue @@ -14,7 +14,7 @@
- +
@@ -186,10 +186,16 @@ export default { daysInARow() { var count = 0 while (true) { - var _date = this.$addDaysToToday(count * -1) - var datestr = this.$formatJsDate(_date, 'yyyy-MM-dd') + const _date = this.$addDaysToToday(count * -1 - 1) + const datestr = this.$formatJsDate(_date, 'yyyy-MM-dd') if (!this.listeningStatsDays[datestr] || this.listeningStatsDays[datestr] === 0) { + // don't require listening today to count towards days in a row, but do count it if already listened today + const today = this.$formatJsDate(new Date(), 'yyyy-MM-dd') + if (this.listeningStatsDays[today]) { + count++ + } + return count } count++ diff --git a/client/components/ui/LoadingIndicator.vue b/client/components/ui/LoadingIndicator.vue index d984bf35e..3b4b6e049 100644 --- a/client/components/ui/LoadingIndicator.vue +++ b/client/components/ui/LoadingIndicator.vue @@ -1,5 +1,5 @@ @@ -23,6 +25,9 @@ export default { computed: { message() { return this.text || this.$strings.MessagePleaseWait + }, + hasSlotContent() { + return this.$slots.default && this.$slots.default.length > 0 } } } diff --git a/client/components/ui/MultiSelect.vue b/client/components/ui/MultiSelect.vue index c7572ba50..3dcfb0495 100644 --- a/client/components/ui/MultiSelect.vue +++ b/client/components/ui/MultiSelect.vue @@ -278,7 +278,7 @@ export default { }) }, insertNewItem(item) { - this.selected.push(item) + if (!this.selected.includes(item)) this.selected.push(item) this.$emit('input', this.selected) this.$emit('newItem', item) this.textInput = null diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 18abc66e7..4bc434cbd 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -287,7 +287,7 @@ export default { }) }, insertNewItem(item) { - this.selected.push(item) + if (!this.selected.find((i) => i.name === item.name)) this.selected.push(item) this.$emit('input', this.selected) this.$emit('newItem', item) this.textInput = null diff --git a/client/components/ui/TextInput.vue b/client/components/ui/TextInput.vue index dd0eaa738..cd3bc6d2d 100644 --- a/client/components/ui/TextInput.vue +++ b/client/components/ui/TextInput.vue @@ -1,6 +1,6 @@ @@ -26,7 +26,8 @@ export default { disabled: Boolean, inputClass: String, showCopy: Boolean, - trimWhitespace: Boolean + trimWhitespace: Boolean, + autocomplete: String }, data() { return {} diff --git a/client/components/ui/Tooltip.vue b/client/components/ui/Tooltip.vue index e6ab0117b..c858cbfec 100644 --- a/client/components/ui/Tooltip.vue +++ b/client/components/ui/Tooltip.vue @@ -22,7 +22,8 @@ export default { type: Number, default: 0 }, - disabled: Boolean + disabled: Boolean, + plaintext: Boolean }, data() { return { @@ -46,7 +47,11 @@ export default { methods: { updateText() { if (this.tooltip) { - this.tooltip.innerHTML = this.text + if (this.plaintext) { + this.tooltip.textContent = this.text + } else { + this.tooltip.innerHTML = this.text + } this.setTooltipPosition(this.tooltip) } }, @@ -58,7 +63,11 @@ export default { tooltip.className = 'tooltip-wrapper absolute px-2 py-1 text-white text-xs rounded-sm shadow-lg max-w-xs text-center hidden sm:block' tooltip.style.zIndex = 100 tooltip.style.backgroundColor = 'rgba(0,0,0,0.85)' - tooltip.innerHTML = this.text + if (this.plaintext) { + tooltip.textContent = this.text + } else { + tooltip.innerHTML = this.text + } tooltip.addEventListener('mouseover', this.cancelHide) tooltip.addEventListener('mouseleave', this.hideTooltip) diff --git a/client/components/widgets/ItemSlider.vue b/client/components/widgets/ItemSlider.vue index 880654a44..5b96e51b4 100644 --- a/client/components/widgets/ItemSlider.vue +++ b/client/components/widgets/ItemSlider.vue @@ -132,10 +132,10 @@ export default { editAuthor(author) { this.$store.commit('globals/showEditAuthorModal', author) }, - editItem(libraryItem) { + editItem(libraryItem, tab = 'details') { var itemIds = this.items.map((e) => e.id) this.$store.commit('setBookshelfBookIds', itemIds) - this.$store.commit('showEditModal', libraryItem) + this.$store.commit('showEditModalOnTab', { libraryItem, tab: tab || 'details' }) }, selectItem(payload) { this.$emit('selectEntity', payload) diff --git a/client/layouts/default.vue b/client/layouts/default.vue index 4b9729248..75753b214 100644 --- a/client/layouts/default.vue +++ b/client/layouts/default.vue @@ -371,11 +371,13 @@ export default { }, customMetadataProviderAdded(provider) { if (!provider?.id) return - this.$store.commit('scanners/addCustomMetadataProvider', provider) + // Refresh providers cache + this.$store.dispatch('scanners/refreshProviders') }, customMetadataProviderRemoved(provider) { if (!provider?.id) return - this.$store.commit('scanners/removeCustomMetadataProvider', provider) + // Refresh providers cache + this.$store.dispatch('scanners/refreshProviders') }, initializeSocket() { if (this.$root.socket) { diff --git a/client/mixins/bookshelfCardsHelpers.js b/client/mixins/bookshelfCardsHelpers.js index fc8a41256..f1571f70e 100644 --- a/client/mixins/bookshelfCardsHelpers.js +++ b/client/mixins/bookshelfCardsHelpers.js @@ -118,8 +118,8 @@ export default { propsData: props, parent: this, created() { - this.$on('edit', (entity) => { - if (_this.editEntity) _this.editEntity(entity) + this.$on('edit', (entity, tab) => { + if (_this.editEntity) _this.editEntity(entity, tab) }) this.$on('select', ({ entity, shiftKey }) => { if (_this.selectEntity) _this.selectEntity(entity, shiftKey) diff --git a/client/package-lock.json b/client/package-lock.json index 0d2699ed1..502ba4b03 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.30.0", + "version": "2.35.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.30.0", + "version": "2.35.1", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", diff --git a/client/package.json b/client/package.json index 50fd0b713..0ec070b32 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.30.0", + "version": "2.35.1", "buildNumber": 1, "description": "Self-hosted audiobook and podcast client", "main": "index.js", diff --git a/client/pages/account.vue b/client/pages/account.vue index e9b5da3cb..8323772a5 100644 --- a/client/pages/account.vue +++ b/client/pages/account.vue @@ -1,6 +1,6 @@