From ce803dd6de252ca7aaf65055c7f566136093d3c2 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 6 Jul 2025 17:39:03 -0500 Subject: [PATCH 1/3] Use getServerSetting to ensure serverSettings is set before accessing --- client/components/cards/LazyBookCard.vue | 2 +- client/components/cards/LazySeriesCard.vue | 2 +- client/components/modals/BookmarksModal.vue | 4 ++-- client/components/modals/ListeningSessionModal.vue | 4 ++-- client/components/modals/ShareModal.vue | 2 +- client/components/modals/changelog/ViewModal.vue | 2 +- client/components/tables/BackupsTable.vue | 4 ++-- client/components/tables/UsersTable.vue | 4 ++-- client/components/tables/podcast/LazyEpisodeRow.vue | 2 +- client/components/tables/podcast/LazyEpisodesTable.vue | 4 ++-- client/components/widgets/CronExpressionBuilder.vue | 2 +- client/pages/config/rss-feeds.vue | 4 ++-- client/pages/config/sessions.vue | 4 ++-- client/pages/config/users/_id/index.vue | 4 ++-- client/pages/config/users/_id/sessions.vue | 4 ++-- client/pages/item/_id/index.vue | 2 +- client/pages/library/_library/podcast/latest.vue | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index 35c959fad..41b733107 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.state.serverSettings.dateFormat + return this.store.getters['getServerSetting']('dateFormat') }, _libraryItem() { return this.libraryItem || {} diff --git a/client/components/cards/LazySeriesCard.vue b/client/components/cards/LazySeriesCard.vue index 3532095b9..34cea7e22 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.state.serverSettings.dateFormat + return this.store.getters['getServerSetting']('dateFormat') }, labelFontSize() { if (this.width < 160) return 0.75 diff --git a/client/components/modals/BookmarksModal.vue b/client/components/modals/BookmarksModal.vue index de8c72b7e..d84a8ed88 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.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/components/modals/ListeningSessionModal.vue b/client/components/modals/ListeningSessionModal.vue index a24698368..ecf00f787 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.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') }, isOpenSession() { return !!this._session.open diff --git a/client/components/modals/ShareModal.vue b/client/components/modals/ShareModal.vue index 24994b223..bd0c9acfb 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.state.serverSettings.dateFormat, this.$store.state.serverSettings.timeFormat) + return this.$formatDatetime(dateMs, this.$store.getters['getServerSetting']('dateFormat'), this.$store.getters['getServerSetting']('timeFormat')) } }, methods: { diff --git a/client/components/modals/changelog/ViewModal.vue b/client/components/modals/changelog/ViewModal.vue index 1b332a1d5..939ee71d2 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.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, releasesToShow() { return this.versionData?.releasesToShow || [] diff --git a/client/components/tables/BackupsTable.vue b/client/components/tables/BackupsTable.vue index 769c8d25c..f769abdb0 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.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/components/tables/UsersTable.vue b/client/components/tables/UsersTable.vue index 20f412288..c71710181 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.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/components/tables/podcast/LazyEpisodeRow.vue b/client/components/tables/podcast/LazyEpisodeRow.vue index ae99e6d3e..ee1899619 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.state.serverSettings.dateFormat + return this.store.getters['getServerSetting']('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 b23cc5605..d23ee3d3b 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.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/components/widgets/CronExpressionBuilder.vue b/client/components/widgets/CronExpressionBuilder.vue index 77b5a54c5..600ed81a1 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.state.serverSettings.dateFormat, this.$store.state.serverSettings.timeFormat) || '' + return this.$formatJsDatetime(parsed, this.$store.getters['getServerSetting']('dateFormat'), this.$store.getters['getServerSetting']('timeFormat')) || '' }, description() { if ((this.selectedInterval !== 'custom' || !this.selectedWeekdays.length) && this.selectedInterval !== 'daily') return '' diff --git a/client/pages/config/rss-feeds.vue b/client/pages/config/rss-feeds.vue index 568239201..b5ba90a38 100644 --- a/client/pages/config/rss-feeds.vue +++ b/client/pages/config/rss-feeds.vue @@ -78,10 +78,10 @@ export default { }, computed: { dateFormat() { - return this.$store.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/pages/config/sessions.vue b/client/pages/config/sessions.vue index 0b1bdaa7a..135922d39 100644 --- a/client/pages/config/sessions.vue +++ b/client/pages/config/sessions.vue @@ -250,10 +250,10 @@ export default { return user?.username || null }, dateFormat() { - return this.$store.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') }, numSelected() { return this.listeningSessions.filter((s) => s.selected).length diff --git a/client/pages/config/users/_id/index.vue b/client/pages/config/users/_id/index.vue index e2f8e2086..f0e6647f3 100644 --- a/client/pages/config/users/_id/index.vue +++ b/client/pages/config/users/_id/index.vue @@ -129,10 +129,10 @@ export default { return this.listeningSessions.sessions[0] }, dateFormat() { - return this.$store.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/pages/config/users/_id/sessions.vue b/client/pages/config/users/_id/sessions.vue index ab33b4b1c..764983d73 100644 --- a/client/pages/config/users/_id/sessions.vue +++ b/client/pages/config/users/_id/sessions.vue @@ -98,10 +98,10 @@ export default { return this.$store.getters['users/getIsUserOnline'](this.user.id) }, dateFormat() { - return this.$store.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, timeFormat() { - return this.$store.state.serverSettings.timeFormat + return this.$store.getters['getServerSetting']('timeFormat') } }, methods: { diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue index 6042f92a2..1d8f0f20b 100644 --- a/client/pages/item/_id/index.vue +++ b/client/pages/item/_id/index.vue @@ -193,7 +193,7 @@ export default { return `${process.env.serverUrl}/api/items/${this.libraryItemId}/download?token=${this.userToken}` }, dateFormat() { - return this.$store.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') }, userIsAdminOrUp() { return this.$store.getters['user/getIsAdminOrUp'] diff --git a/client/pages/library/_library/podcast/latest.vue b/client/pages/library/_library/podcast/latest.vue index 4da25f3ec..4f12043e1 100644 --- a/client/pages/library/_library/podcast/latest.vue +++ b/client/pages/library/_library/podcast/latest.vue @@ -141,7 +141,7 @@ export default { return episodeIds }, dateFormat() { - return this.$store.state.serverSettings.dateFormat + return this.$store.getters['getServerSetting']('dateFormat') } }, methods: { From 4102ed8be47742c435cc7464ab1edd6cf91cf8f2 Mon Sep 17 00:00:00 2001 From: advplyr Date: Mon, 7 Jul 2025 16:49:20 -0500 Subject: [PATCH 2/3] Fix LazySeriesCard component test --- client/cypress/tests/components/cards/LazySeriesCard.cy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/cypress/tests/components/cards/LazySeriesCard.cy.js b/client/cypress/tests/components/cards/LazySeriesCard.cy.js index 346259d27..12eec6924 100644 --- a/client/cypress/tests/components/cards/LazySeriesCard.cy.js +++ b/client/cypress/tests/components/cards/LazySeriesCard.cy.js @@ -40,6 +40,7 @@ describe('LazySeriesCard', () => { }, $store: { getters: { + getServerSetting: () => 'MM/dd/yyyy', 'user/getUserCanUpdate': true, 'user/getUserMediaProgress': (id) => null, 'user/getSizeMultiplier': 1, From df1391d93f6cf2a33b2420b0dc38f6ed40b6bcca Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 9 Jul 2025 13:42:53 -0500 Subject: [PATCH 3/3] Fix scanner after deleting single file books #4459 --- server/scanner/LibraryItemScanner.js | 5 +++++ server/scanner/LibraryScanner.js | 5 +++++ server/utils/fileUtils.js | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/server/scanner/LibraryItemScanner.js b/server/scanner/LibraryItemScanner.js index 133a1e301..501df4274 100644 --- a/server/scanner/LibraryItemScanner.js +++ b/server/scanner/LibraryItemScanner.js @@ -206,6 +206,11 @@ class LibraryItemScanner { async scanPotentialNewLibraryItem(libraryItemPath, library, folder, isSingleMediaItem) { const libraryItemScanData = await this.getLibraryItemScanData(libraryItemPath, library, folder, isSingleMediaItem) + if (!libraryItemScanData.libraryFiles.length) { + Logger.info(`[LibraryItemScanner] Library item at path "${libraryItemPath}" has no files - ignoring`) + return null + } + const scanLogger = new ScanLogger() scanLogger.verbose = true scanLogger.setData('libraryItem', libraryItemScanData.relPath) diff --git a/server/scanner/LibraryScanner.js b/server/scanner/LibraryScanner.js index bc174d7a0..640c82d76 100644 --- a/server/scanner/LibraryScanner.js +++ b/server/scanner/LibraryScanner.js @@ -606,6 +606,11 @@ class LibraryScanner { } else if (library.settings.audiobooksOnly && !hasAudioFiles(fileUpdateGroup, itemDir)) { Logger.debug(`[LibraryScanner] Folder update for relative path "${itemDir}" has no audio files`) continue + } else if (!(await fs.pathExists(fullPath))) { + Logger.info(`[LibraryScanner] File update group "${itemDir}" does not exist - ignoring`) + + itemGroupingResults[itemDir] = ScanResult.NOTHING + continue } // Check if a library item is a subdirectory of this dir diff --git a/server/utils/fileUtils.js b/server/utils/fileUtils.js index f80c4acde..2da6b4c91 100644 --- a/server/utils/fileUtils.js +++ b/server/utils/fileUtils.js @@ -109,7 +109,7 @@ function getIno(path) { .stat(path, { bigint: true }) .then((data) => String(data.ino)) .catch((err) => { - Logger.error('[Utils] Failed to get ino for path', path, err) + Logger.warn(`[Utils] Failed to get ino for path "${path}"`, err) return null }) }