From 7919a8b581bfa1d11d352d87e375f16951abc39e Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 20 Feb 2025 17:40:54 -0600 Subject: [PATCH 01/32] Fix get podcast library items endpoint when not including a limit query param #4014 --- server/utils/queries/libraryItemsPodcastFilters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js index a04113811..7b54eed09 100644 --- a/server/utils/queries/libraryItemsPodcastFilters.js +++ b/server/utils/queries/libraryItemsPodcastFilters.js @@ -112,7 +112,7 @@ module.exports = { countCache.set(cacheKey, count) } - findOptions.limit = limit + findOptions.limit = limit || null findOptions.offset = offset const rows = await model.findAll(findOptions) From a34b01fcb471edd19024f25ec4edacfec85aa177 Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 20 Feb 2025 17:45:09 -0600 Subject: [PATCH 02/32] Add localization strings for Cover Provider and Activities #4017 --- client/components/widgets/NotificationWidget.vue | 4 ++-- client/pages/config/index.vue | 2 +- client/strings/en-us.json | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/components/widgets/NotificationWidget.vue b/client/components/widgets/NotificationWidget.vue index 57bf0257a..6ead559b2 100644 --- a/client/components/widgets/NotificationWidget.vue +++ b/client/components/widgets/NotificationWidget.vue @@ -5,8 +5,8 @@ - - notifications + + notifications
diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index 5636113e4..1fd7e8f04 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -67,7 +67,7 @@
- +
diff --git a/client/strings/en-us.json b/client/strings/en-us.json index e4e1f7cd7..76a773a24 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -220,6 +220,7 @@ "LabelAccountTypeGuest": "Guest", "LabelAccountTypeUser": "User", "LabelActivity": "Activity", + "LabelActivities": "Activities", "LabelAddToCollection": "Add to Collection", "LabelAddToCollectionBatch": "Add {0} Books to Collection", "LabelAddToPlaylist": "Add to Playlist", @@ -283,6 +284,7 @@ "LabelContinueSeries": "Continue Series", "LabelCover": "Cover", "LabelCoverImageURL": "Cover Image URL", + "LabelCoverProvider": "Cover Provider", "LabelCreatedAt": "Created At", "LabelCronExpression": "Cron Expression", "LabelCurrent": "Current", From 0cc2e39367f2ae4168a49f5c472db35a374deec8 Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 20 Feb 2025 17:59:09 -0600 Subject: [PATCH 03/32] Update en-us string order --- client/strings/en-us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 76a773a24..3e3bfe33b 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -219,8 +219,8 @@ "LabelAccountTypeAdmin": "Admin", "LabelAccountTypeGuest": "Guest", "LabelAccountTypeUser": "User", - "LabelActivity": "Activity", "LabelActivities": "Activities", + "LabelActivity": "Activity", "LabelAddToCollection": "Add to Collection", "LabelAddToCollectionBatch": "Add {0} Books to Collection", "LabelAddToPlaylist": "Add to Playlist", From e8b60defb6b04fb3654208c5e9365bbdbcfcdbda Mon Sep 17 00:00:00 2001 From: mikiher Date: Fri, 21 Feb 2025 09:45:10 +0200 Subject: [PATCH 04/32] Invalidate count cache on entity update --- server/models/Book.js | 4 ++++ server/models/Podcast.js | 4 ++++ server/models/PodcastEpisode.js | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/server/models/Book.js b/server/models/Book.js index 1f4193a26..811a7af0a 100644 --- a/server/models/Book.js +++ b/server/models/Book.js @@ -201,6 +201,10 @@ class Book extends Model { Book.addHook('afterCreate', async (instance) => { libraryItemsBookFilters.clearCountCache('afterCreate') }) + + Book.addHook('afterUpdate', async (instance) => { + libraryItemsBookFilters.clearCountCache('afterUpdate') + }) } /** diff --git a/server/models/Podcast.js b/server/models/Podcast.js index fa27821db..c72bda278 100644 --- a/server/models/Podcast.js +++ b/server/models/Podcast.js @@ -157,6 +157,10 @@ class Podcast extends Model { Podcast.addHook('afterCreate', async (instance) => { libraryItemsPodcastFilters.clearCountCache('podcast', 'afterCreate') }) + + Podcast.addHook('afterUpdate', async (instance) => { + libraryItemsPodcastFilters.clearCountCache('podcast', 'afterUpdate') + }) } get hasMediaFiles() { diff --git a/server/models/PodcastEpisode.js b/server/models/PodcastEpisode.js index 4746f3150..38f1287ad 100644 --- a/server/models/PodcastEpisode.js +++ b/server/models/PodcastEpisode.js @@ -140,6 +140,10 @@ class PodcastEpisode extends Model { PodcastEpisode.addHook('afterCreate', async (instance) => { libraryItemsPodcastFilters.clearCountCache('podcastEpisode', 'afterCreate') }) + + PodcastEpisode.addHook('afterUpdate', async (instance) => { + libraryItemsPodcastFilters.clearCountCache('podcastEpisode', 'afterUpdate') + }) } get size() { From 9d7f44f73aca6157e7a4be28361c510200494ad1 Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 21 Feb 2025 17:39:36 -0600 Subject: [PATCH 05/32] Fix RSS Feed Open query --- server/utils/queries/libraryItemsBookFilters.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index d446a5e97..3787cc849 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -434,19 +434,17 @@ module.exports = { const libraryItemIncludes = [] const bookIncludes = [] - if (includeRSSFeed) { + + if (filterGroup === 'feed-open' || includeRSSFeed) { + const rssFeedRequired = filterGroup === 'feed-open' libraryItemIncludes.push({ model: Database.feedModel, - required: filterGroup === 'feed-open', - separate: true + required: rssFeedRequired, + separate: !rssFeedRequired }) } - if (filterGroup === 'feed-open' && !includeRSSFeed) { - libraryItemIncludes.push({ - model: Database.feedModel, - required: true - }) - } else if (filterGroup === 'share-open') { + + if (filterGroup === 'share-open') { bookIncludes.push({ model: Database.mediaItemShareModel, required: true From 452d354b525f36906b3c1f1b327e19bf6d228fa6 Mon Sep 17 00:00:00 2001 From: alexshch09 Date: Sat, 22 Feb 2025 00:44:52 +0100 Subject: [PATCH 06/32] fix(auth): Add admin-level auth to LibraryController delete update and issue removal --- server/controllers/LibraryController.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index 0ece483f8..3585dc51f 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -254,6 +254,11 @@ class LibraryController { * @param {Response} res */ async update(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.user.username}" attempted to update library`) + return res.sendStatus(403) + } + // Validation const updatePayload = {} const keysToCheck = ['name', 'provider', 'mediaType', 'icon'] @@ -519,6 +524,11 @@ class LibraryController { * @param {Response} res */ async delete(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.user.username}" attempted to delete library`) + return res.sendStatus(403) + } + // Remove library watcher Watcher.removeLibrary(req.library) @@ -639,6 +649,11 @@ class LibraryController { * @param {Response} res */ async removeLibraryItemsWithIssues(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.user.username}" attempted to delete library items missing or invalid`) + return res.sendStatus(403) + } + const libraryItemsWithIssues = await Database.libraryItemModel.findAll({ where: { libraryId: req.library.id, From 799879d67d50bc9e0095d1d2d0033a67dfd83433 Mon Sep 17 00:00:00 2001 From: Mike Smith Date: Fri, 21 Feb 2025 18:45:29 -0500 Subject: [PATCH 07/32] prevent long author strings from pushing the player controls down by truncating (#3944) * prevent long author strings from pushing the player controls down by truncating * move truncate to single author, instead of the main container --- client/components/app/MediaPlayerContainer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/app/MediaPlayerContainer.vue b/client/components/app/MediaPlayerContainer.vue index 8aa6188be..962ec2e83 100644 --- a/client/components/app/MediaPlayerContainer.vue +++ b/client/components/app/MediaPlayerContainer.vue @@ -13,7 +13,7 @@
person -
{{ podcastAuthor }}
+
{{ podcastAuthor }}
{{ author.name }}
From 19a65dba984fab9996398154ea6c3bcf29ae5184 Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 21 Feb 2025 18:18:16 -0600 Subject: [PATCH 08/32] Update backup schedule description translations #4017 --- client/pages/config/backups.vue | 2 +- client/plugins/i18n.js | 14 ++++++++++++++ client/plugins/utils.js | 18 +++++++++--------- client/strings/en-us.json | 1 + 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/client/pages/config/backups.vue b/client/pages/config/backups.vue index d1dce014c..98406434e 100644 --- a/client/pages/config/backups.vue +++ b/client/pages/config/backups.vue @@ -122,7 +122,7 @@ export default { }, scheduleDescription() { if (!this.cronExpression) return '' - const parsed = this.$parseCronExpression(this.cronExpression) + const parsed = this.$parseCronExpression(this.cronExpression, this) return parsed ? parsed.description : `${this.$strings.LabelCustomCronExpression} ${this.cronExpression}` }, nextBackupDate() { diff --git a/client/plugins/i18n.js b/client/plugins/i18n.js index 5f6b15089..1769e6eb2 100644 --- a/client/plugins/i18n.js +++ b/client/plugins/i18n.js @@ -107,6 +107,19 @@ Vue.prototype.$formatNumber = (num) => { return Intl.NumberFormat(Vue.prototype.$languageCodes.current).format(num) } +/** + * Get the days of the week for the current language + * Starts with Sunday + * @returns {string[]} + */ +Vue.prototype.$getDaysOfWeek = () => { + const days = [] + for (let i = 0; i < 7; i++) { + days.push(new Date(2025, 0, 5 + i).toLocaleString(Vue.prototype.$languageCodes.current, { weekday: 'long' })) + } + return days +} + const translations = { [defaultCode]: enUsStrings } @@ -148,6 +161,7 @@ async function loadi18n(code) { Vue.prototype.$setDateFnsLocale(languageCodeMap[code].dateFnsLocale) this?.$eventBus?.$emit('change-lang', code) + return true } diff --git a/client/plugins/utils.js b/client/plugins/utils.js index 5ad909d3c..96b1f31cd 100644 --- a/client/plugins/utils.js +++ b/client/plugins/utils.js @@ -93,7 +93,7 @@ Vue.prototype.$elapsedPrettyExtended = (seconds, useDays = true, showSeconds = t return strs.join(' ') } -Vue.prototype.$parseCronExpression = (expression) => { +Vue.prototype.$parseCronExpression = (expression, context) => { if (!expression) return null const pieces = expression.split(' ') if (pieces.length !== 5) { @@ -102,31 +102,31 @@ Vue.prototype.$parseCronExpression = (expression) => { const commonPatterns = [ { - text: 'Every 12 hours', + text: context.$strings.LabelIntervalEvery12Hours, value: '0 */12 * * *' }, { - text: 'Every 6 hours', + text: context.$strings.LabelIntervalEvery6Hours, value: '0 */6 * * *' }, { - text: 'Every 2 hours', + text: context.$strings.LabelIntervalEvery2Hours, value: '0 */2 * * *' }, { - text: 'Every hour', + text: context.$strings.LabelIntervalEveryHour, value: '0 * * * *' }, { - text: 'Every 30 minutes', + text: context.$strings.LabelIntervalEvery30Minutes, value: '*/30 * * * *' }, { - text: 'Every 15 minutes', + text: context.$strings.LabelIntervalEvery15Minutes, value: '*/15 * * * *' }, { - text: 'Every minute', + text: context.$strings.LabelIntervalEveryMinute, value: '* * * * *' } ] @@ -147,7 +147,7 @@ Vue.prototype.$parseCronExpression = (expression) => { return null } - const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] + const weekdays = context.$getDaysOfWeek() var weekdayText = 'day' if (pieces[4] !== '*') weekdayText = pieces[4] diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 3e3bfe33b..8a2206a46 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -393,6 +393,7 @@ "LabelIntervalEvery6Hours": "Every 6 hours", "LabelIntervalEveryDay": "Every day", "LabelIntervalEveryHour": "Every hour", + "LabelIntervalEveryMinute": "Every minute", "LabelInvert": "Invert", "LabelItem": "Item", "LabelJumpBackwardAmount": "Jump backward amount", From 13c20e0cddc22d1431ce9f4865380a3a17b8f18c Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 22 Feb 2025 12:28:51 -0700 Subject: [PATCH 09/32] Add: generic function to ignor files --- server/Watcher.js | 15 +++++-------- server/utils/fileUtils.js | 47 ++++++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/server/Watcher.js b/server/Watcher.js index b2cbc0e8f..c5f64917b 100644 --- a/server/Watcher.js +++ b/server/Watcher.js @@ -5,7 +5,7 @@ const Logger = require('./Logger') const Task = require('./objects/Task') const TaskManager = require('./managers/TaskManager') -const { filePathToPOSIX, isSameOrSubPath, getFileMTimeMs } = require('./utils/fileUtils') +const { filePathToPOSIX, isSameOrSubPath, getFileMTimeMs, shouldIgnoreFile } = require('./utils/fileUtils') /** * @typedef PendingFileUpdate @@ -286,15 +286,10 @@ class FolderWatcher extends EventEmitter { const relPath = path.replace(folderPath, '') - if (Path.extname(relPath).toLowerCase() === '.part') { - Logger.debug(`[Watcher] Ignoring .part file "${relPath}"`) - return false - } - - // Ignore files/folders starting with "." - const hasDotPath = relPath.split('/').find((p) => p.startsWith('.')) - if (hasDotPath) { - Logger.debug(`[Watcher] Ignoring dot path "${relPath}" | Piece "${hasDotPath}"`) + // Check for ignored extensions or directories, such as dotfiles and hidden directories + const shouldIgnore = shouldIgnoreFile(relPath) + if (shouldIgnore) { + Logger.debug(`[Watcher] Ignoring ${shouldIgnore} - "${relPath}"`) return false } diff --git a/server/utils/fileUtils.js b/server/utils/fileUtils.js index 19ac2efe9..b35a3a995 100644 --- a/server/utils/fileUtils.js +++ b/server/utils/fileUtils.js @@ -131,6 +131,41 @@ async function readTextFile(path) { } module.exports.readTextFile = readTextFile +/** + * Check if file or directory should be ignored. Returns a string of the reason to ignore, or null if not ignored + * + * @param {string} path + * @returns {string} + */ +module.exports.shouldIgnoreFile = (path) => { + var extensionIgnores = ['.part', '.tmp', '.crdownload', '.download', '.bak', '.old', '.temp', '.tempfile', '.tempfile~'] + + // Check extension + if (extensionIgnores.includes(Path.extname(path).toLowerCase())) { + // Return the extension that is ignored + return `${Path.extname(path)} file` + } + + // Check if directory or file name starts with "." + if (Path.basename(path).startsWith('.')) { + return 'dotfile' + } + if (path.split('/').find((p) => p.startsWith('.'))) { + return 'dotpath' + } + + // If these strings exist anywhere in the filename or directory name, ignore. Vendor specific hidden directories + var includeAnywhereIgnore = ['@eaDir'] + var filteredInclude = includeAnywhereIgnore.filter((str) => path.includes(str)) + if (filteredInclude.length) { + return `${filteredInclude[0]} directory` + } + + // Should not ignore this file or directory + return null +} +module.exports.shouldIgnoreFile = this.shouldIgnoreFile + /** * @typedef FilePathItem * @property {string} name - file name e.g. "audiofile.m4b" @@ -197,14 +232,10 @@ async function recurseFiles(path, relPathToReplace = null) { return false } - if (item.extension === '.part') { - Logger.debug(`[fileUtils] Ignoring .part file "${relpath}"`) - return false - } - - // Ignore any file if a directory or the filename starts with "." - if (relpath.split('/').find((p) => p.startsWith('.'))) { - Logger.debug(`[fileUtils] Ignoring path has . "${relpath}"`) + // Check for ignored extensions or directories + const shouldIgnore = this.shouldIgnoreFile(relpath) + if (shouldIgnore) { + Logger.debug(`[fileUtils] Ignoring ${shouldIgnore} - "${relpath}"`) return false } From 4662fc52448e572083417213a23aea3b204e6c22 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 22 Feb 2025 14:48:13 -0700 Subject: [PATCH 10/32] Add: log custom metadata provider to match other providers --- server/providers/CustomProviderAdapter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/providers/CustomProviderAdapter.js b/server/providers/CustomProviderAdapter.js index 911a09e95..6841eb8cd 100644 --- a/server/providers/CustomProviderAdapter.js +++ b/server/providers/CustomProviderAdapter.js @@ -41,6 +41,9 @@ class CustomProviderAdapter { } const queryString = new URLSearchParams(queryObj).toString() + const url = `${provider.url}/search?${queryString}` + Logger.debug(`[CustomMetadataProvider] Search url: ${url}`) + // Setup headers const axiosOptions = { timeout @@ -52,7 +55,7 @@ class CustomProviderAdapter { } const matches = await axios - .get(`${provider.url}/search?${queryString}`, axiosOptions) + .get(url, axiosOptions) .then((res) => { if (!res?.data || !Array.isArray(res.data.matches)) return null return res.data.matches From 64185b7519f3ca2dabc4ed2450de5a73c43880c7 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 22 Feb 2025 17:53:05 -0600 Subject: [PATCH 11/32] Add backup schedule string translation #4017 --- client/plugins/utils.js | 2 +- client/strings/en-us.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/plugins/utils.js b/client/plugins/utils.js index 96b1f31cd..8341a9e2d 100644 --- a/client/plugins/utils.js +++ b/client/plugins/utils.js @@ -156,7 +156,7 @@ Vue.prototype.$parseCronExpression = (expression, context) => { .join(', ') return { - description: `Run every ${weekdayText} at ${pieces[1]}:${pieces[0].padStart(2, '0')}` + description: context.$getString('MessageScheduleRunEveryWeekdayAtTime', [weekdayText, `${pieces[1]}:${pieces[0].padStart(2, '0')}`]) } } diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 8a2206a46..301f14a86 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -848,6 +848,7 @@ "MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on", "MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.

Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.

All clients using your server will be automatically refreshed.", "MessageScheduleLibraryScanNote": "For most users, it is recommended to leave this feature disabled and keep the folder watcher setting enabled. The folder watcher will automatically detect changes in your library folders. The folder watcher doesn't work for every file system (like NFS) so scheduled library scans can be used instead.", + "MessageScheduleRunEveryWeekdayAtTime": "Run every {0} at {1}", "MessageSearchResultsFor": "Search results for", "MessageSelected": "{0} selected", "MessageServerCouldNotBeReached": "Server could not be reached", From d6b58c2f10f66c5ef36fc720da2c901657b75021 Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 23 Feb 2025 08:03:10 +0200 Subject: [PATCH 12/32] Revert "Invalidate count cache on entity update" This reverts commit e8b60defb6b04fb3654208c5e9365bbdbcfcdbda. --- server/models/Book.js | 4 ---- server/models/Podcast.js | 4 ---- server/models/PodcastEpisode.js | 4 ---- 3 files changed, 12 deletions(-) diff --git a/server/models/Book.js b/server/models/Book.js index 811a7af0a..1f4193a26 100644 --- a/server/models/Book.js +++ b/server/models/Book.js @@ -201,10 +201,6 @@ class Book extends Model { Book.addHook('afterCreate', async (instance) => { libraryItemsBookFilters.clearCountCache('afterCreate') }) - - Book.addHook('afterUpdate', async (instance) => { - libraryItemsBookFilters.clearCountCache('afterUpdate') - }) } /** diff --git a/server/models/Podcast.js b/server/models/Podcast.js index c72bda278..fa27821db 100644 --- a/server/models/Podcast.js +++ b/server/models/Podcast.js @@ -157,10 +157,6 @@ class Podcast extends Model { Podcast.addHook('afterCreate', async (instance) => { libraryItemsPodcastFilters.clearCountCache('podcast', 'afterCreate') }) - - Podcast.addHook('afterUpdate', async (instance) => { - libraryItemsPodcastFilters.clearCountCache('podcast', 'afterUpdate') - }) } get hasMediaFiles() { diff --git a/server/models/PodcastEpisode.js b/server/models/PodcastEpisode.js index 38f1287ad..4746f3150 100644 --- a/server/models/PodcastEpisode.js +++ b/server/models/PodcastEpisode.js @@ -140,10 +140,6 @@ class PodcastEpisode extends Model { PodcastEpisode.addHook('afterCreate', async (instance) => { libraryItemsPodcastFilters.clearCountCache('podcastEpisode', 'afterCreate') }) - - PodcastEpisode.addHook('afterUpdate', async (instance) => { - libraryItemsPodcastFilters.clearCountCache('podcastEpisode', 'afterUpdate') - }) } get size() { From 364ccd85fe0f9504c66cf7cf332c3534663d50fb Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 23 Feb 2025 08:53:57 +0200 Subject: [PATCH 13/32] Use count cache only when no filter is set --- .../utils/queries/libraryItemsBookFilters.js | 32 +++++++++++-------- .../queries/libraryItemsPodcastFilters.js | 31 ++++++++++-------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index 3787cc849..7839651bc 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -344,22 +344,28 @@ module.exports = { countCache.clear() }, - async findAndCountAll(findOptions, limit, offset) { - const findOptionsKey = stringifySequelizeQuery(findOptions) - Logger.debug(`[LibraryItemsBookFilters] findOptionsKey: ${findOptionsKey}`) + async findAndCountAll(findOptions, limit, offset, useCountCache) { + const model = Database.bookModel + if (useCountCache) { + const countCacheKey = stringifySequelizeQuery(findOptions) + Logger.debug(`[LibraryItemsBookFilters] countCacheKey: ${countCacheKey}`) + if (!countCache.has(countCacheKey)) { + const count = await model.count(findOptions) + countCache.set(countCacheKey, count) + } + + findOptions.limit = limit || null + findOptions.offset = offset + + const rows = await model.findAll(findOptions) + + return { rows, count: countCache.get(countCacheKey) } + } findOptions.limit = limit || null findOptions.offset = offset - if (countCache.has(findOptionsKey)) { - const rows = await Database.bookModel.findAll(findOptions) - - return { rows, count: countCache.get(findOptionsKey) } - } else { - const result = await Database.bookModel.findAndCountAll(findOptions) - countCache.set(findOptionsKey, result.count) - return result - } + return await model.findAndCountAll(findOptions) }, /** @@ -606,7 +612,7 @@ module.exports = { } const findAndCountAll = process.env.QUERY_PROFILING ? profile(this.findAndCountAll) : this.findAndCountAll - const { rows: books, count } = await findAndCountAll(findOptions, limit, offset) + const { rows: books, count } = await findAndCountAll(findOptions, limit, offset, !filterGroup) const libraryItems = books.map((bookExpanded) => { const libraryItem = bookExpanded.libraryItem diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js index 7b54eed09..6527cfbd6 100644 --- a/server/utils/queries/libraryItemsPodcastFilters.js +++ b/server/utils/queries/libraryItemsPodcastFilters.js @@ -105,22 +105,27 @@ module.exports = { countCache.clear() }, - async findAndCountAll(findOptions, model, limit, offset) { - const cacheKey = stringifySequelizeQuery(findOptions) - if (!countCache.has(cacheKey)) { - const count = await model.count(findOptions) - countCache.set(cacheKey, count) + async findAndCountAll(findOptions, model, limit, offset, useCountCache) { + if (useCountCache) { + const countCacheKey = stringifySequelizeQuery(findOptions) + Logger.debug(`[LibraryItemsPodcastFilters] countCacheKey: ${countCacheKey}`) + if (!countCache.has(countCacheKey)) { + const count = await model.count(findOptions) + countCache.set(countCacheKey, count) + } + + findOptions.limit = limit || null + findOptions.offset = offset + + const rows = await model.findAll(findOptions) + + return { rows, count: countCache.get(countCacheKey) } } findOptions.limit = limit || null findOptions.offset = offset - const rows = await model.findAll(findOptions) - - return { - rows, - count: countCache.get(cacheKey) - } + return await model.findAndCountAll(findOptions) }, /** @@ -199,7 +204,7 @@ module.exports = { const findAndCountAll = process.env.QUERY_PROFILING ? profile(this.findAndCountAll) : this.findAndCountAll - const { rows: podcasts, count } = await findAndCountAll(findOptions, Database.podcastModel, limit, offset) + const { rows: podcasts, count } = await findAndCountAll(findOptions, Database.podcastModel, limit, offset, !filterGroup) const libraryItems = podcasts.map((podcastExpanded) => { const libraryItem = podcastExpanded.libraryItem @@ -323,7 +328,7 @@ module.exports = { const findAndCountAll = process.env.QUERY_PROFILING ? profile(this.findAndCountAll) : this.findAndCountAll - const { rows: podcastEpisodes, count } = await findAndCountAll(findOptions, Database.podcastEpisodeModel, limit, offset) + const { rows: podcastEpisodes, count } = await findAndCountAll(findOptions, Database.podcastEpisodeModel, limit, offset, !filterGroup) const libraryItems = podcastEpisodes.map((ep) => { const libraryItem = ep.podcast.libraryItem From 6c0e42db49067e05af263501a9d6d79aa4a95678 Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 23 Feb 2025 18:06:36 +0200 Subject: [PATCH 14/32] Route from Issues to Library if last issue is removed --- client/components/app/LazyBookshelf.vue | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue index 2144b899c..f5601a95e 100644 --- a/client/components/app/LazyBookshelf.vue +++ b/client/components/app/LazyBookshelf.vue @@ -568,6 +568,18 @@ export default { } } }, + routeToBookshelfIfLastIssueRemoved() { + if (this.totalEntities === 0) { + const currentRouteQuery = this.$route.query + if (currentRouteQuery?.filter && currentRouteQuery.filter === 'issues') { + this.$nextTick(() => { + console.log('Last issue removed. Redirecting to library bookshelf') + this.$router.push(`/library/${this.currentLibraryId}/bookshelf`) + this.$store.dispatch('libraries/fetch', this.currentLibraryId) + }) + } + } + }, libraryItemRemoved(libraryItem) { if (this.entityName === 'items' || this.entityName === 'series-books') { var indexOf = this.entities.findIndex((ent) => ent && ent.id === libraryItem.id) @@ -578,6 +590,7 @@ export default { this.executeRebuild() } } + this.routeToBookshelfIfLastIssueRemoved() }, libraryItemsAdded(libraryItems) { console.log('items added', libraryItems) From 20f812403f50db712f2bcf5f350baff523153fd6 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 23 Feb 2025 16:53:11 -0600 Subject: [PATCH 15/32] Add fileUtils recurseFiles and shouldIgnoreFile tests --- server/utils/fileUtils.js | 24 +++--- test/server/utils/fileUtils.test.js | 127 ++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 test/server/utils/fileUtils.test.js diff --git a/server/utils/fileUtils.js b/server/utils/fileUtils.js index b35a3a995..4b6915b74 100644 --- a/server/utils/fileUtils.js +++ b/server/utils/fileUtils.js @@ -138,14 +138,6 @@ module.exports.readTextFile = readTextFile * @returns {string} */ module.exports.shouldIgnoreFile = (path) => { - var extensionIgnores = ['.part', '.tmp', '.crdownload', '.download', '.bak', '.old', '.temp', '.tempfile', '.tempfile~'] - - // Check extension - if (extensionIgnores.includes(Path.extname(path).toLowerCase())) { - // Return the extension that is ignored - return `${Path.extname(path)} file` - } - // Check if directory or file name starts with "." if (Path.basename(path).startsWith('.')) { return 'dotfile' @@ -155,16 +147,23 @@ module.exports.shouldIgnoreFile = (path) => { } // If these strings exist anywhere in the filename or directory name, ignore. Vendor specific hidden directories - var includeAnywhereIgnore = ['@eaDir'] - var filteredInclude = includeAnywhereIgnore.filter((str) => path.includes(str)) + const includeAnywhereIgnore = ['@eaDir'] + const filteredInclude = includeAnywhereIgnore.filter((str) => path.includes(str)) if (filteredInclude.length) { return `${filteredInclude[0]} directory` } + const extensionIgnores = ['.part', '.tmp', '.crdownload', '.download', '.bak', '.old', '.temp', '.tempfile', '.tempfile~'] + + // Check extension + if (extensionIgnores.includes(Path.extname(path).toLowerCase())) { + // Return the extension that is ignored + return `${Path.extname(path)} file` + } + // Should not ignore this file or directory return null } -module.exports.shouldIgnoreFile = this.shouldIgnoreFile /** * @typedef FilePathItem @@ -182,7 +181,7 @@ module.exports.shouldIgnoreFile = this.shouldIgnoreFile * @param {string} [relPathToReplace] * @returns {FilePathItem[]} */ -async function recurseFiles(path, relPathToReplace = null) { +module.exports.recurseFiles = async (path, relPathToReplace = null) => { path = filePathToPOSIX(path) if (!path.endsWith('/')) path = path + '/' @@ -266,7 +265,6 @@ async function recurseFiles(path, relPathToReplace = null) { return list } -module.exports.recurseFiles = recurseFiles /** * diff --git a/test/server/utils/fileUtils.test.js b/test/server/utils/fileUtils.test.js new file mode 100644 index 000000000..59907be98 --- /dev/null +++ b/test/server/utils/fileUtils.test.js @@ -0,0 +1,127 @@ +const chai = require('chai') +const expect = chai.expect +const sinon = require('sinon') +const fileUtils = require('../../../server/utils/fileUtils') +const fs = require('fs') +const Logger = require('../../../server/Logger') + +describe('fileUtils', () => { + it('shouldIgnoreFile', () => { + global.isWin = process.platform === 'win32' + + const testCases = [ + { path: 'test.txt', expected: null }, + { path: 'folder/test.mp3', expected: null }, + { path: 'normal/path/file.m4b', expected: null }, + { path: 'test.txt.part', expected: '.part file' }, + { path: 'test.txt.tmp', expected: '.tmp file' }, + { path: 'test.txt.crdownload', expected: '.crdownload file' }, + { path: 'test.txt.download', expected: '.download file' }, + { path: 'test.txt.bak', expected: '.bak file' }, + { path: 'test.txt.old', expected: '.old file' }, + { path: 'test.txt.temp', expected: '.temp file' }, + { path: 'test.txt.tempfile', expected: '.tempfile file' }, + { path: 'test.txt.tempfile~', expected: '.tempfile~ file' }, + { path: '.gitignore', expected: 'dotfile' }, + { path: 'folder/.hidden', expected: 'dotfile' }, + { path: '.git/config', expected: 'dotpath' }, + { path: 'path/.hidden/file.txt', expected: 'dotpath' }, + { path: '@eaDir', expected: '@eaDir directory' }, + { path: 'folder/@eaDir', expected: '@eaDir directory' }, + { path: 'path/@eaDir/file.txt', expected: '@eaDir directory' }, + { path: '.hidden/test.tmp', expected: 'dotpath' }, + { path: '@eaDir/test.part', expected: '@eaDir directory' } + ] + + testCases.forEach(({ path, expected }) => { + const result = fileUtils.shouldIgnoreFile(path) + expect(result).to.equal(expected) + }) + }) + + describe('recurseFiles', () => { + let readdirStub, realpathStub, statStub + + beforeEach(() => { + global.isWin = process.platform === 'win32' + + // Mock file structure with normalized paths + const mockDirContents = new Map([ + ['/test', ['file1.mp3', 'subfolder', 'ignoreme', 'temp.mp3.tmp']], + ['/test/subfolder', ['file2.m4b']], + ['/test/ignoreme', ['.ignore', 'ignored.mp3']] + ]) + + const mockStats = new Map([ + ['/test/file1.mp3', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '1' }], + ['/test/subfolder', { isDirectory: () => true, size: 0, mtimeMs: Date.now(), ino: '2' }], + ['/test/subfolder/file2.m4b', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '3' }], + ['/test/ignoreme', { isDirectory: () => true, size: 0, mtimeMs: Date.now(), ino: '4' }], + ['/test/ignoreme/.ignore', { isDirectory: () => false, size: 0, mtimeMs: Date.now(), ino: '5' }], + ['/test/ignoreme/ignored.mp3', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '6' }], + ['/test/temp.mp3.tmp', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '7' }] + ]) + + // Stub fs.readdir + readdirStub = sinon.stub(fs, 'readdir') + readdirStub.callsFake((path, callback) => { + const contents = mockDirContents.get(path) + if (contents) { + callback(null, contents) + } else { + callback(new Error(`ENOENT: no such file or directory, scandir '${path}'`)) + } + }) + + // Stub fs.realpath + realpathStub = sinon.stub(fs, 'realpath') + realpathStub.callsFake((path, callback) => { + // Return normalized path + callback(null, fileUtils.filePathToPOSIX(path).replace(/\/$/, '')) + }) + + // Stub fs.stat + statStub = sinon.stub(fs, 'stat') + statStub.callsFake((path, callback) => { + const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '') + const stats = mockStats.get(normalizedPath) + if (stats) { + callback(null, stats) + } else { + callback(new Error(`ENOENT: no such file or directory, stat '${normalizedPath}'`)) + } + }) + + // Stub Logger + sinon.stub(Logger, 'debug') + }) + + afterEach(() => { + sinon.restore() + }) + + it('should return filtered file list', async () => { + const files = await fileUtils.recurseFiles('/test') + expect(files).to.be.an('array') + expect(files).to.have.lengthOf(2) + + expect(files[0]).to.deep.equal({ + name: 'file1.mp3', + path: 'file1.mp3', + reldirpath: '', + fullpath: '/test/file1.mp3', + extension: '.mp3', + deep: 0 + }) + + expect(files[1]).to.deep.equal({ + name: 'file2.m4b', + path: 'subfolder/file2.m4b', + reldirpath: 'subfolder', + fullpath: '/test/subfolder/file2.m4b', + extension: '.m4b', + deep: 1 + }) + }) + }) +}) From 7795bf25d04edc12df6bd4fd2910757825efd0da Mon Sep 17 00:00:00 2001 From: Milo Ivir Date: Thu, 20 Feb 2025 10:17:02 +0000 Subject: [PATCH 16/32] Translated using Weblate (Croatian) Currently translated at 100.0% (1089 of 1089 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/ --- client/strings/hr.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/strings/hr.json b/client/strings/hr.json index e1d372a7e..ae5b95a2d 100644 --- a/client/strings/hr.json +++ b/client/strings/hr.json @@ -16,7 +16,7 @@ "ButtonCancel": "Odustani", "ButtonCancelEncode": "Otkaži kodiranje", "ButtonChangeRootPassword": "Promijeni zaporku root korisnika", - "ButtonCheckAndDownloadNewEpisodes": "Provjeri i preuzmi nove epizode", + "ButtonCheckAndDownloadNewEpisodes": "Provjeri i preuzmi nove nastavke", "ButtonChooseAFolder": "Odaberi mapu", "ButtonChooseFiles": "Odaberi datoteke", "ButtonClearFilter": "Poništi filter", @@ -355,7 +355,7 @@ "LabelFileModifiedDate": "Izmijenjeno {0}", "LabelFilename": "Naziv datoteke", "LabelFilterByUser": "Filtriraj po korisniku", - "LabelFindEpisodes": "Pronađi epizode", + "LabelFindEpisodes": "Pronađi nastavke", "LabelFinished": "Dovršeno", "LabelFolder": "Mapa", "LabelFolders": "Mape", @@ -400,8 +400,8 @@ "LabelLanguages": "Jezici", "LabelLastBookAdded": "Zadnja dodana knjiga", "LabelLastBookUpdated": "Zadnja ažurirana knjiga", - "LabelLastSeen": "Zadnji puta viđen", - "LabelLastTime": "Zadnje vrijeme", + "LabelLastSeen": "Zadnje gledano", + "LabelLastTime": "Vrijeme zadnjeg slušanja", "LabelLastUpdate": "Zadnje ažuriranje", "LabelLayout": "Prikaz", "LabelLayoutSinglePage": "Jedna stranica", @@ -418,7 +418,7 @@ "LabelLogLevelDebug": "Debug", "LabelLogLevelInfo": "Info", "LabelLogLevelWarn": "Warn", - "LabelLookForNewEpisodesAfterDate": "Traži nove epizode nakon ovog datuma", + "LabelLookForNewEpisodesAfterDate": "Traži nove nastavke nakon ovog datuma", "LabelLowestPriority": "Najniži prioritet", "LabelMatchExistingUsersBy": "Prepoznaj postojeće korisnike pomoću", "LabelMatchExistingUsersByDescription": "Rabi se za povezivanje postojećih korisnika. Nakon što se spoje, korisnike se prepoznaje temeljem jedinstvene oznake vašeg pružatelja SSO usluga", @@ -447,7 +447,7 @@ "LabelNew": "Novo", "LabelNewPassword": "Nova zaporka", "LabelNewestAuthors": "Najnoviji autori", - "LabelNewestEpisodes": "Najnovije epizode", + "LabelNewestEpisodes": "Najnoviji nastavci", "LabelNextBackupDate": "Sljedeća izrada sigurnosne kopije", "LabelNextScheduledRun": "Sljedeće zakazano izvođenje", "LabelNoCustomMetadataProviders": "Nema prilagođenih pružatelja meta-podataka", From b109b2edee5d016d94d8814edde79b7f2fd458ca Mon Sep 17 00:00:00 2001 From: Nicholas W Date: Thu, 20 Feb 2025 14:33:10 +0100 Subject: [PATCH 17/32] Added translation using Weblate (Romanian) --- client/strings/ro.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 client/strings/ro.json diff --git a/client/strings/ro.json b/client/strings/ro.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/client/strings/ro.json @@ -0,0 +1 @@ +{} From f87a0bfc2f85aa76d7829cded0823395570dbeac Mon Sep 17 00:00:00 2001 From: Charlie Date: Thu, 20 Feb 2025 20:26:38 +0000 Subject: [PATCH 18/32] Translated using Weblate (French) Currently translated at 99.6% (1085 of 1089 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/ --- client/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/strings/fr.json b/client/strings/fr.json index dbefb5550..9956fbe3b 100644 --- a/client/strings/fr.json +++ b/client/strings/fr.json @@ -707,7 +707,7 @@ "MessageBackupsLocationEditNote": "Remarque : Mettre à jour l'emplacement de sauvegarde ne déplacera pas ou ne modifiera pas les sauvegardes existantes", "MessageBackupsLocationNoEditNote": "Remarque : l’emplacement de sauvegarde est défini via une variable d’environnement et ne peut pas être modifié ici.", "MessageBackupsLocationPathEmpty": "L'emplacement de secours ne peut pas être vide", - "MessageBatchEditPopulateMapDetailsAllHelp": "Remplir les champs disponibles avec les données de tous les éléments. les champs avec des valeurs multiples seront fusionnés", + "MessageBatchEditPopulateMapDetailsAllHelp": "Remplir les champs disponibles avec les données de tous les éléments. Les champs avec des valeurs multiples seront fusionnés.", "MessageBatchQuickMatchDescription": "La recherche par correspondance rapide tentera d’ajouter les couvertures et métadonnées manquantes pour les éléments sélectionnés. Activez les options ci-dessous pour permettre la Recherche par correspondance d’écraser les couvertures et/ou métadonnées existantes.", "MessageBookshelfNoCollections": "Vous n’avez pas encore de collections", "MessageBookshelfNoRSSFeeds": "Aucun flux RSS n’est ouvert", From 2b36caf096d4283b8136cb865bc66fb00b56d8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20R=C4=85czka-Dudek?= Date: Thu, 20 Feb 2025 22:29:16 +0000 Subject: [PATCH 19/32] Translated using Weblate (Polish) Currently translated at 74.5% (812 of 1089 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/ --- client/strings/pl.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/strings/pl.json b/client/strings/pl.json index 9a8157816..1cae559a0 100644 --- a/client/strings/pl.json +++ b/client/strings/pl.json @@ -10,6 +10,8 @@ "ButtonApplyChapters": "Zatwierdź rozdziały", "ButtonAuthors": "Autorzy", "ButtonBack": "Wstecz", + "ButtonBatchEditPopulateFromExisting": "Powiel z poprzednich", + "ButtonBatchEditPopulateMapDetails": "Powiel szczegóły mapy", "ButtonBrowseForFolder": "Wyszukaj folder", "ButtonCancel": "Anuluj", "ButtonCancelEncode": "Anuluj enkodowanie", @@ -31,6 +33,7 @@ "ButtonEditPodcast": "Edytuj podcast", "ButtonEnable": "Włącz", "ButtonFireAndFail": "Fail start", + "ButtonFireOnTest": "Uruchom po zdarzeniu testowym", "ButtonForceReScan": "Wymuś ponowne skanowanie", "ButtonFullPath": "Pełna ścieżka", "ButtonHide": "Ukryj", @@ -87,6 +90,8 @@ "ButtonSaveTracklist": "Zapisz listę odtwarzania", "ButtonScan": "Zeskanuj", "ButtonScanLibrary": "Skanuj bibliotekę", + "ButtonScrollLeft": "Przewiń w lewo", + "ButtonScrollRight": "Przewiń w prawo", "ButtonSearch": "Szukaj", "ButtonSelectFolderPath": "Wybierz ścieżkę folderu", "ButtonSeries": "Seria", @@ -155,13 +160,14 @@ "HeaderMapDetails": "Szczegóły mapowania", "HeaderMatch": "Dopasuj", "HeaderMetadataOrderOfPrecedence": "Kolejność metadanych", - "HeaderMetadataToEmbed": "Osadź metadane", + "HeaderMetadataToEmbed": "Metadane do osadzenia", "HeaderNewAccount": "Nowe konto", "HeaderNewLibrary": "Nowa biblioteka", "HeaderNotificationCreate": "Utwórz powiadomienie", "HeaderNotificationUpdate": "Zaktualizuj powiadomienie", "HeaderNotifications": "Powiadomienia", "HeaderOpenIDConnectAuthentication": "Uwierzytelnianie OpenID Connect", + "HeaderOpenListeningSessions": "Otwarte sesje słuchania", "HeaderOpenRSSFeed": "Utwórz kanał RSS", "HeaderOtherFiles": "Inne pliki", "HeaderPasswordAuthentication": "Uwierzytelnianie hasłem", @@ -188,6 +194,7 @@ "HeaderSettingsExperimental": "Funkcje eksperymentalne", "HeaderSettingsGeneral": "Ogólne", "HeaderSettingsScanner": "Skanowanie", + "HeaderSettingsWebClient": "Klient webowy", "HeaderSleepTimer": "Wyłącznik czasowy", "HeaderStatsLargestItems": "Największe pozycje", "HeaderStatsLongestItems": "Najdłuższe pozycje (godziny)", @@ -438,7 +445,7 @@ "LabelNotificationsMaxQueueSize": "Maksymalny rozmiar kolejki dla powiadomień", "LabelNotificationsMaxQueueSizeHelp": "Zdarzenia są ograniczone do 1 na sekundę. Zdarzenia będą ignorowane jeśli kolejka ma maksymalny rozmiar. Zapobiega to spamowaniu powiadomieniami.", "LabelNumberOfBooks": "Liczba książek", - "LabelNumberOfEpisodes": "# odcinków", + "LabelNumberOfEpisodes": "# Odcinków", "LabelOpenRSSFeed": "Otwórz kanał RSS", "LabelOverwrite": "Nadpisz", "LabelPassword": "Hasło", From b7591abd06b078a0fcc2304e2f9a9c06013b252a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=BF=D0=B8=D0=BD=D1=96=D1=87?= Date: Fri, 21 Feb 2025 10:00:27 +0000 Subject: [PATCH 20/32] Translated using Weblate (Ukrainian) Currently translated at 100.0% (1091 of 1091 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/ --- client/strings/uk.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/strings/uk.json b/client/strings/uk.json index ba1a2d214..1dc82fd5c 100644 --- a/client/strings/uk.json +++ b/client/strings/uk.json @@ -219,6 +219,7 @@ "LabelAccountTypeAdmin": "Адміністратор", "LabelAccountTypeGuest": "Гість", "LabelAccountTypeUser": "Користувач", + "LabelActivities": "Діяльність", "LabelActivity": "Активність", "LabelAddToCollection": "Додати у добірку", "LabelAddToCollectionBatch": "Додати книги до добірки: {0}", @@ -283,6 +284,7 @@ "LabelContinueSeries": "Продовжити серії", "LabelCover": "Обкладинка", "LabelCoverImageURL": "URL-адреса обкладинки", + "LabelCoverProvider": "Постачальник покриття", "LabelCreatedAt": "Дата створення", "LabelCronExpression": "Команда cron", "LabelCurrent": "Поточне", From 6351fd8d7b4b7bdd4fac05cb681ce69eb728f750 Mon Sep 17 00:00:00 2001 From: Troja Date: Fri, 21 Feb 2025 14:03:55 +0000 Subject: [PATCH 21/32] Translated using Weblate (Belarusian) Currently translated at 25.6% (280 of 1091 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/ --- client/strings/be.json | 69 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/client/strings/be.json b/client/strings/be.json index 5533123b5..bc9a45449 100644 --- a/client/strings/be.json +++ b/client/strings/be.json @@ -43,7 +43,7 @@ "ButtonLatest": "Апошняе", "ButtonLibrary": "Бібліятэка", "ButtonLogout": "Выйсці", - "ButtonLookup": "", + "ButtonLookup": "Пошук", "ButtonManageTracks": "Кіраванне дарожкамі", "ButtonMapChapterTitles": "Супаставіць назвы раздзелаў", "ButtonMatchAllAuthors": "Супадзенне ўсіх аўтараў", @@ -159,6 +159,13 @@ "HeaderNotificationUpdate": "Абнавіць апавяшчэнне", "HeaderNotifications": "Апавяшчэнні", "HeaderOpenListeningSessions": "Адкрыць сеансы праслухоўвання", + "HeaderOpenRSSFeed": "Адкрыць RSS-стужку", + "HeaderRSSFeedGeneral": "Падрабязнасці RSS", + "HeaderRSSFeedIsOpen": "RSS-стужка адкрыта", + "HeaderRSSFeeds": "RSS-стужкі", + "HeaderRemoveEpisode": "Выдаліць эпізод", + "HeaderRemoveEpisodes": "Выдаліць {0} эпізодаў", + "HeaderSavedMediaProgress": "Захаваны прагрэс медыя", "HeaderScheduleEpisodeDownloads": "Расклад аўтаматычных спамповак эпізодаў", "HeaderSettings": "Налады", "HeaderSettingsDisplay": "Дысплей", @@ -188,28 +195,88 @@ "LabelEncodingChaptersNotEmbedded": "Раздзелы не ўбудаваны ў шматдарожкавыя аўдыякнігі.", "LabelEncodingFinishedM4B": "Гатовы файл M4B будзе змешчаны ў вашу тэчку з аўдыякнігамі па адрасе:", "LabelEncodingInfoEmbedded": "Метаданыя будуць убудаваны ў аўдыядарожкі ўнутры вашай тэчкі з аўдыякнігамі.", + "LabelEpisode": "Эпізод", + "LabelEpisodeNotLinkedToRssFeed": "Эпізод не звязаны з RSS-стужкай", + "LabelEpisodeUrlFromRssFeed": "URL эпізоду з RSS-стужкі", + "LabelFeedURL": "URL стужкі", "LabelMaxEpisodesToDownload": "Максімальная колькасць эпізодаў для спампоўкі. Выкарыстоўвайце 0 для неабмежаванай колькасці.", "LabelMaxEpisodesToDownloadPerCheck": "Максімальная колькасць новых эпізодаў для спампоўкі за праверку", "LabelMaxEpisodesToKeepHelp": "Значэнне 0 не ўстанаўлівае максімальнага абмежавання. Пасля аўтаматычнай спампоўкі новага эпізоду будзе выдалены самы стары эпізод, калі ў вас больш за X эпізодаў. Пры кожнай новай спампоўцы будзе выдаляцца толькі 1 эпізод.", + "LabelMediaPlayer": "Медыяплэер", + "LabelMediaType": "Тып медыя", + "LabelOpenRSSFeed": "Адкрыць RSS-стужку", "LabelPermissionsDownload": "Можна спампаваць", + "LabelPreventIndexing": "Прадухіліць індэксацыю вашай стужкі каталогамі падкастаў iTunes і Google", + "LabelRSSFeedCustomOwnerEmail": "Карыстальніцкая электронная пошта ўладальніка", + "LabelRSSFeedCustomOwnerName": "Карыстальніцкае імя ўладальніка", + "LabelRSSFeedOpen": "RSS-стужка адкрытая", + "LabelRSSFeedPreventIndexing": "Прадухіліць індэксацыю", + "LabelRSSFeedURL": "URL RSS-стужкі", "LabelReAddSeriesToContinueListening": "Дадаць серыю зноў у Працягваць слухаць", + "LabelSettingsExperimentalFeaturesHelp": "Функцыі ў распрацоўцы, для якіх вашы водгукі і дапамога ў тэставанні будуць карыснымі. Націсніце, каб адкрыць абмеркаванне на GitHub.", + "LabelSettingsLibraryMarkAsFinishedWhen": "Пазначыць элемент медыя як скончаны, калі", "LabelShareDownloadableHelp": "Дазваляе карыстальнікам, якія маюць спасылку на доступ, спампаваць ZIP-файл элемента бібліятэкі.", "LabelStatsAudioTracks": "Аўдыядарожкі", "LabelTracks": "Дарожкі", + "MessageBookshelfNoRSSFeeds": "Няма адкрытых RSS-стужак", + "MessageConfirmCloseFeed": "Вы ўпэўнены, што жадаеце закрыць гэтую стужку?", "MessageConfirmRemoveListeningSessions": "Вы ўпэўнены, што жадаеце выдаліць {0} сеансаў праслухоўвання?", "MessageDownloadingEpisode": "Спампоўка эпізоду", "MessageEpisodesQueuedForDownload": "{0} эпізод(аў) у чарзе для спампоўкі", + "MessageFeedURLWillBe": "URL стужкі будзе {0}", "MessageNoDownloadsInProgress": "Зараз няма актыўных спамповак", "MessageNoDownloadsQueued": "Няма спамповак у чарзе", "MessageNoListeningSessions": "Няма сеансаў праслухоўвання", + "MessageNoMediaProgress": "Няма прагрэсу медыя", + "MessageNoPodcastFeed": "Няправільны падкаст: Няма стужкі", + "MessageOpmlPreviewNote": "Заўвага: гэта папярэдні прагляд разабранага OPML-файла. Фактычная назва падкаста будзе ўзятая з RSS-стужкі.", + "MessagePodcastHasNoRSSFeedForMatching": "У падкаста няма URL RSS-стужкі для супадзення", + "MessagePodcastSearchField": "Увядзіце пошукавы запыт або URL RSS-стужкі", "MessageTaskDownloadingEpisodeDescription": "Спампоўка эпізоду \"{0}\"", + "MessageTaskOpmlImportDescription": "Стварэнне падкастаў з {0} RSS-стужак", + "MessageTaskOpmlImportFeed": "Імпарт стужкі з OPML", + "MessageTaskOpmlImportFeedDescription": "Імпартаванне RSS-стужкі \"{0}\"", + "MessageTaskOpmlImportFeedFailed": "Не ўдалося атрымаць стужку падкаста", + "MessageTaskOpmlImportFeedPodcastDescription": "Стварэнне падкаста \"{0}\"", + "MessageTaskOpmlImportFeedPodcastExists": "Падкаст ужо існуе па гэтым шляху", + "MessageTaskOpmlImportFeedPodcastFailed": "Не ўдалося стварыць падкаст", + "MessageTaskOpmlParseNoneFound": "У OPML-файле не знойдзена стужак", + "NoteRSSFeedPodcastAppsHttps": "Папярэджанне: большасць праграм для падкастаў патрабуюць, каб URL RSS-стужкі выкарыстоўваў HTTPS", + "NoteRSSFeedPodcastAppsPubDate": "Папярэджанне: адзін ці больш вашых эпізодаў не маюць даты публікацыі. Некаторыя праграмы для падкастаў патрабуюць гэтага.", + "NoteUploaderFoldersWithMediaFiles": "Тэчкі з медыяфайламі будуць апрацоўвацца як асобныя элементы бібліятэкі.", "NotificationOnEpisodeDownloadedDescription": "Выклікаецца, калі эпізод падкаста аўтаматычна спампоўваецца", "ToastAccountUpdateSuccess": "Уліковы запіс абноўлены", "ToastEpisodeDownloadQueueClearFailed": "Не ўдалося ачысціць чаргу", "ToastEpisodeDownloadQueueClearSuccess": "Чарга спампоўкі эпізодаў ачышчана", "ToastInvalidMaxEpisodesToDownload": "Няправільная максімальная колькасць эпізодаў для спампоўкі", + "ToastItemMarkedAsFinishedFailed": "Не ўдалося пазначыць як Скончана", + "ToastItemMarkedAsFinishedSuccess": "Элемент пазначаны як Завершаны", + "ToastItemMarkedAsNotFinishedFailed": "Не ўдалося пазначыць як Незавершанае", + "ToastItemMarkedAsNotFinishedSuccess": "Элемент пазначаны як Незавершаны", + "ToastItemUpdateSuccess": "Элемент абноўлены", + "ToastLibraryCreateFailed": "Не ўдалося стварыць бібліятэку", + "ToastLibraryCreateSuccess": "Бібліятэка \"{0}\" створана", + "ToastLibraryDeleteFailed": "Не ўдалося выдаліць бібліятэку", + "ToastLibraryDeleteSuccess": "Бібліятэка выдалена", + "ToastLibraryScanFailedToStart": "Не ўдалося запусціць сканаванне", + "ToastLibraryScanStarted": "Сканаванне бібліятэкі запушчана", + "ToastLibraryUpdateSuccess": "Бібліятэка \"{0}\" абноўлена", + "ToastMatchAllAuthorsFailed": "Не ўдалося знайсці адпаведнасць для ўсіх аўтараў", + "ToastMetadataFilesRemovedError": "Памылка пры выдаленні metadata.{0} файлаў", + "ToastMetadataFilesRemovedNoneFound": "У бібліятэцы не знойдзены metadata.{0} файлаў", + "ToastMetadataFilesRemovedNoneRemoved": "Не выдалена metadata.{0} файлаў", + "ToastMetadataFilesRemovedSuccess": "{0} metadata.{1} файлаў выдалена", + "ToastMustHaveAtLeastOnePath": "Павінен быць хаця б адзін шлях", + "ToastNameEmailRequired": "Імя і электронная пошта абавязковыя", + "ToastNameRequired": "Імя абавязковае", "ToastNewUserCreatedFailed": "Не ўдалося стварыць уліковы запіс: \"{0}\"", "ToastNewUserCreatedSuccess": "Новы ўліковы запіс створаны", + "ToastNoRSSFeed": "У падкаста няма RSS-стужкі", + "ToastPodcastGetFeedFailed": "Не ўдалося атрымаць стужку падкаста", + "ToastPodcastNoEpisodesInFeed": "У RSS-стужцы не знойдзена эпізодаў", + "ToastPodcastNoRssFeed": "У падкаста няма RSS-стужкі", + "ToastRSSFeedCloseFailed": "Не ўдалося закрыць RSS-стужку", + "ToastRSSFeedCloseSuccess": "RSS-стужка закрыта", "ToastUserPasswordMustChange": "Новы пароль не можа супадаць са старым", "ToastUserRootRequireName": "Неабходна ўвесці імя карыстальніка адміністратара" } From 0e9777feec442106bde232435d7877ab1c3ce6af Mon Sep 17 00:00:00 2001 From: Jan-Eric Myhrgren Date: Sat, 22 Feb 2025 08:20:56 +0000 Subject: [PATCH 22/32] Translated using Weblate (Swedish) Currently translated at 92.0% (1005 of 1092 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/ --- client/strings/sv.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/strings/sv.json b/client/strings/sv.json index 771ec0196..aa3ba2144 100644 --- a/client/strings/sv.json +++ b/client/strings/sv.json @@ -206,6 +206,7 @@ "LabelAccountTypeAdmin": "Administratör", "LabelAccountTypeGuest": "Gäst", "LabelAccountTypeUser": "Användare", + "LabelActivities": "Aktiviteter", "LabelActivity": "Aktivitet", "LabelAddToCollection": "Lägg till i en samling", "LabelAddToCollectionBatch": "Lägg till {0} böcker i samlingen", @@ -267,6 +268,7 @@ "LabelContinueSeries": "Fortsätt med serien", "LabelCover": "Omslag", "LabelCoverImageURL": "URL till omslagsbild", + "LabelCoverProvider": "Källa för omslag", "LabelCreatedAt": "Skapad", "LabelCronExpression": "Schemaläggning med hjälp av Cron (Cron Expression)", "LabelCurrent": "Nuvarande", @@ -370,6 +372,7 @@ "LabelIntervalEvery6Hours": "Var 6:e timme", "LabelIntervalEveryDay": "Varje dag", "LabelIntervalEveryHour": "Varje timme", + "LabelIntervalEveryMinute": "Varje minut", "LabelInvert": "Invertera", "LabelItem": "Objekt", "LabelJumpBackwardAmount": "Inställning för \"hopp bakåt\"", @@ -464,12 +467,13 @@ "LabelPodcasts": "Podcasts", "LabelPort": "Port", "LabelPrefixesToIgnore": "Prefix att ignorera (skiftlägesokänsligt)", - "LabelPreventIndexing": "Förhindra att ditt flöde indexeras av iTunes och Google-podcastsökmotorer", + "LabelPreventIndexing": "Förhindra att ditt flöde indexeras av sökmotorer från iTunes och Google", "LabelPrimaryEbook": "Primär e-bok", "LabelProgress": "Framsteg", "LabelProvider": "Källa", "LabelPubDate": "Publiceringsdatum", "LabelPublishYear": "Publiceringsår", + "LabelPublishedDate": "Publicerad {0}", "LabelPublishedDecade": "Årtionde för publicering", "LabelPublisher": "Utgivare", "LabelPublishers": "Utgivare", From 34f7ddfdd7f5caf27032507d5b83f9f8f5ce0e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=BF=D0=B8=D0=BD=D1=96=D1=87?= Date: Sat, 22 Feb 2025 11:02:19 +0000 Subject: [PATCH 23/32] Translated using Weblate (Ukrainian) Currently translated at 100.0% (1092 of 1092 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/ --- client/strings/uk.json | 1 + 1 file changed, 1 insertion(+) diff --git a/client/strings/uk.json b/client/strings/uk.json index 1dc82fd5c..9f9477dd5 100644 --- a/client/strings/uk.json +++ b/client/strings/uk.json @@ -393,6 +393,7 @@ "LabelIntervalEvery6Hours": "Кожні 6 годин", "LabelIntervalEveryDay": "Щодня", "LabelIntervalEveryHour": "Щогодини", + "LabelIntervalEveryMinute": "Кожну хвилину", "LabelInvert": "Інвертувати", "LabelItem": "Елемент", "LabelJumpBackwardAmount": "Час переходу назад", From 78aafe038d16279cd4376e6e5ab98281f999852a Mon Sep 17 00:00:00 2001 From: Troja Date: Sat, 22 Feb 2025 14:07:22 +0000 Subject: [PATCH 24/32] Translated using Weblate (Belarusian) Currently translated at 28.6% (313 of 1092 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/ --- client/strings/be.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/client/strings/be.json b/client/strings/be.json index bc9a45449..ed194e522 100644 --- a/client/strings/be.json +++ b/client/strings/be.json @@ -185,20 +185,48 @@ "LabelAudioBitrate": "Бітрэйт аўдыё (напрыклад, 128к)", "LabelAudioChannels": "Аўдыёканалы (1 або 2)", "LabelAudioCodec": "Аўдыёкодэк", + "LabelAuthor": "Аўтар", + "LabelAuthorFirstLast": "Аўтар (Імя Прозвішча)", + "LabelAuthorLastFirst": "Аўтар (Прозвішча, Імя)", + "LabelAuthors": "Аўтары", "LabelAutoDownloadEpisodes": "Аўтаматычнае спампаванне эпізодаў", "LabelBackupAudioFiles": "Рэзервовае капіраванне аўдыёфайлаў", + "LabelChapters": "Раздзелы", + "LabelCollapseSeries": "Згарнуць серыі", "LabelContinueListening": "Працягваць слухаць", + "LabelContinueSeries": "Працягнуць серыі", + "LabelDiscover": "Знайсці", "LabelDownload": "Спампаваць", "LabelDownloadNEpisodes": "Спампована {0} эпізодаў", "LabelDownloadable": "Спампоўваецца", + "LabelDuration": "Працягласць", + "LabelEbook": "Электронная кніга", + "LabelEbooks": "Электронныя кнігі", "LabelEncodingBackupLocation": "Рэзервовая копія вашых арыгінальных аўдыёфайлаў будзе захавана ў:", "LabelEncodingChaptersNotEmbedded": "Раздзелы не ўбудаваны ў шматдарожкавыя аўдыякнігі.", "LabelEncodingFinishedM4B": "Гатовы файл M4B будзе змешчаны ў вашу тэчку з аўдыякнігамі па адрасе:", "LabelEncodingInfoEmbedded": "Метаданыя будуць убудаваны ў аўдыядарожкі ўнутры вашай тэчкі з аўдыякнігамі.", + "LabelEnd": "Канец", + "LabelEndOfChapter": "Канец раздзела", "LabelEpisode": "Эпізод", "LabelEpisodeNotLinkedToRssFeed": "Эпізод не звязаны з RSS-стужкай", "LabelEpisodeUrlFromRssFeed": "URL эпізоду з RSS-стужкі", "LabelFeedURL": "URL стужкі", + "LabelFile": "Файл", + "LabelFileBirthtime": "Час стварэння файла", + "LabelFileModified": "Час змянення файла", + "LabelFilename": "Імя файла", + "LabelFolder": "Тэчка", + "LabelFontBoldness": "Таўшчыня шрыфта", + "LabelGenre": "Жанр", + "LabelGenres": "Жанры", + "LabelHasEbook": "Мае электронную кнігу", + "LabelHasSupplementaryEbook": "Мае дадатковую электронную кнігу", + "LabelInProgress": "У працэсе", + "LabelLanguage": "Мова", + "LabelLayoutSinglePage": "Аднабаковы", + "LabelLineSpacing": "Міжрадковы інтэрвал", + "LabelListenAgain": "Паслухаць зноў", "LabelMaxEpisodesToDownload": "Максімальная колькасць эпізодаў для спампоўкі. Выкарыстоўвайце 0 для неабмежаванай колькасці.", "LabelMaxEpisodesToDownloadPerCheck": "Максімальная колькасць новых эпізодаў для спампоўкі за праверку", "LabelMaxEpisodesToKeepHelp": "Значэнне 0 не ўстанаўлівае максімальнага абмежавання. Пасля аўтаматычнай спампоўкі новага эпізоду будзе выдалены самы стары эпізод, калі ў вас больш за X эпізодаў. Пры кожнай новай спампоўцы будзе выдаляцца толькі 1 эпізод.", @@ -213,6 +241,10 @@ "LabelRSSFeedPreventIndexing": "Прадухіліць індэксацыю", "LabelRSSFeedURL": "URL RSS-стужкі", "LabelReAddSeriesToContinueListening": "Дадаць серыю зноў у Працягваць слухаць", + "LabelRecentSeries": "Апошнія серыі", + "LabelSeries": "Серыі", + "LabelSetEbookAsPrimary": "Зрабіць асноўным", + "LabelSetEbookAsSupplementary": "Зрабіць дадатковым", "LabelSettingsExperimentalFeaturesHelp": "Функцыі ў распрацоўцы, для якіх вашы водгукі і дапамога ў тэставанні будуць карыснымі. Націсніце, каб адкрыць абмеркаванне на GitHub.", "LabelSettingsLibraryMarkAsFinishedWhen": "Пазначыць элемент медыя як скончаны, калі", "LabelShareDownloadableHelp": "Дазваляе карыстальнікам, якія маюць спасылку на доступ, спампаваць ZIP-файл элемента бібліятэкі.", @@ -224,6 +256,7 @@ "MessageDownloadingEpisode": "Спампоўка эпізоду", "MessageEpisodesQueuedForDownload": "{0} эпізод(аў) у чарзе для спампоўкі", "MessageFeedURLWillBe": "URL стужкі будзе {0}", + "MessageNoChapters": "Няма раздзелаў", "MessageNoDownloadsInProgress": "Зараз няма актыўных спамповак", "MessageNoDownloadsQueued": "Няма спамповак у чарзе", "MessageNoListeningSessions": "Няма сеансаў праслухоўвання", From b2e97d70df0f868b8da5a5ad27b7821cd294fac5 Mon Sep 17 00:00:00 2001 From: mickeynos Date: Sat, 22 Feb 2025 16:25:22 +0000 Subject: [PATCH 25/32] Translated using Weblate (Czech) Currently translated at 99.5% (1087 of 1092 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/ --- client/strings/cs.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/strings/cs.json b/client/strings/cs.json index 5fbcfb645..ecf42a752 100644 --- a/client/strings/cs.json +++ b/client/strings/cs.json @@ -217,6 +217,7 @@ "LabelAccountTypeAdmin": "Správce", "LabelAccountTypeGuest": "Host", "LabelAccountTypeUser": "Uživatel", + "LabelActivities": "Aktivity", "LabelActivity": "Aktivita", "LabelAddToCollection": "Přidat do kolekce", "LabelAddToCollectionBatch": "Přidat {0} knihy do kolekce", @@ -389,6 +390,7 @@ "LabelIntervalEvery6Hours": "Každých 6 hodin", "LabelIntervalEveryDay": "Každý den", "LabelIntervalEveryHour": "Každou hodinu", + "LabelIntervalEveryMinute": "Každou minutu", "LabelInvert": "Invertovat", "LabelItem": "Položka", "LabelJumpBackwardAmount": "Přeskočit zpět o", @@ -484,6 +486,7 @@ "LabelPersonalYearReview": "Váš přehled roku ({0})", "LabelPhotoPathURL": "Cesta k fotografii/URL", "LabelPlayMethod": "Metoda přehrávání", + "LabelPlaybackRateIncrementDecrement": "Velikost kroku pro změnu rychlosti přehrávání", "LabelPlayerChapterNumberMarker": "{0} z {1}", "LabelPlaylists": "Seznamy skladeb", "LabelPodcast": "Podcast", @@ -706,6 +709,7 @@ "MessageBackupsLocationPathEmpty": "Umístění záloh nemůže být prázdné", "MessageBatchQuickMatchDescription": "Rychlá párování se pokusí přidat chybějící obálky a metadata pro vybrané položky. Povolením níže uvedených možností umožníte funkci Rychlé párování přepsat stávající obálky a/nebo metadata.", "MessageBookshelfNoCollections": "Ještě jste nevytvořili žádnou sbírku", + "MessageBookshelfNoCollectionsHelp": "Kolekce jsou veřejné. Mohou je zobrazit všichni uživatelé s přístupem do knihovny.", "MessageBookshelfNoRSSFeeds": "Nejsou otevřeny žádné RSS kanály", "MessageBookshelfNoResultsForFilter": "Filtr \"{0}: {1}\"", "MessageBookshelfNoResultsForQuery": "Žádné výsledky pro dotaz", @@ -816,6 +820,7 @@ "MessageNoTasksRunning": "Nejsou spuštěny žádné úlohy", "MessageNoUpdatesWereNecessary": "Nebyly nutné žádné aktualizace", "MessageNoUserPlaylists": "Nemáte žádné seznamy skladeb", + "MessageNoUserPlaylistsHelp": "Seznamy skladeb jsou soukromé. Zobrazit je může pouze uživatel, který je vytvořil.", "MessageNotYetImplemented": "Ještě není implementováno", "MessageOpmlPreviewNote": "Poznámka: Toto je náhled načteného OMPL souboru. Aktuální název podcastu bude načten z RSS feedu.", "MessageOr": "nebo", From 38483c926961a010025f0ef1916ad322d78cfe04 Mon Sep 17 00:00:00 2001 From: burghy86 Date: Sat, 22 Feb 2025 21:44:43 +0000 Subject: [PATCH 26/32] Translated using Weblate (Italian) Currently translated at 100.0% (1092 of 1092 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/ --- client/strings/it.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/strings/it.json b/client/strings/it.json index ba106ee34..8de084703 100644 --- a/client/strings/it.json +++ b/client/strings/it.json @@ -219,6 +219,7 @@ "LabelAccountTypeAdmin": "Amministratore", "LabelAccountTypeGuest": "Ospite", "LabelAccountTypeUser": "Utente", + "LabelActivities": "Attività", "LabelActivity": "Attività", "LabelAddToCollection": "Aggiungi alla Raccolta", "LabelAddToCollectionBatch": "Aggiungi {0} Libri alla Raccolta", @@ -283,6 +284,7 @@ "LabelContinueSeries": "Continua serie", "LabelCover": "Copertina", "LabelCoverImageURL": "Indirizzo della cover URL", + "LabelCoverProvider": "Cover Provider", "LabelCreatedAt": "Creato A", "LabelCronExpression": "Espressione Cron", "LabelCurrent": "Attuale", @@ -391,6 +393,7 @@ "LabelIntervalEvery6Hours": "Ogni 6 ore", "LabelIntervalEveryDay": "Ogni Giorno", "LabelIntervalEveryHour": "Ogni ora", + "LabelIntervalEveryMinute": "Ogni minuto", "LabelInvert": "Inverti", "LabelItem": "Oggetti", "LabelJumpBackwardAmount": "secondi di avvolgimento", From 84c2931434d5fb28c4899def9bdeb34d42e76e3d Mon Sep 17 00:00:00 2001 From: Vito0912 <86927734+Vito0912@users.noreply.github.com> Date: Sun, 23 Feb 2025 13:01:59 +0000 Subject: [PATCH 27/32] Translated using Weblate (German) Currently translated at 99.9% (1092 of 1093 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/ --- client/strings/de.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/strings/de.json b/client/strings/de.json index 2b07ee031..0803e0620 100644 --- a/client/strings/de.json +++ b/client/strings/de.json @@ -219,7 +219,8 @@ "LabelAccountTypeAdmin": "Admin", "LabelAccountTypeGuest": "Gast", "LabelAccountTypeUser": "Benutzer", - "LabelActivity": "Aktivitäten", + "LabelActivities": "Aktivitäten", + "LabelActivity": "Aktivität", "LabelAddToCollection": "Zur Sammlung hinzufügen", "LabelAddToCollectionBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Sammlung hinzu", "LabelAddToPlaylist": "Zur Wiedergabeliste hinzufügen", @@ -283,6 +284,7 @@ "LabelContinueSeries": "Serien fortsetzen", "LabelCover": "Titelbild", "LabelCoverImageURL": "URL des Titelbildes", + "LabelCoverProvider": "Titelbildanbieter", "LabelCreatedAt": "Erstellt am", "LabelCronExpression": "Cron-Ausdruck", "LabelCurrent": "Aktuell", @@ -391,6 +393,7 @@ "LabelIntervalEvery6Hours": "Alle 6 Stunden", "LabelIntervalEveryDay": "Jeden Tag", "LabelIntervalEveryHour": "Jede Stunde", + "LabelIntervalEveryMinute": "Jede Minute", "LabelInvert": "Umkehren", "LabelItem": "Medium", "LabelJumpBackwardAmount": "Zurückspringen Zeit", @@ -844,6 +847,7 @@ "MessageRestoreBackupConfirm": "Bist du dir sicher, dass du die Sicherung wiederherstellen willst, welche am", "MessageRestoreBackupWarning": "Bei der Wiederherstellung einer Sicherung wird die gesamte Datenbank unter /config und die Titelbilder in /metadata/items und /metadata/authors überschrieben.

Bei der Sicherung werden keine Dateien in deinen Bibliotheksordnern verändert. Wenn du die Servereinstellungen aktiviert hast, um Cover und Metadaten in deinen Bibliotheksordnern zu speichern, werden diese nicht gesichert oder überschrieben.

Alle Clients, die Ihren Server nutzen, werden automatisch aktualisiert.", "MessageScheduleLibraryScanNote": "Für die meisten Nutzer wird empfohlen, diese Funktion deaktiviert zu lassen und stattdessen die Ordnerüberwachung aktiviert zu lassen. Die Ordnerüberwachung erkennt automatisch Änderungen in deinen Bibliotheksordnern. Da die Ordnerüberwachung jedoch nicht mit jedem Dateisystem (z.B. NFS) funktioniert, können alternativ hier geplante Bibliotheks-Scans aktiviert werden.", + "MessageScheduleRunEveryWeekdayAtTime": "Immer {0} um {1} ausführen", "MessageSearchResultsFor": "Suchergebnisse für", "MessageSelected": "{0} ausgewählt", "MessageServerCouldNotBeReached": "Server kann nicht erreicht werden", From 271dbc476425a7072055b90c093f76abc889c0a7 Mon Sep 17 00:00:00 2001 From: biuklija Date: Sun, 23 Feb 2025 07:29:06 +0000 Subject: [PATCH 28/32] Translated using Weblate (Croatian) Currently translated at 100.0% (1093 of 1093 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/ --- client/strings/hr.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/strings/hr.json b/client/strings/hr.json index ae5b95a2d..ba99462f4 100644 --- a/client/strings/hr.json +++ b/client/strings/hr.json @@ -219,6 +219,7 @@ "LabelAccountTypeAdmin": "Administrator", "LabelAccountTypeGuest": "Gost", "LabelAccountTypeUser": "Korisnik", + "LabelActivities": "Aktivnosti", "LabelActivity": "Aktivnost", "LabelAddToCollection": "Dodaj u zbirku", "LabelAddToCollectionBatch": "Dodaj {0} knjiga u zbirku", @@ -283,6 +284,7 @@ "LabelContinueSeries": "Nastavi serijal", "LabelCover": "Naslovnica", "LabelCoverImageURL": "URL naslovnice", + "LabelCoverProvider": "Pružatelj naslovnica", "LabelCreatedAt": "Izrađen", "LabelCronExpression": "Cron izraz", "LabelCurrent": "Trenutan", @@ -391,6 +393,7 @@ "LabelIntervalEvery6Hours": "Svakih 6 sati", "LabelIntervalEveryDay": "Svaki dan", "LabelIntervalEveryHour": "Svaki sat", + "LabelIntervalEveryMinute": "Svaku minutu", "LabelInvert": "Obrni", "LabelItem": "Stavka", "LabelJumpBackwardAmount": "Dužina skoka unatrag", @@ -845,6 +848,7 @@ "MessageRestoreBackupConfirm": "Sigurno želite vratiti sigurnosnu kopiju izrađenu", "MessageRestoreBackupWarning": "Vraćanjem sigurnosne kopije prepisat ćete cijelu bazu podataka koja se nalazi u /config i slike naslovnice u /metadata/items i /metadata/authors.

Sigurnosne kopije ne mijenjaju datoteke koje se nalaze u mapama vaših knjižnica. Ako ste u postavkama poslužitelja uključili mogućnost spremanja naslovnica i meta-podataka u mape knjižnice, te se datoteke neće niti sigurnosno pohraniti niti prepisati.

Svi klijenti koji se spajaju na vaš poslužitelj automatski će se osvježiti.", "MessageScheduleLibraryScanNote": "Za većinu korisnika se preporučuje ostaviti ovu funkciju deaktiviranom i ostaviti postavku promatrača mape aktiviranom. Promatrač mapa će automatski otkriti promjene u mapama vaše knjižnice. Promatrač mapa ne radi na svakom datotečnom sustavu (kao što je NFS) pa se umjesto njega mogu koristiti planirana pretraživanja knjižnice.", + "MessageScheduleRunEveryWeekdayAtTime": "Pokreni svaki {0} u {1}", "MessageSearchResultsFor": "Rezultati pretrage za", "MessageSelected": "{0} odabrano", "MessageServerCouldNotBeReached": "Nije moguće pristupiti poslužitelju", From e01ac489fb4329989396941058fde7dd9c6058c8 Mon Sep 17 00:00:00 2001 From: Jan-Eric Myhrgren Date: Sun, 23 Feb 2025 08:42:47 +0000 Subject: [PATCH 29/32] Translated using Weblate (Swedish) Currently translated at 92.6% (1013 of 1093 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/ --- client/strings/sv.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/strings/sv.json b/client/strings/sv.json index aa3ba2144..17177c9cc 100644 --- a/client/strings/sv.json +++ b/client/strings/sv.json @@ -798,11 +798,13 @@ "MessageRestoreBackupConfirm": "Är du säker på att du vill läsa in säkerhetskopian som skapades den", "MessageRestoreBackupWarning": "Att återställa en säkerhetskopia kommer att skriva över hela databasen som finns i /config och omslagsbilder i /metadata/items & /metadata/authors.

Säkerhetskopior ändrar inte några filer i dina biblioteksmappar. Om du har aktiverat serverinställningar för att lagra omslagskonst och metadata i dina biblioteksmappar säkerhetskopieras eller skrivs de inte över.

Alla klienter som använder din server kommer att uppdateras automatiskt.", "MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas att denna funktion ej aktiveras. Istället bör funktionen 'Watcher' vara aktiverad. Watcher kommer då automatiskt identifiera förändringar i biblioteket. För vissa filsystem (som t.ex. NFS) fungerar inte Watcher. Då kan schemalagda skanningar av biblioteken användas istället.", + "MessageScheduleRunEveryWeekdayAtTime": "Startar varje {0} klockan {1}", "MessageSearchResultsFor": "Sökresultat för", "MessageSelected": "{0} valda", "MessageServerCouldNotBeReached": "Servern kunde inte nås", "MessageSetChaptersFromTracksDescription": "Ställ in kapitel med varje ljudfil som ett kapitel och kapitelrubrik som ljudfilens namn", "MessageStartPlaybackAtTime": "Starta uppspelning av \"{0}\" vid tidpunkt {1}?", + "MessageTaskAudioFileNotWritable": "Det går inte att skriva till ljudfilen \"{0}\"", "MessageTaskCanceledByUser": "Uppgiften avslutades av användaren", "MessageTaskDownloadingEpisodeDescription": "Laddar ner avsnitt \"{0}\"", "MessageTaskEmbeddingMetadata": "Infogar metadata", @@ -816,7 +818,11 @@ "MessageTaskFailedToMoveM4bFile": "Misslyckades med att flytta M4B-filen", "MessageTaskFailedToWriteMetadataFile": "Misslyckades med att skapa filen med metadata", "MessageTaskMatchingBooksInLibrary": "Matchar böcker i biblioteket \"{0}\"", + "MessageTaskNoFilesToScan": "Inga filer finns tillgängliga för skanning", + "MessageTaskOpmlImportDescription": "Skapar podcasts från {0} RSS-flöden", + "MessageTaskOpmlImportFeedDescription": "Importerar RSS-flödet \"{0}\"", "MessageTaskOpmlImportFeedPodcastDescription": "Skapar podcast \"{0}\"", + "MessageTaskOpmlImportFeedPodcastExists": "En podcast finns redan med den adressen", "MessageTaskOpmlImportFeedPodcastFailed": "Misslyckades med att skapa podcast", "MessageTaskOpmlImportFinished": "Adderade {0} podcasts", "MessageTaskOpmlParseFailed": "Misslyckades att tolka OPML-filen", @@ -827,6 +833,7 @@ "MessageTaskScanItemsUpdated": "{0} uppdaterades", "MessageTaskScanNoChangesNeeded": "Inget adderades eller uppdaterades", "MessageTaskScanningLibrary": "Biblioteket \"{0}\" har skannats", + "MessageTaskTargetDirectoryNotWritable": "Det är inte tillåtet att skriva i den angivna katalogen", "MessageThinking": "Tänker...", "MessageUploaderItemFailed": "Misslyckades med att ladda upp", "MessageUploaderItemSuccess": "har blivit uppladdad!", @@ -888,6 +895,7 @@ "ToastBackupRestoreFailed": "Det gick inte att återställa säkerhetskopian", "ToastBackupUploadFailed": "Det gick inte att ladda upp säkerhetskopian", "ToastBackupUploadSuccess": "Säkerhetskopian uppladdad", + "ToastBatchQuickMatchStarted": "Snabbmatchning av {0} böcker har påbörjats!", "ToastBatchUpdateFailed": "Batchuppdateringen misslyckades", "ToastBatchUpdateSuccess": "Batchuppdateringen lyckades", "ToastBookmarkCreateFailed": "Det gick inte att skapa bokmärket", From 599b6bd6ad5e4083c09754d188789170cef399e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=BF=D0=B8=D0=BD=D1=96=D1=87?= Date: Sun, 23 Feb 2025 11:24:46 +0000 Subject: [PATCH 30/32] Translated using Weblate (Ukrainian) Currently translated at 100.0% (1093 of 1093 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/ --- client/strings/uk.json | 1 + 1 file changed, 1 insertion(+) diff --git a/client/strings/uk.json b/client/strings/uk.json index 9f9477dd5..2530df5ae 100644 --- a/client/strings/uk.json +++ b/client/strings/uk.json @@ -848,6 +848,7 @@ "MessageRestoreBackupConfirm": "Ви дійсно бажаєте відновити резервну копію від", "MessageRestoreBackupWarning": "Відновлення резервної копії перезапише всю базу даних, розташовану в /config, і зображення обкладинок в /metadata/items та /metadata/authors.

Резервні копії не змінюють жодних файлів у теках бібліотеки. Якщо у налаштуваннях сервера увімкнено збереження обкладинок і метаданих у теках бібліотеки, вони не створюються під час резервного копіювання і не перезаписуються..

Всі клієнти, що користуються вашим сервером, будуть автоматично оновлені.", "MessageScheduleLibraryScanNote": "Для більшості користувачів рекомендується залишити цю функцію вимкненою та залишити параметр перегляду папок увімкненим. Засіб спостереження за папками автоматично виявить зміни в папках вашої бібліотеки. Засіб спостереження за папками не працює для кожної файлової системи (наприклад, NFS), тому замість нього можна використовувати сканування бібліотек за розкладом.", + "MessageScheduleRunEveryWeekdayAtTime": "Запуск кожні {0} о {1}", "MessageSearchResultsFor": "Результати пошуку для", "MessageSelected": "Вибрано: {0}", "MessageServerCouldNotBeReached": "Не вдалося підключитися до сервера", From 2eccb9465c64128116b04ab2c26eeed36a9cea95 Mon Sep 17 00:00:00 2001 From: Troja Date: Sun, 23 Feb 2025 11:10:56 +0000 Subject: [PATCH 31/32] Translated using Weblate (Belarusian) Currently translated at 31.0% (339 of 1093 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/ --- client/strings/be.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/client/strings/be.json b/client/strings/be.json index ed194e522..85b5fa8a6 100644 --- a/client/strings/be.json +++ b/client/strings/be.json @@ -160,6 +160,8 @@ "HeaderNotifications": "Апавяшчэнні", "HeaderOpenListeningSessions": "Адкрыць сеансы праслухоўвання", "HeaderOpenRSSFeed": "Адкрыць RSS-стужку", + "HeaderPlaylist": "Плэйліст", + "HeaderPlaylistItems": "Элементы плэйліста", "HeaderRSSFeedGeneral": "Падрабязнасці RSS", "HeaderRSSFeedIsOpen": "RSS-стужка адкрыта", "HeaderRSSFeeds": "RSS-стужкі", @@ -173,15 +175,21 @@ "HeaderSettingsGeneral": "Агульныя", "HeaderSettingsScanner": "Сканер", "HeaderSettingsWebClient": "Вэб-кліент", + "HeaderSleepTimer": "Таймер сну", + "HeaderStatsMinutesListeningChart": "Хвіліны праслухоўвання (апошнія 7 дзён)", "HeaderStatsTop10Authors": "10 лепшых аўтараў", "HeaderStatsTop5Genres": "5 лепшых жанраў", "HeaderTableOfContents": "Змест", "HeaderTools": "Інструменты", "HeaderUpdateAccount": "Абнавіць уліковы запіс", + "HeaderYourStats": "Ваша статыстыка", "LabelAccountType": "Тып уліковага запіса", "LabelAccountTypeAdmin": "Адміністратар", "LabelAccountTypeGuest": "Госць", "LabelAccountTypeUser": "Карыстальнік", + "LabelAddToPlaylist": "Дадаць у плэйліст", + "LabelAddedDate": "Дададзена {0}", + "LabelAll": "Усе", "LabelAudioBitrate": "Бітрэйт аўдыё (напрыклад, 128к)", "LabelAudioChannels": "Аўдыёканалы (1 або 2)", "LabelAudioCodec": "Аўдыёкодэк", @@ -191,10 +199,15 @@ "LabelAuthors": "Аўтары", "LabelAutoDownloadEpisodes": "Аўтаматычнае спампаванне эпізодаў", "LabelBackupAudioFiles": "Рэзервовае капіраванне аўдыёфайлаў", + "LabelBooks": "Кнігі", "LabelChapters": "Раздзелы", + "LabelClosePlayer": "Зачыніць прайгравальнік", "LabelCollapseSeries": "Згарнуць серыі", + "LabelComplete": "Завершана", "LabelContinueListening": "Працягваць слухаць", + "LabelContinueReading": "Працягнуць чытанне", "LabelContinueSeries": "Працягнуць серыі", + "LabelDescription": "Апісанне", "LabelDiscover": "Знайсці", "LabelDownload": "Спампаваць", "LabelDownloadNEpisodes": "Спампована {0} эпізодаў", @@ -202,6 +215,7 @@ "LabelDuration": "Працягласць", "LabelEbook": "Электронная кніга", "LabelEbooks": "Электронныя кнігі", + "LabelEnable": "Уключыць", "LabelEncodingBackupLocation": "Рэзервовая копія вашых арыгінальных аўдыёфайлаў будзе захавана ў:", "LabelEncodingChaptersNotEmbedded": "Раздзелы не ўбудаваны ў шматдарожкавыя аўдыякнігі.", "LabelEncodingFinishedM4B": "Гатовы файл M4B будзе змешчаны ў вашу тэчку з аўдыякнігамі па адрасе:", @@ -216,13 +230,17 @@ "LabelFileBirthtime": "Час стварэння файла", "LabelFileModified": "Час змянення файла", "LabelFilename": "Імя файла", + "LabelFinished": "Скончана", "LabelFolder": "Тэчка", "LabelFontBoldness": "Таўшчыня шрыфта", + "LabelFontScale": "Памер шрыфту", "LabelGenre": "Жанр", "LabelGenres": "Жанры", "LabelHasEbook": "Мае электронную кнігу", "LabelHasSupplementaryEbook": "Мае дадатковую электронную кнігу", + "LabelHost": "Хост", "LabelInProgress": "У працэсе", + "LabelIncomplete": "Незавершана", "LabelLanguage": "Мова", "LabelLayoutSinglePage": "Аднабаковы", "LabelLineSpacing": "Міжрадковы інтэрвал", @@ -232,6 +250,12 @@ "LabelMaxEpisodesToKeepHelp": "Значэнне 0 не ўстанаўлівае максімальнага абмежавання. Пасля аўтаматычнай спампоўкі новага эпізоду будзе выдалены самы стары эпізод, калі ў вас больш за X эпізодаў. Пры кожнай новай спампоўцы будзе выдаляцца толькі 1 эпізод.", "LabelMediaPlayer": "Медыяплэер", "LabelMediaType": "Тып медыя", + "LabelMissing": "Адсутнічае", + "LabelMore": "Больш", + "LabelMoreInfo": "Больш інфармацыі", + "LabelName": "Імя", + "LabelNarrator": "Чытальнік", + "LabelNarrators": "Чытальнікі", "LabelOpenRSSFeed": "Адкрыць RSS-стужку", "LabelPermissionsDownload": "Можна спампаваць", "LabelPreventIndexing": "Прадухіліць індэксацыю вашай стужкі каталогамі падкастаў iTunes і Google", @@ -248,6 +272,8 @@ "LabelSettingsExperimentalFeaturesHelp": "Функцыі ў распрацоўцы, для якіх вашы водгукі і дапамога ў тэставанні будуць карыснымі. Націсніце, каб адкрыць абмеркаванне на GitHub.", "LabelSettingsLibraryMarkAsFinishedWhen": "Пазначыць элемент медыя як скончаны, калі", "LabelShareDownloadableHelp": "Дазваляе карыстальнікам, якія маюць спасылку на доступ, спампаваць ZIP-файл элемента бібліятэкі.", + "LabelShowAll": "Паказаць усё", + "LabelSize": "Памер", "LabelStatsAudioTracks": "Аўдыядарожкі", "LabelTracks": "Дарожкі", "MessageBookshelfNoRSSFeeds": "Няма адкрытых RSS-стужак", From c7d8021a16012a5493ae53a1310d2000887204a5 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 23 Feb 2025 17:20:30 -0600 Subject: [PATCH 32/32] Version bump v2.19.5 --- client/package-lock.json | 4 ++-- client/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 2d3955f79..b7f759175 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.19.4", + "version": "2.19.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.19.4", + "version": "2.19.5", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", diff --git a/client/package.json b/client/package.json index c11b3be06..b5b2f9824 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.19.4", + "version": "2.19.5", "buildNumber": 1, "description": "Self-hosted audiobook and podcast client", "main": "index.js", diff --git a/package-lock.json b/package-lock.json index 147a39540..26ea65783 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf", - "version": "2.19.4", + "version": "2.19.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf", - "version": "2.19.4", + "version": "2.19.5", "license": "GPL-3.0", "dependencies": { "axios": "^0.27.2", diff --git a/package.json b/package.json index 801afddcd..639393d6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "2.19.4", + "version": "2.19.5", "buildNumber": 1, "description": "Self-hosted audiobook and podcast server", "main": "index.js",