diff --git a/Dockerfile b/Dockerfile index 816bdd3c..f9c46117 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,7 +57,7 @@ WORKDIR /app # Copy compiled frontend and server from build stages COPY --from=build-client /client/dist /app/client/dist COPY --from=build-server /server /app -COPY --from=build-server ${NUSQLITE3_PATH} ${NUSQLITE3_PATH} +COPY --from=build-server /usr/local/lib/nusqlite3 /usr/local/lib/nusqlite3 EXPOSE 80 diff --git a/client/components/app/ConfigSideNav.vue b/client/components/app/ConfigSideNav.vue index 32e7e694..50fa7a06 100644 --- a/client/components/app/ConfigSideNav.vue +++ b/client/components/app/ConfigSideNav.vue @@ -70,11 +70,6 @@ export default { title: this.$strings.HeaderUsers, path: '/config/users' }, - { - id: 'config-api-keys', - title: this.$strings.HeaderApiKeys, - path: '/config/api-keys' - }, { id: 'config-sessions', title: this.$strings.HeaderListeningSessions, diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue index 854b61b2..61331fb9 100644 --- a/client/components/app/LazyBookshelf.vue +++ b/client/components/app/LazyBookshelf.vue @@ -778,6 +778,10 @@ export default { windowResize() { this.executeRebuild() }, + socketInit() { + // Server settings are set on socket init + this.executeRebuild() + }, initListeners() { window.addEventListener('resize', this.windowResize) @@ -790,6 +794,7 @@ export default { }) this.$eventBus.$on('bookshelf_clear_selection', this.clearSelectedEntities) + this.$eventBus.$on('socket_init', this.socketInit) this.$eventBus.$on('user-settings', this.settingsUpdated) if (this.$root.socket) { @@ -821,6 +826,7 @@ export default { } this.$eventBus.$off('bookshelf_clear_selection', this.clearSelectedEntities) + this.$eventBus.$off('socket_init', this.socketInit) this.$eventBus.$off('user-settings', this.settingsUpdated) if (this.$root.socket) { diff --git a/client/components/cards/AuthorCard.vue b/client/components/cards/AuthorCard.vue index 05347393..82645c57 100644 --- a/client/components/cards/AuthorCard.vue +++ b/client/components/cards/AuthorCard.vue @@ -71,6 +71,9 @@ export default { coverHeight() { return this.cardHeight }, + userToken() { + return this.store.getters['user/getToken'] + }, _author() { return this.author || {} }, diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index 41b73310..35c959fa 100644 --- a/client/components/cards/LazyBookCard.vue +++ b/client/components/cards/LazyBookCard.vue @@ -198,7 +198,7 @@ export default { return this.store.getters['user/getSizeMultiplier'] }, dateFormat() { - return this.store.getters['getServerSetting']('dateFormat') + return this.store.state.serverSettings.dateFormat }, _libraryItem() { return this.libraryItem || {} diff --git a/client/components/cards/LazySeriesCard.vue b/client/components/cards/LazySeriesCard.vue index 34cea7e2..3532095b 100644 --- a/client/components/cards/LazySeriesCard.vue +++ b/client/components/cards/LazySeriesCard.vue @@ -71,7 +71,7 @@ export default { return this.height * this.sizeMultiplier }, dateFormat() { - return this.store.getters['getServerSetting']('dateFormat') + return this.store.state.serverSettings.dateFormat }, labelFontSize() { if (this.width < 160) return 0.75 diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue index 62a9b803..f5eec41a 100644 --- a/client/components/controls/LibraryFilterSelect.vue +++ b/client/components/controls/LibraryFilterSelect.vue @@ -94,9 +94,6 @@ export default { userIsAdminOrUp() { return this.$store.getters['user/getIsAdminOrUp'] }, - userCanAccessExplicitContent() { - return this.$store.getters['user/getUserCanAccessExplicitContent'] - }, libraryMediaType() { return this.$store.getters['libraries/getCurrentLibraryMediaType'] }, @@ -242,15 +239,6 @@ export default { sublist: false } ] - - if (this.userCanAccessExplicitContent) { - items.push({ - text: this.$strings.LabelExplicit, - value: 'explicit', - sublist: false - }) - } - if (this.userIsAdminOrUp) { items.push({ text: this.$strings.LabelShareOpen, @@ -261,7 +249,7 @@ export default { return items }, podcastItems() { - const items = [ + return [ { text: this.$strings.LabelAll, value: 'all' @@ -295,16 +283,6 @@ export default { sublist: false } ] - - if (this.userCanAccessExplicitContent) { - items.push({ - text: this.$strings.LabelExplicit, - value: 'explicit', - sublist: false - }) - } - - return items }, selectItems() { if (this.isSeries) return this.seriesItems diff --git a/client/components/covers/AuthorImage.vue b/client/components/covers/AuthorImage.vue index 084492b0..e320e552 100644 --- a/client/components/covers/AuthorImage.vue +++ b/client/components/covers/AuthorImage.vue @@ -39,6 +39,9 @@ export default { } }, computed: { + userToken() { + return this.$store.getters['user/getToken'] + }, _author() { return this.author || {} }, diff --git a/client/components/modals/AccountModal.vue b/client/components/modals/AccountModal.vue index 6f4b7b67..71ac8155 100644 --- a/client/components/modals/AccountModal.vue +++ b/client/components/modals/AccountModal.vue @@ -309,9 +309,9 @@ export default { } else { console.log('Account updated', data.user) - if (data.user.id === this.user.id && data.user.accessToken !== this.user.accessToken) { - console.log('Current user access token was updated') - this.$store.commit('user/setAccessToken', data.user.accessToken) + if (data.user.id === this.user.id && data.user.token !== this.user.token) { + console.log('Current user token was updated') + this.$store.commit('user/setUserToken', data.user.token) } this.$toast.success(this.$strings.ToastAccountUpdateSuccess) @@ -351,6 +351,9 @@ export default { this.$toast.error(errMsg || 'Failed to create account') }) }, + toggleActive() { + this.newUser.isActive = !this.newUser.isActive + }, userTypeUpdated(type) { this.newUser.permissions = { download: type !== 'guest', diff --git a/client/components/modals/ApiKeyCreatedModal.vue b/client/components/modals/ApiKeyCreatedModal.vue deleted file mode 100644 index 96442a17..00000000 --- a/client/components/modals/ApiKeyCreatedModal.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - diff --git a/client/components/modals/ApiKeyModal.vue b/client/components/modals/ApiKeyModal.vue deleted file mode 100644 index b347abd0..00000000 --- a/client/components/modals/ApiKeyModal.vue +++ /dev/null @@ -1,198 +0,0 @@ - - - diff --git a/client/components/modals/BookmarksModal.vue b/client/components/modals/BookmarksModal.vue index d84a8ed8..de8c72b7 100644 --- a/client/components/modals/BookmarksModal.vue +++ b/client/components/modals/BookmarksModal.vue @@ -79,10 +79,10 @@ export default { return !this.bookmarks.find((bm) => Math.abs(this.currentTime - bm.time) < 1) }, dateFormat() { - return this.$store.getters['getServerSetting']('dateFormat') + return this.$store.state.serverSettings.dateFormat }, timeFormat() { - return this.$store.getters['getServerSetting']('timeFormat') + return this.$store.state.serverSettings.timeFormat } }, methods: { diff --git a/client/components/modals/ListeningSessionModal.vue b/client/components/modals/ListeningSessionModal.vue index ecf00f78..a2469836 100644 --- a/client/components/modals/ListeningSessionModal.vue +++ b/client/components/modals/ListeningSessionModal.vue @@ -159,10 +159,10 @@ export default { return 'Unknown' }, dateFormat() { - return this.$store.getters['getServerSetting']('dateFormat') + return this.$store.state.serverSettings.dateFormat }, timeFormat() { - return this.$store.getters['getServerSetting']('timeFormat') + return this.$store.state.serverSettings.timeFormat }, isOpenSession() { return !!this._session.open diff --git a/client/components/modals/Modal.vue b/client/components/modals/Modal.vue index 31ea1e61..a7d9c0ae 100644 --- a/client/components/modals/Modal.vue +++ b/client/components/modals/Modal.vue @@ -23,7 +23,7 @@ export default { processing: Boolean, persistent: { type: Boolean, - default: false + default: true }, width: { type: [String, Number], @@ -99,7 +99,7 @@ export default { this.preventClickoutside = false return } - if (this.processing || this.persistent) return + if (this.processing && this.persistent) return if (ev.srcElement && ev.srcElement.classList.contains('modal-bg')) { this.show = false } diff --git a/client/components/modals/ShareModal.vue b/client/components/modals/ShareModal.vue index bd0c9acf..24994b22 100644 --- a/client/components/modals/ShareModal.vue +++ b/client/components/modals/ShareModal.vue @@ -144,7 +144,7 @@ export default { expirationDateString() { if (!this.expireDurationSeconds) return this.$strings.LabelPermanent const dateMs = Date.now() + this.expireDurationSeconds * 1000 - return this.$formatDatetime(dateMs, this.$store.getters['getServerSetting']('dateFormat'), this.$store.getters['getServerSetting']('timeFormat')) + return this.$formatDatetime(dateMs, this.$store.state.serverSettings.dateFormat, this.$store.state.serverSettings.timeFormat) } }, methods: { diff --git a/client/components/modals/changelog/ViewModal.vue b/client/components/modals/changelog/ViewModal.vue index 939ee71d..1b332a1d 100644 --- a/client/components/modals/changelog/ViewModal.vue +++ b/client/components/modals/changelog/ViewModal.vue @@ -40,7 +40,7 @@ export default { } }, dateFormat() { - return this.$store.getters['getServerSetting']('dateFormat') + return this.$store.state.serverSettings.dateFormat }, releasesToShow() { return this.versionData?.releasesToShow || [] diff --git a/client/components/modals/item/tabs/Files.vue b/client/components/modals/item/tabs/Files.vue index 15c44261..7be286fe 100644 --- a/client/components/modals/item/tabs/Files.vue +++ b/client/components/modals/item/tabs/Files.vue @@ -29,6 +29,9 @@ export default { media() { return this.libraryItem.media || {} }, + userToken() { + return this.$store.getters['user/getToken'] + }, userCanUpdate() { return this.$store.getters['user/getUserCanUpdate'] }, diff --git a/client/components/modals/podcast/EpisodeFeed.vue b/client/components/modals/podcast/EpisodeFeed.vue index 6b99cee7..7ec14ccd 100644 --- a/client/components/modals/podcast/EpisodeFeed.vue +++ b/client/components/modals/podcast/EpisodeFeed.vue @@ -35,14 +35,7 @@

{{ episode.subtitle }}

-
- -

Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}

- -

{{ $strings.LabelDuration }}: {{ $elapsedPretty(episode.durationSeconds) }}

- -

{{ $strings.LabelSize }}: {{ $bytesPretty(Number(episode.enclosure.length)) }}

-
+

Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}

diff --git a/client/components/modals/podcast/RemoveEpisode.vue b/client/components/modals/podcast/RemoveEpisode.vue index b2cebe84..38dd71cf 100644 --- a/client/components/modals/podcast/RemoveEpisode.vue +++ b/client/components/modals/podcast/RemoveEpisode.vue @@ -11,7 +11,7 @@ {{ $getString('MessageConfirmRemoveEpisode', [episodeTitle]) }}

{{ $getString('MessageConfirmRemoveEpisodes', [episodes.length]) }}

-

{{ $strings.MessageConfirmRemoveEpisodeNote }}

+

Note: This does not delete the audio file unless toggling "Hard delete file"

diff --git a/client/components/modals/podcast/ViewEpisode.vue b/client/components/modals/podcast/ViewEpisode.vue index 2502a5ea..5a520ef4 100644 --- a/client/components/modals/podcast/ViewEpisode.vue +++ b/client/components/modals/podcast/ViewEpisode.vue @@ -16,7 +16,7 @@

{{ title }}

-
+

{{ $strings.MessageNoDescription }}

@@ -34,12 +34,6 @@ {{ audioFileSize }}

-
-

{{ $strings.LabelDuration }}

-

- {{ audioFileDuration }} -

-
@@ -74,7 +68,7 @@ export default { return this.episode.title || 'No Episode Title' }, description() { - return this.parseDescription(this.episode.description || '') + return this.episode.description || '' }, media() { return this.libraryItem?.media || {} @@ -96,49 +90,11 @@ export default { return this.$bytesPretty(size) }, - audioFileDuration() { - const duration = this.episode.duration || 0 - return this.$elapsedPretty(duration) - }, bookCoverAspectRatio() { return this.$store.getters['libraries/getBookCoverAspectRatio'] } }, - methods: { - handleDescriptionClick(e) { - if (e.target.matches('span.time-marker')) { - const time = parseInt(e.target.dataset.time) - if (!isNaN(time)) { - this.$eventBus.$emit('play-item', { - episodeId: this.episodeId, - libraryItemId: this.libraryItem.id, - startTime: time - }) - } - e.preventDefault() - } - }, - parseDescription(description) { - const timeMarkerLinkRegex = /(.*?)<\/a>/g - const timeMarkerRegex = /\b\d{1,2}:\d{1,2}(?::\d{1,2})?\b/g - - function convertToSeconds(time) { - const timeParts = time.split(':').map(Number) - return timeParts.reduce((acc, part, index) => acc * 60 + part, 0) - } - - return description - .replace(timeMarkerLinkRegex, (match, href, displayTime) => { - const time = displayTime.match(timeMarkerRegex)[0] - const seekTimeInSeconds = convertToSeconds(time) - return `${displayTime}` - }) - .replace(timeMarkerRegex, (match) => { - const seekTimeInSeconds = convertToSeconds(match) - return `${match}` - }) - } - }, + methods: {}, mounted() {} } diff --git a/client/components/player/PlayerUi.vue b/client/components/player/PlayerUi.vue index f929943c..82d53552 100644 --- a/client/components/player/PlayerUi.vue +++ b/client/components/player/PlayerUi.vue @@ -129,6 +129,9 @@ export default { return `${hoursRounded}h` } }, + token() { + return this.$store.getters['user/getToken'] + }, timeRemaining() { if (this.useChapterTrack && this.currentChapter) { var currChapTime = this.currentTime - this.currentChapter.start diff --git a/client/components/readers/ComicReader.vue b/client/components/readers/ComicReader.vue index fce26939..28d79bf2 100644 --- a/client/components/readers/ComicReader.vue +++ b/client/components/readers/ComicReader.vue @@ -104,6 +104,9 @@ export default { } }, computed: { + userToken() { + return this.$store.getters['user/getToken'] + }, libraryItemId() { return this.libraryItem?.id }, @@ -231,7 +234,10 @@ export default { async extract() { this.loading = true var buff = await this.$axios.$get(this.ebookUrl, { - responseType: 'blob' + responseType: 'blob', + headers: { + Authorization: `Bearer ${this.userToken}` + } }) const archive = await Archive.open(buff) const originalFilesObject = await archive.getFilesObject() diff --git a/client/components/readers/EpubReader.vue b/client/components/readers/EpubReader.vue index ac8e3397..350d8596 100644 --- a/client/components/readers/EpubReader.vue +++ b/client/components/readers/EpubReader.vue @@ -57,6 +57,9 @@ export default { } }, computed: { + userToken() { + return this.$store.getters['user/getToken'] + }, /** @returns {string} */ libraryItemId() { return this.libraryItem?.id @@ -94,9 +97,9 @@ export default { }, ebookUrl() { if (this.fileId) { - return `/api/items/${this.libraryItemId}/ebook/${this.fileId}` + return `${this.$config.routerBasePath}/api/items/${this.libraryItemId}/ebook/${this.fileId}` } - return `/api/items/${this.libraryItemId}/ebook` + return `${this.$config.routerBasePath}/api/items/${this.libraryItemId}/ebook` }, themeRules() { const isDark = this.ereaderSettings.theme === 'dark' @@ -306,24 +309,14 @@ export default { /** @type {EpubReader} */ const reader = this - // Use axios to make request because we have token refresh logic in interceptor - const customRequest = async (url) => { - try { - return this.$axios.$get(url, { - responseType: 'arraybuffer' - }) - } catch (error) { - console.error('EpubReader.initEpub customRequest failed:', error) - throw error - } - } - /** @type {ePub.Book} */ reader.book = new ePub(reader.ebookUrl, { width: this.readerWidth, height: this.readerHeight - 50, openAs: 'epub', - requestMethod: customRequest + requestHeaders: { + Authorization: `Bearer ${this.userToken}` + } }) /** @type {ePub.Rendition} */ @@ -344,33 +337,29 @@ export default { this.applyTheme() }) - reader.book.ready - .then(() => { - // set up event listeners - reader.rendition.on('relocated', reader.relocated) - reader.rendition.on('keydown', reader.keyUp) + reader.book.ready.then(() => { + // set up event listeners + reader.rendition.on('relocated', reader.relocated) + reader.rendition.on('keydown', reader.keyUp) - reader.rendition.on('touchstart', (event) => { - this.$emit('touchstart', event) - }) - reader.rendition.on('touchend', (event) => { - this.$emit('touchend', event) - }) + reader.rendition.on('touchstart', (event) => { + this.$emit('touchstart', event) + }) + reader.rendition.on('touchend', (event) => { + this.$emit('touchend', event) + }) - // load ebook cfi locations - const savedLocations = this.loadLocations() - if (savedLocations) { - reader.book.locations.load(savedLocations) - } else { - reader.book.locations.generate().then(() => { - this.checkSaveLocations(reader.book.locations.save()) - }) - } - this.getChapters() - }) - .catch((error) => { - console.error('EpubReader.initEpub failed:', error) - }) + // load ebook cfi locations + const savedLocations = this.loadLocations() + if (savedLocations) { + reader.book.locations.load(savedLocations) + } else { + reader.book.locations.generate().then(() => { + this.checkSaveLocations(reader.book.locations.save()) + }) + } + this.getChapters() + }) }, getChapters() { // Load the list of chapters in the book. See https://github.com/futurepress/epub.js/issues/759 diff --git a/client/components/readers/MobiReader.vue b/client/components/readers/MobiReader.vue index 459ae55b..3e784f77 100644 --- a/client/components/readers/MobiReader.vue +++ b/client/components/readers/MobiReader.vue @@ -26,6 +26,9 @@ export default { return {} }, computed: { + userToken() { + return this.$store.getters['user/getToken'] + }, libraryItemId() { return this.libraryItem?.id }, @@ -93,8 +96,11 @@ export default { }, async initMobi() { // Fetch mobi file as blob - const buff = await this.$axios.$get(this.ebookUrl, { - responseType: 'blob' + var buff = await this.$axios.$get(this.ebookUrl, { + responseType: 'blob', + headers: { + Authorization: `Bearer ${this.userToken}` + } }) var reader = new FileReader() reader.onload = async (event) => { diff --git a/client/components/readers/PdfReader.vue b/client/components/readers/PdfReader.vue index d9459d76..c05f459c 100644 --- a/client/components/readers/PdfReader.vue +++ b/client/components/readers/PdfReader.vue @@ -55,8 +55,7 @@ export default { loadedRatio: 0, page: 1, numPages: 0, - pdfDocInitParams: null, - isRefreshing: false + pdfDocInitParams: null } }, computed: { @@ -153,34 +152,7 @@ export default { this.page++ this.updateProgress() }, - async refreshToken() { - if (this.isRefreshing) return - this.isRefreshing = true - const newAccessToken = await this.$store.dispatch('user/refreshToken').catch((error) => { - console.error('Failed to refresh token', error) - return null - }) - if (!newAccessToken) { - // Redirect to login on failed refresh - this.$router.push('/login') - return - } - - // Force Vue to re-render the PDF component by creating a new object - this.pdfDocInitParams = { - url: this.ebookUrl, - httpHeaders: { - Authorization: `Bearer ${newAccessToken}` - } - } - this.isRefreshing = false - }, - async error(err) { - if (err && err.status === 401) { - console.log('Received 401 error, refreshing token') - await this.refreshToken() - return - } + error(err) { console.error(err) }, resize() { diff --git a/client/components/readers/Reader.vue b/client/components/readers/Reader.vue index a7a5ac3d..c2e5986e 100644 --- a/client/components/readers/Reader.vue +++ b/client/components/readers/Reader.vue @@ -266,6 +266,9 @@ export default { isComic() { return this.ebookFormat == 'cbz' || this.ebookFormat == 'cbr' }, + userToken() { + return this.$store.getters['user/getToken'] + }, keepProgress() { return this.$store.state.ereaderKeepProgress }, diff --git a/client/components/tables/ApiKeysTable.vue b/client/components/tables/ApiKeysTable.vue deleted file mode 100644 index feab4e68..00000000 --- a/client/components/tables/ApiKeysTable.vue +++ /dev/null @@ -1,177 +0,0 @@ - - - - - diff --git a/client/components/tables/BackupsTable.vue b/client/components/tables/BackupsTable.vue index f769abdb..769c8d25 100644 --- a/client/components/tables/BackupsTable.vue +++ b/client/components/tables/BackupsTable.vue @@ -78,10 +78,10 @@ export default { return this.$store.getters['user/getToken'] }, dateFormat() { - return this.$store.getters['getServerSetting']('dateFormat') + return this.$store.state.serverSettings.dateFormat }, timeFormat() { - return this.$store.getters['getServerSetting']('timeFormat') + return this.$store.state.serverSettings.timeFormat } }, methods: { diff --git a/client/components/tables/EbookFilesTable.vue b/client/components/tables/EbookFilesTable.vue index 3ce9d30f..cc968acd 100644 --- a/client/components/tables/EbookFilesTable.vue +++ b/client/components/tables/EbookFilesTable.vue @@ -49,6 +49,9 @@ export default { libraryItemId() { return this.libraryItem.id }, + userToken() { + return this.$store.getters['user/getToken'] + }, userCanDownload() { return this.$store.getters['user/getUserCanDownload'] }, diff --git a/client/components/tables/LibraryFilesTable.vue b/client/components/tables/LibraryFilesTable.vue index 6f6e74b8..9be7e249 100644 --- a/client/components/tables/LibraryFilesTable.vue +++ b/client/components/tables/LibraryFilesTable.vue @@ -53,6 +53,9 @@ export default { libraryItemId() { return this.libraryItem.id }, + userToken() { + return this.$store.getters['user/getToken'] + }, userCanDownload() { return this.$store.getters['user/getUserCanDownload'] }, diff --git a/client/components/tables/UsersTable.vue b/client/components/tables/UsersTable.vue index c7171018..20f41228 100644 --- a/client/components/tables/UsersTable.vue +++ b/client/components/tables/UsersTable.vue @@ -76,10 +76,10 @@ export default { return usermap }, dateFormat() { - return this.$store.getters['getServerSetting']('dateFormat') + return this.$store.state.serverSettings.dateFormat }, timeFormat() { - return this.$store.getters['getServerSetting']('timeFormat') + return this.$store.state.serverSettings.timeFormat } }, methods: { diff --git a/client/components/tables/podcast/LazyEpisodeRow.vue b/client/components/tables/podcast/LazyEpisodeRow.vue index ee189961..ae99e6d3 100644 --- a/client/components/tables/podcast/LazyEpisodeRow.vue +++ b/client/components/tables/podcast/LazyEpisodeRow.vue @@ -112,7 +112,7 @@ export default { return this.episode?.publishedAt }, dateFormat() { - return this.store.getters['getServerSetting']('dateFormat') + return this.store.state.serverSettings.dateFormat }, itemProgress() { return this.store.getters['user/getUserMediaProgress'](this.libraryItemId, this.episodeId) diff --git a/client/components/tables/podcast/LazyEpisodesTable.vue b/client/components/tables/podcast/LazyEpisodesTable.vue index d23ee3d3..b23cc560 100644 --- a/client/components/tables/podcast/LazyEpisodesTable.vue +++ b/client/components/tables/podcast/LazyEpisodesTable.vue @@ -239,10 +239,10 @@ export default { }) }, dateFormat() { - return this.$store.getters['getServerSetting']('dateFormat') + return this.$store.state.serverSettings.dateFormat }, timeFormat() { - return this.$store.getters['getServerSetting']('timeFormat') + return this.$store.state.serverSettings.timeFormat } }, methods: { diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 18abc66e..6de16bf2 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -85,6 +85,9 @@ export default { this.$emit('input', val) } }, + userToken() { + return this.$store.getters['user/getToken'] + }, wrapperClass() { var classes = [] if (this.disabled) classes.push('bg-black-300') diff --git a/client/components/ui/SelectInput.vue b/client/components/ui/SelectInput.vue index f38414ac..9e0961c1 100644 --- a/client/components/ui/SelectInput.vue +++ b/client/components/ui/SelectInput.vue @@ -1,9 +1,9 @@ @@ -21,7 +21,6 @@ export default { type: String, default: 'text' }, - min: [String, Number], readonly: Boolean, disabled: Boolean, inputClass: String, diff --git a/client/components/ui/VueTrix.vue b/client/components/ui/VueTrix.vue index 0836df15..2687d934 100644 --- a/client/components/ui/VueTrix.vue +++ b/client/components/ui/VueTrix.vue @@ -318,8 +318,10 @@ export default { } }, handleAttachmentAdd(event) { - // Prevent pasting in images/any files from the browser - event.attachment.remove() + // Prevent pasting in images from the browser + if (!event.attachment.file) { + event.attachment.remove() + } } }, mounted() { diff --git a/client/components/widgets/CronExpressionBuilder.vue b/client/components/widgets/CronExpressionBuilder.vue index 600ed81a..77b5a54c 100644 --- a/client/components/widgets/CronExpressionBuilder.vue +++ b/client/components/widgets/CronExpressionBuilder.vue @@ -85,7 +85,7 @@ export default { nextRun() { if (!this.cronExpression) return '' const parsed = this.$getNextScheduledDate(this.cronExpression) - return this.$formatJsDatetime(parsed, this.$store.getters['getServerSetting']('dateFormat'), this.$store.getters['getServerSetting']('timeFormat')) || '' + return this.$formatJsDatetime(parsed, this.$store.state.serverSettings.dateFormat, this.$store.state.serverSettings.timeFormat) || '' }, description() { if ((this.selectedInterval !== 'custom' || !this.selectedWeekdays.length) && this.selectedInterval !== 'daily') return '' diff --git a/client/components/widgets/EncoderOptionsCard.vue b/client/components/widgets/EncoderOptionsCard.vue index 977d766b..005563b4 100644 --- a/client/components/widgets/EncoderOptionsCard.vue +++ b/client/components/widgets/EncoderOptionsCard.vue @@ -143,18 +143,10 @@ export default { localStorage.setItem('embedMetadataCodec', val) }, getEncodingOptions() { - if (this.showAdvancedView) { - return { - codec: this.customCodec || this.selectedCodec || 'aac', - bitrate: this.customBitrate || this.selectedBitrate || '128k', - channels: this.customChannels || this.selectedChannels || 2 - } - } else { - return { - codec: this.selectedCodec || 'aac', - bitrate: this.selectedBitrate || '128k', - channels: this.selectedChannels || 2 - } + return { + codec: this.selectedCodec || 'aac', + bitrate: this.selectedBitrate || '128k', + channels: this.selectedChannels || 2 } }, setPreset() { diff --git a/client/components/widgets/LoadingSpinner.vue b/client/components/widgets/LoadingSpinner.vue index 8f3de84a..a9c4ef47 100644 --- a/client/components/widgets/LoadingSpinner.vue +++ b/client/components/widgets/LoadingSpinner.vue @@ -248,4 +248,4 @@ export default { transform: scale(0); } } - + \ No newline at end of file diff --git a/client/components/widgets/SeriesInputWidget.vue b/client/components/widgets/SeriesInputWidget.vue index 3dab0605..d6c8cf9f 100644 --- a/client/components/widgets/SeriesInputWidget.vue +++ b/client/components/widgets/SeriesInputWidget.vue @@ -109,4 +109,4 @@ export default { } } } - + \ No newline at end of file diff --git a/client/cypress/tests/components/cards/LazySeriesCard.cy.js b/client/cypress/tests/components/cards/LazySeriesCard.cy.js index 12eec692..346259d2 100644 --- a/client/cypress/tests/components/cards/LazySeriesCard.cy.js +++ b/client/cypress/tests/components/cards/LazySeriesCard.cy.js @@ -40,7 +40,6 @@ describe('LazySeriesCard', () => { }, $store: { getters: { - getServerSetting: () => 'MM/dd/yyyy', 'user/getUserCanUpdate': true, 'user/getUserMediaProgress': (id) => null, 'user/getSizeMultiplier': 1, diff --git a/client/layouts/default.vue b/client/layouts/default.vue index 9f15af67..33e7aa15 100644 --- a/client/layouts/default.vue +++ b/client/layouts/default.vue @@ -33,7 +33,6 @@ export default { return { socket: null, isSocketConnected: false, - isSocketAuthenticated: false, isFirstSocketConnection: true, socketConnectionToastId: null, currentLang: null, @@ -82,28 +81,9 @@ export default { document.body.classList.add('app-bar') } }, - tokenRefreshed(newAccessToken) { - if (this.isSocketConnected && !this.isSocketAuthenticated) { - console.log('[SOCKET] Re-authenticating socket after token refresh') - this.socket.emit('auth', newAccessToken) - } - }, updateSocketConnectionToast(content, type, timeout) { if (this.socketConnectionToastId !== null && this.socketConnectionToastId !== undefined) { - const toastUpdateOptions = { - content: content, - options: { - timeout: timeout, - type: type, - closeButton: false, - position: 'bottom-center', - onClose: () => { - this.socketConnectionToastId = null - }, - closeOnClick: timeout !== null - } - } - this.$toast.update(this.socketConnectionToastId, toastUpdateOptions, false) + this.$toast.update(this.socketConnectionToastId, { content: content, options: { timeout: timeout, type: type, closeButton: false, position: 'bottom-center', onClose: () => null, closeOnClick: timeout !== null } }, false) } else { this.socketConnectionToastId = this.$toast[type](content, { position: 'bottom-center', timeout: timeout, closeButton: false, closeOnClick: timeout !== null }) } @@ -129,7 +109,7 @@ export default { this.updateSocketConnectionToast(this.$strings.ToastSocketDisconnected, 'error', null) }, reconnect() { - console.log('[SOCKET] reconnected') + console.error('[SOCKET] reconnected') }, reconnectAttempt(val) { console.log(`[SOCKET] reconnect attempt ${val}`) @@ -140,10 +120,6 @@ export default { reconnectFailed() { console.error('[SOCKET] reconnect failed') }, - authFailed(payload) { - console.error('[SOCKET] auth failed', payload.message) - this.isSocketAuthenticated = false - }, init(payload) { console.log('Init Payload', payload) @@ -151,7 +127,7 @@ export default { this.$store.commit('users/setUsersOnline', payload.usersOnline) } - this.isSocketAuthenticated = true + this.$eventBus.$emit('socket_init') }, streamOpen(stream) { if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamOpen(stream) @@ -378,15 +354,6 @@ export default { this.$store.commit('scanners/removeCustomMetadataProvider', provider) }, initializeSocket() { - if (this.$root.socket) { - // Can happen in dev due to hot reload - console.warn('Socket already initialized') - this.socket = this.$root.socket - this.isSocketConnected = this.$root.socket?.connected - this.isFirstSocketConnection = false - this.socketConnectionToastId = null - return - } this.socket = this.$nuxtSocket({ name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod', persist: 'main', @@ -397,7 +364,6 @@ export default { path: `${this.$config.routerBasePath}/socket.io` }) this.$root.socket = this.socket - this.isSocketAuthenticated = false console.log('Socket initialized') // Pre-defined socket events @@ -411,7 +377,6 @@ export default { // Event received after authorizing socket this.socket.on('init', this.init) - this.socket.on('auth_failed', this.authFailed) // Stream Listeners this.socket.on('stream_open', this.streamOpen) @@ -606,7 +571,6 @@ export default { this.updateBodyClass() this.resize() this.$eventBus.$on('change-lang', this.changeLanguage) - this.$eventBus.$on('token_refreshed', this.tokenRefreshed) window.addEventListener('resize', this.resize) window.addEventListener('keydown', this.keyDown) @@ -630,7 +594,6 @@ export default { }, beforeDestroy() { this.$eventBus.$off('change-lang', this.changeLanguage) - this.$eventBus.$off('token_refreshed', this.tokenRefreshed) window.removeEventListener('resize', this.resize) window.removeEventListener('keydown', this.keyDown) } diff --git a/client/nuxt.config.js b/client/nuxt.config.js index 7219c784..f54d1cf4 100644 --- a/client/nuxt.config.js +++ b/client/nuxt.config.js @@ -73,8 +73,7 @@ module.exports = { // Axios module configuration: https://go.nuxtjs.dev/config-axios axios: { - baseURL: routerBasePath, - progress: false + baseURL: routerBasePath }, // nuxt/pwa https://pwa.nuxtjs.org diff --git a/client/package-lock.json b/client/package-lock.json index 406ef9db..23ec14a9 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.26.0", + "version": "2.24.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.26.0", + "version": "2.24.0", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", diff --git a/client/package.json b/client/package.json index 5ebaab54..7985cd78 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.26.0", + "version": "2.24.0", "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 e9b5da3c..b157f570 100644 --- a/client/pages/account.vue +++ b/client/pages/account.vue @@ -182,19 +182,18 @@ export default { password: this.password, newPassword: this.newPassword }) - .then(() => { - this.$toast.success(this.$strings.ToastUserPasswordChangeSuccess) - this.resetForm() + .then((res) => { + if (res.success) { + this.$toast.success(this.$strings.ToastUserPasswordChangeSuccess) + this.resetForm() + } else { + this.$toast.error(res.error || this.$strings.ToastUnknownError) + } + this.changingPassword = false }) .catch((error) => { - console.error('Failed to change password', error) - let errorMessage = this.$strings.ToastUnknownError - if (error.response?.data && typeof error.response.data === 'string') { - errorMessage = error.response.data - } - this.$toast.error(errorMessage) - }) - .finally(() => { + console.error(error) + this.$toast.error(this.$strings.ToastUnknownError) this.changingPassword = false }) }, diff --git a/client/pages/audiobook/_id/manage.vue b/client/pages/audiobook/_id/manage.vue index 40672af2..7afe12a9 100644 --- a/client/pages/audiobook/_id/manage.vue +++ b/client/pages/audiobook/_id/manage.vue @@ -28,14 +28,14 @@
-
{{ $strings.LabelMetaTag }}
-
{{ $strings.LabelValue }}
+
{{ $strings.LabelMetaTag }}
+
{{ $strings.LabelValue }}