From fbe9971a8b4fa3606ef8dbd8262a40ba03d40f3c Mon Sep 17 00:00:00 2001 From: votex001 Date: Wed, 3 Sep 2025 18:19:52 +0300 Subject: [PATCH 01/12] [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 c7572ba50..3dcfb0495 100644 --- a/client/components/ui/MultiSelect.vue +++ b/client/components/ui/MultiSelect.vue @@ -278,7 +278,7 @@ export default { }) }, insertNewItem(item) { - this.selected.push(item) + if (!this.selected.includes(item)) this.selected.push(item) this.$emit('input', this.selected) this.$emit('newItem', item) this.textInput = null diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 18abc66e7..4bc434cbd 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -287,7 +287,7 @@ export default { }) }, insertNewItem(item) { - this.selected.push(item) + if (!this.selected.find((i) => i.name === item.name)) this.selected.push(item) this.$emit('input', this.selected) this.$emit('newItem', item) this.textInput = null 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/12] 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 e30bc490c..241a47010 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -22,7 +22,7 @@ add_user() { declare -r descr="${4:-No description}" declare -r shell="${5:-/bin/false}" - if ! getent passwd | grep -q "^$user:"; then + if ! getent passwd "$user" 2>&1 >/dev/null; then echo "Creating system user: $user in $group with $descr and shell $shell" useradd $uid_flags --gid $group --no-create-home --system --shell $shell -c "$descr" $user fi @@ -39,7 +39,7 @@ add_group() { declare -r gid_flags="--gid $gid" fi - if ! getent group | grep -q "^$group:" ; then + if ! getent group "$group" 2>&1 >/dev/null; then echo "Creating system group: $group" groupadd $gid_flags --system $group fi From a5750deaaf824e99fec54848640c21d5395dd626 Mon Sep 17 00:00:00 2001 From: "Tomasz N." Date: Wed, 22 Oct 2025 23:02:49 +0200 Subject: [PATCH 03/12] 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 206068cc4..cf5c87b78 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/12] 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 fe1a61027..c25afe075 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/12] 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 c25afe075..fe1a61027 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/12] 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 e695ccb0c..f8543f1de 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/12] 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 cf5c87b78..af4405987 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/12] 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 2c12ffc1a..6ba01aa83 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 c079a1280..5c8cad75a 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/12] 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 1a95256ab..64c41619e 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/12] 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 316e84234..32c9766ca 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/12] 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 9d12d3096..881bd971b 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/12] 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 2d41d39ef..568274c80 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 33d66c276..df71f6256 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 d703a8edc..4c9e9b9ea 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 0aaf4f5af..2b0d0b6cc 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",