From fbe9971a8b4fa3606ef8dbd8262a40ba03d40f3c Mon Sep 17 00:00:00 2001 From: votex001 Date: Wed, 3 Sep 2025 18:19:52 +0300 Subject: [PATCH 01/19] [fix] prevent duplicates in multi-selects --- client/components/ui/MultiSelect.vue | 2 +- client/components/ui/MultiSelectQueryInput.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/ui/MultiSelect.vue b/client/components/ui/MultiSelect.vue index c7572ba5..3dcfb049 100644 --- a/client/components/ui/MultiSelect.vue +++ b/client/components/ui/MultiSelect.vue @@ -278,7 +278,7 @@ export default { }) }, insertNewItem(item) { - this.selected.push(item) + if (!this.selected.includes(item)) this.selected.push(item) this.$emit('input', this.selected) this.$emit('newItem', item) this.textInput = null diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 18abc66e..4bc434cb 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -287,7 +287,7 @@ export default { }) }, insertNewItem(item) { - this.selected.push(item) + if (!this.selected.find((i) => i.name === item.name)) this.selected.push(item) this.$emit('input', this.selected) this.$emit('newItem', item) this.textInput = null From 797dba244892b3455a9506a44298446844a0f814 Mon Sep 17 00:00:00 2001 From: Frank de Lange Date: Fri, 10 Oct 2025 22:30:38 +0200 Subject: [PATCH 02/19] fix #1617 (useradd: user 'audiobookshelf' already exists) This change fixes the problem of failing upgrades on dpkg-based systems by reworking the check for whether the `audiobookshelf` user/group already exists. --- build/debian/DEBIAN/preinst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/debian/DEBIAN/preinst b/build/debian/DEBIAN/preinst index e30bc490..241a4701 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -22,7 +22,7 @@ add_user() { declare -r descr="${4:-No description}" declare -r shell="${5:-/bin/false}" - if ! getent passwd | grep -q "^$user:"; then + if ! getent passwd "$user" 2>&1 >/dev/null; then echo "Creating system user: $user in $group with $descr and shell $shell" useradd $uid_flags --gid $group --no-create-home --system --shell $shell -c "$descr" $user fi @@ -39,7 +39,7 @@ add_group() { declare -r gid_flags="--gid $gid" fi - if ! getent group | grep -q "^$group:" ; then + if ! getent group "$group" 2>&1 >/dev/null; then echo "Creating system group: $group" groupadd $gid_flags --system $group fi From a5750deaaf824e99fec54848640c21d5395dd626 Mon Sep 17 00:00:00 2001 From: "Tomasz N." Date: Wed, 22 Oct 2025 23:02:49 +0200 Subject: [PATCH 03/19] The key change: Move the Database.bookAuthorModel.create() block outside the if (!author) check, so it runs whether the author was just created OR already existed in the database. This bug was visible when using "Match Books" for a library and the outcome was books had no author(s) assigned despite the custom providers correctly providing those values. --- server/scanner/Scanner.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js index 206068cc..cf5c87b7 100644 --- a/server/scanner/Scanner.js +++ b/server/scanner/Scanner.js @@ -259,7 +259,7 @@ class Scanner { SocketAuthority.emitter('author_added', author.toOldJSON()) // Update filter data Database.addAuthorToFilterData(libraryItem.libraryId, author.name, author.id) - + } await Database.bookAuthorModel .create({ authorId: author.id, @@ -270,7 +270,6 @@ class Scanner { libraryItem.media.authors.push(author) hasAuthorUpdates = true }) - } } const authorsRemoved = libraryItem.media.authors.filter((a) => !matchData.author.find((ma) => ma.toLowerCase() === a.name.toLowerCase())) if (authorsRemoved.length) { From 372c9a5322bca988f66c4d714067bfa5a9922ac8 Mon Sep 17 00:00:00 2001 From: "Tomasz N." Date: Fri, 31 Oct 2025 15:36:47 +0100 Subject: [PATCH 04/19] Increasing the timeout for bookfinder - some metadata providers heavily throttle the requests, original 10s is not enough. --- server/finders/BookFinder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/finders/BookFinder.js b/server/finders/BookFinder.js index fe1a6102..c25afe07 100644 --- a/server/finders/BookFinder.js +++ b/server/finders/BookFinder.js @@ -11,7 +11,7 @@ const { levenshteinDistance, levenshteinSimilarity, escapeRegExp, isValidASIN } const htmlSanitizer = require('../utils/htmlSanitizer') class BookFinder { - #providerResponseTimeout = 10000 + #providerResponseTimeout = 120000 constructor() { this.openLibrary = new OpenLibrary() From 961d066bdd5d18efa2d8774362b2b236a8ca2d82 Mon Sep 17 00:00:00 2001 From: "Tomasz N." Date: Fri, 31 Oct 2025 15:39:12 +0100 Subject: [PATCH 05/19] Wrong branch. --- server/finders/BookFinder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/finders/BookFinder.js b/server/finders/BookFinder.js index c25afe07..fe1a6102 100644 --- a/server/finders/BookFinder.js +++ b/server/finders/BookFinder.js @@ -11,7 +11,7 @@ const { levenshteinDistance, levenshteinSimilarity, escapeRegExp, isValidASIN } const htmlSanitizer = require('../utils/htmlSanitizer') class BookFinder { - #providerResponseTimeout = 120000 + #providerResponseTimeout = 10000 constructor() { this.openLibrary = new OpenLibrary() From 648983708ed2c461a286f153917615d60788cffe Mon Sep 17 00:00:00 2001 From: sir-wilhelm Date: Fri, 12 Dec 2025 04:02:20 +0000 Subject: [PATCH 06/19] Sort the playlist sections alphabetically. --- client/components/modals/playlists/AddCreateModal.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/components/modals/playlists/AddCreateModal.vue b/client/components/modals/playlists/AddCreateModal.vue index e695ccb0..f8543f1d 100644 --- a/client/components/modals/playlists/AddCreateModal.vue +++ b/client/components/modals/playlists/AddCreateModal.vue @@ -97,7 +97,10 @@ export default { ...playlist } }) - .sort((a, b) => (a.isItemIncluded ? -1 : 1)) + .sort((a, b) => { + if (a.isItemIncluded !== b.isItemIncluded) return a.isItemIncluded ? -1 : 1 + return a.name.localeCompare(b.name) + }) }, isBatch() { return this.selectedPlaylistItems.length > 1 From 076ece6fe748fa4b0b44ea3c8e89cb940333781f Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 21 Dec 2025 14:45:04 -0600 Subject: [PATCH 07/19] Auto-formatting --- server/scanner/Scanner.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js index cf5c87b7..af440598 100644 --- a/server/scanner/Scanner.js +++ b/server/scanner/Scanner.js @@ -260,16 +260,16 @@ class Scanner { // Update filter data Database.addAuthorToFilterData(libraryItem.libraryId, author.name, author.id) } - await Database.bookAuthorModel - .create({ - authorId: author.id, - bookId: libraryItem.media.id - }) - .then(() => { - Logger.info(`[Scanner] quickMatchBookBuildUpdatePayload: Added author "${author.name}" to "${libraryItem.media.title}"`) - libraryItem.media.authors.push(author) - hasAuthorUpdates = true - }) + await Database.bookAuthorModel + .create({ + authorId: author.id, + bookId: libraryItem.media.id + }) + .then(() => { + Logger.info(`[Scanner] quickMatchBookBuildUpdatePayload: Added author "${author.name}" to "${libraryItem.media.title}"`) + libraryItem.media.authors.push(author) + hasAuthorUpdates = true + }) } const authorsRemoved = libraryItem.media.authors.filter((a) => !matchData.author.find((ma) => ma.toLowerCase() === a.name.toLowerCase())) if (authorsRemoved.length) { From 7b37c98e88309f67b467fea5fcf71e4736984a0d Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 21 Dec 2025 15:38:34 -0600 Subject: [PATCH 08/19] Book tags genres dedupe (#4927) * Update Audible provider dedupe genres/tags and return tags as array * Update custom metadata provider to dedupe tags/genres and return tags as array --- server/providers/Audible.js | 13 ++++++++---- server/providers/CustomProviderAdapter.js | 25 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/server/providers/Audible.js b/server/providers/Audible.js index 2c12ffc1..6ba01aa8 100644 --- a/server/providers/Audible.js +++ b/server/providers/Audible.js @@ -57,8 +57,13 @@ class Audible { }) } - const genresFiltered = genres ? genres.filter((g) => g.type == 'genre').map((g) => g.name) : [] - const tagsFiltered = genres ? genres.filter((g) => g.type == 'tag').map((g) => g.name) : [] + let genresCleaned = null + let tagsCleaned = null + + if (genres && Array.isArray(genres)) { + genresCleaned = [...new Set(genres.filter((g) => g.type == 'genre').map((g) => g.name))] + tagsCleaned = [...new Set(genres.filter((g) => g.type == 'tag').map((g) => g.name))] + } return { title, @@ -71,8 +76,8 @@ class Audible { cover: image, asin, isbn, - genres: genresFiltered.length ? genresFiltered : null, - tags: tagsFiltered.length ? tagsFiltered.join(', ') : null, + genres: genresCleaned.length ? genresCleaned : null, + tags: tagsCleaned.length ? tagsCleaned : null, series: series.length ? series : null, language: language ? language.charAt(0).toUpperCase() + language.slice(1) : null, duration: runtimeLengthMin && !isNaN(runtimeLengthMin) ? Number(runtimeLengthMin) : 0, diff --git a/server/providers/CustomProviderAdapter.js b/server/providers/CustomProviderAdapter.js index c079a128..5c8cad75 100644 --- a/server/providers/CustomProviderAdapter.js +++ b/server/providers/CustomProviderAdapter.js @@ -89,6 +89,27 @@ class CustomProviderAdapter { }) .filter((s) => s !== undefined) } + /** + * Validates and dedupes tags/genres array + * Can be comma separated string or array of strings + * @param {string|string[]} tagsGenres + * @returns {string[]} + */ + const validateTagsGenresArray = (tagsGenres) => { + if (!tagsGenres || (typeof tagsGenres !== 'string' && !Array.isArray(tagsGenres))) return undefined + + // If string, split by comma and trim each item + if (typeof tagsGenres === 'string') tagsGenres = tagsGenres.split(',') + // If array, ensure all items are strings + else if (!tagsGenres.every((t) => typeof t === 'string')) return undefined + + // Trim and filter out empty strings + tagsGenres = tagsGenres.map((t) => t.trim()).filter(Boolean) + if (!tagsGenres.length) return undefined + + // Dedup + return [...new Set(tagsGenres)] + } // re-map keys to throw out return matches.map((match) => { @@ -105,8 +126,8 @@ class CustomProviderAdapter { cover: toStringOrUndefined(cover), isbn: toStringOrUndefined(isbn), asin: toStringOrUndefined(asin), - genres: Array.isArray(genres) && genres.every((g) => typeof g === 'string') ? genres : undefined, - tags: toStringOrUndefined(tags), + genres: validateTagsGenresArray(genres), + tags: validateTagsGenresArray(tags), series: validateSeriesArray(series), language: toStringOrUndefined(language), duration: !isNaN(duration) && duration !== null ? Number(duration) : undefined From 3c2eec8279d82185cd858bd0a856b865160e2099 Mon Sep 17 00:00:00 2001 From: FiendFEARing Date: Wed, 17 Dec 2025 01:55:49 +0100 Subject: [PATCH 09/19] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (1163 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/ --- client/strings/zh-cn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/strings/zh-cn.json b/client/strings/zh-cn.json index 1a95256a..64c41619 100644 --- a/client/strings/zh-cn.json +++ b/client/strings/zh-cn.json @@ -275,7 +275,7 @@ "LabelBonus": "额外", "LabelBooks": "图书", "LabelButtonText": "按钮文本", - "LabelByAuthor": "由 {0}", + "LabelByAuthor": "作者: {0}", "LabelChangePassword": "修改密码", "LabelChannels": "声道", "LabelChapterCount": "{0} 章节", From ac08e897ee93cd2457d13716d10be94e5d0b0535 Mon Sep 17 00:00:00 2001 From: Ahetek Date: Mon, 15 Dec 2025 21:54:40 +0100 Subject: [PATCH 10/19] Translated using Weblate (Polish) Currently translated at 89.2% (1038 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/ --- client/strings/pl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/strings/pl.json b/client/strings/pl.json index 316e8423..32c9766c 100644 --- a/client/strings/pl.json +++ b/client/strings/pl.json @@ -233,8 +233,8 @@ "LabelAddToCollectionBatch": "Dodaj {0} książki do kolekcji", "LabelAddToPlaylist": "Dodaj do playlisty", "LabelAddToPlaylistBatch": "Dodaj {0} pozycji do playlisty", - "LabelAddedAt": "Dodano w", - "LabelAddedDate": "Dodano", + "LabelAddedAt": "Dodano", + "LabelAddedDate": "Dodano {0}", "LabelAdminUsersOnly": "Tylko użytkownicy administracyjni", "LabelAll": "Wszystkie", "LabelAllEpisodesDownloaded": "Wszystkie odcinki pobrane", From cc48d9f26dc3dc0c2b096b9ba892c501609bfbd9 Mon Sep 17 00:00:00 2001 From: lambolighting Date: Fri, 19 Dec 2025 11:21:03 +0100 Subject: [PATCH 11/19] Translated using Weblate (Greek) Currently translated at 26.9% (313 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/el/ --- client/strings/el.json | 210 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 1 deletion(-) diff --git a/client/strings/el.json b/client/strings/el.json index 9d12d309..881bd971 100644 --- a/client/strings/el.json +++ b/client/strings/el.json @@ -49,6 +49,7 @@ "ButtonMapChapterTitles": "Χαρτογράφηση Τίτλων Κεφαλαίων", "ButtonMatchAllAuthors": "Αντιστοίχιση Όλων των Συγγραφέων", "ButtonMatchBooks": "Αντιστοίχιση Βιβλίων", + "ButtonNevermind": "Άστο", "ButtonNext": "Επόμενο", "ButtonNextChapter": "Επόμενο Κεφάλαιο", "ButtonNextItemInQueue": "Επόμενο Αντικείμενο στην Ουρά", @@ -62,8 +63,13 @@ "ButtonPlaylists": "Λίστες Αναπαραγωγής", "ButtonPrevious": "Προηγούμενο", "ButtonPreviousChapter": "Προηγούμενο Κεφάλαιο", + "ButtonProbeAudioFile": "Ανάλυση Αρχείου Ήχου", + "ButtonPurgeAllCache": "Εκκαθάριση Όλης της Προσωρινής Μνήμης", + "ButtonPurgeItemsCache": "Εκκαθάριση της Μνήμης Αντικειμένων", "ButtonQueueAddItem": "Προσθήκη στην ουρά", "ButtonQueueRemoveItem": "Αφαίρεση απ'την ουρά", + "ButtonQuickEmbed": "Γρήγορη Ενσωμάτωση", + "ButtonQuickEmbedMetadata": "Γρήγορη Ενσωμάτωση Μεταδεδομένων", "ButtonQuickMatch": "Γρήγορη Αντιστοίχηση", "ButtonReScan": "Επανασάρωση", "ButtonRead": "Ανάγνωση", @@ -73,35 +79,237 @@ "ButtonRemove": "Αφαίρεση", "ButtonRemoveAll": "Αφαίρεση Όλων", "ButtonRemoveAllLibraryItems": "Αφαίρεση Όλων των Αντικειμέων Βιβλιοθήκης", + "ButtonRemoveFromContinueListening": "Αφαίρεση από τη Συνέχεια Ακρόασης", + "ButtonRemoveFromContinueReading": "Αφαίρεση από τη Συνέχεια Ανάγνωσης", + "ButtonRemoveSeriesFromContinueSeries": "Αφαίρεση Σειράς από τη Συνέχεια Σειράς", "ButtonReset": "Επαναφορά", "ButtonResetToDefault": "Επαναφορά στις προεπιλογές", "ButtonRestore": "Επαναφορά", "ButtonSave": "Αποθήκευση", "ButtonSaveAndClose": "Αποθήκευση και Κλείσιμο", + "ButtonSaveTracklist": "Αποθήκευση Λίστας Κομματιών", "ButtonScan": "Σάρψση", + "ButtonScanLibrary": "Σάρωση Βιβλιοθήκης", + "ButtonScrollLeft": "Κύλιση Αριστερά", + "ButtonScrollRight": "Κύλιση Δεξιά", "ButtonSearch": "Αναζήτηση", + "ButtonSelectFolderPath": "Επιλογή Διαδρομής Φακέλου", "ButtonSeries": "Σειρά", + "ButtonSetChaptersFromTracks": "Ορισμός κεφαλαίων από τα κομμάτια", + "ButtonShare": "Κοινοποίηση", + "ButtonShiftTimes": "Χρόνοι Μετακίνησης", + "ButtonShow": "Εμφάνιση", + "ButtonStartM4BEncode": "Έναρξη Κωδικοποίησης M4B", + "ButtonStats": "Στατιστικά", "ButtonSubmit": "Υποβολή", + "ButtonTest": "Δοκιμή", + "ButtonUpload": "Μεταφόρτωση", + "ButtonUploadBackup": "Μεταφόρτωση Αντιγράφου Ασφαλείας", + "ButtonUploadCover": "Μεταφόρτωση Εξωφύλλου", + "ButtonUploadOPMLFile": "Μεταφόρτωση Αρχείου OPML", + "ButtonUserDelete": "Διαγραφή Χρήστη {0}", + "ButtonUserEdit": "Επεξεργασίας χρήστη {0}", + "ButtonViewAll": "Εμφάνιση Όλων", "ButtonYes": "Ναι", + "ErrorUploadLacksTitle": "Πρέπει να έχει τίτλο", "HeaderAccount": "Λογαριασμός", "HeaderAdvanced": "Για Προχωρημένους", + "HeaderApiKeys": "Κλειδιά API", "HeaderAudioTracks": "Κομμάτια Ήχου", + "HeaderBackups": "Αντίγραφα Ασφαλείας", + "HeaderBulkChapterModal": "Προσθήκη Πολλαπλών Κεφαλαίων", + "HeaderChangePassword": "Αλλαγή Κωδικού Πρόσβασης", "HeaderChapters": "Κεφάλαια", + "HeaderChooseAFolder": "Επιλογή Φακέλου", "HeaderCollection": "Συλλογή", "HeaderCollectionItems": "Αντικείμενα Συλλογής", + "HeaderCover": "Εξώφυλλο", + "HeaderCurrentDownloads": "Τρέχουσες Λήψεις", "HeaderDetails": "Λεπτομέρειες", + "HeaderDownloadQueue": "Ουρά Λήψης", "HeaderEbookFiles": "Αρχεία Ebook", + "HeaderEmail": "Ηλεκτρονικό Ταχυδρομίο", + "HeaderEmailSettings": "Ρυθμίσεις Ηλεκτρονικού Ταχυδρομίου", "HeaderEpisodes": "Επεισόδια", "HeaderEreaderSettings": "Ρυθμίσεις Ereader", + "HeaderFiles": "Αρχεία", + "HeaderFindChapters": "Εύρεση Κεφαλαίων", + "HeaderItemFiles": "Αρχεία Αντικειμένων", + "HeaderLastListeningSession": "Τελευταία Συνεδρία Ακρόασης", "HeaderLatestEpisodes": "Τελευταία Επεισόδια", "HeaderLibraries": "Βιβλιοθήκες", + "HeaderLibraryFiles": "Αρχεία Βιβλιοθήκης", + "HeaderLibraryStats": "Στατιστικά Βιβλιοθήκης", + "HeaderListeningSessions": "Συνεδρίες Ακρόασης", + "HeaderListeningStats": "Στατιστικά Ακρόασης", + "HeaderMatch": "Ταύτιση", + "HeaderNewAccount": "Νέος Λογαριασμός", + "HeaderNewApiKey": "Νέο Κλειδί API", + "HeaderNewLibrary": "Νέα Βιβλιοθήκη", + "HeaderNotificationCreate": "Δημιουργία Ειδοποίησης", + "HeaderNotificationUpdate": "Ενημέρωση Ειδοποίησης", + "HeaderNotifications": "Ειδοποιήσεις", "HeaderOpenRSSFeed": "Άνοιγμα Τροφοδοσίας RSS", + "HeaderOtherFiles": "Άλλα Αρχεία", + "HeaderPermissions": "Δικαιώματα", + "HeaderPlayerSettings": "Ρυθμίσεις Αναπαραγωγής", "HeaderPlaylist": "Λίστα Αναπαραγωγής", "HeaderPlaylistItems": "Αντικείμενα Λίστας Αναπαραγωγής", + "HeaderPresets": "Προεπιλογές", "HeaderRSSFeedGeneral": "Λεπτομέρειες RSS", "HeaderRSSFeedIsOpen": "Η Τροφοδοσία RSS είναι Ανοιχτή", + "HeaderRemoveEpisode": "Αφαίρεση Επεισοδίου", + "HeaderSession": "Συνεδρία", + "HeaderSetBackupSchedule": "Ορισμός Προγράμματος Αντιγράφων Ασφαλείας", "HeaderSettings": "Ρυθμίσεις", + "HeaderSettingsDisplay": "Προβολή", + "HeaderSettingsGeneral": "Γενικά", + "HeaderSettingsSecurity": "Ασφάλεια", + "HeaderSleepTimer": "Χρονοδιακόπτης Ύπνου", + "HeaderStatsLargestItems": "Μεγαλύτερα Αντικείμενα", + "HeaderStatsLongestItems": "Μεγαλύτερα Αντικείμενα (ώρες)", "HeaderStatsMinutesListeningChart": "Λεπτά Ακρόασης (τελευταίες 7 ημέρες)", "HeaderStatsRecentSessions": "Πρόσφατες Συνεδρίες", - "HeaderTableOfContents": "Πίνακας Περιεχομένων" + "HeaderStatsTop10Authors": "10 Κορυφαίου Συγγραφείς", + "HeaderStatsTop5Genres": "5 Κορυφαία Είδη", + "HeaderTableOfContents": "Πίνακας Περιεχομένων", + "HeaderTools": "Εργαλεία", + "HeaderUpdateAccount": "Ενημέρωση Λογαριασμού", + "HeaderUpdateApiKey": "Ενημέρωση Κλειδιού API", + "HeaderUpdateAuthor": "Ενημέρωση Συγγραφέα", + "HeaderUpdateDetails": "Ενημέρωση Λεπτομερειεών", + "HeaderUpdateLibrary": "Ενημέρωση Βιβλιοθήκης", + "HeaderUsers": "Χρήστες", + "HeaderYourStats": "Τα Στατιστικά Σας", + "LabelAbridged": "Συνοπτικό", + "LabelAccessibleBy": "Προσβάσιμο από", + "LabelAccountType": "Τύπος Λογαριασμού", + "LabelAccountTypeAdmin": "Διαχειριστής", + "LabelAccountTypeGuest": "Επισκέπτης", + "LabelAccountTypeUser": "Χρήστης", + "LabelAddToCollection": "Προσθήκη σε Συλλογή", + "LabelAddToCollectionBatch": "Προσθήκη {0} Βιβλίων στην Συλλογή", + "LabelAddToPlaylist": "Προσθήκη στην Λίστα Αναπαραγωγής", + "LabelAddedAt": "Προστέθηκε Στις", + "LabelAddedDate": "Προστέθηκε {0}", + "LabelAll": "Όλα", + "LabelAllEpisodesDownloaded": "Όλα τα επεισόδια λήφθηκαν", + "LabelAllUsers": "Όλοι οι Χρήστες", + "LabelAlreadyInYourLibrary": "Υπάρχει ήδη στην βιβλιοθήκη", + "LabelAudioChannels": "Κανάλια Ήχου (1 ή 2)", + "LabelAuthor": "Συγγραφέας", + "LabelAuthorFirstLast": "Συγγραφέας (Όνομα Επώνυμο)", + "LabelAuthorLastFirst": "Συγγραφέας (Επώνυμο, Όνομα)", + "LabelAuthors": "Συγγραφείς", + "LabelAutoDownloadEpisodes": "Αυτόματο Κατέβασμα Επεισοδίων", + "LabelAutoLaunch": "Αυτόματη Εκκίνηση", + "LabelBackupLocation": "Τοποθεσία Αντιγράφου Ασφαλείας", + "LabelBackupsEnableAutomaticBackups": "Αυτόματα αντίγραφα ασφαλείας", + "LabelBackupsNumberToKeep": "Αριθμός αντιγράφων ασφαλείας προς διατήρηση", + "LabelBooks": "Βιβλία", + "LabelButtonText": "Κείμενο Κουμπιού", + "LabelByAuthor": "κατά {0}", + "LabelChangePassword": "Αλλαγή Κωδικού Πρόσβασης", + "LabelChannels": "Κανάλια", + "LabelChapterCount": "{0} Κεφάλαια", + "LabelChapterTitle": "Τίτλος Κεφαλαίου", + "LabelChapters": "Κεφάλαια", + "LabelChaptersFound": "κεφάλαια βρέθηκαν", + "LabelClosePlayer": "Κλείσιμο αναπαραγωγής", + "LabelCollection": "Συλλογή", + "LabelCollections": "Συλλογές", + "LabelComplete": "Ολοκλήρωση", + "LabelConfirmPassword": "Επιβεβαίωση Κωδικού Πρόσβασης", + "LabelContinueListening": "Συνέχεια Ακρόασης", + "LabelContinueReading": "Συνέχεια Ανάγνωσης", + "LabelContinueSeries": "Συνέχεια Σειράς", + "LabelCover": "Εξώφυλλο", + "LabelCoverImageURL": "URL Εικόνας Εξωφύλλου", + "LabelCoverProvider": "Πάροχος Εξωφύλλου", + "LabelCreatedAt": "Δημιουρήθηκε Στις", + "LabelCurrent": "Τρέχων", + "LabelCurrently": "Τρέχων:", + "LabelDays": "Ημέρες", + "LabelDescription": "Περιγραφή", + "LabelDevice": "Συσκευή", + "LabelDeviceInfo": "Πληροφορίες Συσκευής", + "LabelDownload": "Λήψη", + "LabelDownloadNEpisodes": "Λήψη {0} επεισοδίων", + "LabelDuration": "Διάρκεια", + "LabelDurationComparisonExactMatch": "(ακριβής ταύτιση)", + "LabelEbook": "Ebook", + "LabelEbooks": "Ebooks", + "LabelEdit": "Επεξεργασία", + "LabelEmail": "Ηλεκτρονικό Ταχυδρομίο", + "LabelEmailSettingsFromAddress": "Από Διεύθυνση", + "LabelEmailSettingsSecure": "Ασφαλές", + "LabelEmailSettingsTestAddress": "Δοκιμή Διεύθυνσης", + "LabelEmbeddedCover": "Ενσωματωμένο Εξώφυλλο", + "LabelEnable": "Ενεργοποίηση", + "LabelEnd": "Τέλος", + "LabelEndOfChapter": "Τέλος Κεφαλαίου", + "LabelEpisode": "Επεισόδιο", + "LabelFile": "Αρχείο", + "LabelFilename": "Όνομα Αρχείου", + "LabelFinished": "Ολοκληρώθηκε", + "LabelFolder": "Φάκελος", + "LabelFontFamily": "Οικογένεια Γραμματοσειράς", + "LabelGenre": "Είδος", + "LabelGenres": "Είδη", + "LabelHost": "Διακομιστής", + "LabelInProgress": "Σε Εξέλιξη", + "LabelLanguage": "Γλώσσα", + "LabelLayoutSinglePage": "Μονή Σελίδα", + "LabelListenAgain": "Επανάληψη Ακρόασης", + "LabelMediaType": "Τύπος Πολυμέσων", + "LabelMore": "Περισσότερα", + "LabelMoreInfo": "Περισσότερες Πληροφορίες", + "LabelName": "Όνομα", + "LabelNarrator": "Αφηγητής", + "LabelNarrators": "Αφηγητές", + "LabelNewestAuthors": "Πρόσφατοι Συγγραφείς", + "LabelNewestEpisodes": "Πρόσφατα Επεισόδια", + "LabelNotStarted": "Δεν Έχει Ξεκινήσει", + "LabelNumberOfEpisodes": "# Επεισοδίων", + "LabelPassword": "Κωδικός Πρόσβασης", + "LabelPath": "Διαδρομή", + "LabelProgress": "Πρόοδος", + "LabelPublishYear": "Χρονολογία Έκδοσης", + "LabelPublishedDate": "Εκδόθηκε {0}", + "LabelRandomly": "Τυχαία", + "LabelRead": "Ανάγνωση", + "LabelReadAgain": "Ανάγνωση Ξανά", + "LabelRecentSeries": "Πρόσφατη Σειρά", + "LabelRecentlyAdded": "Προστέθηκαν Πρόσφατα", + "LabelSeries": "Σειρά", + "LabelSetEbookAsPrimary": "Ορισμός ως πρωτεύων", + "LabelShowAll": "Εμφάνιση Όλων", + "LabelSize": "Μέγεθος", + "LabelSleepTimer": "Χρονοδιακόπτης Ύπνου", + "LabelStart": "Έναρξη", + "LabelStatsBestDay": "Καλύτερη Ημέρα", + "LabelStatsDailyAverage": "Ημερήσιος Μέσος Όρος", + "LabelStatsDays": "Ημέρες", + "LabelStatsDaysListened": "Ημέρες Ακρόασης", + "LabelStatsInARow": "Σε σειρά", + "LabelStatsItemsFinished": "Ολοκληρωμένα Αντικείμενα", + "LabelStatsMinutes": "λεπτά", + "LabelStatsMinutesListening": "Λεπτά Ακρόασης", + "LabelStatsWeekListening": "Εβδομαδιαία Ακρόαση", + "LabelTheme": "Θέμα", + "LabelThemeDark": "Σκοτεινό", + "LabelThemeLight": "Φωτεινό", + "LabelTimeRemaining": "{0} απομένουν", + "LabelTitle": "Τίτλος", + "LabelTracks": "Κομμάτια", + "LabelType": "Τύπος", + "LabelUnknown": "Άγνωστο", + "LabelUser": "Χρήστης", + "LabelUsername": "Όνομα Χρήστη", + "LabelYourProgress": "Η Πρόοδος Σας", + "MessageDownloadingEpisode": "Λήψη επεισοδίου", + "MessageLoading": "Φόρτωση...", + "MessageMarkAsFinished": "Σήμανση ως Ολοκληρωμένο", + "MessageNoItemsFound": "Δεν βρέθηκαν αντικείμενα", + "MessageNoUserPlaylists": "Δεν έχετε λίστες αναπαραγωγής" } From 81e96df9c5f8db502e3d9740ee8cb6b718c5a074 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 21 Dec 2025 15:54:07 -0600 Subject: [PATCH 12/19] Version bump v2.32.0 --- 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 2d41d39e..568274c8 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.31.0", + "version": "2.32.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.31.0", + "version": "2.32.0", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", diff --git a/client/package.json b/client/package.json index 33d66c27..df71f625 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.31.0", + "version": "2.32.0", "buildNumber": 1, "description": "Self-hosted audiobook and podcast client", "main": "index.js", diff --git a/package-lock.json b/package-lock.json index d703a8ed..4c9e9b9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf", - "version": "2.31.0", + "version": "2.32.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf", - "version": "2.31.0", + "version": "2.32.0", "license": "GPL-3.0", "dependencies": { "axios": "^0.27.2", diff --git a/package.json b/package.json index 0aaf4f5a..2b0d0b6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "2.31.0", + "version": "2.32.0", "buildNumber": 1, "description": "Self-hosted audiobook and podcast server", "main": "index.js", From 04eb3bc43733533516566fb3cd9ca5c32e034d4d Mon Sep 17 00:00:00 2001 From: advplyr Date: Tue, 23 Dec 2025 16:44:29 -0600 Subject: [PATCH 13/19] Fix server crash on audible match #4931 --- server/providers/Audible.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/providers/Audible.js b/server/providers/Audible.js index 6ba01aa8..133d3c0d 100644 --- a/server/providers/Audible.js +++ b/server/providers/Audible.js @@ -57,8 +57,8 @@ class Audible { }) } - let genresCleaned = null - let tagsCleaned = null + let genresCleaned = [] + let tagsCleaned = [] if (genres && Array.isArray(genres)) { genresCleaned = [...new Set(genres.filter((g) => g.type == 'genre').map((g) => g.name))] From 4bf15bbffd395f6257a4718d784b301fb710679f Mon Sep 17 00:00:00 2001 From: Ivan Smoliakov Date: Mon, 22 Dec 2025 11:56:18 +0100 Subject: [PATCH 14/19] Translated using Weblate (Russian) Currently translated at 100.0% (1163 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/ --- client/strings/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/strings/ru.json b/client/strings/ru.json index e0b04864..c84fe9dc 100644 --- a/client/strings/ru.json +++ b/client/strings/ru.json @@ -275,7 +275,7 @@ "LabelBonus": "Бонус", "LabelBooks": "Книги", "LabelButtonText": "Текст кнопки", - "LabelByAuthor": "{0}", + "LabelByAuthor": "от {0}", "LabelChangePassword": "Изменить пароль", "LabelChannels": "Ленты", "LabelChapterCount": "{0} Главы", From f3e90bd4206710a2e7f2e6d06edebcc6a1e474d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4m=C3=A4l=C3=A4inen?= Date: Mon, 22 Dec 2025 07:13:02 +0100 Subject: [PATCH 15/19] Translated using Weblate (Finnish) Currently translated at 100.0% (1163 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/ --- client/strings/fi.json | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/client/strings/fi.json b/client/strings/fi.json index 600db265..dde7009e 100644 --- a/client/strings/fi.json +++ b/client/strings/fi.json @@ -275,7 +275,7 @@ "LabelBonus": "Bonus", "LabelBooks": "Kirjat", "LabelButtonText": "Painikkeen teksti", - "LabelByAuthor": "tekijältä {0}", + "LabelByAuthor": "Tekijältä: {0}", "LabelChangePassword": "Vaihda salasana", "LabelChannels": "Kanavat", "LabelChapterCount": "{0} lukua", @@ -790,6 +790,7 @@ "MessageConfirmRemoveAuthor": "Oletko varma, että haluat poistaa tekijän \"{0}\"?", "MessageConfirmRemoveCollection": "Oletko varma, että haluat poistaa kokoelman \"{0}\"?", "MessageConfirmRemoveEpisode": "Oletko varma, että haluat poistaa jakson \"{0}\"?", + "MessageConfirmRemoveEpisodeNote": "Huomioi: Tämä ei poista äänitiedostoa, ellei \"Poista tiedosto pysyvästi\" -asetusta ole valittuna", "MessageConfirmRemoveEpisodes": "Oletko varma, että haluat poistaa {0} jaksoa?", "MessageConfirmRemoveListeningSessions": "Oletko varma, että haluat poistaa {0} kuuntelukertaa?", "MessageConfirmRemoveMetadataFiles": "Oletko varma, että haluat poistaa kaikki metadata.{0}-tiedostot kirjaston kohdekansioista?", @@ -816,6 +817,7 @@ "MessageFetching": "Haetaan...", "MessageForceReScanDescription": "skannaa kaikki tiedostot uudelleen kuten uusi tarkistus. Äänitiedoston ID3-tunnisteet, OPF-tiedostot ja tekstitiedostot skannataan uusina.", "MessageHeatmapListeningTimeTooltip": "{0} kuunnellaan on {1}", + "MessageHeatmapNoListeningSessions": "Ei kuuntelujaksoja {0}", "MessageImportantNotice": "Tärkeä huomautus!", "MessageInsertChapterBelow": "Syötä luku alle", "MessageInvalidAsin": "Virheellinen ASIN", @@ -886,10 +888,11 @@ "MessageResetChaptersConfirm": "Oletko varma, että haluat nollata luvut ja kumota tekemäsi muutokset?", "MessageRestoreBackupConfirm": "Oletko varma, että haluat palauttaa varmuuskopion, joka on luotu", "MessageRestoreBackupWarning": "Varmuuskopion palauttaminen korvaa koko /config:ssa sijaitsevan tietokannan, ja kansikuvat /metadata/items & /metadata/authors:ssa.

Varmuuskopiot eivät muuta kirjastokansioissasi olevia tiedostoja. Jos olet ottanut käyttöön palvelinasetuksissa kansikuvien ja metatietojen tallentamisen kirjaston kansioihin, niitä ei varmuuskopioida tai korvata.

Kaikki palvelintasi käyttävät asiakkaat virkistetään automaattisesti.", - "MessageScheduleLibraryScanNote": "Suurimmalle osaa käyttäjistä on suositeltavaa jättää tämä ominaisuus pois päältä ja \"Tarkkaile kirjaston muutoksia automaattisesti\" -asetus pidetään käytössä - se havaitsee muutokset kirjastokansioissasi automaattisesti. Ota tämä ominaisuus käyttöön, jos \"Tarkkaile kirjaston muutoksia automaattisesti\" ei toimi tiedostojärjestelmässäsi (kuten NFS).\"", + "MessageScheduleLibraryScanNote": "Suurimmalle osaa käyttäjistä on suositeltavaa jättää tämä ominaisuus pois päältä ja \"Tarkkaile kirjaston muutoksia automaattisesti\" -asetus pidetään käytössä - se havaitsee muutokset kirjastokansioissasi automaattisesti. Ota tämä ominaisuus käyttöön, jos \"Tarkkaile kirjaston muutoksia automaattisesti\" ei toimi tiedostojärjestelmässäsi (kuten NFS).", "MessageScheduleRunEveryWeekdayAtTime": "Suorita joka {0} klo {1}", "MessageSearchResultsFor": "Hakutulokset haulle", "MessageSelected": "{0} valittuna", + "MessageSeriesSequenceCannotContainSpaces": "Sarjan sekvenssi ei voi sisältää välilyöntejä", "MessageServerCouldNotBeReached": "Palvelimelle ei saatu yhteyttä", "MessageSetChaptersFromTracksDescription": "Aseta luvut käyttämällä kutakin äänitiedostoa lukuna ja luvun otsikkoa äänitiedoston nimenä", "MessageShareExpirationWillBe": "Umpeutuminen on {0}", @@ -951,7 +954,10 @@ "NotificationOnBackupCompletedDescription": "Laukaistu, kun varmuuskopiointi on valmis", "NotificationOnBackupFailedDescription": "Laukaistu, kun varmuuskopiointi epäonnistuu", "NotificationOnEpisodeDownloadedDescription": "Laukaistu, kun podcast-jakso ladataan automaattisesti", + "NotificationOnRSSFeedDisabledDescription": "Laukaistaan, kun automaattiset jaksolataukset poistetaan käytöstä liian monen epäonnistuneen yrityksen vuoksi", + "NotificationOnRSSFeedFailedDescription": "Laukaistaan, kun RRS-syötteen pyyntö epäonnistuu automaattisessa jaksolatauksessa", "NotificationOnTestDescription": "Tapahtuma ilmoitusjärjestelmän testaamista varten", + "PlaceholderBulkChapterInput": "Syötä luvun otsikko tai käytä numerointia (esim. 'Episodi 1', 'Luku 10', '1.')", "PlaceholderNewCollection": "Uusi kokoelman nimi", "PlaceholderNewFolderPath": "Uusi kansion polku", "PlaceholderNewPlaylist": "Uusi soittolistan nimi", @@ -1005,15 +1011,23 @@ "ToastBookmarkCreateFailed": "Kirjanmerkin luominen epäonnistui", "ToastBookmarkCreateSuccess": "Kirjanmerkki lisätty", "ToastBookmarkRemoveSuccess": "Kirjanmerkki poistettu", + "ToastBulkChapterInvalidCount": "Syötä numero 1 ja 150 välillä", "ToastCachePurgeFailed": "Välimuistin tyhjentäminen epäonnistui", "ToastCachePurgeSuccess": "Välimuisti tyhjennetty onnistuneesti", + "ToastChapterLocked": "Luku on lukittu.", + "ToastChapterStartTimeAdjusted": "Luvun aloitusaikaa on säädetty {0} sekunnilla", + "ToastChaptersAllLocked": "Kaikki luvut ovat lukittuina. Avaa lukuja vaihtaaksesi niiden aikoja.", "ToastChaptersHaveErrors": "Luvuissa on virheitä", + "ToastChaptersInvalidShiftAmountLast": "Virheellinen siirtomäärä. Viimeisen luvun aloitusaika ylittäisi tämän äänikirjan keston.", + "ToastChaptersInvalidShiftAmountStart": "Virheellinen siirtomäärä. Ensimmäisen luvun pituudeksi tulisi nolla tai negatiivinen arvo, ja toinen luku kirjoittaisi sen päälle. Kasvata toisen luvun aloitusaikaa.", "ToastChaptersMustHaveTitles": "Lukuilla on oltava otsikot", "ToastChaptersRemoved": "Luvut poistettu", "ToastChaptersUpdated": "Luvut päivitetty", "ToastCollectionItemsAddFailed": "Kohteen/kohteiden lisääminen kokoelmaan epäonnistui", "ToastCollectionRemoveSuccess": "Kokoelma poistettu", "ToastCollectionUpdateSuccess": "Kokoelma päivitetty", + "ToastConnectionNotAvailable": "Verkkoyhteyttä ei saatavilla. Yritä hetken päästä uudelleen", + "ToastCoverSearchFailed": "Kansikuvan haku epäonnistui", "ToastCoverUpdateFailed": "Kansikuvan päivitys epäonnistui", "ToastDateTimeInvalidOrIncomplete": "Päivämäärä ja aika ovat epäkelvolliset tai puutteelliset", "ToastDeleteFileFailed": "Tiedoston poistaminen epäonnistui", @@ -1029,6 +1043,8 @@ "ToastEpisodeDownloadQueueClearSuccess": "Jakson latausjono tyhjennetty", "ToastEpisodeUpdateSuccess": "{0} jaksoa päivitetty", "ToastErrorCannotShare": "Ei voi jakaa alkuperäisesti tällä laitteella", + "ToastFailedToCreate": "Luonti epäonnistui", + "ToastFailedToDelete": "Poisto epäonnistui", "ToastFailedToLoadData": "Tietojen lataaminen epäonnistui", "ToastFailedToMatch": "Vastaaminen epäonnistui", "ToastFailedToShare": "Jakaminen epäonnistui", @@ -1036,6 +1052,7 @@ "ToastInvalidImageUrl": "Epäkelvollinen kuvan URL-osoite", "ToastInvalidMaxEpisodesToDownload": "Ladattavien jaksojen enimmäismäärä on epäkelvollinen", "ToastInvalidUrl": "Epäkelvollinen URL-osoite", + "ToastInvalidUrls": "Yksi tai useampi URL on virheellinen", "ToastItemCoverUpdateSuccess": "Kohteen kansikuva päivitetty", "ToastItemDeletedFailed": "Kohteen poistaminen epäonnistui", "ToastItemDeletedSuccess": "Poistettu kohde", @@ -1060,6 +1077,7 @@ "ToastMustHaveAtLeastOnePath": "On oltava vähintään yksi polku", "ToastNameEmailRequired": "Nimi ja sähköpostiosoite vaaditaan", "ToastNameRequired": "Nimi vaaditaan", + "ToastNewApiKeyUserError": "Täytyy valita käyttäjä", "ToastNewEpisodesFound": "{0} uutta jaksoa löydetty", "ToastNewUserCreatedFailed": "Tilin \"{0}\" luominen epäonnistui", "ToastNewUserCreatedSuccess": "Uusi tili luotu", @@ -1084,6 +1102,7 @@ "ToastPlaylistUpdateSuccess": "Soittolista päivitetty", "ToastPodcastCreateFailed": "Podcastin luominen epäonnistui", "ToastPodcastCreateSuccess": "Podcastin luominen onnistui", + "ToastPodcastEpisodeUpdated": "Episodi päivitetty", "ToastPodcastGetFeedFailed": "Podcast-syötteen saaminen epäonnistui", "ToastPodcastNoEpisodesInFeed": "RSS-syötteestä ei löytynyt jaksoja", "ToastPodcastNoRssFeed": "Podcastilla ei ole RSS-syötettä", @@ -1134,5 +1153,13 @@ "ToastUserPasswordChangeSuccess": "Salasana vaihdettu onnistuneesti", "ToastUserPasswordMismatch": "Salasanat eivät täsmää", "ToastUserPasswordMustChange": "Uusi salasana ei voi olla sama kuin vanha salasana", - "ToastUserRootRequireName": "Pääkäyttäjän nimi on pakollinen" + "ToastUserRootRequireName": "Pääkäyttäjän nimi on pakollinen", + "TooltipAddChapters": "Lisää luku tai lukuja", + "TooltipAddOneSecond": "Lisää 1 sekunti", + "TooltipAdjustChapterStart": "Napauta säätääksesi aloitusaikaa", + "TooltipLockAllChapters": "Lukitse kaikki luvut", + "TooltipLockChapter": "Lukitse luku (Shift+napauta valitaksesi alueen)", + "TooltipSubtractOneSecond": "Vähennä 1 sekunti", + "TooltipUnlockAllChapters": "Avaa kaikki luvut", + "TooltipUnlockChapter": "Avaa luku (Shift+napauta valitaksesi alueen)" } From e8668d9f224ef4f3bd97ff7f1af82bfb3127a2b0 Mon Sep 17 00:00:00 2001 From: bittin1ddc447d824349b2 Date: Mon, 22 Dec 2025 12:58:36 +0100 Subject: [PATCH 16/19] Translated using Weblate (Swedish) Currently translated at 100.0% (1163 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/ --- client/strings/sv.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/strings/sv.json b/client/strings/sv.json index 7bd9753b..d2fb254e 100644 --- a/client/strings/sv.json +++ b/client/strings/sv.json @@ -821,7 +821,7 @@ "MessageImportantNotice": "Viktig meddelande!", "MessageInsertChapterBelow": "Infoga kapitel nedanför", "MessageInvalidAsin": "Felaktig ASIN-kod", - "MessageItemsSelected": "{0} objekt markerade", + "MessageItemsSelected": "{0} objekt valda", "MessageItemsUpdated": "{0} objekt uppdaterade", "MessageJoinUsOn": "Anslut dig till oss på", "MessageLoading": "Laddar...", From b387d9484a564bdaf347af93ba46e50c6181256d Mon Sep 17 00:00:00 2001 From: Marcin Date: Tue, 23 Dec 2025 05:54:25 +0100 Subject: [PATCH 17/19] Translated using Weblate (Polish) Currently translated at 89.2% (1038 of 1163 strings) Translation: Audiobookshelf/Abs Web Client Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/ --- client/strings/pl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/strings/pl.json b/client/strings/pl.json index 32c9766c..8b70a134 100644 --- a/client/strings/pl.json +++ b/client/strings/pl.json @@ -96,7 +96,7 @@ "ButtonScrollRight": "Przewiń w prawo", "ButtonSearch": "Szukaj", "ButtonSelectFolderPath": "Wybierz ścieżkę folderu", - "ButtonSeries": "Serial", + "ButtonSeries": "Serie", "ButtonSetChaptersFromTracks": "Ustawiaj rozdziały na podstawie utworów", "ButtonShare": "Udostępnij", "ButtonShiftTimes": "Przesunięcie czasowe", From e5c0a9d22c5148a4c1d6c2fee20c9669578eb1e3 Mon Sep 17 00:00:00 2001 From: advplyr Date: Tue, 23 Dec 2025 16:51:54 -0600 Subject: [PATCH 18/19] Version bump v2.32.1 --- 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 568274c8..1e2d52c1 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.32.0", + "version": "2.32.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.32.0", + "version": "2.32.1", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", diff --git a/client/package.json b/client/package.json index df71f625..0eaffb10 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.32.0", + "version": "2.32.1", "buildNumber": 1, "description": "Self-hosted audiobook and podcast client", "main": "index.js", diff --git a/package-lock.json b/package-lock.json index 4c9e9b9e..08707893 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf", - "version": "2.32.0", + "version": "2.32.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf", - "version": "2.32.0", + "version": "2.32.1", "license": "GPL-3.0", "dependencies": { "axios": "^0.27.2", diff --git a/package.json b/package.json index 2b0d0b6c..3ee3fb39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "2.32.0", + "version": "2.32.1", "buildNumber": 1, "description": "Self-hosted audiobook and podcast server", "main": "index.js", From 122fc34a75a6730f99736c3ae01186871b3d90ef Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 24 Dec 2025 17:07:05 -0600 Subject: [PATCH 19/19] Fix server crash filtering by decade with collapsed series --- server/utils/queries/libraryItemsBookFilters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index 494a9564..7ae1dc86 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -236,7 +236,7 @@ module.exports = { } else if (group === 'publishedDecades') { const startYear = parseInt(value) const endYear = parseInt(value, 10) + 9 - mediaWhere = Sequelize.where(Sequelize.literal('CAST(`book`.`publishedYear` AS INTEGER)'), { + mediaWhere = Sequelize.where(Sequelize.literal('CAST(publishedYear AS INTEGER)'), { [Sequelize.Op.between]: [startYear, endYear] }) }