@@ -18,6 +18,7 @@ export default {
data() {
return {
selectedSeries: null,
+ originalSeriesSequence: null,
showSeriesForm: false
}
},
@@ -59,6 +60,7 @@ export default {
..._series
}
+ this.originalSeriesSequence = _series.sequence
this.showSeriesForm = true
},
addNewSeries() {
@@ -68,6 +70,7 @@ export default {
sequence: ''
}
+ this.originalSeriesSequence = null
this.showSeriesForm = true
},
submitSeriesForm() {
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 47b64c1a..939eb9f4 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Run every {0} at {1}",
"MessageSearchResultsFor": "Search results for",
"MessageSelected": "{0} selected",
+ "MessageSeriesSequenceCannotContainSpaces": "Series sequence cannot contain spaces",
"MessageServerCouldNotBeReached": "Server could not be reached",
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
"MessageShareExpirationWillBe": "Expiration will be {0}",
From cae874ef0577acbd2bdf4dc02f601caaaadebddf Mon Sep 17 00:00:00 2001
From: advplyr
Date: Tue, 20 May 2025 17:44:13 -0500
Subject: [PATCH 002/121] Update max allowed json request size #4250
---
client/components/modals/podcast/EpisodeFeed.vue | 4 ++--
server/Server.js | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/client/components/modals/podcast/EpisodeFeed.vue b/client/components/modals/podcast/EpisodeFeed.vue
index 08f2f38c..7ec14ccd 100644
--- a/client/components/modals/podcast/EpisodeFeed.vue
+++ b/client/components/modals/podcast/EpisodeFeed.vue
@@ -244,8 +244,8 @@ export default {
const sizeInMb = payloadSize / 1024 / 1024
const sizeInMbPretty = sizeInMb.toFixed(2) + 'MB'
console.log('Request size', sizeInMb)
- if (sizeInMb > 4.99) {
- return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 5Mb`)
+ if (sizeInMb > 9.99) {
+ return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 10Mb`)
}
this.processing = true
diff --git a/server/Server.js b/server/Server.js
index ef6ecb8a..17c959c0 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -310,7 +310,7 @@ class Server {
})
)
router.use(express.urlencoded({ extended: true, limit: '5mb' }))
- router.use(express.json({ limit: '5mb' }))
+ router.use(express.json({ limit: '10mb' }))
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
router.use('/hls', this.hlsRouter.router)
From 6d1f0b27df782b464de8cb3d04c51b02b34c91d5 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Thu, 22 May 2025 17:30:38 -0500
Subject: [PATCH 003/121] Fix MediaProgress not using the lastUpdate time sent
for local progress syncs
---
server/models/MediaProgress.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/server/models/MediaProgress.js b/server/models/MediaProgress.js
index a4a4185c..af3cbc5f 100644
--- a/server/models/MediaProgress.js
+++ b/server/models/MediaProgress.js
@@ -246,9 +246,10 @@ class MediaProgress extends Model {
// For local sync
if (progressPayload.lastUpdate) {
this.updatedAt = progressPayload.lastUpdate
+ this.changed('updatedAt', true)
}
- return this.save()
+ return this.save({ silent: !!progressPayload.lastUpdate })
}
}
From d03c338b489f2c10f977da34651d8f28321b0f50 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 24 May 2025 17:09:58 -0500
Subject: [PATCH 004/121] Fix log for podcast rss feed with no guid #4325
---
server/utils/podcastUtils.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js
index a7ecce8a..3a1df198 100644
--- a/server/utils/podcastUtils.js
+++ b/server/utils/podcastUtils.js
@@ -205,7 +205,7 @@ function extractEpisodeData(item) {
} else if (typeof guidItem?._ === 'string') {
episode.guid = guidItem._
} else {
- Logger.error(`[podcastUtils] Invalid guid ${item['guid']} for ${episode.enclosure.url}`)
+ Logger.error(`[podcastUtils] Invalid guid for ${episode.enclosure.url}`, item['guid'])
}
}
From f05a513767cbc1640fa54f547c645a89133c61d5 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sun, 25 May 2025 16:12:35 -0500
Subject: [PATCH 005/121] Fix m4b encoder bitrate preset selection #4337
---
client/components/widgets/EncoderOptionsCard.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/components/widgets/EncoderOptionsCard.vue b/client/components/widgets/EncoderOptionsCard.vue
index 9306274d..005563b4 100644
--- a/client/components/widgets/EncoderOptionsCard.vue
+++ b/client/components/widgets/EncoderOptionsCard.vue
@@ -162,7 +162,7 @@ export default {
} else {
// Find closest bitrate rounding up
const bitratesToMatch = [32, 64, 128, 192]
- const closestBitrate = bitratesToMatch.find((bitrate) => bitrate >= this.currentBitrate)
+ const closestBitrate = bitratesToMatch.find((bitrate) => bitrate >= this.currentBitrate) || 192
this.selectedBitrate = closestBitrate + 'k'
}
From 6ce1806359d9560b7aab45252d846434d8b79c4e Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 26 May 2025 16:56:50 -0500
Subject: [PATCH 006/121] Update pathexists file system API endpoint
---
client/pages/upload/index.vue | 5 +-
server/controllers/FileSystemController.js | 74 ++++++++++++++--------
2 files changed, 48 insertions(+), 31 deletions(-)
diff --git a/client/pages/upload/index.vue b/client/pages/upload/index.vue
index 2c9442ca..eef05b60 100644
--- a/client/pages/upload/index.vue
+++ b/client/pages/upload/index.vue
@@ -359,15 +359,14 @@ export default {
// Check if path already exists before starting upload
// uploading fails if path already exists
for (const item of items) {
- const filepath = Path.join(this.selectedFolder.fullPath, item.directory)
const exists = await this.$axios
- .$post(`/api/filesystem/pathexists`, { filepath, directory: item.directory, folderPath: this.selectedFolder.fullPath })
+ .$post(`/api/filesystem/pathexists`, { directory: item.directory, folderPath: this.selectedFolder.fullPath })
.then((data) => {
if (data.exists) {
if (data.libraryItemTitle) {
this.$toast.error(this.$getString('ToastUploaderItemExistsInSubdirectoryError', [data.libraryItemTitle]))
} else {
- this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [filepath]))
+ this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [Path.join(this.selectedFolder.fullPath, item.directory)]))
}
}
return data.exists
diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js
index d0b190a4..7629f9ee 100644
--- a/server/controllers/FileSystemController.js
+++ b/server/controllers/FileSystemController.js
@@ -84,49 +84,67 @@ class FileSystemController {
*/
async checkPathExists(req, res) {
if (!req.user.canUpload) {
- Logger.error(`[FileSystemController] Non-admin user "${req.user.username}" attempting to check path exists`)
+ Logger.error(`[FileSystemController] User "${req.user.username}" without upload permissions attempting to check path exists`)
return res.sendStatus(403)
}
- const { filepath, directory, folderPath } = req.body
+ const { directory, folderPath } = req.body
- if (!filepath?.length || typeof filepath !== 'string') {
+ if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') {
+ Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`)
+ return res.status(400).json({
+ error: 'Invalid request body'
+ })
+ }
+
+ // Check that library folder exists
+ const libraryFolder = await Database.libraryFolderModel.findOne({
+ where: {
+ path: folderPath
+ }
+ })
+
+ if (!libraryFolder) {
+ Logger.error(`[FileSystemController] Library folder not found: ${folderPath}`)
+ return res.sendStatus(404)
+ }
+
+ const filepath = Path.posix.join(libraryFolder.path, directory)
+ // Ensure filepath is inside library folder (prevents directory traversal)
+ if (!filepath.startsWith(libraryFolder.path)) {
+ Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`)
return res.sendStatus(400)
}
- const exists = await fs.pathExists(filepath)
-
- if (exists) {
+ if (await fs.pathExists(filepath)) {
return res.json({
exists: true
})
}
- // If directory and folderPath are passed in, check if a library item exists in a subdirectory
+ // Check if a library item exists in a subdirectory
// See: https://github.com/advplyr/audiobookshelf/issues/4146
- if (typeof directory === 'string' && typeof folderPath === 'string' && directory.length > 0 && folderPath.length > 0) {
- const cleanedDirectory = directory.split('/').filter(Boolean).join('/')
- if (cleanedDirectory.includes('/')) {
- // Can only be 2 levels deep
- const possiblePaths = []
- const subdir = Path.dirname(directory)
- possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir)))
- if (subdir.includes('/')) {
- possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir))))
- }
+ const cleanedDirectory = directory.split('/').filter(Boolean).join('/')
+ if (cleanedDirectory.includes('/')) {
+ // Can only be 2 levels deep
+ const possiblePaths = []
+ const subdir = Path.dirname(directory)
+ possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir)))
+ if (subdir.includes('/')) {
+ possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir))))
+ }
- const libraryItem = await Database.libraryItemModel.findOne({
- where: {
- path: possiblePaths
- }
+ const libraryItem = await Database.libraryItemModel.findOne({
+ where: {
+ path: possiblePaths
+ }
+ })
+
+ if (libraryItem) {
+ return res.json({
+ exists: true,
+ libraryItemTitle: libraryItem.title
})
-
- if (libraryItem) {
- return res.json({
- exists: true,
- libraryItemTitle: libraryItem.title
- })
- }
}
}
From 1119ddef8a12ddc64d566a6c52fd30a32a6b3dca Mon Sep 17 00:00:00 2001
From: advplyr
Date: Tue, 27 May 2025 17:56:27 -0500
Subject: [PATCH 007/121] Add RSS Feed Open filter for podcast libraries to
match book libraries #4335
---
client/components/controls/LibraryFilterSelect.vue | 5 +++++
server/utils/queries/libraryItemsPodcastFilters.js | 7 ++++---
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue
index 4fd72827..f5eec41a 100644
--- a/client/components/controls/LibraryFilterSelect.vue
+++ b/client/components/controls/LibraryFilterSelect.vue
@@ -276,6 +276,11 @@ export default {
text: this.$strings.ButtonIssues,
value: 'issues',
sublist: false
+ },
+ {
+ text: this.$strings.LabelRSSFeedOpen,
+ value: 'feed-open',
+ sublist: false
}
]
},
diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js
index 68097c1b..33bac28f 100644
--- a/server/utils/queries/libraryItemsPodcastFilters.js
+++ b/server/utils/queries/libraryItemsPodcastFilters.js
@@ -149,11 +149,12 @@ module.exports = {
libraryId
}
const libraryItemIncludes = []
- 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 === 'issues') {
From 68a39449a283b7a9229b3f993cff0f832d75b8aa Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Sat, 17 May 2025 18:30:05 +0200
Subject: [PATCH 008/121] Translated using Weblate (German)
Currently translated at 99.6% (1100 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/de.json b/client/strings/de.json
index b08db87c..03b850c4 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -974,6 +974,7 @@
"ToastCachePurgeFailed": "Cache leeren fehlgeschlagen",
"ToastCachePurgeSuccess": "Cache geleert",
"ToastChaptersHaveErrors": "Kapitel sind fehlerhaft",
+ "ToastChaptersInvalidShiftAmountLast": "Die Verschiebung ist nicht möglich, da die Startzeit des letzten Kapitels über die Gesamtdauer dieses Hörbuchs hinausgehen würde.",
"ToastChaptersMustHaveTitles": "Kapitel benötigen eindeutige Namen",
"ToastChaptersRemoved": "Kapitel entfernt",
"ToastChaptersUpdated": "Kapitel aktualisiert",
From 6425d95deb10db6b8492662499233af50d3e1df9 Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Sat, 17 May 2025 19:12:58 +0200
Subject: [PATCH 009/121] Translated using Weblate (Arabic)
Currently translated at 27.4% (303 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index b616e255..17fd494e 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -30,8 +30,8 @@
"ButtonEditChapters": "تعديل الفصول",
"ButtonEditPodcast": "تعديل البودكاست",
"ButtonEnable": "تفعيل",
- "ButtonFireAndFail": "النار والفشل",
- "ButtonFireOnTest": "حادثة إطلاق النار",
+ "ButtonFireAndFail": "محاولة فاشلة",
+ "ButtonFireOnTest": "تشغيل حدث الاختبار",
"ButtonForceReScan": "فرض إعادة المسح",
"ButtonFullPath": "المسار الكامل",
"ButtonHide": "إخفاء",
@@ -258,6 +258,40 @@
"LabelStatsItemsFinished": "العناصر المنتهية",
"LabelStatsMinutes": "دقائق",
"LabelStatsMinutesListening": "دقائق الاستماع",
+ "LabelStatsWeekListening": "استماع هذا الأسبوع",
+ "LabelTag": "علامة",
+ "LabelTags": "علامات",
+ "LabelTheme": "النمط",
+ "LabelThemeDark": "غامق",
+ "LabelThemeLight": "فاتح",
+ "LabelTimeRemaining": "{0} متبقية",
+ "LabelTitle": "عنوان",
+ "LabelTracks": "المسارات",
+ "LabelType": "نوع",
+ "LabelUnknown": "مجهول",
+ "LabelUser": "مستخدم",
+ "LabelUsername": "اسم المستخدم",
+ "LabelYearReviewHide": "إخفاء ملخص العام",
+ "LabelYearReviewShow": "عرض ملخص العام",
+ "LabelYourBookmarks": "علاماتك المرجعية",
+ "LabelYourProgress": "تقدمك",
+ "MessageDownloadingEpisode": "جاري تنزيل الحلقة",
+ "MessageEpisodesQueuedForDownload": "تمت إضافة {0} حلقة (حلقات) إلى قائمة انتظار التنزيل",
+ "MessageFeedURLWillBe": "سيكون رابط التغذية هو {0}",
+ "MessageFetching": "جاري الجلب...",
+ "MessageLoading": "جاري التحميل...",
+ "MessageMarkAsFinished": "وضع علامة \"تم الإنتهاء\"",
+ "MessageNoBookmarks": "لا توجد علامات مرجعية",
+ "MessageNoChapters": "لا توجد فصول",
+ "MessageNoCollections": "لا توجد مجموعات",
+ "MessageNoItems": "لا توجد عناصر",
+ "MessageNoItemsFound": "لم يتم العثور على عناصر",
+ "MessageNoListeningSessions": "لا توجد جلسات استماع",
+ "MessageNoPodcastsFound": "لم يتم العثور على أي بودكاست",
+ "MessageNoUpdatesWereNecessary": "لا حاجة لأي تحديثات",
+ "MessageNoUserPlaylists": "ليست لديك أي قوائم تشغيل",
+ "MessagePodcastSearchField": "أدخل مصطلح البحث أو عنوان URL لخلاصة RSS",
+ "MessageReportBugsAndContribute": "أبلغ عن الأخطاء، واطلب الميزات، وساهم في",
"NoteRSSFeedPodcastAppsHttps": "تحذير: تتطلب معظم تطبيقات البث الصوتي أن يكون عنوان URL لخلاصة RSS يستخدم HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "تحذير: حلقة واحدة أو أكثر من حلقاتك ليس لها تاريخ نشر. بعض تطبيقات البودكاست تتطلب هذا.",
"ToastBookmarkCreateFailed": "فشل في إنشاء الإشارة المرجعية",
From 273866fe92a61e3199503805e081c41a282e6fe1 Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Sat, 17 May 2025 21:35:33 +0200
Subject: [PATCH 010/121] Translated using Weblate (Arabic)
Currently translated at 36.5% (404 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 101 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index 17fd494e..717a88a0 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -10,6 +10,8 @@
"ButtonApplyChapters": "حفظ الفصول",
"ButtonAuthors": "المؤلفون",
"ButtonBack": "الرجوع",
+ "ButtonBatchEditPopulateFromExisting": "تعبئة من الموجود",
+ "ButtonBatchEditPopulateMapDetails": "تعبئة تفاصيل الخريطة",
"ButtonBrowseForFolder": "البحث عن المجلد",
"ButtonCancel": "إلغاء",
"ButtonCancelEncode": "إلغاء الترميز",
@@ -88,6 +90,8 @@
"ButtonSaveTracklist": "حفظ قائمة التشغيل",
"ButtonScan": "تَحَقُق",
"ButtonScanLibrary": "تَحَقُق من المكتبة",
+ "ButtonScrollLeft": "تمرير لليسار",
+ "ButtonScrollRight": "تمرير لليمين",
"ButtonSearch": "بحث",
"ButtonSelectFolderPath": "حدد مسار المجلد",
"ButtonSeries": "سلسلة",
@@ -153,30 +157,127 @@
"HeaderLogs": "السجلات",
"HeaderManageGenres": "إدارة الانواع",
"HeaderManageTags": "إدارة العلامات",
+ "HeaderMapDetails": "تفاصيل الخريطة",
+ "HeaderMatch": "مطابقة",
+ "HeaderMetadataOrderOfPrecedence": "ترتيب أولوية البيانات الوصفية",
+ "HeaderMetadataToEmbed": "البيانات الوصفية المراد تضمينها",
+ "HeaderNewAccount": "حساب جديد",
+ "HeaderNewLibrary": "مكتبة جديدة",
+ "HeaderNotificationCreate": "إنشاء إشعار",
+ "HeaderNotificationUpdate": "تحديث إشعار",
+ "HeaderNotifications": "إشعارات",
+ "HeaderOpenIDConnectAuthentication": "مصادقة OpenID Connect",
+ "HeaderOpenListeningSessions": "جلسات الاستماع المفتوحة",
"HeaderOpenRSSFeed": "فتح تغذية RSS",
+ "HeaderOtherFiles": "ملفات أخرى",
+ "HeaderPasswordAuthentication": "مصادقة كلمة المرور",
+ "HeaderPermissions": "الصلاحيات",
+ "HeaderPlayerQueue": "قائمة انتظار المشغل",
+ "HeaderPlayerSettings": "إعدادات المشغل",
"HeaderPlaylist": "قائمة تشغيل",
"HeaderPlaylistItems": "عناصر قائمة التشغيل",
+ "HeaderPodcastsToAdd": "بودكاست لإضافتها",
+ "HeaderPresets": "إعدادات مسبقة",
+ "HeaderPreviewCover": "معاينة الغلاف",
"HeaderRSSFeedGeneral": "تفاصيل RSS",
"HeaderRSSFeedIsOpen": "مغذي RSS مفتوح",
+ "HeaderRSSFeeds": "خلاصات RSS",
+ "HeaderRemoveEpisode": "إزالة حلقة",
+ "HeaderRemoveEpisodes": "إزالة {0} حلقات",
+ "HeaderSavedMediaProgress": "تقدم الوسائط المحفوظة",
+ "HeaderSchedule": "جَدْوَل",
+ "HeaderScheduleEpisodeDownloads": "جدولة التنزيلات التلقائية للحلقات",
+ "HeaderScheduleLibraryScans": "جدولة عمليات المسح التلقائي للمكتبة",
+ "HeaderSession": "الجلسة",
+ "HeaderSetBackupSchedule": "تعيين جدول النسخ الاحتياطي",
"HeaderSettings": "إعدادات",
+ "HeaderSettingsDisplay": "عرض",
+ "HeaderSettingsExperimental": "ميزات تجريبية",
+ "HeaderSettingsGeneral": "عام",
+ "HeaderSettingsScanner": "إعدادات المسح",
+ "HeaderSettingsWebClient": "عميل الويب",
"HeaderSleepTimer": "مؤقت النوم",
+ "HeaderStatsLargestItems": "أكبر العناصر حجماً",
+ "HeaderStatsLongestItems": "أطول العناصر (بالساعات)",
"HeaderStatsMinutesListeningChart": "الدقائق المسموعة (آخر 7 أيام)",
"HeaderStatsRecentSessions": "الجلسات الأخيرة",
+ "HeaderStatsTop10Authors": "أفضل 10 مؤلفين",
+ "HeaderStatsTop5Genres": "أفضل 5 أنواع",
"HeaderTableOfContents": "جدول المحتويات",
+ "HeaderTools": "أدوات",
+ "HeaderUpdateAccount": "تحديث الحساب",
+ "HeaderUpdateAuthor": "تحديث المؤلف",
+ "HeaderUpdateDetails": "تحديث التفاصيل",
+ "HeaderUpdateLibrary": "تحديث المكتبة",
+ "HeaderUsers": "المستخدمون",
+ "HeaderYearReview": "ملخص عام {0}",
"HeaderYourStats": "إحصائياتك",
+ "LabelAbridged": "مختصر",
+ "LabelAbridgedChecked": "مختصر (محدد)",
+ "LabelAbridgedUnchecked": "غير مختصر (غير محدد)",
+ "LabelAccessibleBy": "يمكن الوصول إليه بواسطة",
+ "LabelAccountType": "نوع الحساب",
+ "LabelAccountTypeAdmin": "مدير",
+ "LabelAccountTypeGuest": "ضيف",
+ "LabelAccountTypeUser": "مستخدم",
+ "LabelActivities": "النشاطات",
+ "LabelActivity": "نشاط",
+ "LabelAddToCollection": "إضافة إلى المجموعة",
+ "LabelAddToCollectionBatch": "إضافة {0} كتابًا إلى المجموعة",
"LabelAddToPlaylist": "أضف إلى قائمة التشغيل",
+ "LabelAddToPlaylistBatch": "إضافة {0} عناصر إلى قائمة التشغيل",
"LabelAddedAt": "أضيفت على",
"LabelAddedDate": "تمت الإضافة",
+ "LabelAdminUsersOnly": "للمستخدمين المديرين فقط",
"LabelAll": "الكل",
+ "LabelAllEpisodesDownloaded": "تم تنزيل جميع الحلقات",
+ "LabelAllUsers": "جميع المستخدمين",
+ "LabelAllUsersExcludingGuests": "جميع المستخدمين باستثناء الضيوف",
+ "LabelAllUsersIncludingGuests": "جميع المستخدمين بما في ذلك الضيوف",
+ "LabelAlreadyInYourLibrary": "موجود بالفعل في مكتبتك",
+ "LabelApiToken": "رمز API",
+ "LabelAppend": "إلحاق",
+ "LabelAudioBitrate": "معدل بت الصوت (على سبيل المثال 128 كيلو بايت)",
+ "LabelAudioChannels": "قنوات الصوت (1 أو 2)",
+ "LabelAudioCodec": "برنامج ترميز الصوت",
"LabelAuthor": "المؤلف",
"LabelAuthorFirstLast": "المؤلف (الاسم الأول الأخير)",
"LabelAuthorLastFirst": "المؤلف (الاسم الأخير، الأول)",
"LabelAuthors": "المؤلفون",
"LabelAutoDownloadEpisodes": "تنزيل الحلقات تلقائيًا",
+ "LabelAutoFetchMetadata": "جلب البيانات الوصفية تلقائيًا",
+ "LabelAutoFetchMetadataHelp": "يجلب البيانات الوصفية للعنوان والمؤلف والسلسلة لتسهيل عملية الرفع. قد يلزم مطابقة بيانات وصفية إضافية بعد الرفع.",
+ "LabelAutoLaunch": "تشغيل تلقائي",
+ "LabelAutoLaunchDescription": "إعادة التوجيه إلى مزود المصادقة تلقائيًا عند الانتقال إلى صفحة تسجيل الدخول (مسار التجاوز اليدوي /login?autoLaunch=0)",
+ "LabelAutoRegister": "تسجيل تلقائي",
+ "LabelAutoRegisterDescription": "إنشاء مستخدمين جدد تلقائيًا بعد تسجيل الدخول",
+ "LabelBackToUser": "العودة إلى المستخدم",
+ "LabelBackupAudioFiles": "نسخ ملفات الصوت احتياطيًا",
+ "LabelBackupLocation": "موقع النسخ الاحتياطي",
+ "LabelBackupsEnableAutomaticBackups": "نسخ احتياطية تلقائية",
+ "LabelBackupsEnableAutomaticBackupsHelp": "النسخ الاحتياطية المحفوظة في / البيانات الوصفية / النسخ الاحتياطية",
+ "LabelBackupsMaxBackupSize": "الحد الأقصى لحجم النسخ الاحتياطي (بالجيجابايت) (0 لغير محدود)",
+ "LabelBackupsMaxBackupSizeHelp": "كإجراء وقائي ضد سوء التكوين، ستفشل عمليات النسخ الاحتياطي إذا تجاوزت الحجم المحدد.",
+ "LabelBackupsNumberToKeep": "عدد النسخ الاحتياطية التي يجب الاحتفاظ بها",
+ "LabelBackupsNumberToKeepHelp": "ستتم إزالة نسخة احتياطية واحدة فقط في كل مرة، لذا إذا كان لديك بالفعل عدد نسخ احتياطية أكبر من هذا، فيجب عليك إزالتها يدويًا.",
+ "LabelBitrate": "معدل البت",
+ "LabelBonus": "مكافأة",
"LabelBooks": "الكتب",
+ "LabelButtonText": "نص الزر",
+ "LabelByAuthor": "بواسطة {0}",
+ "LabelChangePassword": "تغيير كلمة المرور",
+ "LabelChannels": "قنوات",
+ "LabelChapterCount": "{0} فصول",
+ "LabelChapterTitle": "عنوان الفصل",
"LabelChapters": "الفصول",
+ "LabelChaptersFound": "تم العثور على فصول",
+ "LabelClickForMoreInfo": "انقر لمزيد من المعلومات",
+ "LabelClickToUseCurrentValue": "انقر لاستخدام القيمة الحالية",
"LabelClosePlayer": "إغلاق المشغل",
+ "LabelCodec": "برنامج الترميز",
"LabelCollapseSeries": "إخفاء المسلسلات",
+ "LabelCollapseSubSeries": "إخفاء المسلسلات الفرعية",
+ "LabelCollection": "مجموعة",
"LabelComplete": "مكتمل",
"LabelContinueListening": "استمرار الاستماع",
"LabelContinueReading": "استمرار القراءة",
From 7486a0659b1a865dca1c5a3d720ef46d7a8e56a9 Mon Sep 17 00:00:00 2001
From: SunSpring
Date: Sun, 18 May 2025 10:47:04 +0200
Subject: [PATCH 011/121] Translated using Weblate (Chinese (Simplified Han
script))
Currently translated at 100.0% (1104 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
---
client/strings/zh-cn.json | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/client/strings/zh-cn.json b/client/strings/zh-cn.json
index b6365b6e..21293a4a 100644
--- a/client/strings/zh-cn.json
+++ b/client/strings/zh-cn.json
@@ -177,6 +177,7 @@
"HeaderPlaylist": "播放列表",
"HeaderPlaylistItems": "播放列表项目",
"HeaderPodcastsToAdd": "要添加的播客",
+ "HeaderPresets": "预设",
"HeaderPreviewCover": "预览封面",
"HeaderRSSFeedGeneral": "RSS 详细信息",
"HeaderRSSFeedIsOpen": "RSS 源已打开",
@@ -530,6 +531,7 @@
"LabelReleaseDate": "发布日期",
"LabelRemoveAllMetadataAbs": "删除所有 metadata.abs 文件",
"LabelRemoveAllMetadataJson": "删除所有 metadata.json 文件",
+ "LabelRemoveAudibleBranding": "删除章节中的 Audible 简介和结尾",
"LabelRemoveCover": "移除封面",
"LabelRemoveMetadataFile": "删除库项目文件夹中的元数据文件",
"LabelRemoveMetadataFileHelp": "删除 {0} 文件夹中的所有 metadata.json 和 metadata.abs 文件.",
@@ -706,6 +708,7 @@
"MessageAddToPlayerQueue": "添加到播放队列",
"MessageAppriseDescription": "要使用此功能,你需要运行一个 Apprise API 实例或一个可以处理这些相同请求的 API. Apprise API Url 应该是发送通知的完整 URL 路径, 例如: 如果你的 API 实例运行在 http://192.168.1.1:8337, 那么你可以输入 http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "确保你使用的 ASIN 来自正确的 Audible 地区, 而不是亚马逊.",
+ "MessageAuthenticationOIDCChangesRestart": "保存后重新启动服务器以应用 OIDC 更改.",
"MessageBackupsDescription": "备份包括用户, 用户进度, 媒体库项目详细信息, 服务器设置和图像, 存储在 /metadata/items & /metadata/authors. 备份不包括存储在你的媒体库文件夹中的任何文件.",
"MessageBackupsLocationEditNote": "注意: 更新备份位置不会移动或修改现有备份",
"MessageBackupsLocationNoEditNote": "注意: 备份位置是通过环境变量设置的, 不能在此处更改.",
@@ -971,6 +974,8 @@
"ToastCachePurgeFailed": "清除缓存失败",
"ToastCachePurgeSuccess": "缓存清除成功",
"ToastChaptersHaveErrors": "章节有错误",
+ "ToastChaptersInvalidShiftAmountLast": "偏移量无效. 最后一章的开始时间将超过这本有声读物的持续时间.",
+ "ToastChaptersInvalidShiftAmountStart": "偏移量无效. 第一章的长度将为零或负数, 并会被第二章覆盖. 请增加第二章的起始时长.",
"ToastChaptersMustHaveTitles": "章节必须有标题",
"ToastChaptersRemoved": "已删除章节",
"ToastChaptersUpdated": "章节已更新",
From 535ebc10f030e68481b5a8389498697b4899ee3d Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Sat, 17 May 2025 22:25:45 +0200
Subject: [PATCH 012/121] Translated using Weblate (Arabic)
Currently translated at 98.5% (1088 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 692 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 688 insertions(+), 4 deletions(-)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index 717a88a0..5c9265b5 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -278,129 +278,813 @@
"LabelCollapseSeries": "إخفاء المسلسلات",
"LabelCollapseSubSeries": "إخفاء المسلسلات الفرعية",
"LabelCollection": "مجموعة",
+ "LabelCollections": "مجموعات",
"LabelComplete": "مكتمل",
+ "LabelConfirmPassword": "تأكيد كلمة المرور",
"LabelContinueListening": "استمرار الاستماع",
"LabelContinueReading": "استمرار القراءة",
"LabelContinueSeries": "استمرار المسلسلات",
+ "LabelCover": "الغلاف",
+ "LabelCoverImageURL": "رابط صورة الغلاف",
+ "LabelCoverProvider": "مزود الغلاف",
+ "LabelCreatedAt": "تاريخ الإنشاء",
+ "LabelCronExpression": "تعبير Cron",
+ "LabelCurrent": "الحالي",
+ "LabelCurrently": "حاليًا:",
+ "LabelCustomCronExpression": "تعبير Cron مخصص:",
+ "LabelDatetime": "التاريخ والوقت",
+ "LabelDays": "أيام",
+ "LabelDeleteFromFileSystemCheckbox": "حذف من نظام الملفات (إلغاء التحديد للإزالة من قاعدة البيانات فقط)",
"LabelDescription": "الوصف",
+ "LabelDeselectAll": "إلغاء تحديد الكل",
+ "LabelDevice": "الجهاز",
+ "LabelDeviceInfo": "معلومات الجهاز",
+ "LabelDeviceIsAvailableTo": "الجهاز متاح لـ...",
+ "LabelDirectory": "مجلد / دليل",
+ "LabelDiscFromFilename": "القرص من اسم الملف",
+ "LabelDiscFromMetadata": "القرص من البيانات الوصفية",
"LabelDiscover": "استكشف",
"LabelDownload": "تنزيل",
+ "LabelDownloadNEpisodes": "تنزيل {0} حلقات",
+ "LabelDownloadable": "قابل للتنزيل",
"LabelDuration": "المدة",
+ "LabelDurationComparisonExactMatch": "(تطابق تام)",
+ "LabelDurationComparisonLonger": "(أطول بـ {0})",
+ "LabelDurationComparisonShorter": "({0} أقصر)",
+ "LabelDurationFound": "المدة الموجودة:",
"LabelEbook": "الكتاب الإلكتروني",
"LabelEbooks": "الكتب الإلكترونية",
+ "LabelEdit": "تعديل",
+ "LabelEmail": "البريد الإلكتروني",
+ "LabelEmailSettingsFromAddress": "عنوان المرسل",
+ "LabelEmailSettingsRejectUnauthorized": "رفض الشهادات غير المصرح بها",
+ "LabelEmailSettingsRejectUnauthorizedHelp": "قد يؤدي تعطيل التحقق من شهادة SSL إلى تعريض اتصالك لمخاطر أمنية، مثل هجمات الوسيط. لا تقم بتعطيل هذا الخيار إلا إذا كنت تفهم الآثار المترتبة عليه وتثق في خادم البريد الذي تتصل به.",
+ "LabelEmailSettingsSecure": "آمن",
+ "LabelEmailSettingsSecureHelp": "إذا كانت القيمة true، فسيستخدم الاتصال TLS عند الاتصال بالخادم. وإذا كانت false، فسيتم استخدام TLS إذا كان الخادم يدعم امتداد STARTTLS. في معظم الحالات، اضبط هذه القيمة على true إذا كنت تتصل بالمنفذ 465. أما بالنسبة للمنفذ 587 أو 25، فاحتفظ بها على false. (من nodemailer.com/smtp/#authentication)",
+ "LabelEmailSettingsTestAddress": "عنوان الاختبار",
+ "LabelEmbeddedCover": "غلاف مضمن",
"LabelEnable": "تمكين",
+ "LabelEncodingBackupLocation": "سيتم تخزين نسخة احتياطية من ملفاتك الصوتية الأصلية في:",
+ "LabelEncodingChaptersNotEmbedded": "الفصول غير مضمنة في الكتب الصوتية متعددة المسارات.",
+ "LabelEncodingClearItemCache": "تأكد من مسح ذاكرة التخزين المؤقت للعناصر بشكل دوري.",
+ "LabelEncodingFinishedM4B": "سيتم وضع ملف M4B النهائي في مجلد الكتب الصوتية الخاص بك في:",
+ "LabelEncodingInfoEmbedded": "سيتم تضمين البيانات الوصفية في المسارات الصوتية داخل مجلد الكتب الصوتية الخاص بك.",
+ "LabelEncodingStartedNavigation": "بمجرد بدء المهمة، يمكنك الانتقال من هذه الصفحة.",
+ "LabelEncodingTimeWarning": "قد تستغرق عملية الترميز ما يصل إلى 30 دقيقة.",
+ "LabelEncodingWarningAdvancedSettings": "تحذير: لا تقم بتحديث هذه الإعدادات إلا إذا كنت على دراية بخيارات ترميز ffmpeg.",
+ "LabelEncodingWatcherDisabled": "إذا قمت بتعطيل المراقب، فستحتاج إلى إعادة فحص هذا الكتاب الصوتي بعد ذلك.",
"LabelEnd": "انهاء",
"LabelEndOfChapter": "نهاية الفصل",
"LabelEpisode": "الحلقة",
+ "LabelEpisodeNotLinkedToRssFeed": "الحلقة غير مرتبطة بخلاصة RSS",
+ "LabelEpisodeNumber": "الحلقة #{0}",
+ "LabelEpisodeTitle": "عنوان الحلقة",
+ "LabelEpisodeType": "نوع الحلقة",
+ "LabelEpisodeUrlFromRssFeed": "رابط الحلقة من خلاصة RSS",
+ "LabelEpisodes": "حلقات",
+ "LabelEpisodic": "عرضي / حلقي",
+ "LabelExample": "مثال",
+ "LabelExpandSeries": "توسيع السلاسل",
+ "LabelExpandSubSeries": "توسيع السلاسل الفرعية",
+ "LabelExplicit": "صريح",
+ "LabelExplicitChecked": "صريح (محدد)",
+ "LabelExplicitUnchecked": "غير صريح (غير محدد)",
+ "LabelExportOPML": "تصدير OPML",
"LabelFeedURL": "عنوان التغذية",
+ "LabelFetchingMetadata": "جلب البيانات الوصفية",
"LabelFile": "الملف",
"LabelFileBirthtime": "وقت انشاء الملف",
+ "LabelFileBornDate": "تاريخ الإنشاء {0}",
"LabelFileModified": "تم تعديل الملف",
+ "LabelFileModifiedDate": "تم التعديل في {0}",
"LabelFilename": "اسم الملف",
+ "LabelFilterByUser": "تصفية حسب المستخدم",
+ "LabelFindEpisodes": "البحث عن حلقات",
"LabelFinished": "المنجزة",
"LabelFolder": "المجلد",
+ "LabelFolders": "مجلدات",
+ "LabelFontBold": "عريض",
"LabelFontBoldness": "تعريض الخط",
+ "LabelFontFamily": "عائلة الخط",
+ "LabelFontItalic": "مائل",
"LabelFontScale": "نطاق الخط",
+ "LabelFontStrikethrough": "يتوسطه خط",
+ "LabelFormat": "تنسيق",
+ "LabelFull": "كامل",
"LabelGenre": "التصنيف",
"LabelGenres": "التصانيف",
+ "LabelHardDeleteFile": "حذف الملف نهائيًا",
"LabelHasEbook": "يحتوي كتاب إلكتروني",
"LabelHasSupplementaryEbook": "يحتوي كتاب إلكتروني تكميلي",
+ "LabelHideSubtitles": "إخفاء الترجمة",
+ "LabelHighestPriority": "الأولوية القصوى",
"LabelHost": "المضيف",
+ "LabelHour": "ساعة",
+ "LabelHours": "ساعات",
+ "LabelIcon": "أيقونة",
+ "LabelImageURLFromTheWeb": "رابط الصورة من الويب",
"LabelInProgress": "تحت التنفيذ",
+ "LabelIncludeInTracklist": "تضمين في قائمة المسارات",
"LabelIncomplete": "غير مكتمل",
+ "LabelInterval": "فاصل زمني",
+ "LabelIntervalCustomDailyWeekly": "يومي/أسبوعي مخصص",
+ "LabelIntervalEvery12Hours": "كل 12 ساعة",
+ "LabelIntervalEvery15Minutes": "كل 15 دقيقة",
+ "LabelIntervalEvery2Hours": "كل ساعتين",
+ "LabelIntervalEvery30Minutes": "كل 30 دقيقة",
+ "LabelIntervalEvery6Hours": "كل 6 ساعات",
+ "LabelIntervalEveryDay": "كل يوم",
+ "LabelIntervalEveryHour": "كل ساعة",
+ "LabelIntervalEveryMinute": "كل دقيقة",
+ "LabelInvert": "عكس",
+ "LabelItem": "عنصر",
+ "LabelJumpBackwardAmount": "مقدار الرجوع للخلف",
+ "LabelJumpForwardAmount": "مقدار التقدم للأمام",
"LabelLanguage": "اللغة",
+ "LabelLanguageDefaultServer": "لغة الخادم الافتراضية",
+ "LabelLanguages": "اللغات",
+ "LabelLastBookAdded": "آخر كتاب تمت إضافته",
+ "LabelLastBookUpdated": "آخر كتاب تم تحديثه",
+ "LabelLastSeen": "آخر ظهور",
+ "LabelLastTime": "آخر مرة",
+ "LabelLastUpdate": "آخر تحديث",
"LabelLayout": "التنسيق",
"LabelLayoutSinglePage": "صفحة واحدة",
+ "LabelLayoutSplitPage": "صفحتان متقابلتان",
+ "LabelLess": "أقل",
+ "LabelLibrariesAccessibleToUser": "المكتبات المتاحة للمستخدم",
+ "LabelLibrary": "مكتبة",
+ "LabelLibraryFilterSublistEmpty": "لا يوجد {0}",
+ "LabelLibraryItem": "عنصر المكتبة",
+ "LabelLibraryName": "اسم المكتبة",
+ "LabelLimit": "حد",
"LabelLineSpacing": "تباعد الأسطر",
"LabelListenAgain": "الاستماع مجدداً",
+ "LabelLogLevelDebug": "تصحيح الأخطاء",
+ "LabelLogLevelInfo": "معلومات",
+ "LabelLogLevelWarn": "تحذير",
+ "LabelLookForNewEpisodesAfterDate": "البحث عن حلقات جديدة بعد هذا التاريخ",
+ "LabelLowestPriority": "الأولوية الأدنى",
+ "LabelMatchExistingUsersBy": "مطابقة المستخدمين الحاليين بواسطة",
+ "LabelMatchExistingUsersByDescription": "يستخدم لربط المستخدمين الحاليين. بمجرد الاتصال، سيتم مطابقة المستخدمين بواسطة معرف فريد من مزود SSO الخاص بك",
+ "LabelMaxEpisodesToDownload": "الحد الأقصى لعدد الحلقات التي سيتم تنزيلها. استخدم 0 لغير محدود.",
+ "LabelMaxEpisodesToDownloadPerCheck": "الحد الأقصى لعدد الحلقات الجديدة التي سيتم تنزيلها في كل فحص",
+ "LabelMaxEpisodesToKeep": "الحد الأقصى لعدد الحلقات التي سيتم الاحتفاظ بها",
+ "LabelMaxEpisodesToKeepHelp": "القيمة 0 لا تضع حدًا أقصى. بعد تنزيل حلقة جديدة تلقائيًا، سيؤدي هذا إلى حذف أقدم حلقة إذا كان لديك أكثر من X حلقة. سيؤدي هذا إلى حذف حلقة واحدة فقط لكل تنزيل جديد.",
+ "LabelMediaPlayer": "مشغل الوسائط",
"LabelMediaType": "نوع الوسائط",
+ "LabelMetaTag": "علامة بيانات وصفية",
+ "LabelMetaTags": "علامات البيانات الوصفية",
+ "LabelMetadataOrderOfPrecedenceDescription": "ستتجاوز مصادر البيانات الوصفية ذات الأولوية الأعلى مصادر البيانات الوصفية ذات الأولوية الأقل",
+ "LabelMetadataProvider": "مزود البيانات الوصفية",
+ "LabelMinute": "دقيقة",
+ "LabelMinutes": "دقائق",
"LabelMissing": "مفقود",
+ "LabelMissingEbook": "لا يوجد كتاب إلكتروني",
+ "LabelMissingSupplementaryEbook": "لا يوجد كتاب إلكتروني تكميلي",
+ "LabelMobileRedirectURIs": "معرفات URI لإعادة التوجيه المسموح بها لتطبيقات الجوال",
+ "LabelMobileRedirectURIsDescription": "هذه قائمة بيضاء لمعرفات URI لإعادة التوجيه الصالحة لتطبيقات الجوال. المعرف الافتراضي هو audiobookshelf://oauth، والذي يمكنك إزالته أو استكماله بمعرفات URI إضافية لتكامل تطبيقات الطرف الثالث. استخدام علامة النجمة (*) كإدخال وحيد يسمح بأي معرف URI.",
"LabelMore": "أكثر",
"LabelMoreInfo": "معلومات أكثر",
"LabelName": "الاسم",
"LabelNarrator": "الراوي",
"LabelNarrators": "الرواة",
+ "LabelNew": "جديد",
+ "LabelNewPassword": "كلمة سر جديدة",
"LabelNewestAuthors": "أجدد المؤلفين",
"LabelNewestEpisodes": "أجدد الحلقات",
+ "LabelNextBackupDate": "تاريخ النسخ الاحتياطي التالي",
+ "LabelNextScheduledRun": "التشغيل المجدول التالي",
+ "LabelNoCustomMetadataProviders": "لا يوجد مزودو بيانات وصفية مخصصون",
+ "LabelNoEpisodesSelected": "لم يتم تحديد أي حلقات",
"LabelNotFinished": "لم يتم الانتهاء",
"LabelNotStarted": "لم يتم البدء",
+ "LabelNotes": "ملاحظات",
+ "LabelNotificationAppriseURL": "رابط (روابط) Apprise",
+ "LabelNotificationAvailableVariables": "المتغيرات المتاحة",
+ "LabelNotificationBodyTemplate": "قالب النص",
+ "LabelNotificationEvent": "حدث الإشعار",
+ "LabelNotificationTitleTemplate": "قالب العنوان",
+ "LabelNotificationsMaxFailedAttempts": "الحد الأقصى لعدد المحاولات الفاشلة",
+ "LabelNotificationsMaxFailedAttemptsHelp": "يتم تعطيل الإشعارات بمجرد فشل إرسالها لهذا العدد من المرات",
+ "LabelNotificationsMaxQueueSize": "الحد الأقصى لحجم قائمة انتظار أحداث الإشعارات",
+ "LabelNotificationsMaxQueueSizeHelp": "تقتصر الأحداث على التشغيل مرة واحدة في الثانية. سيتم تجاهل الأحداث إذا كانت قائمة الانتظار في الحد الأقصى لحجمها. هذا يمنع إرسال الإشعارات بشكل متكرر.",
+ "LabelNumberOfBooks": "عدد الكتب",
"LabelNumberOfEpisodes": "# من الحلقات",
+ "LabelOpenIDAdvancedPermsClaimDescription": "اسم مطالبة OpenID التي تحتوي على أذونات متقدمة لإجراءات المستخدم داخل التطبيق والتي ستطبق على الأدوار غير الإدارية (إذا تم تكوينها). إذا كانت المطالبة مفقودة من الاستجابة، فسيتم رفض الوصول إلى ABS. إذا كان هناك خيار واحد مفقودًا، فسيتم التعامل معه على أنه false. تأكد من أن مطالبة موفر الهوية تطابق البنية المتوقعة:",
+ "LabelOpenIDClaims": "اترك الخيارات التالية فارغة لتعطيل تعيين المجموعة والأذونات المتقدمة، وسيتم تعيين مجموعة \"مستخدم\" تلقائيًا بعد ذلك.",
+ "LabelOpenIDGroupClaimDescription": "اسم مطالبة OpenID التي تحتوي على قائمة بمجموعات المستخدم. يشار إليها عادةً باسم groups.إذا تم تكوينها، فسيقوم التطبيق تلقائيًا بتعيين الأدوار بناءً على عضويات مجموعة المستخدم، بشرط أن تسمى هذه المجموعات بشكل غير حساس لحالة الأحرف \"admin\" أو \"user\" أو \"guest\" في المطالبة. يجب أن تحتوي المطالبة على قائمة، وإذا كان المستخدم ينتمي إلى مجموعات متعددة، فسيقوم التطبيق بتعيين الدور المقابل لأعلى مستوى من الوصول. إذا لم تتطابق أي مجموعة، فسيتم رفض الوصول.",
+ "LabelOpenRSSFeed": "فتح تغذية RSS",
+ "LabelOverwrite": "استبدال",
+ "LabelPaginationPageXOfY": "صفحة {0} من {1}",
"LabelPassword": "كلمة المرور",
"LabelPath": "مسار",
+ "LabelPermanent": "دائم",
+ "LabelPermissionsAccessAllLibraries": "يمكنه الوصول إلى جميع المكتبات",
+ "LabelPermissionsAccessAllTags": "يمكنه الوصول إلى جميع العلامات",
+ "LabelPermissionsAccessExplicitContent": "يمكنه الوصول إلى المحتوى الصريح",
+ "LabelPermissionsCreateEreader": "يمكنه إنشاء قارئ إلكتروني",
+ "LabelPermissionsDelete": "يمكنه الحذف",
+ "LabelPermissionsDownload": "يمكنه التنزيل",
+ "LabelPermissionsUpdate": "يمكنه التحديث",
+ "LabelPermissionsUpload": "يمكنه الرفع",
+ "LabelPersonalYearReview": "ملخص عامك ({0})",
+ "LabelPhotoPathURL": "مسار/رابط الصورة",
+ "LabelPlayMethod": "طريقة التشغيل",
+ "LabelPlaybackRateIncrementDecrement": "مقدار زيادة/نقصان سرعة التشغيل",
+ "LabelPlayerChapterNumberMarker": "{0} من {1}",
+ "LabelPlaylists": "قوائم التشغيل",
"LabelPodcast": "مدونة صوتية",
- "LabelPodcasts": "مدونات صوتية",
+ "LabelPodcastSearchRegion": "منطقة البحث عن البودكاست",
+ "LabelPodcastType": "نوع البودكاست",
+ "LabelPodcasts": "بودكاست",
+ "LabelPort": "منفذ",
+ "LabelPrefixesToIgnore": "البادئات التي يجب تجاهلها (غير حساسة لحالة الأحرف)",
"LabelPreventIndexing": "منع فهرسة تغذيتك بواسطة دليل آيتونز وقوقل بودكاست",
+ "LabelPrimaryEbook": "الكتاب الإلكتروني الأساسي",
"LabelProgress": "تقدم",
+ "LabelProvider": "مزود",
+ "LabelProviderAuthorizationValue": "قيمة رأس التفويض",
"LabelPubDate": "تاريخ النشر",
"LabelPublishYear": "سنة النشر",
"LabelPublishedDate": "منشور {0}",
+ "LabelPublishedDecade": "عقد النشر",
+ "LabelPublishedDecades": "عقود النشر",
+ "LabelPublisher": "الناشر",
+ "LabelPublishers": "الناشرون",
"LabelRSSFeedCustomOwnerEmail": "البريد الالكتروني المخصص للمالك",
"LabelRSSFeedCustomOwnerName": "الاسم المخصص للمالك",
+ "LabelRSSFeedOpen": "فتح تغذية RSS",
"LabelRSSFeedPreventIndexing": "منع الفهرسة",
- "LabelRSSFeedSlug": "رابط تغذية RSS",
+ "LabelRSSFeedSlug": "اسم تعريف تغذية RSS",
+ "LabelRSSFeedURL": "رابط تغذية RSS",
"LabelRandomly": "عشوائياً",
+ "LabelReAddSeriesToContinueListening": "إعادة إضافة السلسلة إلى \"متابعة الاستماع\"",
"LabelRead": "اقرأ",
"LabelReadAgain": "اقرأ مرة أخرى",
+ "LabelReadEbookWithoutProgress": "قراءة الكتاب الإلكتروني دون حفظ التقدم",
"LabelRecentSeries": "المسلسلات الحديثة",
"LabelRecentlyAdded": "المضافة حديثاً",
+ "LabelRecommended": "موصى به",
+ "LabelRedo": "إعادة",
+ "LabelRegion": "المنطقة",
+ "LabelReleaseDate": "تاريخ الإصدار",
+ "LabelRemoveAllMetadataAbs": "إزالة جميع ملفات metadata.abs",
+ "LabelRemoveAllMetadataJson": "إزالة جميع ملفات metadata.json",
+ "LabelRemoveAudibleBranding": "إزالة مقدمة وخاتمة Audible من الفصول",
+ "LabelRemoveCover": "إزالة الغلاف",
+ "LabelRemoveMetadataFile": "إزالة ملفات البيانات الوصفية في مجلدات عناصر المكتبة",
+ "LabelRemoveMetadataFileHelp": "إزالة جميع ملفات metadata.json و metadata.abs في مجلدات {0} الخاصة بك.",
+ "LabelRowsPerPage": "عدد الصفوف في الصفحة",
+ "LabelSearchTerm": "مصطلح البحث",
+ "LabelSearchTitle": "بحث بالعنوان",
+ "LabelSearchTitleOrASIN": "بحث بالعنوان أو ASIN",
"LabelSeason": "الموسم",
+ "LabelSeasonNumber": "الموسم #{0}",
+ "LabelSelectAll": "تحديد الكل",
+ "LabelSelectAllEpisodes": "تحديد جميع الحلقات",
+ "LabelSelectEpisodesShowing": "تحديد {0} حلقة معروضة",
+ "LabelSelectUsers": "تحديد المستخدمين",
+ "LabelSendEbookToDevice": "إرسال الكتاب الإلكتروني إلى...",
+ "LabelSequence": "تسلسل",
+ "LabelSerial": "مسلسل",
"LabelSeries": "المسلسلات",
+ "LabelSeriesName": "اسم السلسلة",
+ "LabelSeriesProgress": "تقدم السلسلة",
+ "LabelServerLogLevel": "مستوى سجل الخادم",
+ "LabelServerYearReview": "ملخص عام الخادم ({0})",
"LabelSetEbookAsPrimary": "تعيين كرئيسي",
"LabelSetEbookAsSupplementary": "تعيين كتكميلي",
- "LabelShowAll": "اظهار الكل",
+ "LabelSettingsAllowIframe": "السماح بالتضمين في إطار iframe",
+ "LabelSettingsAudiobooksOnly": "كتب صوتية فقط",
+ "LabelSettingsAudiobooksOnlyHelp": "سيؤدي تمكين هذا الإعداد إلى تجاهل ملفات الكتب الإلكترونية ما لم تكن داخل مجلد كتاب صوتي، وفي هذه الحالة سيتم تعيينها ككتب إلكترونية تكميلية",
+ "LabelSettingsBookshelfViewHelp": "تصميم يحاكي الواقع مع رفوف خشبية",
+ "LabelSettingsChromecastSupport": "دعم Chromecast",
+ "LabelSettingsDateFormat": "تنسيق التاريخ",
+ "LabelSettingsEnableWatcher": "فحص المكتبات تلقائيًا بحثًا عن تغييرات",
+ "LabelSettingsEnableWatcherForLibrary": "فحص المكتبة تلقائيًا بحثًا عن تغييرات",
+ "LabelSettingsEnableWatcherHelp": "يمكّن الإضافة/التحديث التلقائي للعناصر عند اكتشاف تغييرات في الملفات. *يتطلب إعادة تشغيل الخادم",
+ "LabelSettingsEpubsAllowScriptedContent": "السماح بالمحتوى النصي في ملفات epub",
+ "LabelSettingsEpubsAllowScriptedContentHelp": "السماح لملفات epub بتنفيذ النصوص البرمجية. يوصى بإبقاء هذا الإعداد معطلاً ما لم تثق في مصدر ملفات epub.",
+ "LabelSettingsExperimentalFeatures": "ميزات تجريبية",
+ "LabelSettingsExperimentalFeaturesHelp": "ميزات قيد التطوير يمكنها استخدام ملاحظاتك والمساعدة في اختبارها. انقر لفتح مناقشة على GitHub.",
+ "LabelSettingsFindCovers": "البحث عن الأغلفة",
+ "LabelSettingsFindCoversHelp": "إذا لم يكن لدى كتابك الصوتي غلاف مضمن أو صورة غلاف داخل المجلد، فسيحاول الماسح الضوئي العثور على غلاف.<br> ملاحظة: سيؤدي هذا إلى إطالة وقت الفحص",
+ "LabelSettingsHideSingleBookSeries": "إخفاء السلاسل ذات الكتاب الواحد",
+ "LabelSettingsHideSingleBookSeriesHelp": "سيتم إخفاء السلاسل التي تحتوي على كتاب واحد من صفحة السلاسل وأرفف الصفحة الرئيسية.",
+ "LabelSettingsHomePageBookshelfView": "استخدام عرض الرفوف في الصفحة الرئيسية",
+ "LabelSettingsLibraryBookshelfView": "استخدام عرض الرفوف في المكتبة",
+ "LabelSettingsLibraryMarkAsFinishedPercentComplete": "النسبة المئوية المكتملة أكبر من",
+ "LabelSettingsLibraryMarkAsFinishedTimeRemaining": "الوقت المتبقي أقل من (ثواني)",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "تعليم عنصر الوسائط على أنه منتهٍ عند",
+ "LabelSettingsOnlyShowLaterBooksInContinueSeries": "تخطي الكتب السابقة في \"متابعة السلسلة\"",
+ "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "يعرض رف \"متابعة السلسلة\" في الصفحة الرئيسية أول كتاب لم يبدأ في السلاسل التي تحتوي على كتاب واحد على الأقل منتهي ولا يوجد كتب قيد التقدم. سيؤدي تمكين هذا الإعداد إلى متابعة السلاسل من أبعد كتاب مكتمل بدلاً من أول كتاب لم يبدأ.",
+ "LabelSettingsParseSubtitles": "تحليل الترجمة",
+ "LabelSettingsParseSubtitlesHelp": "استخراج الترجمة من أسماء مجلدات الكتب الصوتية.<br>يجب فصل الترجمة بـ \" - \"<br>مثال: \"عنوان الكتاب - ترجمة هنا\" تحتوي على الترجمة \"ترجمة هنا\"",
+ "LabelSettingsPreferMatchedMetadata": "تفضيل البيانات الوصفية المطابقة",
+ "LabelSettingsPreferMatchedMetadataHelp": "ستتجاوز البيانات المطابقة تفاصيل العنصر عند استخدام المطابقة السريعة. بشكل افتراضي، ستملأ المطابقة السريعة التفاصيل المفقودة فقط.",
+ "LabelSettingsSkipMatchingBooksWithASIN": "تخطي مطابقة الكتب التي لديها ASIN بالفعل",
+ "LabelSettingsSkipMatchingBooksWithISBN": "تخطي مطابقة الكتب التي لديها ISBN بالفعل",
+ "LabelSettingsSortingIgnorePrefixes": "تجاهل البادئات عند الفرز",
+ "LabelSettingsSortingIgnorePrefixesHelp": "مثال: بالنسبة للبادئة \"the\"، سيتم فرز عنوان الكتاب \"The Book Title\" كـ \"Book Title, The\"",
+ "LabelSettingsSquareBookCovers": "استخدام أغلفة كتب مربعة",
+ "LabelSettingsSquareBookCoversHelp": "تفضيل استخدام الأغلفة المربعة على أغلفة الكتب القياسية بنسبة 1.6:1",
+ "LabelSettingsStoreCoversWithItem": "تخزين الأغلفة مع العنصر",
+ "LabelSettingsStoreCoversWithItemHelp": "بشكل افتراضي، يتم تخزين الأغلفة في /metadata/items، وسيؤدي تمكين هذا الإعداد إلى تخزين الأغلفة في مجلد عنصر المكتبة الخاص بك. سيتم الاحتفاظ بملف واحد فقط باسم \"cover\"",
+ "LabelSettingsStoreMetadataWithItem": "تخزين البيانات الوصفية مع العنصر",
+ "LabelSettingsStoreMetadataWithItemHelp": "بشكل افتراضي، يتم تخزين ملفات البيانات الوصفية في /metadata/items، وسيؤدي تمكين هذا الإعداد إلى تخزين ملفات البيانات الوصفية في مجلدات عناصر المكتبة الخاصة بك",
+ "LabelSettingsTimeFormat": "تنسيق الوقت",
+ "LabelShare": "مشاركة",
+ "LabelShareDownloadableHelp": "يسمح للمستخدمين الذين لديهم رابط المشاركة بتنزيل ملف مضغوط لعنصر المكتبة.",
+ "LabelShareOpen": "فتح المشاركة",
+ "LabelShareURL": "رابط المشاركة",
+ "LabelShowAll": "إظهار الكل",
+ "LabelShowSeconds": "إظهار الثواني",
+ "LabelShowSubtitles": "إظهار الترجمة",
"LabelSize": "الحجم",
"LabelSleepTimer": "مؤقت النوم",
+ "LabelSlug": "اسم تعريفي سهل القراءة",
+ "LabelSortAscending": "تصاعدي",
+ "LabelSortDescending": "تنازلي",
+ "LabelSortPubDate": "فرز حسب تاريخ النشر",
"LabelStart": "ابدأ",
+ "LabelStartTime": "وقت البدء",
+ "LabelStarted": "بدأ",
+ "LabelStartedAt": "بدأ في",
+ "LabelStatsAudioTracks": "مسارات الصوت",
+ "LabelStatsAuthors": "المؤلفون",
"LabelStatsBestDay": "أفضل يوم",
"LabelStatsDailyAverage": "المتوسط اليومي",
"LabelStatsDays": "أيام",
"LabelStatsDaysListened": "أيام الاستماع",
+ "LabelStatsHours": "ساعات",
"LabelStatsInARow": "على التوالي",
"LabelStatsItemsFinished": "العناصر المنتهية",
+ "LabelStatsItemsInLibrary": "العناصر في المكتبة",
"LabelStatsMinutes": "دقائق",
"LabelStatsMinutesListening": "دقائق الاستماع",
+ "LabelStatsOverallDays": "إجمالي الأيام",
+ "LabelStatsOverallHours": "إجمالي الساعات",
"LabelStatsWeekListening": "استماع هذا الأسبوع",
+ "LabelSubtitle": "عنوان فرعي / ترجمة",
+ "LabelSupportedFileTypes": "أنواع الملفات المدعومة",
"LabelTag": "علامة",
"LabelTags": "علامات",
+ "LabelTagsAccessibleToUser": "العلامات المتاحة للمستخدم",
+ "LabelTagsNotAccessibleToUser": "العلامات غير المتاحة للمستخدم",
+ "LabelTasks": "المهام قيد التشغيل",
+ "LabelTextEditorBulletedList": "قائمة نقطية",
+ "LabelTextEditorLink": "رابط",
+ "LabelTextEditorNumberedList": "قائمة مرقمة",
+ "LabelTextEditorUnlink": "إزالة الرابط",
"LabelTheme": "النمط",
"LabelThemeDark": "غامق",
"LabelThemeLight": "فاتح",
+ "LabelTimeBase": "قاعدة الوقت",
+ "LabelTimeDurationXHours": "{0} ساعات",
+ "LabelTimeDurationXMinutes": "{0} دقائق",
+ "LabelTimeDurationXSeconds": "{0} ثواني",
+ "LabelTimeInMinutes": "الوقت بالدقائق",
+ "LabelTimeLeft": "باقي {0}",
+ "LabelTimeListened": "الوقت المستمع إليه",
+ "LabelTimeListenedToday": "الوقت المستمع إليه اليوم",
"LabelTimeRemaining": "{0} متبقية",
+ "LabelTimeToShift": "الوقت المراد إزاحته بالثواني",
"LabelTitle": "عنوان",
+ "LabelToolsEmbedMetadata": "تضمين البيانات الوصفية",
+ "LabelToolsEmbedMetadataDescription": "تضمين البيانات الوصفية في ملفات الصوت بما في ذلك صورة الغلاف والفصول.",
+ "LabelToolsM4bEncoder": "ترميز M4B",
+ "LabelToolsMakeM4b": "إنشاء ملف كتاب صوتي M4B",
+ "LabelToolsMakeM4bDescription": "إنشاء ملف كتاب صوتي .M4B مع بيانات وصفية مضمنة وصورة غلاف وفصول.",
+ "LabelToolsSplitM4b": "تقسيم M4B إلى ملفات MP3",
+ "LabelToolsSplitM4bDescription": "إنشاء ملفات MP3 من ملف M4B مقسم حسب الفصول مع بيانات وصفية مضمنة وصورة غلاف وفصول.",
+ "LabelTotalDuration": "المدة الكلية",
+ "LabelTotalTimeListened": "إجمالي وقت الاستماع",
+ "LabelTrackFromFilename": "المسار من اسم الملف",
+ "LabelTrackFromMetadata": "المسار من البيانات الوصفية",
"LabelTracks": "المسارات",
+ "LabelTracksMultiTrack": "متعدد المسارات",
+ "LabelTracksNone": "لا توجد مسارات",
+ "LabelTracksSingleTrack": "مسار واحد",
+ "LabelTrailer": "مقطع دعائي",
"LabelType": "نوع",
+ "LabelUnabridged": "غير مختصر",
+ "LabelUndo": "تراجع",
"LabelUnknown": "مجهول",
+ "LabelUnknownPublishDate": "تاريخ النشر مجهول",
+ "LabelUpdateCover": "تحديث الغلاف",
+ "LabelUpdateCoverHelp": "السماح باستبدال الأغلفة الموجودة للكتب المحددة عند العثور على تطابق",
+ "LabelUpdateDetails": "تحديث التفاصيل",
+ "LabelUpdateDetailsHelp": "السماح باستبدال التفاصيل الموجودة للكتب المحددة عند العثور على تطابق",
+ "LabelUpdatedAt": "تاريخ التحديث",
+ "LabelUploaderDragAndDrop": "اسحب وأفلت الملفات أو المجلدات",
+ "LabelUploaderDragAndDropFilesOnly": "اسحب وأفلت الملفات",
+ "LabelUploaderDropFiles": "إفلات الملفات",
+ "LabelUploaderItemFetchMetadataHelp": "جلب العنوان والمؤلف والسلسلة تلقائيًا",
+ "LabelUseAdvancedOptions": "استخدام الخيارات المتقدمة",
+ "LabelUseChapterTrack": "استخدام مسار الفصل",
+ "LabelUseFullTrack": "استخدام المسار الكامل",
+ "LabelUseZeroForUnlimited": "استخدم 0 لغير محدود",
"LabelUser": "مستخدم",
"LabelUsername": "اسم المستخدم",
+ "LabelValue": "القيمة",
+ "LabelVersion": "الإصدار",
+ "LabelViewBookmarks": "عرض الإشارات المرجعية",
+ "LabelViewChapters": "عرض الفصول",
+ "LabelViewPlayerSettings": "عرض إعدادات المشغل",
+ "LabelViewQueue": "عرض قائمة انتظار المشغل",
+ "LabelVolume": "مستوى الصوت",
+ "LabelWebRedirectURLsDescription": "قم بتخويل عناوين URL هذه في موفر OAuth الخاص بك للسماح بإعادة التوجيه إلى تطبيق الويب بعد تسجيل الدخول:",
+ "LabelWebRedirectURLsSubfolder": "مجلد فرعي لعناوين URL لإعادة التوجيه",
+ "LabelWeekdaysToRun": "أيام الأسبوع المراد التشغيل فيها",
+ "LabelXBooks": "{0} كتب",
+ "LabelXItems": "{0} عناصر",
"LabelYearReviewHide": "إخفاء ملخص العام",
"LabelYearReviewShow": "عرض ملخص العام",
+ "LabelYourAudiobookDuration": "مدة كتابك الصوتي",
"LabelYourBookmarks": "علاماتك المرجعية",
+ "LabelYourPlaylists": "قوائم التشغيل الخاصة بك",
"LabelYourProgress": "تقدمك",
+ "MessageAddToPlayerQueue": "إضافة إلى قائمة انتظار المشغل",
+ "MessageAppriseDescription": "لاستخدام هذه الميزة، ستحتاج إلى تشغيل مثيل Apprise API أو واجهة برمجة تطبيقات تتعامل مع نفس الطلبات. يجب أن يكون عنوان URL الخاص بـ Apprise API هو مسار URL الكامل لإرسال الإشعار، على سبيل المثال، إذا كان مثيل واجهة برمجة التطبيقات الخاصة بك يعمل على http://192.168.1.1:8337، فستضع http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "تأكد من أنك تستخدم ASIN من منطقة Audible الصحيحة، وليس Amazon.",
+ "MessageAuthenticationOIDCChangesRestart": "أعد تشغيل الخادم بعد الحفظ لتطبيق تغييرات OIDC.",
+ "MessageBackupsDescription": "تتضمن النسخ الاحتياطية المستخدمين وتقدم المستخدم وتفاصيل عنصر المكتبة وإعدادات الخادم والصور المخزنة في /metadata/items و /metadata/authors. لا تتضمن النسخ الاحتياطية أي ملفات مخزنة في مجلدات مكتبتك.",
+ "MessageBackupsLocationEditNote": "ملاحظة: لن يؤدي تحديث موقع النسخ الاحتياطي إلى نقل أو تعديل النسخ الاحتياطية الموجودة",
+ "MessageBackupsLocationNoEditNote": "ملاحظة: يتم تعيين موقع النسخ الاحتياطي من خلال متغير بيئة ولا يمكن تغييره هنا.",
+ "MessageBackupsLocationPathEmpty": "لا يمكن أن يكون مسار موقع النسخ الاحتياطي فارغًا",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "املأ الحقول الممكّنة ببيانات من جميع العناصر. سيتم دمج الحقول ذات القيم المتعددة",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "املأ حقول تفاصيل الخريطة الممكّنة ببيانات من هذا العنصر",
+ "MessageBatchQuickMatchDescription": "ستحاول المطابقة السريعة إضافة الأغلفة والبيانات الوصفية المفقودة للعناصر المحددة. قم بتمكين الخيارات أدناه للسماح للمطابقة السريعة بالكتابة فوق الأغلفة و/أو البيانات الوصفية الموجودة.",
+ "MessageBookshelfNoCollections": "لم تنشئ أي مجموعات حتى الآن",
+ "MessageBookshelfNoCollectionsHelp": "المجموعات عامة. يمكن لجميع المستخدمين الذين لديهم حق الوصول إلى المكتبة رؤيتها.",
+ "MessageBookshelfNoRSSFeeds": "لا توجد خلاصات RSS مفتوحة",
+ "MessageBookshelfNoResultsForFilter": "لا توجد نتائج للفلتر \"{0}: {1}\"",
+ "MessageBookshelfNoResultsForQuery": "لا توجد نتائج للاستعلام",
+ "MessageBookshelfNoSeries": "ليس لديك أي مسلسلات",
+ "MessageChapterEndIsAfter": "نهاية الفصل بعد نهاية كتابك الصوتي",
+ "MessageChapterErrorFirstNotZero": "يجب أن يبدأ الفصل الأول عند 0",
+ "MessageChapterErrorStartGteDuration": "يجب أن يكون وقت البدء غير الصالح أقل من مدة الكتاب الصوتي",
+ "MessageChapterErrorStartLtPrev": "يجب أن يكون وقت البدء غير الصالح أكبر من أو يساوي وقت بدء الفصل السابق",
+ "MessageChapterStartIsAfter": "بداية الفصل بعد نهاية كتابك الصوتي",
+ "MessageChaptersNotFound": "لم يتم العثور على فصول",
+ "MessageCheckingCron": "جارٍ فحص cron...",
+ "MessageConfirmCloseFeed": "هل أنت متأكد أنك تريد إغلاق هذه التغذية؟",
+ "MessageConfirmDeleteBackup": "هل أنت متأكد أنك تريد حذف النسخ الاحتياطي لـ {0}؟",
+ "MessageConfirmDeleteDevice": "هل أنت متأكد أنك تريد حذف جهاز القارئ الإلكتروني \"{0}\"؟",
+ "MessageConfirmDeleteFile": "سيؤدي هذا إلى حذف الملف من نظام الملفات الخاص بك. هل أنت متأكد؟",
+ "MessageConfirmDeleteLibrary": "هل أنت متأكد أنك تريد حذف المكتبة \"{0}\" نهائيًا؟",
+ "MessageConfirmDeleteLibraryItem": "سيؤدي هذا إلى حذف عنصر المكتبة من قاعدة البيانات ونظام الملفات الخاص بك. هل أنت متأكد؟",
+ "MessageConfirmDeleteLibraryItems": "سيؤدي هذا إلى حذف {0} عنصرًا من عناصر المكتبة من قاعدة البيانات ونظام الملفات الخاص بك. هل أنت متأكد؟",
+ "MessageConfirmDeleteMetadataProvider": "هل أنت متأكد أنك تريد حذف مزود البيانات الوصفية المخصص \"{0}\"؟",
+ "MessageConfirmDeleteNotification": "هل أنت متأكد أنك تريد حذف هذا الإشعار؟",
+ "MessageConfirmDeleteSession": "هل أنت متأكد أنك تريد حذف هذه الجلسة؟",
+ "MessageConfirmEmbedMetadataInAudioFiles": "هل أنت متأكد أنك تريد تضمين البيانات الوصفية في {0} ملفًا صوتيًا؟",
+ "MessageConfirmForceReScan": "هل أنت متأكد أنك تريد فرض إعادة الفحص؟",
+ "MessageConfirmMarkAllEpisodesFinished": "هل أنت متأكد أنك تريد تعليم جميع الحلقات على أنها منتهية؟",
+ "MessageConfirmMarkAllEpisodesNotFinished": "هل أنت متأكد أنك تريد تعليم جميع الحلقات على أنها غير منتهية؟",
+ "MessageConfirmMarkItemFinished": "هل أنت متأكد أنك تريد تعليم \"{0}\" على أنه منتهٍ؟",
+ "MessageConfirmMarkItemNotFinished": "هل أنت متأكد أنك تريد تعليم \"{0}\" على أنه غير منتهٍ؟",
+ "MessageConfirmMarkSeriesFinished": "هل أنت متأكد أنك تريد تعليم جميع الكتب في هذه السلسلة على أنها منتهية؟",
+ "MessageConfirmMarkSeriesNotFinished": "هل أنت متأكد أنك تريد تعليم جميع الكتب في هذه السلسلة على أنها غير منتهية؟",
+ "MessageConfirmNotificationTestTrigger": "هل تريد تشغيل هذا الإشعار ببيانات اختبار؟",
+ "MessageConfirmPurgeCache": "سيؤدي مسح ذاكرة التخزين المؤقت إلى حذف الدليل بأكمله في /metadata/cache.
هل أنت متأكد أنك تريد إزالة دليل ذاكرة التخزين المؤقت؟",
+ "MessageConfirmPurgeItemsCache": "سيؤدي مسح ذاكرة التخزين المؤقت للعناصر إلى حذف الدليل بأكمله في /metadata/cache/items هل أنت متأكد؟",
+ "MessageConfirmQuickEmbed": "تحذير! لن يقوم التضمين السريع بنسخ ملفاتك الصوتية احتياطيًا. تأكد من أن لديك نسخة احتياطية من ملفاتك الصوتية.
هل ترغب في المتابعة؟",
+ "MessageConfirmQuickMatchEpisodes": "ستؤدي المطابقة السريعة للحلقات إلى الكتابة فوق التفاصيل إذا تم العثور على تطابق. سيتم تحديث الحلقات غير المتطابقة فقط. هل أنت متأكد؟",
+ "MessageConfirmReScanLibraryItems": "هل أنت متأكد أنك تريد إعادة فحص {0} عنصرًا؟",
+ "MessageConfirmRemoveAllChapters": "هل أنت متأكد أنك تريد إزالة جميع الفصول؟",
+ "MessageConfirmRemoveAuthor": "هل أنت متأكد أنك تريد إزالة المؤلف \"{0}\"؟",
+ "MessageConfirmRemoveCollection": "هل أنت متأكد أنك تريد إزالة المجموعة \"{0}\"؟",
+ "MessageConfirmRemoveEpisode": "هل أنت متأكد أنك تريد إزالة الحلقة \"{0}\"؟",
+ "MessageConfirmRemoveEpisodes": "هل أنت متأكد أنك تريد إزالة {0} حلقة؟",
+ "MessageConfirmRemoveListeningSessions": "هل أنت متأكد أنك تريد إزالة {0} جلسة استماع؟",
+ "MessageConfirmRemoveMetadataFiles": "هل أنت متأكد أنك تريد إزالة جميع ملفات البيانات الوصفية {0} الموجودة في مجلدات عناصر مكتبتك؟",
+ "MessageConfirmRemoveNarrator": "هل أنت متأكد أنك تريد إزالة الراوي \"{0}\"؟",
+ "MessageConfirmRemovePlaylist": "هل أنت متأكد أنك تريد إزالة قائمة التشغيل الخاصة بك \"{0}\"؟",
+ "MessageConfirmRenameGenre": "هل أنت متأكد أنك تريد إعادة تسمية النوع \"{0}\" إلى \"{1}\" لجميع العناصر؟",
+ "MessageConfirmRenameGenreMergeNote": "ملاحظة: هذا النوع موجود بالفعل لذا سيتم دمجهما.",
+ "MessageConfirmRenameGenreWarning": "تحذير! يوجد نوع مشابه بحالة أحرف مختلفة بالفعل \"{0}\".",
+ "MessageConfirmRenameTag": "هل أنت متأكد أنك تريد إعادة تسمية العلامة \"{0}\" إلى \"{1}\" لجميع العناصر؟",
+ "MessageConfirmRenameTagMergeNote": "ملاحظة: هذه العلامة موجودة بالفعل لذا سيتم دمجهما.",
+ "MessageConfirmRenameTagWarning": "تحذير! توجد علامة مشابهة بحالة أحرف مختلفة بالفعل \"{0}\".",
+ "MessageConfirmResetProgress": "هل أنت متأكد أنك تريد إعادة تعيين تقدمك؟",
+ "MessageConfirmSendEbookToDevice": "هل أنت متأكد أنك تريد إرسال الكتاب الإلكتروني \"{1}\" ({0}) إلى الجهاز \"{2}\"؟",
+ "MessageConfirmUnlinkOpenId": "هل أنت متأكد أنك تريد فصل هذا المستخدم عن OpenID؟",
+ "MessageDaysListenedInTheLastYear": "تم الاستماع لمدة {0} يومًا في العام الماضي",
"MessageDownloadingEpisode": "جاري تنزيل الحلقة",
+ "MessageDragFilesIntoTrackOrder": "اسحب الملفات إلى ترتيب المسارات الصحيح",
+ "MessageEmbedFailed": "فشل التضمين!",
+ "MessageEmbedFinished": "تم الانتهاء من التضمين!",
+ "MessageEmbedQueue": "تمت إضافته إلى قائمة انتظار تضمين البيانات الوصفية ({0} في قائمة الانتظار)",
"MessageEpisodesQueuedForDownload": "تمت إضافة {0} حلقة (حلقات) إلى قائمة انتظار التنزيل",
+ "MessageEreaderDevices": "لضمان تسليم الكتب الإلكترونية، قد تحتاج إلى إضافة عنوان البريد الإلكتروني أعلاه كمرسل صالح لكل جهاز مدرج أدناه.",
"MessageFeedURLWillBe": "سيكون رابط التغذية هو {0}",
"MessageFetching": "جاري الجلب...",
+ "MessageForceReScanDescription": "سيقوم بفحص جميع الملفات مرة أخرى كفحص جديد. سيتم فحص علامات ID3 لملفات الصوت وملفات OPF والملفات النصية كأنها جديدة.",
+ "MessageImportantNotice": "إشعار هام!",
+ "MessageInsertChapterBelow": "إدراج فصل أدناه",
+ "MessageInvalidAsin": "ASIN غير صالح",
+ "MessageItemsSelected": "تم تحديد {0} عنصرًا",
+ "MessageItemsUpdated": "تم تحديث {0} عنصرًا",
+ "MessageJoinUsOn": "انضم إلينا على",
"MessageLoading": "جاري التحميل...",
+ "MessageLoadingFolders": "جاري تحميل المجلدات...",
+ "MessageLogsDescription": "يتم تخزين السجلات في /metadata/logs كملفات JSON. يتم تخزين سجلات الأعطال في /metadata/logs/crash_logs.txt.",
+ "MessageM4BFailed": "فشل M4B!",
+ "MessageM4BFinished": "تم الانتهاء من M4B!",
+ "MessageMapChapterTitles": "ربط عناوين الفصول بفصول كتابك الصوتي الحالي دون تعديل الطوابع الزمنية",
+ "MessageMarkAllEpisodesFinished": "تعليم جميع الحلقات على أنها منتهية",
+ "MessageMarkAllEpisodesNotFinished": "تعليم جميع الحلقات على أنها غير منتهية",
"MessageMarkAsFinished": "وضع علامة \"تم الإنتهاء\"",
+ "MessageMarkAsNotFinished": "وضع علامة \"غير منته\"",
+ "MessageMatchBooksDescription": "سيحاول مطابقة الكتب في المكتبة مع كتاب من مزود البحث المحدد وملء التفاصيل الفارغة وصورة الغلاف. لا يستبدل التفاصيل الموجودة.",
+ "MessageNoAudioTracks": "لا توجد مسارات صوتية",
+ "MessageNoAuthors": "لا يوجد مؤلفون",
+ "MessageNoBackups": "لا توجد نسخ احتياطية",
"MessageNoBookmarks": "لا توجد علامات مرجعية",
"MessageNoChapters": "لا توجد فصول",
"MessageNoCollections": "لا توجد مجموعات",
+ "MessageNoCoversFound": "لم يتم العثور على أغلفة",
+ "MessageNoDescription": "لا يوجد وصف",
+ "MessageNoDevices": "لا توجد أجهزة",
+ "MessageNoDownloadsInProgress": "لا توجد تنزيلات قيد التقدم حاليًا",
+ "MessageNoDownloadsQueued": "لا توجد تنزيلات في قائمة الانتظار",
+ "MessageNoEpisodeMatchesFound": "لم يتم العثور على أي تطابقات للحلقات",
+ "MessageNoEpisodes": "لا توجد حلقات",
+ "MessageNoFoldersAvailable": "لا توجد مجلدات متاحة",
+ "MessageNoGenres": "لا توجد تصانيف",
+ "MessageNoIssues": "لا توجد مشاكل",
"MessageNoItems": "لا توجد عناصر",
"MessageNoItemsFound": "لم يتم العثور على عناصر",
"MessageNoListeningSessions": "لا توجد جلسات استماع",
+ "MessageNoLogs": "لا توجد سجلات",
+ "MessageNoMediaProgress": "لا يوجد تقدم للوسائط",
+ "MessageNoNotifications": "لا توجد إشعارات",
+ "MessageNoPodcastFeed": "بودكاست غير صالح: لا يوجد تغذية",
"MessageNoPodcastsFound": "لم يتم العثور على أي بودكاست",
+ "MessageNoResults": "لا توجد نتائج",
+ "MessageNoSearchResultsFor": "لا توجد نتائج بحث عن \"{0}\"",
+ "MessageNoSeries": "لا توجد مسلسلات",
+ "MessageNoTags": "لا توجد علامات",
+ "MessageNoTasksRunning": "لا توجد مهام قيد التشغيل",
"MessageNoUpdatesWereNecessary": "لا حاجة لأي تحديثات",
"MessageNoUserPlaylists": "ليست لديك أي قوائم تشغيل",
+ "MessageNoUserPlaylistsHelp": "قوائم التشغيل خاصة. لا يمكن إلا للمستخدم الذي ينشئها رؤيتها.",
+ "MessageNotYetImplemented": "لم يتم تنفيذه بعد",
+ "MessageOpmlPreviewNote": "ملاحظة: هذه معاينة لملف OPML الذي تم تحليله. سيتم أخذ عنوان البودكاست الفعلي من خلاصة RSS.",
+ "MessageOr": "أو",
+ "MessagePauseChapter": "إيقاف تشغيل الفصل مؤقتًا",
+ "MessagePlayChapter": "الاستماع إلى بداية الفصل",
+ "MessagePlaylistCreateFromCollection": "إنشاء قائمة تشغيل من المجموعة",
+ "MessagePleaseWait": "الرجاء الانتظار...",
+ "MessagePodcastHasNoRSSFeedForMatching": "لا يحتوي البودكاست على عنوان URL لخلاصة RSS لاستخدامه في المطابقة",
"MessagePodcastSearchField": "أدخل مصطلح البحث أو عنوان URL لخلاصة RSS",
+ "MessageQuickEmbedInProgress": "التضمين السريع قيد التقدم",
+ "MessageQuickEmbedQueue": "تمت إضافته إلى قائمة انتظار التضمين السريع ({0} في قائمة الانتظار)",
+ "MessageQuickMatchAllEpisodes": "مطابقة سريعة لجميع الحلقات",
+ "MessageQuickMatchDescription": "املأ تفاصيل العنصر الفارغة والغلاف بأول نتيجة مطابقة من '{0}'. لا يستبدل التفاصيل إلا إذا تم تمكين إعداد الخادم 'تفضيل البيانات الوصفية المطابقة'.",
+ "MessageRemoveChapter": "إزالة الفصل",
+ "MessageRemoveEpisodes": "إزالة {0} حلقة (حلقات)",
+ "MessageRemoveFromPlayerQueue": "إزالة من قائمة انتظار المشغل",
+ "MessageRemoveUserWarning": "هل أنت متأكد أنك تريد حذف المستخدم \"{0}\" نهائيًا؟",
"MessageReportBugsAndContribute": "أبلغ عن الأخطاء، واطلب الميزات، وساهم في",
+ "MessageResetChaptersConfirm": "هل أنت متأكد أنك تريد إعادة تعيين الفصول والتراجع عن التغييرات التي أجريتها؟",
+ "MessageRestoreBackupConfirm": "هل أنت متأكد أنك تريد استعادة النسخ الاحتياطي الذي تم إنشاؤه في",
+ "MessageRestoreBackupWarning": "ستؤدي استعادة النسخ الاحتياطي إلى الكتابة فوق قاعدة البيانات بأكملها الموجودة في /config وصور الأغلفة في /metadata/items و /metadata/authors.
لا تعدل النسخ الاحتياطية أي ملفات في مجلدات مكتبتك. إذا قمت بتمكين إعدادات الخادم لتخزين صور الأغلفة والبيانات الوصفية في مجلدات مكتبتك، فلن يتم نسخها احتياطيًا أو الكتابة فوقها.
سيتم تحديث جميع العملاء الذين يستخدمون الخادم الخاص بك تلقائيًا.",
+ "MessageScheduleLibraryScanNote": "بالنسبة لمعظم المستخدمين، يوصى بترك هذه الميزة معطلة وإبقاء إعداد مراقب المجلدات ممكّنًا. سيكتشف مراقب المجلدات تلقائيًا التغييرات في مجلدات مكتبتك. لا يعمل مراقب المجلدات مع كل نظام ملفات (مثل NFS)، لذا يمكن استخدام عمليات فحص المكتبة المجدولة بدلاً من ذلك.",
+ "MessageScheduleRunEveryWeekdayAtTime": "تشغيل كل {0} في الساعة {1}",
+ "MessageSearchResultsFor": "نتائج البحث عن",
+ "MessageSelected": "تم تحديد {0}",
+ "MessageServerCouldNotBeReached": "تعذر الوصول إلى الخادم",
+ "MessageSetChaptersFromTracksDescription": "تعيين الفصول باستخدام كل ملف صوتي كفصل وعنوان الفصل كاسم الملف الصوتي",
+ "MessageShareExpirationWillBe": "سيكون تاريخ الانتهاء {0}",
+ "MessageShareExpiresIn": "ينتهي خلال {0}",
+ "MessageShareURLWillBe": "سيكون رابط المشاركة هو {0}",
+ "MessageStartPlaybackAtTime": "هل تريد بدء التشغيل لـ \"{0}\" في الساعة {1}؟",
+ "MessageTaskAudioFileNotWritable": "الملف الصوتي \"{0}\" غير قابل للكتابة",
+ "MessageTaskCanceledByUser": "تم إلغاء المهمة بواسطة المستخدم",
+ "MessageTaskDownloadingEpisodeDescription": "جاري تنزيل الحلقة \"{0}\"",
+ "MessageTaskEmbeddingMetadata": "جاري تضمين البيانات الوصفية",
+ "MessageTaskEmbeddingMetadataDescription": "جاري تضمين البيانات الوصفية في الكتاب الصوتي \"{0}\"",
+ "MessageTaskEncodingM4b": "جاري ترميز M4B",
+ "MessageTaskEncodingM4bDescription": "جاري ترميز الكتاب الصوتي \"{0}\" في ملف m4b واحد",
+ "MessageTaskFailed": "فشل",
+ "MessageTaskFailedToBackupAudioFile": "فشل في نسخ الملف الصوتي \"{0}\" احتياطيًا",
+ "MessageTaskFailedToCreateCacheDirectory": "فشل في إنشاء دليل ذاكرة التخزين المؤقت",
+ "MessageTaskFailedToEmbedMetadataInFile": "فشل في تضمين البيانات الوصفية في الملف \"{0}\"",
+ "MessageTaskFailedToMergeAudioFiles": "فشل في دمج الملفات الصوتية",
+ "MessageTaskFailedToMoveM4bFile": "فشل في نقل ملف m4b",
+ "MessageTaskFailedToWriteMetadataFile": "فشل في كتابة ملف البيانات الوصفية",
+ "MessageTaskMatchingBooksInLibrary": "جارٍ مطابقة الكتب في المكتبة \"{0}\"",
+ "MessageTaskNoFilesToScan": "لا توجد ملفات للفحص",
+ "MessageTaskOpmlImport": "استيراد OPML",
+ "MessageTaskOpmlImportDescription": "جارٍ إنشاء بودكاست من {0} خلاصة RSS",
+ "MessageTaskOpmlImportFeed": "استيراد تغذية OPML",
+ "MessageTaskOpmlImportFeedDescription": "جارٍ استيراد خلاصة RSS \"{0}\"",
+ "MessageTaskOpmlImportFeedFailed": "فشل في الحصول على تغذية البودكاست",
+ "MessageTaskOpmlImportFeedPodcastDescription": "جارٍ إنشاء بودكاست \"{0}\"",
+ "MessageTaskOpmlImportFeedPodcastExists": "البودكاست موجود بالفعل في المسار",
+ "MessageTaskOpmlImportFeedPodcastFailed": "فشل في إنشاء البودكاست",
+ "MessageTaskOpmlImportFinished": "تمت إضافة {0} بودكاست",
+ "MessageTaskOpmlParseFailed": "فشل في تحليل ملف OPML",
+ "MessageTaskOpmlParseFastFail": "ملف OPML غير صالح، لم يتم العثور على علامة أو لم يتم العثور على علامة ",
+ "MessageTaskOpmlParseNoneFound": "لم يتم العثور على أي خلاصات في ملف OPML",
+ "MessageTaskScanItemsAdded": "تمت إضافة {0}",
+ "MessageTaskScanItemsMissing": "{0} مفقود",
+ "MessageTaskScanItemsUpdated": "{0} تم تحديثه",
+ "MessageTaskScanNoChangesNeeded": "لا توجد تغييرات مطلوبة",
+ "MessageTaskScanningFileChanges": "جاري فحص تغييرات الملفات في \"{0}\"",
+ "MessageTaskScanningLibrary": "جاري فحص مكتبة \"{0}\"",
+ "MessageTaskTargetDirectoryNotWritable": "الدليل الهدف غير قابل للكتابة",
+ "MessageThinking": "جاري التفكير...",
+ "MessageUploaderItemFailed": "فشل الرفع",
+ "MessageUploaderItemSuccess": "تم الرفع بنجاح!",
+ "MessageUploading": "جاري الرفع...",
+ "MessageValidCronExpression": "تعبير Cron صالح",
+ "MessageWatcherIsDisabledGlobally": "المراقب معطل عالميًا في إعدادات الخادم",
+ "MessageXLibraryIsEmpty": "مكتبة {0} فارغة!",
+ "MessageYourAudiobookDurationIsLonger": "مدة كتابك الصوتي أطول من المدة التي تم العثور عليها",
+ "MessageYourAudiobookDurationIsShorter": "مدة كتابك الصوتي أقصر من المدة التي تم العثور عليها",
+ "NoteChangeRootPassword": "مستخدم الجذر هو المستخدم الوحيد الذي يمكن أن يكون لديه كلمة مرور فارغة",
+ "NoteChapterEditorTimes": "ملاحظة: يجب أن يظل وقت بدء الفصل الأول عند 0:00 ولا يمكن أن يتجاوز وقت بدء الفصل الأخير مدة هذا الكتاب الصوتي.",
+ "NoteFolderPicker": "ملاحظة: لن يتم عرض المجلدات التي تم تعيينها بالفعل",
"NoteRSSFeedPodcastAppsHttps": "تحذير: تتطلب معظم تطبيقات البث الصوتي أن يكون عنوان URL لخلاصة RSS يستخدم HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "تحذير: حلقة واحدة أو أكثر من حلقاتك ليس لها تاريخ نشر. بعض تطبيقات البودكاست تتطلب هذا.",
+ "NoteUploaderFoldersWithMediaFiles": "سيتم التعامل مع المجلدات التي تحتوي على ملفات وسائط كعناصر مكتبة منفصلة.",
+ "NoteUploaderOnlyAudioFiles": "في حالة رفع ملفات صوتية فقط، سيتم التعامل مع كل ملف صوتي ككتاب صوتي منفصل.",
+ "NoteUploaderUnsupportedFiles": "يتم تجاهل الملفات غير المدعومة. عند اختيار مجلد أو إسقاطه، يتم تجاهل الملفات الأخرى التي ليست في مجلد عنصر.",
+ "NotificationOnBackupCompletedDescription": "يتم تشغيله عند اكتمال النسخ الاحتياطي",
+ "NotificationOnBackupFailedDescription": "يتم تشغيله عند فشل النسخ الاحتياطي",
+ "NotificationOnEpisodeDownloadedDescription": "يتم تشغيله عند تنزيل حلقة بودكاست تلقائيًا",
+ "NotificationOnTestDescription": "حدث لاختبار نظام الإشعارات",
+ "PlaceholderNewCollection": "اسم المجموعة الجديدة",
+ "PlaceholderNewFolderPath": "مسار المجلد الجديد",
+ "PlaceholderNewPlaylist": "اسم قائمة التشغيل الجديدة",
+ "PlaceholderSearch": "بحث..",
+ "PlaceholderSearchEpisode": "بحث عن حلقة..",
+ "StatsAuthorsAdded": "تمت إضافة مؤلفين",
+ "StatsBooksAdded": "تمت إضافة كتب",
+ "StatsBooksAdditional": "تتضمن بعض الإضافات…",
+ "StatsBooksFinished": "كتب تم الانتهاء منها",
+ "StatsBooksFinishedThisYear": "بعض الكتب التي تم الانتهاء منها هذا العام…",
+ "StatsBooksListenedTo": "كتب تم الاستماع إليها",
+ "StatsCollectionGrewTo": "نما مجموعتك من الكتب لتصبح…",
+ "StatsSessions": "جلسات",
+ "StatsSpentListening": "تم قضاء وقت في الاستماع",
+ "StatsTopAuthor": "أفضل مؤلف",
+ "StatsTopAuthors": "أفضل المؤلفين",
+ "StatsTopGenre": "أفضل تصنيف",
+ "StatsTopGenres": "أفضل التصنيفات",
+ "StatsTopMonth": "أفضل شهر",
+ "StatsTopNarrator": "أفضل راوي",
+ "StatsTopNarrators": "أفضل الرواة",
+ "StatsTotalDuration": "بإجمالي مدة…",
+ "StatsYearInReview": "ملخص العام",
+ "ToastAccountUpdateSuccess": "تم تحديث الحساب",
+ "ToastAppriseUrlRequired": "يجب إدخال عنوان URL لـ Apprise",
+ "ToastAsinRequired": "ASIN مطلوب",
+ "ToastAuthorImageRemoveSuccess": "تمت إزالة صورة المؤلف",
+ "ToastAuthorNotFound": "لم يتم العثور على المؤلف \"{0}\"",
+ "ToastAuthorRemoveSuccess": "تمت إزالة المؤلف",
+ "ToastAuthorSearchNotFound": "لم يتم العثور على المؤلف",
+ "ToastAuthorUpdateMerged": "تم دمج المؤلف",
+ "ToastAuthorUpdateSuccess": "تم تحديث المؤلف",
+ "ToastAuthorUpdateSuccessNoImageFound": "تم تحديث المؤلف (لم يتم العثور على صورة)",
+ "ToastBackupAppliedSuccess": "تم تطبيق النسخ الاحتياطي",
+ "ToastBackupCreateFailed": "فشل إنشاء النسخ الاحتياطي",
+ "ToastBackupCreateSuccess": "تم إنشاء النسخ الاحتياطي",
+ "ToastBackupDeleteFailed": "فشل حذف النسخ الاحتياطي",
+ "ToastBackupDeleteSuccess": "تم حذف النسخ الاحتياطي",
+ "ToastBackupInvalidMaxKeep": "عدد غير صالح للنسخ الاحتياطية التي يجب الاحتفاظ بها",
+ "ToastBackupInvalidMaxSize": "حجم أقصى غير صالح للنسخ الاحتياطي",
+ "ToastBackupRestoreFailed": "فشل استعادة النسخ الاحتياطي",
+ "ToastBackupUploadFailed": "فشل رفع النسخ الاحتياطي",
+ "ToastBackupUploadSuccess": "تم رفع النسخ الاحتياطي",
+ "ToastBatchApplyDetailsToItemsSuccess": "تم تطبيق التفاصيل على العناصر",
+ "ToastBatchDeleteFailed": "فشل الحذف المجمّع",
+ "ToastBatchDeleteSuccess": "نجاح الحذف المجمّع",
+ "ToastBatchQuickMatchFailed": "فشلت المطابقة السريعة المجمّعة!",
+ "ToastBatchQuickMatchStarted": "بدأت المطابقة السريعة المجمّعة لـ {0} كتابًا!",
+ "ToastBatchUpdateFailed": "فشل التحديث المجمّع",
+ "ToastBatchUpdateSuccess": "نجاح التحديث المجمّع",
"ToastBookmarkCreateFailed": "فشل في إنشاء الإشارة المرجعية",
+ "ToastBookmarkCreateSuccess": "تمت إضافة الإشارة المرجعية",
+ "ToastBookmarkRemoveSuccess": "تمت إزالة الإشارة المرجعية",
+ "ToastCachePurgeFailed": "فشل مسح ذاكرة التخزين المؤقت",
+ "ToastCachePurgeSuccess": "تم مسح ذاكرة التخزين المؤقت بنجاح",
+ "ToastChaptersHaveErrors": "الفصول تحتوي على أخطاء",
+ "ToastChaptersInvalidShiftAmountLast": "مقدار إزاحة غير صالح. سيمتد وقت بدء الفصل الأخير إلى ما بعد مدة هذا الكتاب الصوتي.",
+ "ToastChaptersInvalidShiftAmountStart": "مقدار إزاحة غير صالح. سيكون للفصل الأول طول صفري أو سالب وسيتم الكتابة فوقه بواسطة الفصل الثاني. قم بزيادة مدة بدء الفصل الثاني.",
+ "ToastChaptersMustHaveTitles": "يجب أن تحتوي الفصول على عناوين",
+ "ToastChaptersRemoved": "تمت إزالة الفصول",
+ "ToastChaptersUpdated": "تم تحديث الفصول",
+ "ToastCollectionItemsAddFailed": "فشل إضافة عنصر (عناصر) إلى المجموعة",
+ "ToastCollectionRemoveSuccess": "تمت إزالة المجموعة",
+ "ToastCollectionUpdateSuccess": "تم تحديث المجموعة",
+ "ToastCoverUpdateFailed": "فشل تحديث الغلاف",
+ "ToastDateTimeInvalidOrIncomplete": "التاريخ والوقت غير صالحين أو غير مكتملين",
+ "ToastDeleteFileFailed": "فشل حذف الملف",
+ "ToastDeleteFileSuccess": "تم حذف الملف",
+ "ToastDeviceAddFailed": "فشل إضافة الجهاز",
+ "ToastDeviceNameAlreadyExists": "جهاز قارئ إلكتروني بهذا الاسم موجود بالفعل",
+ "ToastDeviceTestEmailFailed": "فشل إرسال البريد الإلكتروني التجريبي",
+ "ToastDeviceTestEmailSuccess": "تم إرسال البريد الإلكتروني التجريبي",
+ "ToastEmailSettingsUpdateSuccess": "تم تحديث إعدادات البريد الإلكتروني",
+ "ToastEncodeCancelFailed": "فشل إلغاء الترميز",
+ "ToastEncodeCancelSucces": "تم إلغاء الترميز",
+ "ToastEpisodeDownloadQueueClearFailed": "فشل مسح قائمة انتظار تنزيل الحلقات",
+ "ToastEpisodeDownloadQueueClearSuccess": "تم مسح قائمة انتظار تنزيل الحلقات",
+ "ToastEpisodeUpdateSuccess": "تم تحديث {0} حلقة",
+ "ToastErrorCannotShare": "لا يمكن المشاركة محليًا على هذا الجهاز",
+ "ToastFailedToLoadData": "فشل تحميل البيانات",
+ "ToastFailedToMatch": "فشل التطابق",
+ "ToastFailedToShare": "فشل المشاركة",
+ "ToastFailedToUpdate": "فشل التحديث",
+ "ToastInvalidImageUrl": "رابط صورة غير صالح",
+ "ToastInvalidMaxEpisodesToDownload": "الحد الأقصى غير صالح لعدد الحلقات المراد تنزيلها",
+ "ToastInvalidUrl": "رابط غير صالح",
+ "ToastItemCoverUpdateSuccess": "تم تحديث غلاف العنصر",
+ "ToastItemDeletedFailed": "فشل حذف العنصر",
+ "ToastItemDeletedSuccess": "تم حذف العنصر",
+ "ToastItemDetailsUpdateSuccess": "تم تحديث تفاصيل العنصر",
"ToastItemMarkedAsFinishedFailed": "فشل في وضع علامة على الانتهاء",
+ "ToastItemMarkedAsFinishedSuccess": "تم وضع علامة \"تم الانتهاء\" على العنصر",
"ToastItemMarkedAsNotFinishedFailed": "فشل في وضع علامة \"غير مكتمل\"",
+ "ToastItemMarkedAsNotFinishedSuccess": "تم وضع علامة \"غير مكتمل\" على العنصر",
+ "ToastItemUpdateSuccess": "تم تحديث العنصر",
+ "ToastLibraryCreateFailed": "فشل إنشاء المكتبة",
+ "ToastLibraryCreateSuccess": "تم إنشاء المكتبة \"{0}\"",
+ "ToastLibraryDeleteFailed": "فشل حذف المكتبة",
+ "ToastLibraryDeleteSuccess": "تم حذف المكتبة",
+ "ToastLibraryScanFailedToStart": "فشل بدء الفحص",
+ "ToastLibraryScanStarted": "بدأ فحص المكتبة",
+ "ToastLibraryUpdateSuccess": "تم تحديث المكتبة \"{0}\"",
+ "ToastMatchAllAuthorsFailed": "فشل مطابقة جميع المؤلفين",
+ "ToastMetadataFilesRemovedError": "حدث خطأ أثناء إزالة ملفات البيانات الوصفية. {0}",
+ "ToastMetadataFilesRemovedNoneFound": "لا توجد بيانات وصفية. تم العثور على {0} ملف في المكتبة",
+ "ToastMetadataFilesRemovedNoneRemoved": "لا توجد بيانات وصفية. تمت إزالة {0} ملفًا",
+ "ToastMetadataFilesRemovedSuccess": "{0} بيانات وصفية. تم إزالة {1} ملف",
+ "ToastMustHaveAtLeastOnePath": "يجب أن يكون هناك مسار واحد على الأقل",
+ "ToastNameEmailRequired": "الاسم والبريد الإلكتروني مطلوبان",
+ "ToastNameRequired": "الاسم مطلوب",
+ "ToastNewEpisodesFound": "تم العثور على {0} حلقة جديدة",
+ "ToastNewUserCreatedFailed": "فشل إنشاء الحساب: \"{0}\"",
+ "ToastNewUserCreatedSuccess": "تم إنشاء حساب جديد",
+ "ToastNewUserLibraryError": "يجب تحديد مكتبة واحدة على الأقل",
+ "ToastNewUserPasswordError": "يجب أن يكون لديك كلمة مرور، يمكن لمستخدم الجذر فقط أن يكون لديه كلمة مرور فارغة",
+ "ToastNewUserTagError": "يجب تحديد علامة واحدة على الأقل",
+ "ToastNewUserUsernameError": "أدخل اسم مستخدم",
+ "ToastNoNewEpisodesFound": "لم يتم العثور على حلقات جديدة",
+ "ToastNoRSSFeed": "لا يحتوي البودكاست على خلاصة RSS",
+ "ToastNoUpdatesNecessary": "لا توجد تحديثات ضرورية",
+ "ToastNotificationCreateFailed": "فشل إنشاء الإشعار",
+ "ToastNotificationDeleteFailed": "فشل حذف الإشعار",
+ "ToastNotificationFailedMaximum": "يجب أن يكون الحد الأقصى للمحاولات الفاشلة >= 0",
+ "ToastNotificationQueueMaximum": "يجب أن يكون الحد الأقصى لقائمة انتظار الإشعارات >= 0",
+ "ToastNotificationSettingsUpdateSuccess": "تم تحديث إعدادات الإشعارات",
+ "ToastNotificationTestTriggerFailed": "فشل تشغيل إشعار الاختبار",
+ "ToastNotificationTestTriggerSuccess": "تم تشغيل إشعار الاختبار",
+ "ToastNotificationUpdateSuccess": "تم تحديث الإشعار",
"ToastPlaylistCreateFailed": "فشل إنشاء قائمة التشغيل",
+ "ToastPlaylistCreateSuccess": "تم إنشاء قائمة التشغيل",
+ "ToastPlaylistRemoveSuccess": "تمت إزالة قائمة التشغيل",
+ "ToastPlaylistUpdateSuccess": "تم تحديث قائمة التشغيل",
"ToastPodcastCreateFailed": "فشل في إنشاء البودكاست",
"ToastPodcastCreateSuccess": "تم إنشاء البودكاست بنجاح",
+ "ToastPodcastGetFeedFailed": "فشل في الحصول على تغذية البودكاست",
+ "ToastPodcastNoEpisodesInFeed": "لم يتم العثور على حلقات في خلاصة RSS",
+ "ToastPodcastNoRssFeed": "لا يحتوي البودكاست على خلاصة RSS",
+ "ToastProgressIsNotBeingSynced": "لا تتم مزامنة التقدم، أعد تشغيل التشغيل",
+ "ToastProviderCreatedFailed": "فشل إضافة المزود",
+ "ToastProviderCreatedSuccess": "تمت إضافة مزود جديد",
+ "ToastProviderNameAndUrlRequired": "الاسم والرابط مطلوبان",
+ "ToastProviderRemoveSuccess": "تمت إزالة المزود",
"ToastRSSFeedCloseFailed": "فشل في إغلاق موجز RSS",
- "ToastRSSFeedCloseSuccess": "تم إغلاق موجز RSS"
+ "ToastRSSFeedCloseSuccess": "تم إغلاق موجز RSS",
+ "ToastRemoveFailed": "فشل الإزالة",
+ "ToastRemoveItemFromCollectionFailed": "فشل إزالة العنصر من المجموعة",
+ "ToastRemoveItemFromCollectionSuccess": "تمت إزالة العنصر من المجموعة",
+ "ToastRemoveItemsWithIssuesFailed": "فشل إزالة عناصر المكتبة التي بها مشاكل",
+ "ToastRemoveItemsWithIssuesSuccess": "تمت إزالة عناصر المكتبة التي بها مشاكل",
+ "ToastRenameFailed": "فشل إعادة التسمية",
+ "ToastRescanFailed": "فشل إعادة الفحص لـ {0}",
+ "ToastRescanRemoved": "اكتملت إعادة الفحص، وتمت إزالة العنصر",
+ "ToastRescanUpToDate": "اكتملت إعادة الفحص، العنصر كان محدثًا",
+ "ToastRescanUpdated": "اكتملت إعادة الفحص، وتم تحديث العنصر",
+ "ToastScanFailed": "فشل فحص عنصر المكتبة",
+ "ToastSelectAtLeastOneUser": "حدد مستخدمًا واحدًا على الأقل",
+ "ToastSendEbookToDeviceFailed": "فشل إرسال الكتاب الإلكتروني إلى الجهاز",
+ "ToastSendEbookToDeviceSuccess": "تم إرسال الكتاب الإلكتروني إلى الجهاز \"{0}\"",
+ "ToastSeriesSubmitFailedSameName": "لا يمكن إضافة سلسلتين بنفس الاسم",
+ "ToastSeriesUpdateFailed": "فشل تحديث السلسلة",
+ "ToastSeriesUpdateSuccess": "نجاح تحديث السلسلة",
+ "ToastServerSettingsUpdateSuccess": "تم تحديث إعدادات الخادم",
+ "ToastSessionCloseFailed": "فشل إغلاق الجلسة",
+ "ToastSessionDeleteFailed": "فشل حذف الجلسة",
+ "ToastSessionDeleteSuccess": "تم حذف الجلسة",
+ "ToastSleepTimerDone": "انتهى مؤقت النوم... ششششش",
+ "ToastSlugMustChange": "يحتوي الاسم التعريفي على أحرف غير صالحة",
+ "ToastSlugRequired": "الاسم التعريفي مطلوب",
+ "ToastSocketConnected": "تم الاتصال بالمقبس"
}
From 19dc096d226f3be0973f307af47b6a82ab4597a1 Mon Sep 17 00:00:00 2001
From: Antoniy Chonkov
Date: Mon, 19 May 2025 06:36:20 +0200
Subject: [PATCH 013/121] Translated using Weblate (Bulgarian)
Currently translated at 75.5% (834 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
---
client/strings/bg.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/bg.json b/client/strings/bg.json
index 7d94876c..481a3bb8 100644
--- a/client/strings/bg.json
+++ b/client/strings/bg.json
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Плейлист",
"HeaderPlaylistItems": "Елементи от плейлист",
"HeaderPodcastsToAdd": "Подкасти за Добавяне",
+ "HeaderPresets": "Настройки по подразбиране",
"HeaderPreviewCover": "Преглед на Корица",
"HeaderRSSFeedGeneral": "RSS подробности",
"HeaderRSSFeedIsOpen": "RSS емисията е отворена",
@@ -219,6 +220,7 @@
"LabelAccountTypeAdmin": "Администратор",
"LabelAccountTypeGuest": "Гост",
"LabelAccountTypeUser": "Потребител",
+ "LabelActivities": "Дейности",
"LabelActivity": "Дейност",
"LabelAddToCollection": "Добави в Колекция",
"LabelAddToCollectionBatch": "Добави {0} Книги в Колекция",
From 35c2a5c1a3bf16c8c822aa5fed421e57cc0eaa07 Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Sun, 18 May 2025 11:59:36 +0200
Subject: [PATCH 014/121] Translated using Weblate (Arabic)
Currently translated at 100.0% (1104 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index 5c9265b5..59fcd4cf 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -98,7 +98,7 @@
"ButtonSetChaptersFromTracks": "تعيين الفصول من الملفات",
"ButtonShare": "نشر",
"ButtonShiftTimes": "أوقات العمل",
- "ButtonShow": "عرض",
+ "ButtonShow": "أعرض",
"ButtonStartM4BEncode": "ابدأ ترميز M4B",
"ButtonStartMetadataEmbed": "ابدأ تضمين البيانات الوصفية",
"ButtonStats": "الإحصائيات",
@@ -168,7 +168,7 @@
"HeaderNotifications": "إشعارات",
"HeaderOpenIDConnectAuthentication": "مصادقة OpenID Connect",
"HeaderOpenListeningSessions": "جلسات الاستماع المفتوحة",
- "HeaderOpenRSSFeed": "فتح تغذية RSS",
+ "HeaderOpenRSSFeed": "عرض تغذية RSS",
"HeaderOtherFiles": "ملفات أخرى",
"HeaderPasswordAuthentication": "مصادقة كلمة المرور",
"HeaderPermissions": "الصلاحيات",
@@ -474,7 +474,7 @@
"LabelOpenIDAdvancedPermsClaimDescription": "اسم مطالبة OpenID التي تحتوي على أذونات متقدمة لإجراءات المستخدم داخل التطبيق والتي ستطبق على الأدوار غير الإدارية (إذا تم تكوينها). إذا كانت المطالبة مفقودة من الاستجابة، فسيتم رفض الوصول إلى ABS. إذا كان هناك خيار واحد مفقودًا، فسيتم التعامل معه على أنه false. تأكد من أن مطالبة موفر الهوية تطابق البنية المتوقعة:",
"LabelOpenIDClaims": "اترك الخيارات التالية فارغة لتعطيل تعيين المجموعة والأذونات المتقدمة، وسيتم تعيين مجموعة \"مستخدم\" تلقائيًا بعد ذلك.",
"LabelOpenIDGroupClaimDescription": "اسم مطالبة OpenID التي تحتوي على قائمة بمجموعات المستخدم. يشار إليها عادةً باسم groups.إذا تم تكوينها، فسيقوم التطبيق تلقائيًا بتعيين الأدوار بناءً على عضويات مجموعة المستخدم، بشرط أن تسمى هذه المجموعات بشكل غير حساس لحالة الأحرف \"admin\" أو \"user\" أو \"guest\" في المطالبة. يجب أن تحتوي المطالبة على قائمة، وإذا كان المستخدم ينتمي إلى مجموعات متعددة، فسيقوم التطبيق بتعيين الدور المقابل لأعلى مستوى من الوصول. إذا لم تتطابق أي مجموعة، فسيتم رفض الوصول.",
- "LabelOpenRSSFeed": "فتح تغذية RSS",
+ "LabelOpenRSSFeed": "تغذية RSS مفتوحة",
"LabelOverwrite": "استبدال",
"LabelPaginationPageXOfY": "صفحة {0} من {1}",
"LabelPassword": "كلمة المرور",
@@ -1086,5 +1086,21 @@
"ToastSleepTimerDone": "انتهى مؤقت النوم... ششششش",
"ToastSlugMustChange": "يحتوي الاسم التعريفي على أحرف غير صالحة",
"ToastSlugRequired": "الاسم التعريفي مطلوب",
- "ToastSocketConnected": "تم الاتصال بالمقبس"
+ "ToastSocketConnected": "تم الاتصال بالمقبس",
+ "ToastSocketDisconnected": "تم قطع الاتصال بالمقبس",
+ "ToastSocketFailedToConnect": "فشل الاتصال بالمقبس",
+ "ToastSortingPrefixesEmptyError": "يجب أن يكون هناك بادئة فرز واحدة على الأقل",
+ "ToastSortingPrefixesUpdateSuccess": "تم تحديث بادئات الفرز ({0} عنصرًا)",
+ "ToastTitleRequired": "العنوان مطلوب",
+ "ToastUnknownError": "خطأ غير معروف",
+ "ToastUnlinkOpenIdFailed": "فشل فصل المستخدم عن OpenID",
+ "ToastUnlinkOpenIdSuccess": "تم فصل المستخدم عن OpenID",
+ "ToastUploaderFilepathExistsError": "مسار الملف \"{0}\" موجود بالفعل على الخادم",
+ "ToastUploaderItemExistsInSubdirectoryError": "يستخدم العنصر \"{0}\" دليلًا فرعيًا لمسار الرفع.",
+ "ToastUserDeleteFailed": "فشل حذف المستخدم",
+ "ToastUserDeleteSuccess": "تم حذف المستخدم",
+ "ToastUserPasswordChangeSuccess": "تم تغيير كلمة المرور بنجاح",
+ "ToastUserPasswordMismatch": "كلمات المرور غير متطابقة",
+ "ToastUserPasswordMustChange": "يجب ألا تطابق كلمة المرور الجديدة كلمة المرور القديمة",
+ "ToastUserRootRequireName": "يجب إدخال اسم مستخدم الجذر"
}
From d8e2ff8b0e2f612ee10451fbae84d2db18e41d08 Mon Sep 17 00:00:00 2001
From: peter cerny
Date: Mon, 19 May 2025 08:42:11 +0200
Subject: [PATCH 015/121] Translated using Weblate (Slovak)
Currently translated at 99.5% (1099 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
---
client/strings/sk.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/strings/sk.json b/client/strings/sk.json
index 714ad88a..f5a3aa6b 100644
--- a/client/strings/sk.json
+++ b/client/strings/sk.json
@@ -786,7 +786,7 @@
"MessageItemsSelected": "{0} vybraných položiek",
"MessageItemsUpdated": "{0} aktualizovaných položiek",
"MessageJoinUsOn": "Pridajte sa k nám",
- "MessageLoading": "Načítanie...",
+ "MessageLoading": "Načítavam...",
"MessageLoadingFolders": "Načítanie priečinkov...",
"MessageLogsDescription": "Záznamy logovania sú uložené v /metadata/logs vo forme JSON súborov. Záznamy kritických chýb sú uložené v /metadata/logs/crash_logs.txt.",
"MessageM4BFailed": "M4B zlyhalo!",
@@ -826,7 +826,7 @@
"MessageNoSeries": "Žiadne série",
"MessageNoTags": "Žiadne štítky",
"MessageNoTasksRunning": "Žiadne prebiehajúce úlohy",
- "MessageNoUpdatesWereNecessary": "Žiadne nutné aktualizácie",
+ "MessageNoUpdatesWereNecessary": "Neboli potrebné žiadne aktualizácie",
"MessageNoUserPlaylists": "Nemáte žiadny playlist",
"MessageNoUserPlaylistsHelp": "Playlisty sú súkromné. Každý playlist môže vidieť iba používateľ, ktorý ho vytvoril.",
"MessageNotYetImplemented": "Ešte neimplementované",
From 13d94628688a5dc1b99b32ae5bc80a2f1aacb659 Mon Sep 17 00:00:00 2001
From: Antoniy Chonkov
Date: Mon, 19 May 2025 19:58:37 +0200
Subject: [PATCH 016/121] Translated using Weblate (Bulgarian)
Currently translated at 81.7% (903 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
---
client/strings/bg.json | 71 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 69 insertions(+), 2 deletions(-)
diff --git a/client/strings/bg.json b/client/strings/bg.json
index 481a3bb8..9fc3f7af 100644
--- a/client/strings/bg.json
+++ b/client/strings/bg.json
@@ -255,7 +255,7 @@
"LabelBackupLocation": "Местоположение на Архив",
"LabelBackupsEnableAutomaticBackups": "Включи автоматично архивиране",
"LabelBackupsEnableAutomaticBackupsHelp": "Архиви запазени в /metadata/backups",
- "LabelBackupsMaxBackupSize": "Максимален размер на архива (в GB)",
+ "LabelBackupsMaxBackupSize": "Максимален размер на архива (в GB) (0 за неограничен)",
"LabelBackupsMaxBackupSizeHelp": "За защита срещу грешки в конфигурацията, архивите ще се провалят ако надхвърлят конфигурирания размер.",
"LabelBackupsNumberToKeep": "Брой архиви за запазване",
"LabelBackupsNumberToKeepHelp": "Само 1 архив ще бъде премахнат на веднъж, така че ако вече имате повече архиви от това трябва да ги премахнете ръчно.",
@@ -285,6 +285,7 @@
"LabelContinueSeries": "Продължи серии",
"LabelCover": "Корица",
"LabelCoverImageURL": "URL на Корица",
+ "LabelCoverProvider": "Източник за обложки",
"LabelCreatedAt": "Създадено на",
"LabelCronExpression": "Cron израз",
"LabelCurrent": "Текущо",
@@ -327,11 +328,20 @@
"LabelEncodingClearItemCache": "Уверете се, че периодично изчиствате кеша на елементите.",
"LabelEncodingFinishedM4B": "Завършеният M4B файл ще бъде поставен в папката на вашите аудиокниги на:",
"LabelEncodingInfoEmbedded": "Метаданните ще бъдат вградени в аудио траковете в папката на вашите аудиокниги.",
+ "LabelEncodingStartedNavigation": "Когато задачата е стартирана, можете да смените тази страница.",
+ "LabelEncodingTimeWarning": "Кодирането може да отнеме до 30 минути.",
+ "LabelEncodingWarningAdvancedSettings": "Внимание: Не променяйте тези настройки, ако не сте запознати с ffmpeg настройките за кодиране.",
+ "LabelEncodingWatcherDisabled": "Ако сте изключили наблюдението на папки, ще е нужно да сканирате повторно аудио книгата.",
"LabelEnd": "Край",
"LabelEndOfChapter": "Край на глава",
"LabelEpisode": "Епизод",
+ "LabelEpisodeNotLinkedToRssFeed": "Епизодът не е свързан с RSS канал",
+ "LabelEpisodeNumber": "Епизод #{0}",
"LabelEpisodeTitle": "Заглавие на Епизод",
"LabelEpisodeType": "Тип на Епизод",
+ "LabelEpisodeUrlFromRssFeed": "URL адрес на епизод от RSS канал",
+ "LabelEpisodes": "Епизоди",
+ "LabelEpisodic": "Епизодичен",
"LabelExample": "Пример",
"LabelExpandSeries": "Покажи сериите",
"LabelExpandSubSeries": "Покажи съб сериите",
@@ -343,7 +353,9 @@
"LabelFetchingMetadata": "Взимане на Метаданни",
"LabelFile": "Файл",
"LabelFileBirthtime": "Дата на създаване на файла",
+ "LabelFileBornDate": "Роден {0}",
"LabelFileModified": "Дата на модификация на файла",
+ "LabelFileModifiedDate": "Променен {0}",
"LabelFilename": "Име на файла",
"LabelFilterByUser": "Филтриране по Потребител",
"LabelFindEpisodes": "Намери Епизоди",
@@ -357,14 +369,17 @@
"LabelFontScale": "Мащаб на шрифта",
"LabelFontStrikethrough": "Зачертан",
"LabelFormat": "Формат",
+ "LabelFull": "Пълен",
"LabelGenre": "Жанр",
"LabelGenres": "Жанрове",
"LabelHardDeleteFile": "Пълно Изтриване на Файл",
"LabelHasEbook": "Има е-книга",
"LabelHasSupplementaryEbook": "Има допълнителна е-книга",
+ "LabelHideSubtitles": "Скрий субтитри",
"LabelHighestPriority": "Най-висок Приоритет",
"LabelHost": "Хост",
"LabelHour": "Час",
+ "LabelHours": "Часа",
"LabelIcon": "Икона",
"LabelImageURLFromTheWeb": "URL на Изображение от Интернет",
"LabelInProgress": "В процес на изпълнение",
@@ -379,8 +394,11 @@
"LabelIntervalEvery6Hours": "Всеки 6 часа",
"LabelIntervalEveryDay": "Всеки ден",
"LabelIntervalEveryHour": "Всеки час",
+ "LabelIntervalEveryMinute": "Всяка минута",
"LabelInvert": "Обърни",
"LabelItem": "Елемент",
+ "LabelJumpBackwardAmount": "Количество за прескачане назад",
+ "LabelJumpForwardAmount": "Количество за прескачане напред",
"LabelLanguage": "Език",
"LabelLanguageDefaultServer": "Език по подразбиране на сървъра",
"LabelLanguages": "Езици",
@@ -395,6 +413,7 @@
"LabelLess": "По-малко",
"LabelLibrariesAccessibleToUser": "Библиотеки Достъпни за Потребителя",
"LabelLibrary": "Библиотека",
+ "LabelLibraryFilterSublistEmpty": "Не {0}",
"LabelLibraryItem": "Елемент на Библиотека",
"LabelLibraryName": "Име на Библиотека",
"LabelLimit": "Лимит",
@@ -407,6 +426,10 @@
"LabelLowestPriority": "Най-нисък Приоритет",
"LabelMatchExistingUsersBy": "Съпостави съществуващи потребители по",
"LabelMatchExistingUsersByDescription": "Използва се за свързване на съществуващи потребители. След свързване потребителите ще бъдат съпоставени по уникален идентификатор от вашия доставчик на SSO",
+ "LabelMaxEpisodesToDownload": "Максимален брой епизоди за сваляне. Използвай 0 за неограничен.",
+ "LabelMaxEpisodesToDownloadPerCheck": "Максимален брой нови епизоди за сваляне за проверка",
+ "LabelMaxEpisodesToKeep": "Максимален брой епизоди за запазване",
+ "LabelMaxEpisodesToKeepHelp": "Стойност 0 указва без максимален лимит. След като нов епизод е автоматично свален, най-старият епизод ще бъде изтрит, ако имате повече от X епизода. Само по един епизод ще бъде изтриван за всеки нов свален такъв.",
"LabelMediaPlayer": "Медия Плейър",
"LabelMediaType": "Тип медия",
"LabelMetaTag": "Мета Таг",
@@ -414,6 +437,7 @@
"LabelMetadataOrderOfPrecedenceDescription": "По-високите източници на метаданни ще заменят по-ниските",
"LabelMetadataProvider": "Доставчик на Метаданни",
"LabelMinute": "Минута",
+ "LabelMinutes": "Минути",
"LabelMissing": "Липсващо",
"LabelMissingEbook": "Няма електронна книга",
"LabelMissingSupplementaryEbook": "Няма допълнителна електронна книга",
@@ -451,11 +475,14 @@
"LabelOpenIDGroupClaimDescription": "Име на OpenID твърдението, което съдържа списък с групите на потребителя. Обикновено се нарича groups. Ако е конфигурирано, приложението автоматично ще присвоява роли въз основа на членството на потребителя в групи, при условие че тези групи са наименувани без чувствителност към регистъра като 'admin', 'user' или 'guest' в твърдението. Твърдението трябва да съдържа списък и ако потребителят принадлежи към множество групи, приложението ще присвои ролята, съответстваща на най-високото ниво на достъп. Ако няма съвпадение с група, достъпът ще бъде отказан.",
"LabelOpenRSSFeed": "Отвори RSS Feed",
"LabelOverwrite": "Презапиши",
+ "LabelPaginationPageXOfY": "Страница {0} от {1}",
"LabelPassword": "Парола",
"LabelPath": "Път",
+ "LabelPermanent": "Постоянен",
"LabelPermissionsAccessAllLibraries": "Може да достъпи до всички библиотеки",
"LabelPermissionsAccessAllTags": "Може да достъпи всички тагове",
"LabelPermissionsAccessExplicitContent": "Може да достъпи експлицитно съдържание",
+ "LabelPermissionsCreateEreader": "Може да създава електронен четец",
"LabelPermissionsDelete": "Може да трие",
"LabelPermissionsDownload": "Може да сваля",
"LabelPermissionsUpdate": "Може да обновява",
@@ -463,6 +490,8 @@
"LabelPersonalYearReview": "Преглед на годината Ви ({0})",
"LabelPhotoPathURL": "Път/URL на Снимка",
"LabelPlayMethod": "Метод на Пускане",
+ "LabelPlaybackRateIncrementDecrement": "Размер на увеличаване/намаляне при скоростта на възпроизвеждане",
+ "LabelPlayerChapterNumberMarker": "{0} от {1}",
"LabelPlaylists": "Плейлисти",
"LabelPodcast": "Подкаст",
"LabelPodcastSearchRegion": "Регион за Търсене на Подкасти",
@@ -474,9 +503,12 @@
"LabelPrimaryEbook": "Основна Електронна Книга",
"LabelProgress": "Прогрес",
"LabelProvider": "Доставчик",
+ "LabelProviderAuthorizationValue": "Стойност на Authorization Header",
"LabelPubDate": "Дата на публикуване",
"LabelPublishYear": "Година на публикуване",
"LabelPublishedDate": "Публикувани {0}",
+ "LabelPublishedDecade": "Десетилетие на публикуване",
+ "LabelPublishedDecades": "Десетилетия на публикуване",
"LabelPublisher": "Издател",
"LabelPublishers": "Издателство",
"LabelRSSFeedCustomOwnerEmail": "Персонализиран имейл на собственика",
@@ -486,6 +518,7 @@
"LabelRSSFeedSlug": "идентификатор на RSS емисия",
"LabelRSSFeedURL": "URL на RSS емисия",
"LabelRandomly": "Случайно",
+ "LabelReAddSeriesToContinueListening": "Добави отново в \"Продължете да слушате\"",
"LabelRead": "Прочети",
"LabelReadAgain": "Прочети отново",
"LabelReadEbookWithoutProgress": "Прочети електронната книга без записване прогрес",
@@ -495,29 +528,40 @@
"LabelRedo": "Повтори",
"LabelRegion": "Регион",
"LabelReleaseDate": "Дата на Издаване",
+ "LabelRemoveAllMetadataAbs": "Премахни всички metadata.abs файлове",
+ "LabelRemoveAllMetadataJson": "Премахни всички metadata.json файлове",
+ "LabelRemoveAudibleBranding": "Премахни въведението и заключението на Audible от главите",
"LabelRemoveCover": "Премахни Корица",
+ "LabelRemoveMetadataFile": "Премахни файловете с метаданни от папката на библиотеката",
+ "LabelRemoveMetadataFileHelp": "Премахни всички metadata.json и metadata.abs файлове от вашата {0} папка.",
"LabelRowsPerPage": "Редове на Страница",
"LabelSearchTerm": "Търси Термин",
"LabelSearchTitle": "Търси Заглавие",
"LabelSearchTitleOrASIN": "Търси Заглавие или ASIN",
"LabelSeason": "Сезон",
+ "LabelSeasonNumber": "Сезон #{0}",
"LabelSelectAll": "Избери всичко",
"LabelSelectAllEpisodes": "Избери всички епизоди",
"LabelSelectEpisodesShowing": "Избери {0} епизоди показани",
"LabelSelectUsers": "Избери Потребители",
"LabelSendEbookToDevice": "Изпрати електронна книга до ...",
"LabelSequence": "Последователност",
+ "LabelSerial": "Сериал",
"LabelSeries": "От сериите",
"LabelSeriesName": "Име на Серия",
"LabelSeriesProgress": "Прогрес на Серия",
+ "LabelServerLogLevel": "Ниво на сървърен журнал",
"LabelServerYearReview": "Преглед на годината на сървъра ({0})",
"LabelSetEbookAsPrimary": "Направи главен",
"LabelSetEbookAsSupplementary": "Направи второстепенен",
+ "LabelSettingsAllowIframe": "Разреши вграждане в iframe",
"LabelSettingsAudiobooksOnly": "Само аудиокниги",
"LabelSettingsAudiobooksOnlyHelp": "Активирането на тази настройка ще игнорира файловете на електронни книги, освен ако не са в папка с аудиокниги, в което случай ще бъдат зададени като допълнителни електронни книги",
"LabelSettingsBookshelfViewHelp": "Скеуморфен дизайн с дървени рафтове",
"LabelSettingsChromecastSupport": "Chromecast поддръжка",
"LabelSettingsDateFormat": "Формат на Дата",
+ "LabelSettingsEnableWatcher": "Автоматично сканиране на библиотеките за промени",
+ "LabelSettingsEnableWatcherForLibrary": "Автоматично сканиране на библиотеката за промени",
"LabelSettingsEnableWatcherHelp": "Включва автоматичното добавяне/обновяване на елементи, когато се открият промени във файловете. *Изисква рестарт на сървъра",
"LabelSettingsEpubsAllowScriptedContent": "Позволи скриптово съдържание в epub-и",
"LabelSettingsEpubsAllowScriptedContentHelp": "Позволи epub файловете да изпълняват скриптове. Препоръчително е да бъде изключено освен ако не се доверявате на източника на epub файловете.",
@@ -529,10 +573,13 @@
"LabelSettingsHideSingleBookSeriesHelp": "Сериите с една книга ще бъдат скрити от страницата на серията и рафтовете на началната страница.",
"LabelSettingsHomePageBookshelfView": "Начална страница изглед на рафт",
"LabelSettingsLibraryBookshelfView": "Библиотека изглед на рафт",
+ "LabelSettingsLibraryMarkAsFinishedPercentComplete": "Процент завършеност е по-голям от",
+ "LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Оставащо време е по-малко от (секунди)",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "Отбелязване на мултимедиен елемент като завършен когато",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Пропусни предишни книги в Продължи Поредица",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Рафтът на началната страница 'Продължи поредицата' показва първата книга, която не е започната в поредици, в които има поне една завършена книга и няма книги в процес на четене. Активирането на тази настройка ще продължи поредицата от най-далечната завършена книга вместо от първата незапочната книга.",
"LabelSettingsParseSubtitles": "Извлечи подзаглавия",
- "LabelSettingsParseSubtitlesHelp": "Извлича подзаглавия от имената на папките на аудиокнигите. Подзаглавията трябва да бъдат разделени с \" - \" например \"Заглавие на Книга - Тук е Подзаглавито\" има подзаглавие \"Тук е Подзаглавито\"",
+ "LabelSettingsParseSubtitlesHelp": "Извлича подзаглавия от имената на папките на аудио книгите. Подзаглавията трябва да бъдат разделени с \" - \" например \"Заглавие на Книга - Тук е подзаглавието\" има подзаглавие \"Тук е подзаглавието\"",
"LabelSettingsPreferMatchedMetadata": "Предпочети съвпадащи метаданни",
"LabelSettingsPreferMatchedMetadataHelp": "Съвпадащите данни ще заменят детайлите на елемента при използване на Бързо Съпоставяне. По подразбиране Бързото Съпоставяне ще попълни само липсващите детайли.",
"LabelSettingsSkipMatchingBooksWithASIN": "Пропусни съвпадащи книги, които вече имат ASIN",
@@ -546,11 +593,19 @@
"LabelSettingsStoreMetadataWithItem": "Запази метаданните с елемента",
"LabelSettingsStoreMetadataWithItemHelp": "По подразбиране метаданните се съхраняват в /metadata/items, като активирате тази настройка метаданните ще се съхраняват в папката на елемента на вашата библиотека",
"LabelSettingsTimeFormat": "Формат на Време",
+ "LabelShare": "Сподели",
+ "LabelShareDownloadableHelp": "Разреши на потребителите през връзка за споделяне да свалят zip файл с мултимедийния елемент.",
+ "LabelShareOpen": "Общодостъпно",
+ "LabelShareURL": "URL за споделяне",
"LabelShowAll": "Покажи всички",
"LabelShowSeconds": "Покажи секунди",
+ "LabelShowSubtitles": "Показвай подзаглавия",
"LabelSize": "Размер",
"LabelSleepTimer": "Таймер за изключване",
"LabelSlug": "Слъг",
+ "LabelSortAscending": "Възходящ",
+ "LabelSortDescending": "Низходящ",
+ "LabelSortPubDate": "Подреди по дата на публикуване",
"LabelStart": "Старт",
"LabelStartTime": "Начално Време",
"LabelStarted": "Стартирано",
@@ -585,6 +640,11 @@
"LabelThemeDark": "Тъмна",
"LabelThemeLight": "Светла",
"LabelTimeBase": "Времева Основа",
+ "LabelTimeDurationXHours": "{0} часа",
+ "LabelTimeDurationXMinutes": "{0} минути",
+ "LabelTimeDurationXSeconds": "{0} секунди",
+ "LabelTimeInMinutes": "Време в минути",
+ "LabelTimeLeft": "остава {0}",
"LabelTimeListened": "Време Слушано",
"LabelTimeListenedToday": "Време Слушано Днес",
"LabelTimeRemaining": "{0} оставащи",
@@ -592,6 +652,7 @@
"LabelTitle": "Заглавие",
"LabelToolsEmbedMetadata": "Вграждане на Метаданни",
"LabelToolsEmbedMetadataDescription": "Вграждане на метаданни в аудио файлове, включително корица и глави.",
+ "LabelToolsM4bEncoder": "M4B кодировчик",
"LabelToolsMakeM4b": "Направи M4B Аудиокнига Файл",
"LabelToolsMakeM4bDescription": "Генериране на .M4B аудиокнига файл с вградени метаданни, корица и глави.",
"LabelToolsSplitM4b": "Раздели M4B на MP3-ки",
@@ -604,26 +665,32 @@
"LabelTracksMultiTrack": "Многоканален",
"LabelTracksNone": "Няма канали",
"LabelTracksSingleTrack": "Единичен канал",
+ "LabelTrailer": "Трейлър",
"LabelType": "Тип",
"LabelUnabridged": "Несъкратен",
"LabelUndo": "Отмени",
"LabelUnknown": "Неизвестен",
+ "LabelUnknownPublishDate": "Неизвестна дата на публикуване",
"LabelUpdateCover": "Обнови Корица",
"LabelUpdateCoverHelp": "Позволи презаписване на съществуващите корици за избраните книги, когато се намери съвпадение",
"LabelUpdateDetails": "Обнови Детайли",
"LabelUpdateDetailsHelp": "Позволи презаписване на съществуващите детайли за избраните книги, когато се намери съвпадение",
"LabelUpdatedAt": "Обновено на",
"LabelUploaderDragAndDrop": "Плъзни и Пусни Файлове или Папки",
+ "LabelUploaderDragAndDropFilesOnly": "Извлачване на файлове",
"LabelUploaderDropFiles": "Пусни Файлове",
"LabelUploaderItemFetchMetadataHelp": "Автоматично вземи заглавие, автор и серия",
+ "LabelUseAdvancedOptions": "Използвай разширени опции",
"LabelUseChapterTrack": "Използвай канал за глава",
"LabelUseFullTrack": "Използвай пълен канал",
+ "LabelUseZeroForUnlimited": "Използвай 0 за неограничен",
"LabelUser": "Потребител",
"LabelUsername": "Потребителско име",
"LabelValue": "Стойност",
"LabelVersion": "Версия",
"LabelViewBookmarks": "Виж Отметки",
"LabelViewChapters": "Виж Глави",
+ "LabelViewPlayerSettings": "Виж настройки на плеъра",
"LabelViewQueue": "Виж Опашка",
"LabelVolume": "Сила на Звука",
"LabelWeekdaysToRun": "Делници за изпълнение",
From 0cd698cc8d95a5d0b503a0d5e78eef5a761244c3 Mon Sep 17 00:00:00 2001
From: peter cerny
Date: Mon, 19 May 2025 13:13:58 +0200
Subject: [PATCH 017/121] Translated using Weblate (Slovak)
Currently translated at 99.9% (1103 of 1104 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
---
client/strings/sk.json | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/client/strings/sk.json b/client/strings/sk.json
index f5a3aa6b..c1b3bfdc 100644
--- a/client/strings/sk.json
+++ b/client/strings/sk.json
@@ -531,6 +531,7 @@
"LabelReleaseDate": "Dátum vydania",
"LabelRemoveAllMetadataAbs": "Odstrániť všetky súbory metadata.abs",
"LabelRemoveAllMetadataJson": "Odstrániť všetky súbory metadata.json",
+ "LabelRemoveAudibleBranding": "Odstrániť z kapitol Audible intro a outro",
"LabelRemoveCover": "Odstrániť prebal",
"LabelRemoveMetadataFile": "Odstrániť súbory metadát z priečinkov položiek v knižnici",
"LabelRemoveMetadataFileHelp": "Odstrániť všetky súbory metadata.json a metadata.abs vo Vašich {0} priečinkoch.",
@@ -707,6 +708,7 @@
"MessageAddToPlayerQueue": "Pridať do zoznamu prehrávania",
"MessageAppriseDescription": "Aby ste mohli používať túto funkciumusíte mať k dispozícii inštanciu Apprise API alebo inú, ktorá dokáže spracovávať rovnaké požiadavky/requesty. Apprise URL musí byť úplná URL určená na zasielanie notifikácií, tj. ak napr. vaša APi beží na http://192.168.1.1:8337, vložte do daného poľa http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "Uistite sa, že používate ASIN zo správneho regiónu Audible, nie Amazonu.",
+ "MessageAuthenticationOIDCChangesRestart": "Reštartujte svoj server po uložení, aby mohli byť použité zmeny OIDC.",
"MessageBackupsDescription": "Zálohy pokrývajú používateľov, ich aktuálne stavy počúvania, detaily položiek knižnice, nastavenia servera a obrázky uložené v /metadata/items a /metadata/authors. Zálohy neobsahujú súbory v priečinkoch vašich knižníc.",
"MessageBackupsLocationEditNote": "Poznámka: Zmena umiestnenia záloh nepresunie ani nezmení existujúce zálohy",
"MessageBackupsLocationNoEditNote": "Poznámka: Umietnenie záloh je nastavené prostredníctvom premennej prostredia a nie je ho možné zmeniť z tohto miesta.",
@@ -907,8 +909,8 @@
"NoteChangeRootPassword": "Root používateľ je jediný používateľ, ktorý môže mať prázdne heslo",
"NoteChapterEditorTimes": "Poznámka: Prvá kapitola musí vždy začínať v 0:00 a začiatok poslednej kapitoly nemôže prekročiť trvanie tejto audioknihy.",
"NoteFolderPicker": "Poznámka: Priečinky, ktoré už boli priradené, sa ďalej nezobrazujú",
- "NoteRSSFeedPodcastAppsHttps": "Varovanie: Väčšina podcastových aplikácií vyžaduje, aby URL RSS zdroja vyžívala HTTPS",
- "NoteRSSFeedPodcastAppsPubDate": "Varovanie: 1 alebo viacero vašich epizód neobsahuje infomáciu o dátume vydania. Niektoré podcastové aplikácie ju vyžadujú.",
+ "NoteRSSFeedPodcastAppsHttps": "Varovanie: Väčšina podcastových aplikácií požaduje URL RSS zdroja s HTTPS",
+ "NoteRSSFeedPodcastAppsPubDate": "Varovanie: 1 alebo viac vašich epizód neobsahuje infomáciu o dátum vydania. Niektoré podcastové ju vyžadujú.",
"NoteUploaderFoldersWithMediaFiles": "Priečinky obsahujúce súbory médií budú považované za samostatné položky knižnice.",
"NoteUploaderOnlyAudioFiles": "Ak budú nahraté iba zvukové súbory, každý zvukový súbor bude považovaný za samostatnú audioknihu.",
"NoteUploaderUnsupportedFiles": "Nepodporované súbory budú ignorované. Pri výbere alebo prenesení priečinka, budú všetky súbory, ktoré nie sú v priečinku niektorej z položiek, ignorované.",
@@ -972,6 +974,8 @@
"ToastCachePurgeFailed": "Vyčistenie vyrovnávacej pamäte zlyhalo",
"ToastCachePurgeSuccess": "Vyrovnávacia pamäť vyčistená",
"ToastChaptersHaveErrors": "Kapitoly obsahujú chyby",
+ "ToastChaptersInvalidShiftAmountLast": "Neplatná hodnota veľkosti posunutia. Začiatok poslednej kapitoly by ležal za koncom audioknihy.",
+ "ToastChaptersInvalidShiftAmountStart": "Nesprávna hodnota posunutia. Prvá kapitola by mala nulovú alebo zápornú dĺžku a bola by nahradená nasledujúcou kapitolou. Navýšte čas začiatku druhej kapitoly.",
"ToastChaptersMustHaveTitles": "Kapitoly musia mať názvy",
"ToastChaptersRemoved": "Kapitoly boli odstránené",
"ToastChaptersUpdated": "Kapitoly boli aktualizované",
From 846a8c38812df7468a936df029ac031547bfcfe3 Mon Sep 17 00:00:00 2001
From: biuklija
Date: Tue, 20 May 2025 07:50:09 +0200
Subject: [PATCH 018/121] Translated using Weblate (Croatian)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
---
client/strings/hr.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/hr.json b/client/strings/hr.json
index 7e4e82c4..c4715ff3 100644
--- a/client/strings/hr.json
+++ b/client/strings/hr.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Pokreni svaki {0} u {1}",
"MessageSearchResultsFor": "Rezultati pretrage za",
"MessageSelected": "{0} odabrano",
+ "MessageSeriesSequenceCannotContainSpaces": "Slijed serijala ne može sadržavati praznine",
"MessageServerCouldNotBeReached": "Nije moguće pristupiti poslužitelju",
"MessageSetChaptersFromTracksDescription": "Postavi poglavlja koristeći se zvučnom datotekom kao poglavljem i nazivom datoteke kao naslovom poglavlja",
"MessageShareExpirationWillBe": "Vrijeme isteka će biti {0}",
From 6a51cb07e8516ee80c35e209a34749936f4b36d9 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: Tue, 20 May 2025 07:21:07 +0200
Subject: [PATCH 019/121] Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1105 of 1105 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 5b652049..51e5f69e 100644
--- a/client/strings/uk.json
+++ b/client/strings/uk.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Запуск кожні {0} о {1}",
"MessageSearchResultsFor": "Результати пошуку для",
"MessageSelected": "Вибрано: {0}",
+ "MessageSeriesSequenceCannotContainSpaces": "Послідовність серій не може містити пробілів",
"MessageServerCouldNotBeReached": "Не вдалося підключитися до сервера",
"MessageSetChaptersFromTracksDescription": "Створити глави з аудіодоріжок, встановивши назви файлів за заголовки",
"MessageShareExpirationWillBe": "Термін сплине за {0}",
From 8979e19e92e072327e9850b584ab7014d6257eb2 Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Tue, 20 May 2025 20:08:41 +0200
Subject: [PATCH 020/121] Translated using Weblate (Arabic)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index 59fcd4cf..fce38bc6 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "تشغيل كل {0} في الساعة {1}",
"MessageSearchResultsFor": "نتائج البحث عن",
"MessageSelected": "تم تحديد {0}",
+ "MessageSeriesSequenceCannotContainSpaces": "السلسلة المتعاقبة لا يمكن أن تحتوي على مسافات",
"MessageServerCouldNotBeReached": "تعذر الوصول إلى الخادم",
"MessageSetChaptersFromTracksDescription": "تعيين الفصول باستخدام كل ملف صوتي كفصل وعنوان الفصل كاسم الملف الصوتي",
"MessageShareExpirationWillBe": "سيكون تاريخ الانتهاء {0}",
From c950ac7d69a3f28de424b37bb47b57927c26eb3d Mon Sep 17 00:00:00 2001
From: Adolfo Jayme Barrientos
Date: Wed, 21 May 2025 18:54:51 +0200
Subject: [PATCH 021/121] Translated using Weblate (Spanish)
Currently translated at 99.9% (1104 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
---
client/strings/es.json | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/client/strings/es.json b/client/strings/es.json
index 80652f39..0da47082 100644
--- a/client/strings/es.json
+++ b/client/strings/es.json
@@ -855,6 +855,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Ejecutar cada {0} a las {1}",
"MessageSearchResultsFor": "Resultados de la búsqueda de",
"MessageSelected": "{0} seleccionado(s)",
+ "MessageSeriesSequenceCannotContainSpaces": "La secuencia de la serie no puede contener espacios",
"MessageServerCouldNotBeReached": "No se pudo establecer la conexión con el servidor",
"MessageSetChaptersFromTracksDescription": "Establecer capítulos usando cada archivo de audio como un capítulo y el título del capítulo como el nombre del archivo de audio",
"MessageShareExpirationWillBe": "La caducidad será {0}",
@@ -958,11 +959,11 @@
"ToastBackupInvalidMaxKeep": "Número no válido de copias de seguridad a conservar",
"ToastBackupInvalidMaxSize": "Tamaño máximo de copia de seguridad no válido",
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
- "ToastBackupUploadFailed": "Error al subir el respaldo",
+ "ToastBackupUploadFailed": "Error al cargar la copia de respaldo",
"ToastBackupUploadSuccess": "Respaldo cargado",
"ToastBatchApplyDetailsToItemsSuccess": "Detalles aplicados a los elementos",
"ToastBatchDeleteFailed": "Falló la eliminación por lotes",
- "ToastBatchDeleteSuccess": "Borrado por lotes correcto",
+ "ToastBatchDeleteSuccess": "Se eliminó por lotes correctamente",
"ToastBatchQuickMatchFailed": "¡Error en la sincronización rápida por lotes!",
"ToastBatchQuickMatchStarted": "¡Se inició el lote de búsqueda rápida de {0} libros!",
"ToastBatchUpdateFailed": "Falló la actualización por lotes",
@@ -974,6 +975,7 @@
"ToastCachePurgeSuccess": "Se purgó la antememoria correctamente",
"ToastChaptersHaveErrors": "Los capítulos tienen errores",
"ToastChaptersInvalidShiftAmountLast": "Cantidad de desplazamiento no válida. La hora de inicio del último capítulo se extendería más allá de la duración de este audiolibro.",
+ "ToastChaptersInvalidShiftAmountStart": "Cantidad de desplazamiento no válida. El primer capítulo tendría una duración cero o negativa y lo sobrescribiría el segundo capítulo. Aumente la duración inicial del segundo capítulo.",
"ToastChaptersMustHaveTitles": "Los capítulos deben tener título",
"ToastChaptersRemoved": "Capítulos eliminados",
"ToastChaptersUpdated": "Capítulos actualizados",
From 0fc6afec260ed75ec97734824b1698dea0c2cb9b Mon Sep 17 00:00:00 2001
From: SunSpring
Date: Wed, 21 May 2025 07:43:46 +0200
Subject: [PATCH 022/121] Translated using Weblate (Chinese (Simplified Han
script))
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
---
client/strings/zh-cn.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/zh-cn.json b/client/strings/zh-cn.json
index 21293a4a..8715ac6e 100644
--- a/client/strings/zh-cn.json
+++ b/client/strings/zh-cn.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "每隔 {0} 在 {1} 运行一次",
"MessageSearchResultsFor": "搜索结果",
"MessageSelected": "{0} 已选择",
+ "MessageSeriesSequenceCannotContainSpaces": "系列序列不能包含空格",
"MessageServerCouldNotBeReached": "无法访问服务器",
"MessageSetChaptersFromTracksDescription": "把每个音频文件设置为章节并将章节标题设置为音频文件名",
"MessageShareExpirationWillBe": "到期日期为 {0}",
From 6e67b1d9dd55b9c27d6f00e5ba8a98cfb0e0909b Mon Sep 17 00:00:00 2001
From: Adolfo Jayme Barrientos
Date: Wed, 21 May 2025 18:46:37 +0200
Subject: [PATCH 023/121] Translated using Weblate (Catalan)
Currently translated at 96.0% (1061 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
---
client/strings/ca.json | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/client/strings/ca.json b/client/strings/ca.json
index 2aaf0377..ee104674 100644
--- a/client/strings/ca.json
+++ b/client/strings/ca.json
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Llista de Reproducció",
"HeaderPlaylistItems": "Elements de la Llista de Reproducció",
"HeaderPodcastsToAdd": "Pòdcasts a afegir",
+ "HeaderPresets": "Valors predefinits",
"HeaderPreviewCover": "Previsualització de la Portada",
"HeaderRSSFeedGeneral": "Detalls RSS",
"HeaderRSSFeedIsOpen": "La Font RSS està oberta",
@@ -802,16 +803,18 @@
"MessageQuickEmbedQueue": "En cua per a inserció ràpida ({0} en cua)",
"MessageQuickMatchAllEpisodes": "Combina ràpidament tots els episodis",
"MessageQuickMatchDescription": "Omple detalls d'elements buits i portades amb els primers resultats de '{0}'. No sobreescriu els detalls tret que l'opció \"Preferir metadades trobades\" del servidor estigui habilitada.",
- "MessageRemoveChapter": "Eliminar capítols",
- "MessageRemoveEpisodes": "Eliminar {0} episodi(s)",
- "MessageRemoveFromPlayerQueue": "Eliminar de la cua del reproductor",
+ "MessageRemoveChapter": "Elimina el capítol",
+ "MessageRemoveEpisodes": "Elimina {0} episodi(s)",
+ "MessageRemoveFromPlayerQueue": "Elimina de la cua del reproductor",
"MessageRemoveUserWarning": "Segur que voleu suprimir permanentment l'usuari «{0}»?",
"MessageReportBugsAndContribute": "Informa d'errors, sol·licita funcions i contribueix a",
"MessageResetChaptersConfirm": "Segur que voleu desfer els canvis i revertir els capítols al seu estat original?",
"MessageRestoreBackupConfirm": "Segur que voleu restaurar la còpia de seguretat creada a",
"MessageRestoreBackupWarning": "Restaurar sobreescriurà tota la base de dades situada a /config i les imatges de portades a /metadata/items i /metadata/authors.
La còpia de seguretat no modifica cap fitxer a les carpetes de la teva biblioteca. Si has activat l'opció del servidor per guardar portades i metadades a les carpetes de la biblioteca, aquests fitxers no es guarden ni sobreescriuen.
Tots els clients que utilitzin el teu servidor s'actualitzaran automàticament.",
+ "MessageScheduleRunEveryWeekdayAtTime": "Executa cada {0} a les {1}",
"MessageSearchResultsFor": "Resultats de la cerca de",
"MessageSelected": "{0} seleccionat(s)",
+ "MessageSeriesSequenceCannotContainSpaces": "La seqüència de la sèrie no pot contenir espais",
"MessageServerCouldNotBeReached": "No es va poder establir la connexió amb el servidor",
"MessageSetChaptersFromTracksDescription": "Establir capítols utilitzant cada fitxer d'àudio com un capítol i el títol del capítol com el nom del fitxer d'àudio",
"MessageShareExpirationWillBe": "La caducitat serà {0}",
@@ -917,6 +920,7 @@
"ToastBackupRestoreFailed": "Error en restaurar la còpia de seguretat",
"ToastBackupUploadFailed": "Error en carregar la còpia de seguretat",
"ToastBackupUploadSuccess": "Còpia de seguretat carregada",
+ "ToastBatchApplyDetailsToItemsSuccess": "S'han aplicat els detalls als elements",
"ToastBatchDeleteFailed": "Error en l'eliminació per lots",
"ToastBatchDeleteSuccess": "Eliminació per lots correcte",
"ToastBatchQuickMatchFailed": "Error en la sincronització ràpida per lots!",
@@ -930,6 +934,7 @@
"ToastCachePurgeSuccess": "Memòria cau purgada amb èxit",
"ToastChaptersHaveErrors": "Els capítols tenen errors",
"ToastChaptersInvalidShiftAmountLast": "La quantitat de desplaçament no és vàlida. L'hora d'inici de l'últim capítol s'estendria més enllà de la durada d'aquest audiollibre.",
+ "ToastChaptersInvalidShiftAmountStart": "La quantitat de desplaçament no és vàlida. El primer capítol tindria una durada zero o negativa i el sobreescriuria el segon capítol. Augmenteu la durada inicial del segon capítol.",
"ToastChaptersMustHaveTitles": "Els capítols han de tenir un títol",
"ToastChaptersRemoved": "Capítols eliminats",
"ToastChaptersUpdated": "Capítols actualitzats",
@@ -937,6 +942,7 @@
"ToastCollectionRemoveSuccess": "Col·lecció eliminada",
"ToastCollectionUpdateSuccess": "Col·lecció actualitzada",
"ToastCoverUpdateFailed": "Error en actualitzar la portada",
+ "ToastDateTimeInvalidOrIncomplete": "La data i hora no és vàlida o està incompleta",
"ToastDeleteFileFailed": "No s'ha pogut suprimir el fitxer",
"ToastDeleteFileSuccess": "Fitxer suprimit",
"ToastDeviceAddFailed": "Error en afegir el dispositiu",
From 4fd97510b82f9a16f5be2529d0cdbf952c448a14 Mon Sep 17 00:00:00 2001
From: peter cerny
Date: Thu, 22 May 2025 10:48:48 +0200
Subject: [PATCH 024/121] Translated using Weblate (Slovak)
Currently translated at 99.9% (1104 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
---
client/strings/sk.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/sk.json b/client/strings/sk.json
index c1b3bfdc..39b3c0ae 100644
--- a/client/strings/sk.json
+++ b/client/strings/sk.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Spustiť každú {0} o {1}",
"MessageSearchResultsFor": "Výsledky vyhľadávania pre",
"MessageSelected": "{0} vybrané",
+ "MessageSeriesSequenceCannotContainSpaces": "Poradie série nemôže obsahovať medzery",
"MessageServerCouldNotBeReached": "Nepodarilo sa pripojiť na server",
"MessageSetChaptersFromTracksDescription": "Nastaviť jednotlivé zvukové súbory ako kapitoly a názvy zvukových súborov ako názvy týchto kapitol",
"MessageShareExpirationWillBe": "Expiruje {0}",
From 7275b1063b03b281949382a4305a71a74648ac98 Mon Sep 17 00:00:00 2001
From: Jan Schoenfeld
Date: Fri, 23 May 2025 22:39:27 +0200
Subject: [PATCH 025/121] Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/de.json b/client/strings/de.json
index 03b850c4..e15a22f7 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Immer {0} um {1} ausführen",
"MessageSearchResultsFor": "Suchergebnisse für",
"MessageSelected": "{0} ausgewählt",
+ "MessageSeriesSequenceCannotContainSpaces": "Serie Abfolge kann keine Leerzeichen enthalten",
"MessageServerCouldNotBeReached": "Server kann nicht erreicht werden",
"MessageSetChaptersFromTracksDescription": "Kaitelerstellung basiert auf den existierenden einzelnen Audiodateien. Pro existierende Audiodatei wird 1 Kapitel erstellt, wobei deren Kapitelname aus dem Audiodateinamen extrahiert wird",
"MessageShareExpirationWillBe": "Läuft am {0} ab",
@@ -975,6 +976,7 @@
"ToastCachePurgeSuccess": "Cache geleert",
"ToastChaptersHaveErrors": "Kapitel sind fehlerhaft",
"ToastChaptersInvalidShiftAmountLast": "Die Verschiebung ist nicht möglich, da die Startzeit des letzten Kapitels über die Gesamtdauer dieses Hörbuchs hinausgehen würde.",
+ "ToastChaptersInvalidShiftAmountStart": "Ungültige Höhe der Verschiebung. Das erste Kapitel hätte eine Länge von Null oder eine negative Länge und würde vom zweiten Kapitel überschrieben werden. Erhöhen Sie die Startdauer des zweiten Kapitels.",
"ToastChaptersMustHaveTitles": "Kapitel benötigen eindeutige Namen",
"ToastChaptersRemoved": "Kapitel entfernt",
"ToastChaptersUpdated": "Kapitel aktualisiert",
From 243baaf77504d890398bce9d0b9019571cc3aeca Mon Sep 17 00:00:00 2001
From: thehijacker
Date: Sat, 24 May 2025 19:20:17 +0200
Subject: [PATCH 026/121] Translated using Weblate (Slovenian)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
---
client/strings/sl.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/sl.json b/client/strings/sl.json
index 2794e080..2f028403 100644
--- a/client/strings/sl.json
+++ b/client/strings/sl.json
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Zaženi vsakih {0} ob {1}",
"MessageSearchResultsFor": "Rezultati iskanja za",
"MessageSelected": "{0} izbrano",
+ "MessageSeriesSequenceCannotContainSpaces": "Zaporedje serij ne sme vsebovati presledkov",
"MessageServerCouldNotBeReached": "Strežnika ni bilo mogoče doseči",
"MessageSetChaptersFromTracksDescription": "Nastavi poglavja z uporabo vsake zvočne datoteke kot poglavja in naslova poglavja kot imena zvočne datoteke",
"MessageShareExpirationWillBe": "Potečeno bo {0}",
From ff425212e70e3865ae36e4f83303e5355c1142b9 Mon Sep 17 00:00:00 2001
From: ABS translator
Date: Sun, 25 May 2025 09:11:49 +0200
Subject: [PATCH 027/121] Translated using Weblate (Arabic)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index fce38bc6..a1383047 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -59,7 +59,7 @@
"ButtonPause": "إيقاف مؤقت",
"ButtonPlay": "تشغيل",
"ButtonPlayAll": "تشغيل الكل",
- "ButtonPlaying": "مشغل الآن",
+ "ButtonPlaying": "جاري التشغيل",
"ButtonPlaylists": "قوائم التشغيل",
"ButtonPrevious": "سابِق",
"ButtonPreviousChapter": "الفصل السابق",
@@ -829,7 +829,7 @@
"MessageNoTags": "لا توجد علامات",
"MessageNoTasksRunning": "لا توجد مهام قيد التشغيل",
"MessageNoUpdatesWereNecessary": "لا حاجة لأي تحديثات",
- "MessageNoUserPlaylists": "ليست لديك أي قوائم تشغيل",
+ "MessageNoUserPlaylists": "ليس لديك أي قوائم تشغيل",
"MessageNoUserPlaylistsHelp": "قوائم التشغيل خاصة. لا يمكن إلا للمستخدم الذي ينشئها رؤيتها.",
"MessageNotYetImplemented": "لم يتم تنفيذه بعد",
"MessageOpmlPreviewNote": "ملاحظة: هذه معاينة لملف OPML الذي تم تحليله. سيتم أخذ عنوان البودكاست الفعلي من خلاصة RSS.",
@@ -839,7 +839,7 @@
"MessagePlaylistCreateFromCollection": "إنشاء قائمة تشغيل من المجموعة",
"MessagePleaseWait": "الرجاء الانتظار...",
"MessagePodcastHasNoRSSFeedForMatching": "لا يحتوي البودكاست على عنوان URL لخلاصة RSS لاستخدامه في المطابقة",
- "MessagePodcastSearchField": "أدخل مصطلح البحث أو عنوان URL لخلاصة RSS",
+ "MessagePodcastSearchField": "أدخل مصطلح البحث أو عنوان URL الخاص بتغذية RSS",
"MessageQuickEmbedInProgress": "التضمين السريع قيد التقدم",
"MessageQuickEmbedQueue": "تمت إضافته إلى قائمة انتظار التضمين السريع ({0} في قائمة الانتظار)",
"MessageQuickMatchAllEpisodes": "مطابقة سريعة لجميع الحلقات",
@@ -910,7 +910,7 @@
"NoteChangeRootPassword": "مستخدم الجذر هو المستخدم الوحيد الذي يمكن أن يكون لديه كلمة مرور فارغة",
"NoteChapterEditorTimes": "ملاحظة: يجب أن يظل وقت بدء الفصل الأول عند 0:00 ولا يمكن أن يتجاوز وقت بدء الفصل الأخير مدة هذا الكتاب الصوتي.",
"NoteFolderPicker": "ملاحظة: لن يتم عرض المجلدات التي تم تعيينها بالفعل",
- "NoteRSSFeedPodcastAppsHttps": "تحذير: تتطلب معظم تطبيقات البث الصوتي أن يكون عنوان URL لخلاصة RSS يستخدم HTTPS",
+ "NoteRSSFeedPodcastAppsHttps": "تحذير: تتطلب معظم تطبيقات البث الصوتي أن يكون عنوان URL الخاص بتغذية RSS يستخدم HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "تحذير: حلقة واحدة أو أكثر من حلقاتك ليس لها تاريخ نشر. بعض تطبيقات البودكاست تتطلب هذا.",
"NoteUploaderFoldersWithMediaFiles": "سيتم التعامل مع المجلدات التي تحتوي على ملفات وسائط كعناصر مكتبة منفصلة.",
"NoteUploaderOnlyAudioFiles": "في حالة رفع ملفات صوتية فقط، سيتم التعامل مع كل ملف صوتي ككتاب صوتي منفصل.",
@@ -1009,9 +1009,9 @@
"ToastItemDeletedFailed": "فشل حذف العنصر",
"ToastItemDeletedSuccess": "تم حذف العنصر",
"ToastItemDetailsUpdateSuccess": "تم تحديث تفاصيل العنصر",
- "ToastItemMarkedAsFinishedFailed": "فشل في وضع علامة على الانتهاء",
+ "ToastItemMarkedAsFinishedFailed": "فشل وضع علامة \"مكتمل\"",
"ToastItemMarkedAsFinishedSuccess": "تم وضع علامة \"تم الانتهاء\" على العنصر",
- "ToastItemMarkedAsNotFinishedFailed": "فشل في وضع علامة \"غير مكتمل\"",
+ "ToastItemMarkedAsNotFinishedFailed": "فشل وضع علامة \"غير مكتمل\"",
"ToastItemMarkedAsNotFinishedSuccess": "تم وضع علامة \"غير مكتمل\" على العنصر",
"ToastItemUpdateSuccess": "تم تحديث العنصر",
"ToastLibraryCreateFailed": "فشل إنشاء المكتبة",
@@ -1051,7 +1051,7 @@
"ToastPlaylistCreateSuccess": "تم إنشاء قائمة التشغيل",
"ToastPlaylistRemoveSuccess": "تمت إزالة قائمة التشغيل",
"ToastPlaylistUpdateSuccess": "تم تحديث قائمة التشغيل",
- "ToastPodcastCreateFailed": "فشل في إنشاء البودكاست",
+ "ToastPodcastCreateFailed": "فشل إنشاء البودكاست",
"ToastPodcastCreateSuccess": "تم إنشاء البودكاست بنجاح",
"ToastPodcastGetFeedFailed": "فشل في الحصول على تغذية البودكاست",
"ToastPodcastNoEpisodesInFeed": "لم يتم العثور على حلقات في خلاصة RSS",
@@ -1061,8 +1061,8 @@
"ToastProviderCreatedSuccess": "تمت إضافة مزود جديد",
"ToastProviderNameAndUrlRequired": "الاسم والرابط مطلوبان",
"ToastProviderRemoveSuccess": "تمت إزالة المزود",
- "ToastRSSFeedCloseFailed": "فشل في إغلاق موجز RSS",
- "ToastRSSFeedCloseSuccess": "تم إغلاق موجز RSS",
+ "ToastRSSFeedCloseFailed": "فشل إغلاق مغذّي RSS",
+ "ToastRSSFeedCloseSuccess": "تم إغلاق مغذّي RSS",
"ToastRemoveFailed": "فشل الإزالة",
"ToastRemoveItemFromCollectionFailed": "فشل إزالة العنصر من المجموعة",
"ToastRemoveItemFromCollectionSuccess": "تمت إزالة العنصر من المجموعة",
From 2589121908ece894a66d1d8ddaa3af686c389de8 Mon Sep 17 00:00:00 2001
From: Biepa
Date: Mon, 26 May 2025 17:40:00 +0200
Subject: [PATCH 028/121] Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/de.json b/client/strings/de.json
index e15a22f7..55b33d8a 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -88,7 +88,7 @@
"ButtonSave": "Speichern",
"ButtonSaveAndClose": "Speichern & Schließen",
"ButtonSaveTracklist": "Speichere die Titelliste",
- "ButtonScan": "Partial-Scan (nur geänderte/neue Medien)",
+ "ButtonScan": "Scannen",
"ButtonScanLibrary": "Bibliothek scannen",
"ButtonScrollLeft": "Nach Links scrollen",
"ButtonScrollRight": "Nach Rechts scrollen",
From d71f091e3ec5c364db9140afbf423ae7654db6b4 Mon Sep 17 00:00:00 2001
From: Adolfo Jayme Barrientos
Date: Mon, 26 May 2025 07:30:13 +0200
Subject: [PATCH 029/121] Translated using Weblate (Spanish)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
---
client/strings/es.json | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/client/strings/es.json b/client/strings/es.json
index 0da47082..4dac8272 100644
--- a/client/strings/es.json
+++ b/client/strings/es.json
@@ -499,7 +499,7 @@
"LabelPodcastType": "Tipo de pódcast",
"LabelPodcasts": "Pódcast",
"LabelPort": "Puerto",
- "LabelPrefixesToIgnore": "Prefijos para Ignorar (no distingue entre mayúsculas y minúsculas.)",
+ "LabelPrefixesToIgnore": "Prefijos para ignorar (no distingue entre mayúsculas y minúsculas)",
"LabelPreventIndexing": "Evite que los directorios de pódcast de iTunes y Google indicen su suministro",
"LabelPrimaryEbook": "Libro electrónico principal",
"LabelProgress": "Progreso",
@@ -515,7 +515,7 @@
"LabelRSSFeedCustomOwnerEmail": "Correo electrónico de dueño personalizado",
"LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
"LabelRSSFeedOpen": "Suministro RSS abierto",
- "LabelRSSFeedPreventIndexing": "Prevenir indexado",
+ "LabelRSSFeedPreventIndexing": "Evitar indización",
"LabelRSSFeedSlug": "«Slug» de suministro RSS",
"LabelRSSFeedURL": "URL de suministro RSS",
"LabelRandomly": "Aleatorio",
@@ -531,6 +531,7 @@
"LabelReleaseDate": "Fecha de estreno",
"LabelRemoveAllMetadataAbs": "Eliminar todos los archivos metadata.abs",
"LabelRemoveAllMetadataJson": "Eliminar todos los archivos metadata.json",
+ "LabelRemoveAudibleBranding": "Quitar introducción y cierre de Audible de los capítulos",
"LabelRemoveCover": "Quitar cubierta",
"LabelRemoveMetadataFile": "Eliminar archivos de metadatos en carpetas de elementos de biblioteca",
"LabelRemoveMetadataFileHelp": "Elimine todos los archivos metadata.json y metadata.abs de sus carpetas {0}.",
@@ -539,7 +540,7 @@
"LabelSearchTitle": "Buscar título",
"LabelSearchTitleOrASIN": "Buscar título o ASIN",
"LabelSeason": "Temporada",
- "LabelSeasonNumber": "Sesión #{0}",
+ "LabelSeasonNumber": "{0}.ª temporada",
"LabelSelectAll": "Seleccionar todo",
"LabelSelectAllEpisodes": "Seleccionar todos los episodios",
"LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
@@ -749,7 +750,7 @@
"MessageConfirmNotificationTestTrigger": "¿Activar esta notificación con datos de prueba?",
"MessageConfirmPurgeCache": "Purgar la antememoria eliminará el directorio completo ubicado en /metadata/cache.
¿Confirma que quiere eliminar el directorio de antememoria?",
"MessageConfirmPurgeItemsCache": "Purgar la antememoria de elementos eliminará el directorio completo ubicado en /metadata/cache/items. ¿Lo confirma?",
- "MessageConfirmQuickEmbed": "¡Advertencia! La integración rápida no realiza copias de seguridad a ninguno de tus archivos de audio. Asegúrate de haber realizado una copia de los mismos previamente.
¿Deseas continuar?",
+ "MessageConfirmQuickEmbed": "Atención: la incrustación rápida no realiza copias de respaldo a ninguno de sus archivos de audio. Cerciórese de haber realizado una copia de los mismos previamente.
¿Quiere continuar?",
"MessageConfirmQuickMatchEpisodes": "El reconocimiento rápido de extensiones sobrescribirá los detalles si se encuentra una coincidencia. Se actualizarán las extensiones no reconocidas. ¿Quiere continuar?",
"MessageConfirmReScanLibraryItems": "¿Confirma que quiere volver a analizar {0} elementos?",
"MessageConfirmRemoveAllChapters": "¿Confirma que quiere quitar todos los capítulos?",
@@ -842,7 +843,7 @@
"MessageQuickEmbedInProgress": "Integración rápida en proceso",
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
"MessageQuickMatchAllEpisodes": "Combina rápidamente todos los episodios",
- "MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.",
+ "MessageQuickMatchDescription": "Rellena los detalles y la cubierta de los elementos vacíos con el primer resultado coincidente de «{0}». No sobrescribe los detalles a menos que se active la opción del servidor «Preferir metadatos coincidentes».",
"MessageRemoveChapter": "Quitar capítulo",
"MessageRemoveEpisodes": "Quitar {0} episodio(s)",
"MessageRemoveFromPlayerQueue": "Quitar de la cola de reproducción",
@@ -956,8 +957,8 @@
"ToastBackupCreateSuccess": "Respaldo creado",
"ToastBackupDeleteFailed": "Error al eliminar respaldo",
"ToastBackupDeleteSuccess": "Respaldo eliminado",
- "ToastBackupInvalidMaxKeep": "Número no válido de copias de seguridad a conservar",
- "ToastBackupInvalidMaxSize": "Tamaño máximo de copia de seguridad no válido",
+ "ToastBackupInvalidMaxKeep": "Número no válido de copias de respaldo para conservar",
+ "ToastBackupInvalidMaxSize": "Tamaño máximo de copia de respaldo no válido",
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
"ToastBackupUploadFailed": "Error al cargar la copia de respaldo",
"ToastBackupUploadSuccess": "Respaldo cargado",
From 9eaa0c26cd4d439faa7c7f6d5d8c14fb2ffb3056 Mon Sep 17 00:00:00 2001
From: Grzegorz Orlowski
Date: Mon, 26 May 2025 21:59:55 +0200
Subject: [PATCH 030/121] Translated using Weblate (Polish)
Currently translated at 73.3% (810 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
---
client/strings/pl.json | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/client/strings/pl.json b/client/strings/pl.json
index 026d7c49..e1822755 100644
--- a/client/strings/pl.json
+++ b/client/strings/pl.json
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Playlista",
"HeaderPlaylistItems": "Pozycje listy odtwarzania",
"HeaderPodcastsToAdd": "Podcasty do dodania",
+ "HeaderPresets": "Ustawienia wstępne",
"HeaderPreviewCover": "Podgląd okładki",
"HeaderRSSFeedGeneral": "Szczegóły RSS",
"HeaderRSSFeedIsOpen": "Kanał RSS jest otwarty",
@@ -219,6 +220,7 @@
"LabelAccountTypeAdmin": "Administrator",
"LabelAccountTypeGuest": "Gość",
"LabelAccountTypeUser": "Użytkownik",
+ "LabelActivities": "Aktywności",
"LabelActivity": "Aktywność",
"LabelAddToCollection": "Dodaj do kolekcji",
"LabelAddToCollectionBatch": "Dodaj {0} książki do kolekcji",
@@ -228,6 +230,7 @@
"LabelAddedDate": "Dodano {0}",
"LabelAdminUsersOnly": "Tylko użytkownicy administracyjni",
"LabelAll": "Wszystkie",
+ "LabelAllEpisodesDownloaded": "Wszystkie odcinki pobrane",
"LabelAllUsers": "Wszyscy użytkownicy",
"LabelAllUsersExcludingGuests": "Wszyscy użytkownicy z wyłączeniem gości",
"LabelAllUsersIncludingGuests": "Wszyscy użytkownicy, łącznie z gośćmi",
@@ -245,6 +248,7 @@
"LabelAutoFetchMetadata": "Automatycznie pobierz metadane",
"LabelAutoFetchMetadataHelp": "Pobiera metadane dotyczące tytułu, autora i serii, aby usprawnić przesyłanie. Po przesłaniu może być konieczne dopasowanie dodatkowych metadanych.",
"LabelAutoLaunch": "Uruchom automatycznie",
+ "LabelAutoLaunchDescription": "Automatyczne przekierowanie do dostawcy uwierzytelniania podczas przechodzenia na stronę logowania (ręczna zamiana ścieżki /login?autoLaunch=0)",
"LabelAutoRegister": "Automatyczna rejestracja",
"LabelAutoRegisterDescription": "Automatycznie utwórz nowych użytkowników po zalogowaniu",
"LabelBackToUser": "Powrót",
@@ -282,6 +286,7 @@
"LabelContinueSeries": "Kontynuuj serię",
"LabelCover": "Okładka",
"LabelCoverImageURL": "URL okładki",
+ "LabelCoverProvider": "Dostawca okładki",
"LabelCreatedAt": "Utworzone",
"LabelCronExpression": "Wyrażenie CRON",
"LabelCurrent": "Aktualny",
From 949c8ce230ce3a81555d8ede0460e37a9441679d Mon Sep 17 00:00:00 2001
From: Adolfo Jayme Barrientos
Date: Mon, 26 May 2025 04:03:17 +0200
Subject: [PATCH 031/121] Translated using Weblate (Catalan)
Currently translated at 96.2% (1064 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
---
client/strings/ca.json | 65 ++++++++++++++++++++++--------------------
1 file changed, 34 insertions(+), 31 deletions(-)
diff --git a/client/strings/ca.json b/client/strings/ca.json
index ee104674..d882385d 100644
--- a/client/strings/ca.json
+++ b/client/strings/ca.json
@@ -440,7 +440,7 @@
"LabelMinute": "Minut",
"LabelMinutes": "Minuts",
"LabelMissing": "Absent",
- "LabelMissingEbook": "No té ebook",
+ "LabelMissingEbook": "No té llibre electrònic",
"LabelMissingSupplementaryEbook": "No té ebook complementari",
"LabelMobileRedirectURIs": "URI de redirecció mòbil permeses",
"LabelMobileRedirectURIsDescription": "Aquesta és una llista blanca d'URI de redirecció vàlides per a aplicacions mòbils. El predeterminat és audiobookshelf, que pots eliminar o complementar amb URI addicionals per a la integració d'aplicacions de tercers. Usant un asterisc ( *) com a única entrada que permet qualsevol URI.",
@@ -498,25 +498,25 @@
"LabelPodcastType": "Tipus de pòdcast",
"LabelPodcasts": "Pòdcasts",
"LabelPort": "Port",
- "LabelPrefixesToIgnore": "Prefixos per Ignorar (no distingeix entre majúscules i minúscules.)",
+ "LabelPrefixesToIgnore": "Prefixos a ignorar (no distingeix entre majúscules i minúscules)",
"LabelPreventIndexing": "Evita que el vostre canal l'indexin els directoris de pòdcasts de l'iTunes i Google",
- "LabelPrimaryEbook": "Ebook Principal",
+ "LabelPrimaryEbook": "Llibre electrònic principal",
"LabelProgress": "Progrés",
"LabelProvider": "Proveïdor",
"LabelProviderAuthorizationValue": "Valor de l'encapçalament d'autorització",
- "LabelPubDate": "Data de Publicació",
- "LabelPublishYear": "Any de Publicació",
+ "LabelPubDate": "Data de publicació",
+ "LabelPublishYear": "Any de publicació",
"LabelPublishedDate": "Publicat {0}",
- "LabelPublishedDecade": "Dècada de Publicació",
+ "LabelPublishedDecade": "Dècada de publicació",
"LabelPublishedDecades": "Dècades Publicades",
"LabelPublisher": "Editor",
"LabelPublishers": "Editors",
"LabelRSSFeedCustomOwnerEmail": "Correu Electrònic Personalitzat del Propietari",
"LabelRSSFeedCustomOwnerName": "Nom Personalitzat del Propietari",
"LabelRSSFeedOpen": "Font RSS Oberta",
- "LabelRSSFeedPreventIndexing": "Evitar l'indexació",
- "LabelRSSFeedSlug": "Font RSS Slug",
- "LabelRSSFeedURL": "URL de la Font RSS",
+ "LabelRSSFeedPreventIndexing": "Evita la indexació",
+ "LabelRSSFeedSlug": "URL semàntic del canal RSS",
+ "LabelRSSFeedURL": "URL del canal RSS",
"LabelRandomly": "A l'atzar",
"LabelReAddSeriesToContinueListening": "Reafegir la sèrie per continuar escoltant-la",
"LabelRead": "Llegit",
@@ -525,39 +525,40 @@
"LabelRecentSeries": "Sèries recents",
"LabelRecentlyAdded": "Addicions recents",
"LabelRecommended": "Recomanats",
- "LabelRedo": "Refer",
+ "LabelRedo": "Refés",
"LabelRegion": "Regió",
- "LabelReleaseDate": "Data d'Estrena",
- "LabelRemoveAllMetadataAbs": "Eliminar tots els fitxers metadata.abs",
- "LabelRemoveAllMetadataJson": "Eliminar tots els fitxers metadata.json",
- "LabelRemoveCover": "Eliminar Coberta",
+ "LabelReleaseDate": "Data d'estrena",
+ "LabelRemoveAllMetadataAbs": "Elimina tots els fitxers metadata.abs",
+ "LabelRemoveAllMetadataJson": "Elimina tots els fitxers metadata.json",
+ "LabelRemoveAudibleBranding": "Elimina la introducció i el tancament de l'Audible dels capítols",
+ "LabelRemoveCover": "Elimina la coberta",
"LabelRemoveMetadataFile": "Eliminar fitxers de metadades en carpetes d'elements de biblioteca",
"LabelRemoveMetadataFileHelp": "Elimina tots els fitxers metadata.json i metadata.abs de les vostres carpetes {0}.",
- "LabelRowsPerPage": "Files per Pàgina",
- "LabelSearchTerm": "Cercar Terme",
- "LabelSearchTitle": "Cercar Títol",
- "LabelSearchTitleOrASIN": "Cercar Títol o ASIN",
+ "LabelRowsPerPage": "Files per pàgina",
+ "LabelSearchTerm": "Cerca terme",
+ "LabelSearchTitle": "Cerca títol",
+ "LabelSearchTitleOrASIN": "Cerca títol o ASIN",
"LabelSeason": "Temporada",
- "LabelSeasonNumber": "Temporada #{0}",
- "LabelSelectAll": "Seleccionar tot",
- "LabelSelectAllEpisodes": "Seleccionar tots els episodis",
+ "LabelSeasonNumber": "{0}a temporada",
+ "LabelSelectAll": "Selecciona-ho tot",
+ "LabelSelectAllEpisodes": "Selecciona tots els episodis",
"LabelSelectEpisodesShowing": "Seleccionar els {0} episodis visibles",
"LabelSelectUsers": "Seleccionar usuaris",
"LabelSendEbookToDevice": "Enviar Ebook a...",
"LabelSequence": "Seqüència",
"LabelSerial": "En sèrie",
- "LabelSeries": "Sèries",
- "LabelSeriesName": "Nom de la Sèrie",
- "LabelSeriesProgress": "Progrés de la Sèrie",
+ "LabelSeries": "Sèrie",
+ "LabelSeriesName": "Nom de la sèrie",
+ "LabelSeriesProgress": "Progrés de la sèrie",
"LabelServerLogLevel": "Nivell de registre del servidor",
"LabelServerYearReview": "Resum de l'any del servidor ({0})",
"LabelSetEbookAsPrimary": "Establir com a principal",
"LabelSetEbookAsSupplementary": "Establir com a suplementari",
- "LabelSettingsAudiobooksOnly": "Només Audiollibres",
- "LabelSettingsAudiobooksOnlyHelp": "Activant aquesta opció s'ignoraran els fitxers d'ebook, excepte si estan dins d'una carpeta d'audiollibre, en aquest cas es marcaran com ebooks suplementaris",
+ "LabelSettingsAudiobooksOnly": "Només audiollibres",
+ "LabelSettingsAudiobooksOnlyHelp": "En activar aquesta opció s'ignoraran els fitxers de llibre electrònic, excepte si estan dins d'una carpeta d'audiollibre; en aquest cas es marcaran com a llibres suplementaris",
"LabelSettingsBookshelfViewHelp": "Disseny esqueomorf amb prestatgeries de fusta",
"LabelSettingsChromecastSupport": "Compatibilitat amb Chromecast",
- "LabelSettingsDateFormat": "Format de Data",
+ "LabelSettingsDateFormat": "Format de data",
"LabelSettingsEnableWatcherHelp": "Permet afegir/actualitzar elements automàticament quan es detectin canvis en els fitxers. *Requereix reiniciar el servidor",
"LabelSettingsEpubsAllowScriptedContent": "Permetre scripts en epubs",
"LabelSettingsEpubsAllowScriptedContentHelp": "Permetre que els fitxers epub executin scripts. Es recomana mantenir aquesta opció desactivada tret que confiïs en l'origen dels fitxers epub.",
@@ -577,6 +578,8 @@
"LabelSize": "Mida",
"LabelSleepTimer": "Temporitzador de repòs",
"LabelSlug": "Slug",
+ "LabelSortAscending": "Ascendent",
+ "LabelSortDescending": "Descendent",
"LabelStart": "Inicia",
"LabelStartTime": "Hora d'inici",
"LabelStarted": "Iniciat",
@@ -802,7 +805,7 @@
"MessageQuickEmbedInProgress": "Integració ràpida en procés",
"MessageQuickEmbedQueue": "En cua per a inserció ràpida ({0} en cua)",
"MessageQuickMatchAllEpisodes": "Combina ràpidament tots els episodis",
- "MessageQuickMatchDescription": "Omple detalls d'elements buits i portades amb els primers resultats de '{0}'. No sobreescriu els detalls tret que l'opció \"Preferir metadades trobades\" del servidor estigui habilitada.",
+ "MessageQuickMatchDescription": "Emplena els detalls i la coberta dels elements buits amb el resultat de la primera coincidència de «{0}». No sobreescriu els detalls tret que s'activi el paràmetre del servidor «Prefereix metadades coincidents».",
"MessageRemoveChapter": "Elimina el capítol",
"MessageRemoveEpisodes": "Elimina {0} episodi(s)",
"MessageRemoveFromPlayerQueue": "Elimina de la cua del reproductor",
@@ -820,7 +823,7 @@
"MessageShareExpirationWillBe": "La caducitat serà {0}",
"MessageShareExpiresIn": "Caduca en {0}",
"MessageShareURLWillBe": "La URL per compartir serà {0}",
- "MessageStartPlaybackAtTime": "Començar la reproducció per a \"{0}\" a {1}?",
+ "MessageStartPlaybackAtTime": "Voleu començar la reproducció per a «{0}» a {1}?",
"MessageTaskAudioFileNotWritable": "El fitxer d'àudio «{0}» no es pot escriure",
"MessageTaskCanceledByUser": "Tasca cancel·lada per l'usuari",
"MessageTaskDownloadingEpisodeDescription": "S'està baixant l'episodi «{0}»",
@@ -991,7 +994,7 @@
"ToastNewUserCreatedFailed": "No s'ha pogut crear el compte: «{0}»",
"ToastNewUserCreatedSuccess": "Nou compte creat",
"ToastNewUserLibraryError": "S'ha de seleccionar almenys una biblioteca",
- "ToastNewUserPasswordError": "Necessites una contrasenya, només el root pot estar sense contrasenya",
+ "ToastNewUserPasswordError": "Cal una contrasenya; només l'usuari primari pot estar sense contrasenya",
"ToastNewUserTagError": "S'ha de seleccionar almenys una etiqueta",
"ToastNewUserUsernameError": "Introduïu un nom d'usuari",
"ToastNoNewEpisodesFound": "No s'han trobat nous episodis",
@@ -1034,7 +1037,7 @@
"ToastScanFailed": "No s'ha pogut escanejar l'element de la biblioteca",
"ToastSelectAtLeastOneUser": "Selecciona almenys un usuari",
"ToastSendEbookToDeviceFailed": "Error en enviar l'ebook al dispositiu",
- "ToastSendEbookToDeviceSuccess": "Ebook enviat al dispositiu \"{0}\"",
+ "ToastSendEbookToDeviceSuccess": "El llibre electrònic s'ha enviat al dispositiu «{0}»",
"ToastSeriesSubmitFailedSameName": "No és possible afegir dues sèries amb el mateix nom",
"ToastSeriesUpdateFailed": "Error en actualitzar la sèrie",
"ToastSeriesUpdateSuccess": "Sèrie actualitzada",
From abaa7b5ad0debb238b1afba3f7a8c8c37b76c82f Mon Sep 17 00:00:00 2001
From: advplyr
Date: Wed, 28 May 2025 17:09:39 -0500
Subject: [PATCH 032/121] Add arabic language option
---
client/plugins/i18n.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/plugins/i18n.js b/client/plugins/i18n.js
index 1769e6eb..86109175 100644
--- a/client/plugins/i18n.js
+++ b/client/plugins/i18n.js
@@ -5,6 +5,7 @@ import { supplant } from './utils'
const defaultCode = 'en-us'
const languageCodeMap = {
+ ar: { label: 'عربي', dateFnsLocale: 'ar' },
bg: { label: 'Български', dateFnsLocale: 'bg' },
bn: { label: 'বাংলা', dateFnsLocale: 'bn' },
ca: { label: 'Català', dateFnsLocale: 'ca' },
From 0c8e334b1a77c13bc97ea9e9775f0fd132cce212 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Thu, 29 May 2025 17:27:29 -0500
Subject: [PATCH 033/121] Update rich text editor to prevent pasting in images
from the browser
---
client/components/ui/VueTrix.vue | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/client/components/ui/VueTrix.vue b/client/components/ui/VueTrix.vue
index 6a24f1d3..2687d934 100644
--- a/client/components/ui/VueTrix.vue
+++ b/client/components/ui/VueTrix.vue
@@ -31,7 +31,7 @@
-
+
@@ -316,6 +316,12 @@ export default {
if (this.$refs.trix && this.$refs.trix.blur) {
this.$refs.trix.blur()
}
+ },
+ handleAttachmentAdd(event) {
+ // Prevent pasting in images from the browser
+ if (!event.attachment.file) {
+ event.attachment.remove()
+ }
}
},
mounted() {
From 4968864498fd525e90cb9ccf714a6fe975a0cab4 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 30 May 2025 17:33:15 -0500
Subject: [PATCH 034/121] Fix safari specific issue with line clamp on
description #4348
---
client/pages/item/_id/index.vue | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue
index 38152cb1..6042f92a 100644
--- a/client/pages/item/_id/index.vue
+++ b/client/pages/item/_id/index.vue
@@ -819,6 +819,17 @@ export default {
-webkit-line-clamp: 4;
max-height: calc(6 * 1lh);
}
+
+/* Safari-specific fix for the description clamping */
+@supports (-webkit-touch-callout: none) {
+ #item-description {
+ position: relative;
+ display: block;
+ overflow: hidden;
+ max-height: calc(6 * 1lh);
+ }
+}
+
#item-description.show-full {
-webkit-line-clamp: unset;
max-height: 999rem;
From 9052ceedd3659aca1e6b227e62fe0687c1d94336 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 31 May 2025 17:01:58 -0500
Subject: [PATCH 035/121] Sanitize media item & episode description on update
---
server/controllers/PodcastController.js | 10 ++++++++++
server/models/Book.js | 11 ++++++++++-
server/models/Podcast.js | 10 ++++++++++
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js
index 6395e05b..1ebe1d11 100644
--- a/server/controllers/PodcastController.js
+++ b/server/controllers/PodcastController.js
@@ -9,6 +9,7 @@ const fs = require('../libs/fsExtra')
const { getPodcastFeed, findMatchingEpisodes } = require('../utils/podcastUtils')
const { getFileTimestampsWithIno, filePathToPOSIX } = require('../utils/fileUtils')
const { validateUrl } = require('../utils/index')
+const htmlSanitizer = require('../utils/htmlSanitizer')
const Scanner = require('../scanner/Scanner')
const CoverManager = require('../managers/CoverManager')
@@ -404,6 +405,15 @@ class PodcastController {
const supportedStringKeys = ['title', 'subtitle', 'description', 'pubDate', 'episode', 'season', 'episodeType']
for (const key in req.body) {
if (supportedStringKeys.includes(key) && typeof req.body[key] === 'string') {
+ // Sanitize description HTML
+ if (key === 'description' && req.body[key]) {
+ const sanitizedDescription = htmlSanitizer.sanitize(req.body[key])
+ if (sanitizedDescription !== req.body[key]) {
+ Logger.debug(`[PodcastController] Sanitized description from "${req.body[key]}" to "${sanitizedDescription}"`)
+ req.body[key] = sanitizedDescription
+ }
+ }
+
updatePayload[key] = req.body[key]
} else if (key === 'chapters' && Array.isArray(req.body[key]) && req.body[key].every((ch) => typeof ch === 'object' && ch.title && ch.start)) {
updatePayload[key] = req.body[key]
diff --git a/server/models/Book.js b/server/models/Book.js
index 0dd0b785..96371f3a 100644
--- a/server/models/Book.js
+++ b/server/models/Book.js
@@ -377,8 +377,17 @@ class Book extends Model {
if (typeof payload.metadata[key] == 'number') {
payload.metadata[key] = String(payload.metadata[key])
}
-
+
if ((typeof payload.metadata[key] === 'string' || payload.metadata[key] === null) && this[key] !== payload.metadata[key]) {
+ // Sanitize description HTML
+ if (key === 'description' && payload.metadata[key]) {
+ const sanitizedDescription = htmlSanitizer.sanitize(payload.metadata[key])
+ if (sanitizedDescription !== payload.metadata[key]) {
+ Logger.debug(`[Book] "${this.title}" Sanitized description from "${payload.metadata[key]}" to "${sanitizedDescription}"`)
+ payload.metadata[key] = sanitizedDescription
+ }
+ }
+
this[key] = payload.metadata[key] || null
if (key === 'title') {
diff --git a/server/models/Podcast.js b/server/models/Podcast.js
index fa27821d..d99a66df 100644
--- a/server/models/Podcast.js
+++ b/server/models/Podcast.js
@@ -2,6 +2,7 @@ const { DataTypes, Model } = require('sequelize')
const { getTitlePrefixAtEnd, getTitleIgnorePrefix } = require('../utils')
const Logger = require('../Logger')
const libraryItemsPodcastFilters = require('../utils/queries/libraryItemsPodcastFilters')
+const htmlSanitizer = require('../utils/htmlSanitizer')
/**
* @typedef PodcastExpandedProperties
@@ -215,6 +216,15 @@ class Podcast extends Model {
newKey = 'itunesPageURL'
}
if ((typeof payload.metadata[key] === 'string' || payload.metadata[key] === null) && payload.metadata[key] !== this[newKey]) {
+ // Sanitize description HTML
+ if (key === 'description' && payload.metadata[key]) {
+ const sanitizedDescription = htmlSanitizer.sanitize(payload.metadata[key])
+ if (sanitizedDescription !== payload.metadata[key]) {
+ Logger.debug(`[Podcast] "${this.title}" Sanitized description from "${payload.metadata[key]}" to "${sanitizedDescription}"`)
+ payload.metadata[key] = sanitizedDescription
+ }
+ }
+
this[newKey] = payload.metadata[key] || null
if (key === 'title') {
From 96401c377c5a3a0db0eef2b40a2d6b801944f137 Mon Sep 17 00:00:00 2001
From: Tommaso Bellandi
Date: Thu, 29 May 2025 20:36:39 +0200
Subject: [PATCH 036/121] Translated using Weblate (Italian)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
---
client/strings/it.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/it.json b/client/strings/it.json
index ae2facd1..41c06175 100644
--- a/client/strings/it.json
+++ b/client/strings/it.json
@@ -708,6 +708,7 @@
"MessageAddToPlayerQueue": "Aggiungi alla coda di riproduzione",
"MessageAppriseDescription": "Per utilizzare questa funzione è necessario disporre di un'istanza di Apprise API in esecuzione o un'API che gestirà quelle stesse richieste. L'API Url dovrebbe essere il percorso URL completo per inviare la notifica, ad esempio se la tua istanza API è servita cosi .http://192.168.1.1:8337 Allora dovrai mettere http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "Assicurati di utilizzare l'ASIN della regione Audible corretta, non di Amazon.",
+ "MessageAuthenticationOIDCChangesRestart": "Riavvia il tuo server dopo aver salvato per applicare le modifiche OIDC.",
"MessageBackupsDescription": "I backup includono utenti, progressi degli utenti, dettagli sugli elementi della libreria, impostazioni del server e immagini archiviate in /metadata/items & /metadata/authors. I backup non includono i file archiviati nelle cartelle della libreria.",
"MessageBackupsLocationEditNote": "Nota: l'aggiornamento della posizione di backup non sposterà o modificherà i backup esistenti",
"MessageBackupsLocationNoEditNote": "Nota: la posizione del backup viene impostata tramite una variabile di ambiente e non può essere modificata qui.",
@@ -855,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Esegui ogni {0} alle {1}",
"MessageSearchResultsFor": "cerca risultati per",
"MessageSelected": "{0} selezionati",
+ "MessageSeriesSequenceCannotContainSpaces": "La sequenza della serie non può contenere spazi",
"MessageServerCouldNotBeReached": "Impossibile raggiungere il server",
"MessageSetChaptersFromTracksDescription": "Impostare i capitoli utilizzando ciascun file audio come capitolo e il titolo del capitolo come nome del file audio",
"MessageShareExpirationWillBe": "Scadrà tra {0}",
From 28c2e62e61d50ea1586f9a780b1ef662599662a8 Mon Sep 17 00:00:00 2001
From: Simple16
Date: Thu, 29 May 2025 18:52:44 +0200
Subject: [PATCH 037/121] Translated using Weblate (Russian)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
---
client/strings/ru.json | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/client/strings/ru.json b/client/strings/ru.json
index 1af0c642..07a786a1 100644
--- a/client/strings/ru.json
+++ b/client/strings/ru.json
@@ -212,9 +212,9 @@
"HeaderUsers": "Пользователи",
"HeaderYearReview": "Итоги {0} года",
"HeaderYourStats": "Ваша статистика",
- "LabelAbridged": "Сокращенное издание",
+ "LabelAbridged": "Сокращенная форма",
"LabelAbridgedChecked": "Сокращено (отмечено)",
- "LabelAbridgedUnchecked": "Без сокращений (не отмечено)",
+ "LabelAbridgedUnchecked": "Полное издание (не отмечено)",
"LabelAccessibleBy": "Доступ",
"LabelAccountType": "Тип учетной записи",
"LabelAccountTypeAdmin": "Администратор",
@@ -346,8 +346,8 @@
"LabelExample": "Пример",
"LabelExpandSeries": "Развернуть серию",
"LabelExpandSubSeries": "Развернуть подсерию",
- "LabelExplicit": "Явный",
- "LabelExplicitChecked": "Явный (отмечено)",
+ "LabelExplicit": "18+",
+ "LabelExplicitChecked": "18+ (отмечено)",
"LabelExplicitUnchecked": "Не явно (не отмечено)",
"LabelExportOPML": "Экспорт OPML",
"LabelFeedURL": "URL канала",
@@ -856,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Запуск каждые {0} по {1}",
"MessageSearchResultsFor": "Результаты поиска для",
"MessageSelected": "{0} выбрано",
+ "MessageSeriesSequenceCannotContainSpaces": "Последовательность серии должна быть без пропусков",
"MessageServerCouldNotBeReached": "Не удалось связаться с сервером",
"MessageSetChaptersFromTracksDescription": "Установка глав с использованием каждого аудиофайла в качестве главы и заголовка главы в качестве имени аудиофайла",
"MessageShareExpirationWillBe": "Срок действия истекает {0}",
From db66b9eaebadfd3ae36c1d72518a6cedb69c6e2a Mon Sep 17 00:00:00 2001
From: FiendFEARing
Date: Thu, 29 May 2025 05:21:43 +0200
Subject: [PATCH 038/121] Translated using Weblate (Chinese (Simplified Han
script))
Currently translated at 100.0% (1105 of 1105 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 8715ac6e..0f9f4a99 100644
--- a/client/strings/zh-cn.json
+++ b/client/strings/zh-cn.json
@@ -21,7 +21,7 @@
"ButtonChooseFiles": "选择文件",
"ButtonClearFilter": "清除过滤器",
"ButtonCloseFeed": "关闭源",
- "ButtonCloseSession": "关闭开放会话",
+ "ButtonCloseSession": "关闭活动会话",
"ButtonCollections": "收藏",
"ButtonConfigureScanner": "配置扫描",
"ButtonCreate": "创建",
From 1ad2e71fd5fd85dd56dbb77dd8d797d397b5200a Mon Sep 17 00:00:00 2001
From: kuci-JK
Date: Sun, 1 Jun 2025 00:01:56 +0200
Subject: [PATCH 039/121] Translated using Weblate (Czech)
Currently translated at 98.9% (1093 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
---
client/strings/cs.json | 3 +++
1 file changed, 3 insertions(+)
diff --git a/client/strings/cs.json b/client/strings/cs.json
index 5d44e98a..b17075b0 100644
--- a/client/strings/cs.json
+++ b/client/strings/cs.json
@@ -705,6 +705,7 @@
"LabelYourProgress": "Váš pokrok",
"MessageAddToPlayerQueue": "Přidat do fronty přehrávače",
"MessageAppriseDescription": "Abyste mohli používat tuto funkci, musíte mít spuštěnou instanci Apprise API nebo API, které bude zpracovávat stejné požadavky. Adresa URL API Apprise by měla být úplná URL cesta pro odeslání oznámení, např. pokud je vaše instance API obsluhována na adrese http://192.168.1.1:8337 pak byste měli zadat http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "Ujistěte se, že používáte ASIN ze správného regionu Audible a ne z Amazonu.",
"MessageBackupsDescription": "Zálohy zahrnují uživatele, průběh uživatele, podrobnosti o položkách knihovny, nastavení serveru a obrázky uložené v /metadata/items a /metadata/authors. Zálohy ne zahrnují všechny soubory uložené ve složkách knihovny.",
"MessageBackupsLocationEditNote": "Poznámka: Změna umístění záloh nepřesune ani nezmění existující zálohy",
"MessageBackupsLocationNoEditNote": "Poznámka: Umístění záloh je nastavené z proměnných prostředí a nelze zde změnit.",
@@ -723,6 +724,7 @@
"MessageChapterErrorStartGteDuration": "Neplatný čas začátku, musí být kratší než doba trvání audioknihy",
"MessageChapterErrorStartLtPrev": "Neplatný čas začátku, musí být větší nebo roven času začátku předchozí kapitoly",
"MessageChapterStartIsAfter": "Začátek kapitoly přesahuje konec audioknihy",
+ "MessageChaptersNotFound": "Kapitoly nenalezeny",
"MessageCheckingCron": "Kontrola cronu...",
"MessageConfirmCloseFeed": "Opravdu chcete zavřít tento kanál?",
"MessageConfirmDeleteBackup": "Opravdu chcete smazat zálohu pro {0}?",
@@ -779,6 +781,7 @@
"MessageForceReScanDescription": "znovu prohledá všechny soubory jako při novém skenování. ID3 tagy zvukových souborů OPF soubory a textové soubory budou skenovány jako nové.",
"MessageImportantNotice": "Důležité upozornění!",
"MessageInsertChapterBelow": "Vložit kapitolu níže",
+ "MessageInvalidAsin": "Neplatný ASIN",
"MessageItemsSelected": "{0} vybraných položek",
"MessageItemsUpdated": "{0} položky byly aktualizovány",
"MessageJoinUsOn": "Přidejte se k nám",
From 81572adab6b81b5564e0d39fa4286e568adf7b1d Mon Sep 17 00:00:00 2001
From: DR
Date: Sat, 31 May 2025 16:26:04 +0200
Subject: [PATCH 040/121] Translated using Weblate (Hebrew)
Currently translated at 76.4% (845 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
---
client/strings/he.json | 166 +++++++++++++++++++++++++++++------------
1 file changed, 118 insertions(+), 48 deletions(-)
diff --git a/client/strings/he.json b/client/strings/he.json
index 19da58e7..8b5bcdd0 100644
--- a/client/strings/he.json
+++ b/client/strings/he.json
@@ -10,6 +10,8 @@
"ButtonApplyChapters": "החל פרקים",
"ButtonAuthors": "סופרים",
"ButtonBack": "חזור",
+ "ButtonBatchEditPopulateFromExisting": "מלא משדות קיימים",
+ "ButtonBatchEditPopulateMapDetails": "מלא פרטי מפה",
"ButtonBrowseForFolder": "עיין בתיקייה",
"ButtonCancel": "ביטול",
"ButtonCancelEncode": "בטל קידוד",
@@ -29,7 +31,9 @@
"ButtonEdit": "ערוך",
"ButtonEditChapters": "ערוך פרקים",
"ButtonEditPodcast": "ערוך פודקאסט",
- "ButtonEnable": "הפעל",
+ "ButtonEnable": "אפשר",
+ "ButtonFireAndFail": "שלח בכישלון",
+ "ButtonFireOnTest": "שלח באירוע בדיקה",
"ButtonForceReScan": "סרוק מחדש בכוח",
"ButtonFullPath": "נתיב מלא",
"ButtonHide": "הסתר",
@@ -37,7 +41,7 @@
"ButtonIssues": "תקלות",
"ButtonJumpBackward": "דלג אחורה",
"ButtonJumpForward": "דלג קדימה",
- "ButtonLatest": "חדש ביותר",
+ "ButtonLatest": "אחרון",
"ButtonLibrary": "ספרייה",
"ButtonLogout": "התנתק",
"ButtonLookup": "חפש",
@@ -70,7 +74,7 @@
"ButtonReScan": "סרוק מחדש",
"ButtonRead": "קרא",
"ButtonReadLess": "קרא פחות",
- "ButtonReadMore": "קרא יותר",
+ "ButtonReadMore": "קרא עוד",
"ButtonRefresh": "רענן",
"ButtonRemove": "הסר",
"ButtonRemoveAll": "הסר הכל",
@@ -86,7 +90,9 @@
"ButtonSaveTracklist": "שמור רשימת רצועות",
"ButtonScan": "סרוק",
"ButtonScanLibrary": "סרוק ספרייה",
- "ButtonSearch": "חפש",
+ "ButtonScrollLeft": "גלול שמאלה",
+ "ButtonScrollRight": "גלול ימינה",
+ "ButtonSearch": "חיפוש",
"ButtonSelectFolderPath": "בחר נתיב לתיקייה",
"ButtonSeries": "סדרה",
"ButtonSetChaptersFromTracks": "קבע פרקים לפי הרצועות",
@@ -96,7 +102,7 @@
"ButtonStartM4BEncode": "התחל קידוד M4B",
"ButtonStartMetadataEmbed": "התחל הטמעת מטא-נתונים",
"ButtonStats": "סטטיסטיקות",
- "ButtonSubmit": "שלח",
+ "ButtonSubmit": "שליחה",
"ButtonTest": "בדיקה",
"ButtonUnlinkOpenId": "נתק OpenID",
"ButtonUpload": "העלה",
@@ -122,26 +128,26 @@
"HeaderChapters": "פרקים",
"HeaderChooseAFolder": "בחר תיקייה",
"HeaderCollection": "אוסף",
- "HeaderCollectionItems": "פריטי אוסף",
+ "HeaderCollectionItems": "פרטי אוסף",
"HeaderCover": "כריכה",
"HeaderCurrentDownloads": "הורדות נוכחיות",
"HeaderCustomMessageOnLogin": "הודעה מותאמת אישית בהתחברות",
"HeaderCustomMetadataProviders": "ספקי מטא-נתונים מותאמים אישית",
"HeaderDetails": "פרטים",
"HeaderDownloadQueue": "תור הורדה",
- "HeaderEbookFiles": "קבצי ספר אלקטרוני",
+ "HeaderEbookFiles": "קבצי Ebook",
"HeaderEmail": "אימייל",
"HeaderEmailSettings": "הגדרות אימייל",
"HeaderEpisodes": "פרקים",
"HeaderEreaderDevices": "התקני קריאה דיגיטליים",
- "HeaderEreaderSettings": "הגדרות התקני קריאה דיגיטליים",
+ "HeaderEreaderSettings": "הגדרות קורא אלקטרוני",
"HeaderFiles": "קבצים",
"HeaderFindChapters": "מצא פרקים",
"HeaderIgnoredFiles": "קבצים שנתעלמו",
"HeaderItemFiles": "קבצי פריט",
"HeaderItemMetadataUtils": "כלי מטא-נתונים",
"HeaderLastListeningSession": "הפעלת האזנה אחרונה",
- "HeaderLatestEpisodes": "הפרקים העדכניים ביותר",
+ "HeaderLatestEpisodes": "פרקים אחרונים",
"HeaderLibraries": "ספריות",
"HeaderLibraryFiles": "קבצי ספרייה",
"HeaderLibraryStats": "סטטיסטיקות ספרייה",
@@ -171,8 +177,9 @@
"HeaderPlaylist": "רשימת השמעה",
"HeaderPlaylistItems": "פריטי רשימת השמעה",
"HeaderPodcastsToAdd": "פודקאסטים להוספה",
+ "HeaderPresets": "קביעות מוגדרות מראש",
"HeaderPreviewCover": "תצוגה מקדימה של כריכה",
- "HeaderRSSFeedGeneral": "פרטי ערוץ RSS",
+ "HeaderRSSFeedGeneral": "פרטי RSS",
"HeaderRSSFeedIsOpen": "ערוץ RSS פתוח",
"HeaderRSSFeeds": "ערוצי RSS",
"HeaderRemoveEpisode": "הסר פרק",
@@ -188,14 +195,15 @@
"HeaderSettingsExperimental": "תכונות ניסיוניות",
"HeaderSettingsGeneral": "כללי",
"HeaderSettingsScanner": "סורק",
+ "HeaderSettingsWebClient": "מערך",
"HeaderSleepTimer": "טיימר שינה",
"HeaderStatsLargestItems": "הפריטים הגדולים ביותר",
"HeaderStatsLongestItems": "הפריטים הארוכים ביותר (בשעות)",
- "HeaderStatsMinutesListeningChart": "דקות האזנה (בימים האחרונים)",
- "HeaderStatsRecentSessions": "הפעלות אחרונות",
+ "HeaderStatsMinutesListeningChart": "דקות האזנה (7 ימים אחרונים)",
+ "HeaderStatsRecentSessions": "האזנות אחרונות",
"HeaderStatsTop10Authors": "10 היוצרים המובילים",
"HeaderStatsTop5Genres": "הז'אנרים המובילים 5",
- "HeaderTableOfContents": "תוכן העניינים",
+ "HeaderTableOfContents": "תוכן עניינים",
"HeaderTools": "כלים",
"HeaderUpdateAccount": "עדכן חשבון",
"HeaderUpdateAuthor": "עדכן יוצר",
@@ -212,15 +220,17 @@
"LabelAccountTypeAdmin": "מנהל",
"LabelAccountTypeGuest": "אורח",
"LabelAccountTypeUser": "משתמש",
+ "LabelActivities": "פעילויות",
"LabelActivity": "פעילות",
"LabelAddToCollection": "הוסף לאוסף",
"LabelAddToCollectionBatch": "הוסף {0} ספרים לאוסף",
"LabelAddToPlaylist": "הוסף לרשימת השמעה",
"LabelAddToPlaylistBatch": "הוסף {0} פריטים לרשימת השמעה",
- "LabelAddedAt": "נוסף בתאריך",
+ "LabelAddedAt": "נוסף ב-",
"LabelAddedDate": "נוסף ב-{0}",
"LabelAdminUsersOnly": "רק מנהלים",
"LabelAll": "הכל",
+ "LabelAllEpisodesDownloaded": "כל הפרקים הורדו",
"LabelAllUsers": "כל המשתמשים",
"LabelAllUsersExcludingGuests": "כל המשתמשים, ללא אורחים",
"LabelAllUsersIncludingGuests": "כל המשתמשים כולל אורחים",
@@ -230,10 +240,10 @@
"LabelAudioBitrate": "קצב סיביות (לדוגמא 128k)",
"LabelAudioChannels": "ערוצי קול (1 או 2)",
"LabelAudioCodec": "קידוד קול",
- "LabelAuthor": "יוצר",
- "LabelAuthorFirstLast": "יוצר (שם פרטי שם משפחה)",
- "LabelAuthorLastFirst": "יוצר (שם משפחה, שם פרטי)",
- "LabelAuthors": "יוצרים",
+ "LabelAuthor": "סופר",
+ "LabelAuthorFirstLast": "סופר (שם, משפחה)",
+ "LabelAuthorLastFirst": "סופר (משפחה, שם)",
+ "LabelAuthors": "סופרים",
"LabelAutoDownloadEpisodes": "הורד פרקים באופן אוטומטי",
"LabelAutoFetchMetadata": "חפש והורד מטא-נתונים באופן אוטומטי",
"LabelAutoFetchMetadataHelp": "מחפש ומוריד מטא-נתונים לשדות כותרת, יוצר וסדרה כדי לשפר את תהליך ההעלאה. ייתכן שיהיה צורך להתאים מטא-נתונים נוסף לאחר ההעלאה.",
@@ -242,36 +252,48 @@
"LabelAutoRegister": "הרשמה אוטומטית",
"LabelAutoRegisterDescription": "יצירת משתמשים חדשים אוטומטית לאחר התחברות",
"LabelBackToUser": "חזרה למשתמש",
+ "LabelBackupAudioFiles": "גיבוי קבצי שמע",
"LabelBackupLocation": "מיקום גיבוי",
- "LabelBackupsEnableAutomaticBackups": "הפעל גיבויים אוטומטיים",
+ "LabelBackupsEnableAutomaticBackups": "גיבויים אוטומטיים",
"LabelBackupsEnableAutomaticBackupsHelp": "גיבויים שמורים ב /metadata/backups",
- "LabelBackupsMaxBackupSize": "גודל הגיבוי המרבי (בג'יגה-בייט)",
+ "LabelBackupsMaxBackupSize": "גודל הגיבוי המרבי (בג'יגה-בייט) (0 - ללא הגבלה)",
"LabelBackupsMaxBackupSizeHelp": "כהגנה על עצמך מפני תצורה שגויה, הגיבויים ייכשלו אם הם יעברו את הגודל שהוגדר.",
"LabelBackupsNumberToKeep": "מספר הגיבויים לשמירה",
"LabelBackupsNumberToKeepHelp": "רק גיבוי אחד יוסר בכל פעם, לכן אם יש לך כבר יותר מגיבוי אחד יש להסיר אותם באופן ידני.",
"LabelBitrate": "קצב סיביות",
+ "LabelBonus": "בונוס",
"LabelBooks": "ספרים",
"LabelButtonText": "טקסט לחצן",
+ "LabelByAuthor": "על ידי {0}",
"LabelChangePassword": "שינוי סיסמה",
"LabelChannels": "ערוצים",
+ "LabelChapterCount": "{0} פרקים",
"LabelChapterTitle": "כותרת הפרק",
"LabelChapters": "פרקים",
"LabelChaptersFound": "פרקים שנמצאו",
"LabelClickForMoreInfo": "לחץ למידע נוסף",
+ "LabelClickToUseCurrentValue": "לחץ לשימוש בערך הנוכחי",
"LabelClosePlayer": "סגור נגן",
- "LabelCollapseSeries": "צמצום סדרה",
+ "LabelCodec": "Coded",
+ "LabelCollapseSeries": "הסתר סדרה",
+ "LabelCollapseSubSeries": "הסתר תת סדרה",
"LabelCollection": "אוסף",
"LabelCollections": "אוספים",
- "LabelComplete": "מלא",
+ "LabelComplete": "הושלם",
"LabelConfirmPassword": "אישור סיסמה",
"LabelContinueListening": "המשך האזנה",
"LabelContinueReading": "המשך קריאה",
"LabelContinueSeries": "המשך סדרה",
"LabelCover": "כריכה",
"LabelCoverImageURL": "כתובת התמונה ברשת",
+ "LabelCoverProvider": "ספק כריכה",
"LabelCreatedAt": "נוצר בתאריך",
+ "LabelCronExpression": "ביטוי cron",
"LabelCurrent": "נוכחי",
"LabelCurrently": "כעת:",
+ "LabelCustomCronExpression": "ביטוי cron מותאם אישית:",
+ "LabelDatetime": "Datetime",
+ "LabelDays": "ימים",
"LabelDeleteFromFileSystemCheckbox": "מחיקה מהמערכת הקבצים (הסר סימון למחיקה רק ממסד הנתונים)",
"LabelDescription": "תיאור",
"LabelDeselectAll": "הסר בחירת כל הפריטים",
@@ -282,51 +304,83 @@
"LabelDiscFromFilename": "דיסק משם הקובץ",
"LabelDiscFromMetadata": "דיסק מהמטא-נתונים",
"LabelDiscover": "גלה",
- "LabelDownload": "הורד",
+ "LabelDownload": "הורדה",
"LabelDownloadNEpisodes": "הורד {0} פרקים",
+ "LabelDownloadable": "ניתן להורדה",
"LabelDuration": "משך",
+ "LabelDurationComparisonExactMatch": "(התאמה מדוייקת)",
+ "LabelDurationComparisonLonger": "({0} ארוך יותר)",
+ "LabelDurationComparisonShorter": "({0} קצר יותר)",
"LabelDurationFound": "משך נמצא:",
"LabelEbook": "ספר אלקטרוני",
"LabelEbooks": "ספרים אלקטרוניים",
"LabelEdit": "עריכה",
"LabelEmail": "דואר אלקטרוני",
"LabelEmailSettingsFromAddress": "מאת",
+ "LabelEmailSettingsRejectUnauthorized": "דחה תעודות לא מאושרות",
+ "LabelEmailSettingsRejectUnauthorizedHelp": "השבתת אימות תעודת SSL עלולה לחשוף את החיבור שלך לסיכוני אבטחה, כגון התקפות \"אדם באמצע\". השבת אפשרות זו רק אם אתה מבין את ההשלכות ובוטח בשרת הדואר שאליו אתה מתחבר.",
"LabelEmailSettingsSecure": "מאובטח",
"LabelEmailSettingsSecureHelp": "אם מופעל, החיבור ישתמש ב-TLS בעת ההתחברות לשרת. אם לא, אז TLS יהיה בשימוש אם השרת תומך בהרחבת STARTTLS. ברוב המקרים מומלץ להפעיל את הגדרה זו אם אתה מתחבר לפורט 465. לפורט 587 או 25, השאר כבוי. (from nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "כתובת לבדיקה",
"LabelEmbeddedCover": "כריכה מוטמעת",
- "LabelEnable": "הפעל",
- "LabelEnd": "סיום",
+ "LabelEnable": "אפשר",
+ "LabelEncodingBackupLocation": "גיבוי של קבצי אודיו מקוריים יישמר ב:",
+ "LabelEncodingChaptersNotEmbedded": "פרקים אינם מוטבעים בספרי אודיו מרובי רצועות.",
+ "LabelEncodingClearItemCache": "הקפד לנקות מטמון פריטים מעת לעת.",
+ "LabelEncodingFinishedM4B": "קובץ M4B סופי יישמר בתיקייה ה-audiobook ב:",
+ "LabelEncodingInfoEmbedded": "מטה דאטה יוטמע ברצועות השמע בתוך תיקיית ה-audiobook.",
+ "LabelEncodingStartedNavigation": "לאחר שהמשימה תתחיל אפשר לנווט לדף אחר.",
+ "LabelEncodingTimeWarning": "קידוד יכול להימשך עד 30 דקות.",
+ "LabelEncodingWarningAdvancedSettings": "אזהרה: אל תעדכן את ההגדרות האלה אלא אם כן אתה מכיר את אפשרויות קידוד ffmpeg.",
+ "LabelEncodingWatcherDisabled": "אם ה-watcher כבוי, יש לסרוק את הספר מחדש לאחר מכן.",
+ "LabelEnd": "סוף",
+ "LabelEndOfChapter": "סוף הפרק",
"LabelEpisode": "פרק",
+ "LabelEpisodeNotLinkedToRssFeed": "פרק לא מקושר לערוץ RSS",
+ "LabelEpisodeNumber": "פרק #{0}",
"LabelEpisodeTitle": "כותרת הפרק",
"LabelEpisodeType": "סוג הפרק",
+ "LabelEpisodeUrlFromRssFeed": "קישור פרק מערוץ RSS",
+ "LabelEpisodes": "פרקים",
+ "LabelEpisodic": "ארעי",
"LabelExample": "דוגמה",
+ "LabelExpandSeries": "הרחב סדרה",
+ "LabelExpandSubSeries": "הרחב תת סדרה",
"LabelExplicit": "בוטה",
+ "LabelExplicitChecked": "בוטה (מסומן)",
+ "LabelExplicitUnchecked": "לא בוטה (לא מסומן)",
+ "LabelExportOPML": "ייצוא OPML",
"LabelFeedURL": "כתובת ערוץ",
"LabelFetchingMetadata": "מושך מטא-נתונים",
"LabelFile": "קובץ",
"LabelFileBirthtime": "זמן יצירת הקובץ",
- "LabelFileModified": "הקובץ שונה",
- "LabelFilename": "שם הקובץ",
+ "LabelFileBornDate": "נוצר {0}",
+ "LabelFileModified": "קובץ נערך",
+ "LabelFileModifiedDate": "שונה {0}",
+ "LabelFilename": "שם קובץ",
"LabelFilterByUser": "סינון לפי משתמש",
"LabelFindEpisodes": "מצא פרקים",
"LabelFinished": "הושלם",
"LabelFolder": "תיקייה",
"LabelFolders": "תיקיות",
"LabelFontBold": "מודגש",
+ "LabelFontBoldness": "עובי פונט",
"LabelFontFamily": "משפחת הפונטים",
"LabelFontItalic": "נטוי",
- "LabelFontScale": "קנה מידה של הפונט",
+ "LabelFontScale": "גודל פונט",
"LabelFontStrikethrough": "קו חוצה",
"LabelFormat": "תבנית",
- "LabelGenre": "ז'אנר",
- "LabelGenres": "ז'אנרים",
+ "LabelFull": "מלא",
+ "LabelGenre": "סגנון",
+ "LabelGenres": "סגנונות",
"LabelHardDeleteFile": "מחיקה חזקה של הקובץ",
- "LabelHasEbook": "ספר אלקטרוני קיים",
- "LabelHasSupplementaryEbook": "קיים ספר אלקטרוני נלווה",
+ "LabelHasEbook": "קיים ספר אלקטרוני",
+ "LabelHasSupplementaryEbook": "קיים ספר אלקטרוני משלים",
+ "LabelHideSubtitles": "הסתר תת כותרות",
"LabelHighestPriority": "העדיפות הגבוהה ביותר",
"LabelHost": "מארח",
"LabelHour": "שעה",
+ "LabelHours": "שעות",
"LabelIcon": "סמל",
"LabelImageURLFromTheWeb": "כתובת התמונה מהרשת",
"LabelInProgress": "בתהליך",
@@ -341,25 +395,30 @@
"LabelIntervalEvery6Hours": "כל 6 שעות",
"LabelIntervalEveryDay": "כל יום",
"LabelIntervalEveryHour": "כל שעה",
+ "LabelIntervalEveryMinute": "כל דקה",
"LabelInvert": "הפוך",
"LabelItem": "פריט",
+ "LabelJumpBackwardAmount": "כמות הרצה לאחור",
+ "LabelJumpForwardAmount": "כמות הרצה קדימה",
"LabelLanguage": "שפה",
"LabelLanguageDefaultServer": "שפת ברירת המחדל של השרת",
+ "LabelLanguages": "שפות",
"LabelLastBookAdded": "הספר האחרון שנוסף",
"LabelLastBookUpdated": "הספר האחרון שעודכן",
"LabelLastSeen": "נראה לאחרונה",
"LabelLastTime": "הזמן האחרון",
"LabelLastUpdate": "עדכון אחרון",
- "LabelLayout": "פריסה",
- "LabelLayoutSinglePage": "דף בודד",
+ "LabelLayout": "Layout",
+ "LabelLayoutSinglePage": "עמוד יחיד",
"LabelLayoutSplitPage": "פיצול הדף",
"LabelLess": "פחות",
"LabelLibrariesAccessibleToUser": "ספריות נגישות למשתמש",
"LabelLibrary": "ספרייה",
+ "LabelLibraryFilterSublistEmpty": "לא {0}",
"LabelLibraryItem": "פריט ספרייה",
"LabelLibraryName": "שם הספרייה",
"LabelLimit": "מגבלה",
- "LabelLineSpacing": "ריווח שורות",
+ "LabelLineSpacing": "מרווח שורה",
"LabelListenAgain": "האזן שוב",
"LabelLogLevelDebug": "דיבוג",
"LabelLogLevelInfo": "מידע",
@@ -368,6 +427,10 @@
"LabelLowestPriority": "העדיפות הנמוכה ביותר",
"LabelMatchExistingUsersBy": "התאם משתמשים קיימים לפי",
"LabelMatchExistingUsersByDescription": "משמש לחיבור משתמשים קיימים. לאחר החיבור, המשתמשים יותאמו לפי זיהוי ייחודי מספק ה-SSO שלך",
+ "LabelMaxEpisodesToDownload": "מספר פרקים מקסימלי להורדה. 0 - ללא הגבלה.",
+ "LabelMaxEpisodesToDownloadPerCheck": "מספר פרקים חדשים מקסימלי להורדה בכל בדיקה",
+ "LabelMaxEpisodesToKeep": "מספר פרקים מקסימלי לשמור",
+ "LabelMaxEpisodesToKeepHelp": "ערך של 0 קובע ללא מגבלה. לאחר הורדה אוטומטית של פרק חדש יימחק את הפרק הישן ביותר אם יש לך יותר מ-X פרקים. פעולה זו תמחק רק פרק אחד לכל הורדה חדשה.",
"LabelMediaPlayer": "נגן מדיה",
"LabelMediaType": "סוג מדיה",
"LabelMetaTag": "תג מטא",
@@ -375,6 +438,7 @@
"LabelMetadataOrderOfPrecedenceDescription": "מקורות המטא-נתונים עם עדיפות גבוהה יחליפו מקורות עם עדיפות נמוכה יותר",
"LabelMetadataProvider": "ספק מטא-נתונים",
"LabelMinute": "דקה",
+ "LabelMinutes": "דקות",
"LabelMissing": "חסר",
"LabelMissingEbook": "אין ספר אלקטרוני",
"LabelMissingSupplementaryEbook": "אין ספר אלקטרוני נלווה",
@@ -387,10 +451,11 @@
"LabelNarrators": "מספרים",
"LabelNew": "חדש",
"LabelNewPassword": "סיסמה חדשה",
- "LabelNewestAuthors": "הסופרים החדשים ביותר",
+ "LabelNewestAuthors": "הסופרים האחרונים",
"LabelNewestEpisodes": "הפרקים החדשים ביותר",
"LabelNextBackupDate": "תאריך הגיבוי הבא",
"LabelNextScheduledRun": "הרצה מתוזמנת הבאה",
+ "LabelNoCustomMetadataProviders": "אין ספקי מטא-נתונים מותאמים אישית",
"LabelNoEpisodesSelected": "לא נבחרו פרקים",
"LabelNotFinished": "לא הושלם",
"LabelNotStarted": "לא התחיל",
@@ -405,7 +470,9 @@
"LabelNotificationsMaxQueueSize": "גודל התור המרבי לאירועי התראה",
"LabelNotificationsMaxQueueSizeHelp": "האירועים מוגבלים לשליחה אחת לשנייה. האירועים יתעלמו אם התור מלא. הגדרה זו נועדה למנוע ספאם התראות.",
"LabelNumberOfBooks": "מספר הספרים",
- "LabelNumberOfEpisodes": "מספר הפרקים",
+ "LabelNumberOfEpisodes": "# פרקים",
+ "LabelOpenIDAdvancedPermsClaimDescription": "שם OpenID claim המכילה הרשאות מתקדמות לפעולות משתמש בתוך האפליקציה, אשר יחולו על תפקידים שאינם מנהלי מערכת (אם הוגדרה). אם התביעה חסרה בתגובה, הגישה ל-ABS תידחה. אם אפשרות אחת חסרה, היא תטופל כ-false יש לוודא שטענת ספק הזהויות תואמת את המבנה הצפוי:",
+ "LabelOpenIDClaims": "השאר את האפשרויות הבאות ריקות כדי להשבית הקצאת קבוצות והרשאות מתקדמת, ולאחר מכן להקצות אוטומטית את קבוצת 'משתמש'.",
"LabelOpenRSSFeed": "פתח ערוץ RSS",
"LabelOverwrite": "לשכפל",
"LabelPassword": "סיסמה",
@@ -433,13 +500,15 @@
"LabelProvider": "ספק",
"LabelPubDate": "תאריך פרסום",
"LabelPublishYear": "שנת הפרסום",
+ "LabelPublishedDate": "פורסם {0}",
"LabelPublisher": "מוציא לאור",
"LabelRSSFeedCustomOwnerEmail": "אימייל בעלים מותאם אישית",
"LabelRSSFeedCustomOwnerName": "שם בעלים מותאם אישית",
- "LabelRSSFeedOpen": "פתח ערוץ RSS",
+ "LabelRSSFeedOpen": "ערוץ RSS פתוח",
"LabelRSSFeedPreventIndexing": "מנע רישום",
"LabelRSSFeedSlug": "Slug של ערוץ ה-RSS",
"LabelRSSFeedURL": "כתובת ערוץ ה-RSS",
+ "LabelRandomly": "באופן אקראי",
"LabelRead": "קריאה",
"LabelReadAgain": "קרא שוב",
"LabelReadEbookWithoutProgress": "קרא/י ספר אלקטרוני ללא שמירת התקדמות",
@@ -465,7 +534,7 @@
"LabelSeriesProgress": "התקדמות בסדרה",
"LabelServerYearReview": "השנה בסקירה של השרת ({0})",
"LabelSetEbookAsPrimary": "קבע כראשי",
- "LabelSetEbookAsSupplementary": "קבע כספר אלקטרוני נלווה",
+ "LabelSetEbookAsSupplementary": "קבע כמשלים",
"LabelSettingsAudiobooksOnly": "רק ספרי קול",
"LabelSettingsAudiobooksOnlyHelp": "הפעלת ההגדרה הזו תתעלם מקבצי ספרים אלקטרוניים אלא אם כן הם נמצאים בתיקיית ספרי קול, שבמקרה זה יקבעו כספרים אלקטרוניים נלווים",
"LabelSettingsBookshelfViewHelp": "עיצוב סקאומורפי עם מדפי עץ",
@@ -500,7 +569,7 @@
"LabelShowAll": "הצג הכל",
"LabelSize": "גודל",
"LabelSleepTimer": "טיימר שינה",
- "LabelStart": "התחלה",
+ "LabelStart": "התחל",
"LabelStartTime": "זמן התחלה",
"LabelStarted": "התחיל",
"LabelStartedAt": "התחיל ב",
@@ -576,8 +645,8 @@
"LabelViewQueue": "הצג תור נגן",
"LabelVolume": "עוצמת קול",
"LabelWeekdaysToRun": "ימי השבוע להרצה",
- "LabelYearReviewHide": "הסתר שנת סקירה",
- "LabelYearReviewShow": "הצג שנת סקירה",
+ "LabelYearReviewHide": "הסתר סקירת שנה",
+ "LabelYearReviewShow": "הצג סקירת שנה",
"LabelYourAudiobookDuration": "משך הספר הקולי שלך",
"LabelYourBookmarks": "הסימניות שלך",
"LabelYourPlaylists": "הפלייליסטים שלך",
@@ -628,8 +697,8 @@
"MessageDownloadingEpisode": "מוריד פרק",
"MessageDragFilesIntoTrackOrder": "גרור קבצים לסדר ההשמעה נכון",
"MessageEmbedFinished": "ההטמעה הושלמה!",
- "MessageEpisodesQueuedForDownload": "{0} פרקים בתור להורדה",
- "MessageFeedURLWillBe": "כתובת URL של העדכון תהיה {0}",
+ "MessageEpisodesQueuedForDownload": "{0} פרק/ים בתור להורדה",
+ "MessageFeedURLWillBe": "כתובת ה- URL של הערוץ תהיה {0}",
"MessageFetching": "מושך...",
"MessageForceReScanDescription": "תבוצע סריקה מחדש כמו סריקה חדש מאפס, תגי ID3 של קבצי קול, קבצי OPF, וקבצי טקסט ייסרקו כחדשים.",
"MessageImportantNotice": "הודעה חשובה!",
@@ -644,7 +713,7 @@
"MessageMapChapterTitles": "מפה שמות פרקים לפרקי הספר השמורים שלך ללא שינוי תגי זמן",
"MessageMarkAllEpisodesFinished": "סמן את כל הפרקים כהסתיימו",
"MessageMarkAllEpisodesNotFinished": "סמן את כל הפרקים כלא הסתיימו",
- "MessageMarkAsFinished": "סמן כהסתיים",
+ "MessageMarkAsFinished": "סמן כהושלם",
"MessageMarkAsNotFinished": "סמן כלא הסתיים",
"MessageMatchBooksDescription": "ינסה להתאים ספרים בספריית הספרים שלך עם ספר מספק החיפוש הנבחר וימלא פרטים ריקים ותמונות כריכה. לא יחליף פרטים קיימים.",
"MessageNoAudioTracks": "אין רצועות שמע",
@@ -674,7 +743,7 @@
"MessageNoSeries": "אין סדרות",
"MessageNoTags": "אין תגיות",
"MessageNoTasksRunning": "אין משימות פעילות",
- "MessageNoUpdatesWereNecessary": "לא היה צורך בעדכונים",
+ "MessageNoUpdatesWereNecessary": "לא נדרש עדכון",
"MessageNoUserPlaylists": "אין לך רשימות השמעה",
"MessageNotYetImplemented": "עדיין לא מיושם",
"MessageOr": "או",
@@ -682,6 +751,7 @@
"MessagePlayChapter": "הקשב לתחילת הפרק",
"MessagePlaylistCreateFromCollection": "צור רשימת השמעה מאוסף",
"MessagePodcastHasNoRSSFeedForMatching": "לפודקאסט אין כתובת URL של ערוץ RSS להתאמה",
+ "MessagePodcastSearchField": "הזן מונח חיפוש או כתובת URL של ערוץ RSS",
"MessageQuickMatchDescription": "ממלא פרטים ריקים וכריכות עם התוצאה הראשונה מ '{0}'. לא ימחק פרטים אלא אם הגדרת השרת 'העדף מטה-נתונים מותאמים' מופעלת.",
"MessageRemoveChapter": "הסר פרק",
"MessageRemoveEpisodes": "הסר {0} פרקים",
@@ -708,7 +778,7 @@
"NoteChangeRootPassword": "המשתמש root הוא המשתמש היחיד שיכולה להיות לו סיסמה ריקה",
"NoteChapterEditorTimes": "הערה: זמן ההתחלה של הפרק הראשון חייב להישאר 0:00 וזמן ההתחלה של הפרק האחרון לא יכול לחרוג מהזמן של ספר השמע.",
"NoteFolderPicker": "הערה: תיקיות שכבר מופו לא יוצגו",
- "NoteRSSFeedPodcastAppsHttps": "אזהרה: רוב יישומי הפודקאסט דורשים שכתובת ה-URL ערוץ ה-RSS תשתמש ב-HTTPS",
+ "NoteRSSFeedPodcastAppsHttps": "אזהרה: רוב אפליקציות הפודקאסטים ידרשו שכתובת האתר של ערוץ ה-RSS תשתמש ב-HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "אזהרה: פרק אחד או יותר לא מכילים תאריך פרסום. חלק מיישומי הפודקאסט דורשים זאת.",
"NoteUploaderFoldersWithMediaFiles": "תיקיות עם קבצי מדיה יעובדו כפריטי ספריה נפרדים.",
"NoteUploaderOnlyAudioFiles": "אם מועלים רק קבצי שמע, כל קובץ שמע יעובד כספר שמע נפרד.",
@@ -741,7 +811,7 @@
"ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה",
"ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה",
"ToastItemDetailsUpdateSuccess": "פרטי הפריט עודכנו בהצלחה",
- "ToastItemMarkedAsFinishedFailed": "סימון כפריט כהושלם נכשל",
+ "ToastItemMarkedAsFinishedFailed": "סימון כפריט שהושלם נכשל",
"ToastItemMarkedAsFinishedSuccess": "הפריט סומן כהושלם בהצלחה",
"ToastItemMarkedAsNotFinishedFailed": "סימון כפריט שלא הושלם נכשל",
"ToastItemMarkedAsNotFinishedSuccess": "הפריט סומן כלא הושלם בהצלחה",
From 32fc4f65552f2d7907280d69352570ed543b0bab Mon Sep 17 00:00:00 2001
From: Charlie
Date: Sun, 1 Jun 2025 14:45:53 +0200
Subject: [PATCH 041/121] Translated using Weblate (French)
Currently translated at 99.9% (1104 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
---
client/strings/fr.json | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/client/strings/fr.json b/client/strings/fr.json
index c6a28ddd..03a0cdee 100644
--- a/client/strings/fr.json
+++ b/client/strings/fr.json
@@ -530,6 +530,7 @@
"LabelReleaseDate": "Date de parution",
"LabelRemoveAllMetadataAbs": "Supprimer tous les fichiers metadata.abs",
"LabelRemoveAllMetadataJson": "Supprimer tous les fichiers metadata.json",
+ "LabelRemoveAudibleBranding": "Supprimer l’intro et la fin Audible des chapitres",
"LabelRemoveCover": "Supprimer la couverture",
"LabelRemoveMetadataFile": "Supprimer les fichiers de métadonnées dans les dossiers des éléments de la bibliothèque",
"LabelRemoveMetadataFileHelp": "Supprimer tous les fichiers metadata.json et metadata.abs de vos dossiers {0}.",
@@ -705,6 +706,8 @@
"LabelYourProgress": "Votre progression",
"MessageAddToPlayerQueue": "Ajouter en file d’attente",
"MessageAppriseDescription": "Nécessite une instance d’API Apprise pour utiliser cette fonctionnalité ou une api qui prend en charge les mêmes requêtes. L’URL de l’API Apprise doit comprendre le chemin complet pour envoyer la notification. Par exemple, si votre instance écoute sur http://192.168.1.1:8337 alors vous devez mettre http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "Assurez-vous d’utiliser l’ASIN de la bonne région Audible, et non d’Amazon.",
+ "MessageAuthenticationOIDCChangesRestart": "Redémarrez votre serveur après avoir enregistré pour appliquer les modifications OIDC.",
"MessageBackupsDescription": "Les sauvegardes incluent les utilisateurs, la progression des utilisateurs, les détails des éléments de la bibliothèque, les paramètres du serveur et les images stockées dans /metadata/items & /metadata/authors. Les sauvegardes n’incluent pas les fichiers stockés dans les dossiers de votre bibliothèque.",
"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.",
@@ -723,6 +726,7 @@
"MessageChapterErrorStartGteDuration": "Horodatage invalide car il doit débuter avant la fin du livre",
"MessageChapterErrorStartLtPrev": "Horodatage invalide car il doit débuter au moins après le précédent chapitre",
"MessageChapterStartIsAfter": "Le premier chapitre est situé au début de votre livre audio",
+ "MessageChaptersNotFound": "Chapitres non trouvés",
"MessageCheckingCron": "Vérification du cron…",
"MessageConfirmCloseFeed": "Êtes-vous sûr·e de vouloir fermer ce flux ?",
"MessageConfirmDeleteBackup": "Êtes-vous sûr·e de vouloir supprimer la sauvegarde de « {0} » ?",
@@ -779,6 +783,7 @@
"MessageForceReScanDescription": "analysera de nouveau tous les fichiers. Les étiquettes ID3 des fichiers audio, les fichiers OPF et les fichiers texte seront analysés comme s’ils étaient nouveaux.",
"MessageImportantNotice": "Information importante !",
"MessageInsertChapterBelow": "Insérer le chapitre ci-dessous",
+ "MessageInvalidAsin": "ASIN invalide",
"MessageItemsSelected": "{0} éléments sélectionnés",
"MessageItemsUpdated": "{0} éléments mis à jour",
"MessageJoinUsOn": "Rejoignez-nous sur",
@@ -850,6 +855,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Exécuté tous les {0} à {1}",
"MessageSearchResultsFor": "Résultats de recherche pour",
"MessageSelected": "{0} sélectionnés",
+ "MessageSeriesSequenceCannotContainSpaces": "La séquence de séries ne peut pas contenir d’espaces",
"MessageServerCouldNotBeReached": "Serveur inaccessible",
"MessageSetChaptersFromTracksDescription": "Positionne un chapitre par fichier audio, avec le titre du fichier comme titre de chapitre",
"MessageShareExpirationWillBe": "Expire le {0}",
@@ -968,6 +974,8 @@
"ToastCachePurgeFailed": "Échec de la purge du cache",
"ToastCachePurgeSuccess": "Cache purgé avec succès",
"ToastChaptersHaveErrors": "Les chapitres contiennent des erreurs",
+ "ToastChaptersInvalidShiftAmountLast": "Durée de décalage non valide. L’heure de début du dernier chapitre pourrait dépasser la durée de ce livre audio.",
+ "ToastChaptersInvalidShiftAmountStart": "Durée de décalage non valide. Le premier chapitre aurait une longueur nulle ou négative et serait écrasé par le second. Augmentez la durée de début du second chapitre.",
"ToastChaptersMustHaveTitles": "Les chapitre doivent avoir un titre",
"ToastChaptersRemoved": "Chapitres supprimés",
"ToastChaptersUpdated": "Chapitres mis à jour",
From c377b57601f82f76d677b09e6bbabda732c18861 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sun, 1 Jun 2025 16:00:16 -0500
Subject: [PATCH 042/121] Version bump v2.24.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 56a98514..23ec14a9 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf-client",
- "version": "2.23.0",
+ "version": "2.24.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf-client",
- "version": "2.23.0",
+ "version": "2.24.0",
"license": "ISC",
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
diff --git a/client/package.json b/client/package.json
index a2bd7e62..7985cd78 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
- "version": "2.23.0",
+ "version": "2.24.0",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast client",
"main": "index.js",
diff --git a/package-lock.json b/package-lock.json
index 91a5f283..a8074794 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf",
- "version": "2.23.0",
+ "version": "2.24.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf",
- "version": "2.23.0",
+ "version": "2.24.0",
"license": "GPL-3.0",
"dependencies": {
"axios": "^0.27.2",
diff --git a/package.json b/package.json
index 9b08daee..d4831736 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
- "version": "2.23.0",
+ "version": "2.24.0",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
From 9968743a9349c28bd5e22268e943757d0b60567a Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Mon, 2 Jun 2025 19:32:52 +0200
Subject: [PATCH 043/121] fix wrong display and ignored values
---
client/components/widgets/EncoderOptionsCard.vue | 16 ++++++++++++----
client/pages/audiobook/_id/manage.vue | 2 ++
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/client/components/widgets/EncoderOptionsCard.vue b/client/components/widgets/EncoderOptionsCard.vue
index 005563b4..977d766b 100644
--- a/client/components/widgets/EncoderOptionsCard.vue
+++ b/client/components/widgets/EncoderOptionsCard.vue
@@ -143,10 +143,18 @@ export default {
localStorage.setItem('embedMetadataCodec', val)
},
getEncodingOptions() {
- return {
- codec: this.selectedCodec || 'aac',
- bitrate: this.selectedBitrate || '128k',
- channels: this.selectedChannels || 2
+ if (this.showAdvancedView) {
+ return {
+ codec: this.customCodec || this.selectedCodec || 'aac',
+ bitrate: this.customBitrate || this.selectedBitrate || '128k',
+ channels: this.customChannels || this.selectedChannels || 2
+ }
+ } else {
+ return {
+ codec: this.selectedCodec || 'aac',
+ bitrate: this.selectedBitrate || '128k',
+ channels: this.selectedChannels || 2
+ }
}
},
setPreset() {
diff --git a/client/pages/audiobook/_id/manage.vue b/client/pages/audiobook/_id/manage.vue
index 7afe12a9..f5db9cce 100644
--- a/client/pages/audiobook/_id/manage.vue
+++ b/client/pages/audiobook/_id/manage.vue
@@ -356,6 +356,8 @@ export default {
const encodeOptions = this.$refs.encoderOptionsCard.getEncodingOptions()
+ this.encodingOptions = encodeOptions;
+
const queryParams = new URLSearchParams(encodeOptions)
this.processing = true
From b6995ba5d1573c7186b01b63d1443cc20d20503b Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Mon, 2 Jun 2025 19:33:50 +0200
Subject: [PATCH 044/121] prettier
---
client/pages/audiobook/_id/manage.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/pages/audiobook/_id/manage.vue b/client/pages/audiobook/_id/manage.vue
index f5db9cce..37734934 100644
--- a/client/pages/audiobook/_id/manage.vue
+++ b/client/pages/audiobook/_id/manage.vue
@@ -356,7 +356,7 @@ export default {
const encodeOptions = this.$refs.encoderOptionsCard.getEncodingOptions()
- this.encodingOptions = encodeOptions;
+ this.encodingOptions = encodeOptions
const queryParams = new URLSearchParams(encodeOptions)
From 424ef1aec31a0b53997363198a5cd15eb598577d Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Mon, 2 Jun 2025 19:34:25 +0200
Subject: [PATCH 045/121] prettier 2
---
client/components/widgets/LoadingSpinner.vue | 2 +-
client/components/widgets/SeriesInputWidget.vue | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/components/widgets/LoadingSpinner.vue b/client/components/widgets/LoadingSpinner.vue
index a9c4ef47..8f3de84a 100644
--- a/client/components/widgets/LoadingSpinner.vue
+++ b/client/components/widgets/LoadingSpinner.vue
@@ -248,4 +248,4 @@ export default {
transform: scale(0);
}
}
-
\ No newline at end of file
+
diff --git a/client/components/widgets/SeriesInputWidget.vue b/client/components/widgets/SeriesInputWidget.vue
index d6c8cf9f..3dab0605 100644
--- a/client/components/widgets/SeriesInputWidget.vue
+++ b/client/components/widgets/SeriesInputWidget.vue
@@ -109,4 +109,4 @@ export default {
}
}
}
-
\ No newline at end of file
+
From 63ccdb68f0c61a1a28e2bbedb6b49b94490f9cd3 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 2 Jun 2025 16:50:03 -0500
Subject: [PATCH 046/121] Fix m4b encoder backup file overwriting the encoded
file when they have the same filename
---
server/managers/AbMergeManager.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/server/managers/AbMergeManager.js b/server/managers/AbMergeManager.js
index f6a56160..3611d294 100644
--- a/server/managers/AbMergeManager.js
+++ b/server/managers/AbMergeManager.js
@@ -203,7 +203,15 @@ class AbMergeManager {
// Move library item tracks to cache
for (const [index, trackPath] of task.data.originalTrackPaths.entries()) {
const trackFilename = Path.basename(trackPath)
- const moveToPath = Path.join(task.data.itemCachePath, trackFilename)
+ let moveToPath = Path.join(task.data.itemCachePath, trackFilename)
+
+ // If the track is the same as the temp file, we need to rename it to avoid overwriting it
+ if (task.data.tempFilepath === moveToPath) {
+ const trackExtname = Path.extname(task.data.tempFilepath)
+ const newTrackFilename = Path.basename(task.data.tempFilepath, trackExtname) + '.backup' + trackExtname
+ moveToPath = Path.join(task.data.itemCachePath, newTrackFilename)
+ }
+
Logger.debug(`[AbMergeManager] Backing up original track "${trackPath}" to ${moveToPath}`)
if (index === 0) {
// copy the first track to the cache directory
From 4d846e225a40ebea605edf88c2ae973095646f3b Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Wed, 4 Jun 2025 10:02:17 +0200
Subject: [PATCH 047/121] Adds ENV for MaxFailedEpisodeChecks
---
server/managers/PodcastManager.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index 052ba8b3..c2fae077 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -30,7 +30,7 @@ class PodcastManager {
this.currentDownload = null
this.failedCheckMap = {}
- this.MaxFailedEpisodeChecks = 24
+ this.MaxFailedEpisodeChecks = process.env.MAX_FAILED_EPISODE_CHECKS || 24
}
getEpisodeDownloadsInQueue(libraryItemId) {
@@ -345,7 +345,7 @@ class PodcastManager {
// Allow up to MaxFailedEpisodeChecks failed attempts before disabling auto download
if (!this.failedCheckMap[libraryItem.id]) this.failedCheckMap[libraryItem.id] = 0
this.failedCheckMap[libraryItem.id]++
- if (this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
+ if (this.MaxFailedEpisodeChecks != 0 && this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
Logger.error(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.title}" - disabling auto download`)
libraryItem.media.autoDownloadEpisodes = false
delete this.failedCheckMap[libraryItem.id]
From 709c33f27af56a5790bdc5f8312c00b2f8f01eab Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Wed, 4 Jun 2025 10:05:16 +0200
Subject: [PATCH 048/121] ensure proper type
---
server/managers/PodcastManager.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index c2fae077..54ce4c9f 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -30,7 +30,7 @@ class PodcastManager {
this.currentDownload = null
this.failedCheckMap = {}
- this.MaxFailedEpisodeChecks = process.env.MAX_FAILED_EPISODE_CHECKS || 24
+ this.MaxFailedEpisodeChecks = parseInt(process.env.MAX_FAILED_EPISODE_CHECKS, 10) || 24
}
getEpisodeDownloadsInQueue(libraryItemId) {
@@ -345,7 +345,7 @@ class PodcastManager {
// Allow up to MaxFailedEpisodeChecks failed attempts before disabling auto download
if (!this.failedCheckMap[libraryItem.id]) this.failedCheckMap[libraryItem.id] = 0
this.failedCheckMap[libraryItem.id]++
- if (this.MaxFailedEpisodeChecks != 0 && this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
+ if (this.MaxFailedEpisodeChecks !== 0 && this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
Logger.error(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.title}" - disabling auto download`)
libraryItem.media.autoDownloadEpisodes = false
delete this.failedCheckMap[libraryItem.id]
From 9bb4dc3ab0a00c9d9df26390413f3a52b1071a94 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Wed, 4 Jun 2025 10:58:44 +0200
Subject: [PATCH 049/121] potential fix
---
server/managers/PodcastManager.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index 052ba8b3..bb4a408c 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -384,7 +384,13 @@ class PodcastManager {
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes no feed url for ${podcastLibraryItem.media.title} (ID: ${podcastLibraryItem.id})`)
return null
}
- const feed = await getPodcastFeed(podcastLibraryItem.media.feedURL)
+ const feed = await Promise.race([
+ getPodcastFeed(podcastLibraryItem.media.feedURL),
+ new Promise((_, reject) =>
+ // The added second is to make sure that axios can fail first and only falls back later
+ setTimeout(() => reject(new Error('Timeout. getPodcastFeed seemed to timeout but not triggering the timeout.')), global.PodcastDownloadTimeout + 1000)
+ )
+ ])
if (!feed?.episodes) {
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes invalid feed payload for ${podcastLibraryItem.media.title} (ID: ${podcastLibraryItem.id})`, feed)
return null
From 357176b301551b8c2551b94f41be87c28df3e950 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Wed, 4 Jun 2025 16:15:18 +0200
Subject: [PATCH 050/121] catch timeout
---
server/managers/PodcastManager.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index bb4a408c..625688c9 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -390,7 +390,11 @@ class PodcastManager {
// The added second is to make sure that axios can fail first and only falls back later
setTimeout(() => reject(new Error('Timeout. getPodcastFeed seemed to timeout but not triggering the timeout.')), global.PodcastDownloadTimeout + 1000)
)
- ])
+ ]).catch((error) => {
+ Logger.error(`[PodcastManager] checkPodcastForNewEpisodes failed to fetch feed for ${podcastLibraryItem.media.title} (ID: ${podcastLibraryItem.id}):`, error)
+ return null
+ })
+
if (!feed?.episodes) {
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes invalid feed payload for ${podcastLibraryItem.media.title} (ID: ${podcastLibraryItem.id})`, feed)
return null
From 759c58d3f7561b6d56b61fa78ec6880f10796fa8 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Wed, 4 Jun 2025 16:38:01 +0200
Subject: [PATCH 051/121] remove any attachment
---
client/components/ui/VueTrix.vue | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/client/components/ui/VueTrix.vue b/client/components/ui/VueTrix.vue
index 2687d934..0836df15 100644
--- a/client/components/ui/VueTrix.vue
+++ b/client/components/ui/VueTrix.vue
@@ -318,10 +318,8 @@ export default {
}
},
handleAttachmentAdd(event) {
- // Prevent pasting in images from the browser
- if (!event.attachment.file) {
- event.attachment.remove()
- }
+ // Prevent pasting in images/any files from the browser
+ event.attachment.remove()
}
},
mounted() {
From 6aa7c8a3d8734b55f932e8c270d830bc8644d36f Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Thu, 5 Jun 2025 13:34:18 +0200
Subject: [PATCH 052/121] added notification
---
server/managers/NotificationManager.js | 48 ++++++++++++++++++++++++++
server/managers/PodcastManager.js | 2 ++
server/utils/notifications.js | 32 +++++++++++++++++
3 files changed, 82 insertions(+)
diff --git a/server/managers/NotificationManager.js b/server/managers/NotificationManager.js
index 8edcf428..4f7072be 100644
--- a/server/managers/NotificationManager.js
+++ b/server/managers/NotificationManager.js
@@ -71,6 +71,54 @@ class NotificationManager {
this.triggerNotification('onBackupCompleted', eventData)
}
+ /**
+ * Handles RSS feed updates
+ * @param feedUrl
+ * @param numFailed
+ * @param title
+ * @returns {Promise}
+ */
+ async onRSSFeedFailed(feedUrl, numFailed, title) {
+ if (!Database.notificationSettings.isUseable) return
+
+ if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onRSSFeedFailed')) {
+ Logger.debug(`[NotificationManager] onRSSFeedFailed: No active notifications`)
+ return
+ }
+
+ Logger.debug(`[NotificationManager] onRSSFeedFailed: RSS feed update failed for ${feedUrl}`)
+ const eventData = {
+ feedUrl: feedUrl,
+ numFailed: numFailed || 0,
+ title: title || 'Unknown Title'
+ }
+ this.triggerNotification('onRSSFeedFailed', eventData)
+ }
+
+ /**
+ * Handles RSS feed being disabled due to too many failed updates
+ * @param feedUrl
+ * @param numFailed
+ * @param title
+ * @returns {Promise}
+ */
+ async onRSSFeedDisabled(feedUrl, numFailed, title) {
+ if (!Database.notificationSettings.isUseable) return
+
+ if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onRSSFeedDisabled')) {
+ Logger.debug(`[NotificationManager] onRSSFeedDisabled: No active notifications`)
+ return
+ }
+
+ Logger.debug(`[NotificationManager] onRSSFeedDisabled: RSS feed disabled due to ${numFailed} failed updates for ${feedUrl}`)
+ const eventData = {
+ feedUrl: feedUrl,
+ numFailed: numFailed || 0,
+ title: title || 'Unknown Title'
+ }
+ this.triggerNotification('onRSSFeedDisabled', eventData)
+ }
+
/**
*
* @param {string} errorMsg
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index 625688c9..e5da8fe6 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -347,10 +347,12 @@ class PodcastManager {
this.failedCheckMap[libraryItem.id]++
if (this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
Logger.error(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.title}" - disabling auto download`)
+ void NotificationManager.onRSSFeedDisabled(libraryItem.media.feedURL, this.failedCheckMap[libraryItem.id], libraryItem.media.title)
libraryItem.media.autoDownloadEpisodes = false
delete this.failedCheckMap[libraryItem.id]
} else {
Logger.warn(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.title}"`)
+ void NotificationManager.onRSSFeedFailed(libraryItem.media.feedURL, this.failedCheckMap[libraryItem.id], libraryItem.media.title)
}
} else if (newEpisodes.length) {
delete this.failedCheckMap[libraryItem.id]
diff --git a/server/utils/notifications.js b/server/utils/notifications.js
index 7a3e1198..1b9612b9 100644
--- a/server/utils/notifications.js
+++ b/server/utils/notifications.js
@@ -60,6 +60,38 @@ module.exports.notificationData = {
errorMsg: 'Example error message'
}
},
+ {
+ name: 'onRSSFeedFailed',
+ requiresLibrary: true,
+ description: 'Triggered when an RSS feed request/update fails, but gets not disabled',
+ descriptionKey: 'NotificationOnRSSFeedFailedDescription',
+ variables: ['feedUrl', 'numFailed', 'title'],
+ defaults: {
+ title: 'RSS Feed Update Failed',
+ body: 'Failed to update RSS feed for {{title}}.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
+ },
+ testData: {
+ title: 'Test RSS Feed',
+ feedUrl: 'https://example.com/rss',
+ numFailed: 3
+ }
+ },
+ {
+ name: 'onRSSFeedDisabled',
+ requiresLibrary: true,
+ description: 'Triggered when an RSS feed is disabled due to too many failed attempts',
+ descriptionKey: 'NotificationOnRSSFeedDisabledDescription',
+ variables: ['feedUrl', 'numFailed', 'title'],
+ defaults: {
+ title: 'RSS Feed Disabled',
+ body: 'RSS feed for {{title}} has been disabled due to too many failed updates.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
+ },
+ testData: {
+ title: 'Test RSS Feed',
+ feedUrl: 'https://example.com/rss',
+ numFailed: 5
+ }
+ },
{
name: 'onTest',
requiresLibrary: false,
From 346df3680ce8dd415ce3d77d5ce789686e8dd5f9 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Thu, 5 Jun 2025 14:02:29 +0200
Subject: [PATCH 053/121] local strings
---
client/strings/en-us.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 939eb9f4..65fd5f0e 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Triggered when a backup is completed",
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
+ "NotificationOnRSSFeedFailedDescription": "Triggered when an RSS feed request/update fails, but gets not disabled",
+ "NotificationOnRSSFeedDisabledDescription": "Triggered when an RSS feed is disabled due to too many failed attempts",
"NotificationOnTestDescription": "Event for testing the notification system",
"PlaceholderNewCollection": "New collection name",
"PlaceholderNewFolderPath": "New folder path",
From 84c9c6cb50d6e4a725d0de6c6cba2449e50eab88 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Thu, 5 Jun 2025 14:07:35 +0200
Subject: [PATCH 054/121] move to global
---
server/Server.js | 6 ++++++
server/managers/PodcastManager.js | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/server/Server.js b/server/Server.js
index 17c959c0..317dab38 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -91,6 +91,12 @@ class Server {
global.PodcastDownloadTimeout = 30000
}
+ if (process.env.MAX_FAILED_EPISODE_CHECKS) {
+ global.MaxFailedEpisodeChecks = process.env.MAX_FAILED_EPISODE_CHECKS
+ } else {
+ global.MaxFailedEpisodeChecks = 24
+ }
+
if (!fs.pathExistsSync(global.ConfigPath)) {
fs.mkdirSync(global.ConfigPath)
}
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index 54ce4c9f..0c7da925 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -30,7 +30,7 @@ class PodcastManager {
this.currentDownload = null
this.failedCheckMap = {}
- this.MaxFailedEpisodeChecks = parseInt(process.env.MAX_FAILED_EPISODE_CHECKS, 10) || 24
+ this.MaxFailedEpisodeChecks = global.MaxFailedEpisodeChecks
}
getEpisodeDownloadsInQueue(libraryItemId) {
From f0525d4f0de8392b87d788fda3a0a6bda57d15c8 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Thu, 5 Jun 2025 14:09:35 +0200
Subject: [PATCH 055/121] abc is hard
---
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 65fd5f0e..afecd671 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -918,8 +918,8 @@
"NotificationOnBackupCompletedDescription": "Triggered when a backup is completed",
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
- "NotificationOnRSSFeedFailedDescription": "Triggered when an RSS feed request/update fails, but gets not disabled",
"NotificationOnRSSFeedDisabledDescription": "Triggered when an RSS feed is disabled due to too many failed attempts",
+ "NotificationOnRSSFeedFailedDescription": "Triggered when an RSS feed request/update fails, but gets not disabled",
"NotificationOnTestDescription": "Event for testing the notification system",
"PlaceholderNewCollection": "New collection name",
"PlaceholderNewFolderPath": "New folder path",
From 8e0185907502926b818fc36a2fea844c5dbac588 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Thu, 5 Jun 2025 14:31:12 -0500
Subject: [PATCH 056/121] Cast PODCAST_DOWNLOAD_TIMEOUT and
MAX_FAILED_EPISODE_CHECKS env vars to numbers
---
server/Server.js | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/server/Server.js b/server/Server.js
index 317dab38..5c6f3c16 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -12,6 +12,7 @@ const { version } = require('../package.json')
// Utils
const fileUtils = require('./utils/fileUtils')
+const { toNumber } = require('./utils/index')
const Logger = require('./Logger')
const Auth = require('./Auth')
@@ -84,18 +85,8 @@ class Server {
global.DisableSsrfRequestFilter = (url) => whitelistedUrls.includes(new URL(url).hostname)
}
}
-
- if (process.env.PODCAST_DOWNLOAD_TIMEOUT) {
- global.PodcastDownloadTimeout = process.env.PODCAST_DOWNLOAD_TIMEOUT
- } else {
- global.PodcastDownloadTimeout = 30000
- }
-
- if (process.env.MAX_FAILED_EPISODE_CHECKS) {
- global.MaxFailedEpisodeChecks = process.env.MAX_FAILED_EPISODE_CHECKS
- } else {
- global.MaxFailedEpisodeChecks = 24
- }
+ global.PodcastDownloadTimeout = toNumber(process.env.PODCAST_DOWNLOAD_TIMEOUT, 30000)
+ global.MaxFailedEpisodeChecks = toNumber(process.env.MAX_FAILED_EPISODE_CHECKS, 24)
if (!fs.pathExistsSync(global.ConfigPath)) {
fs.mkdirSync(global.ConfigPath)
From eda7036f70c9a3a1cd699231c8ac9531368aee31 Mon Sep 17 00:00:00 2001
From: Jan Kubovy
Date: Fri, 6 Jun 2025 10:43:52 +0000
Subject: [PATCH 057/121] Use fuse.js for podcast episode search
Replace levenshtein distance with fuse.js fuzzy searching library. Search in episode's title and subtitle
---
package-lock.json | 9 +++++++++
package.json | 1 +
server/utils/podcastUtils.js | 39 +++++++++++++++++-------------------
3 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index a8074794..9147d9e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"cookie-parser": "^1.4.6",
"express": "^4.17.1",
"express-session": "^1.17.3",
+ "fuse.js": "^7.1.0",
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"lru-cache": "^10.0.3",
@@ -2105,6 +2106,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/fuse.js": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
+ "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
diff --git a/package.json b/package.json
index d4831736..2a77ec87 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"cookie-parser": "^1.4.6",
"express": "^4.17.1",
"express-session": "^1.17.3",
+ "fuse.js": "^7.1.0",
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"lru-cache": "^10.0.3",
diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js
index 3a1df198..74a71cc1 100644
--- a/server/utils/podcastUtils.js
+++ b/server/utils/podcastUtils.js
@@ -3,6 +3,7 @@ const ssrfFilter = require('ssrf-req-filter')
const Logger = require('../Logger')
const { xmlToJSON, levenshteinDistance, timestampToSeconds } = require('./index')
const htmlSanitizer = require('../utils/htmlSanitizer')
+const Fuse = require('fuse.js')
/**
* @typedef RssPodcastChapter
@@ -407,7 +408,7 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => {
})
}
-// Return array of episodes ordered by closest match (Levenshtein distance of 6 or less)
+// Return array of episodes ordered by closest match using fuse.js
module.exports.findMatchingEpisodes = async (feedUrl, searchTitle) => {
const feed = await this.getPodcastFeed(feedUrl).catch(() => {
return null
@@ -420,32 +421,28 @@ module.exports.findMatchingEpisodes = async (feedUrl, searchTitle) => {
*
* @param {RssPodcast} feed
* @param {string} searchTitle
- * @returns {Array<{ episode: RssPodcastEpisode, levenshtein: number }>}
+ * @returns {Array<{ episode: RssPodcastEpisode }>}
*/
module.exports.findMatchingEpisodesInFeed = (feed, searchTitle) => {
- searchTitle = searchTitle.toLowerCase().trim()
if (!feed?.episodes) {
return null
}
+ const fuseOptions = {
+ ignoreDiacritics: true,
+ threshold: 0.4, // default 0.6 return too many matches
+ keys: [
+ {name: 'title', weight: 0.7}, // prefer match in title
+ {name: 'subtitle', weight: 0.3}
+ ]
+ }
+ const fuse = new Fuse(feed.episodes, fuseOptions)
+
const matches = []
- feed.episodes.forEach((ep) => {
- if (!ep.title) return
- const epTitle = ep.title.toLowerCase().trim()
- if (epTitle === searchTitle) {
- matches.push({
- episode: ep,
- levenshtein: 0
- })
- } else {
- const levenshtein = levenshteinDistance(searchTitle, epTitle, true)
- if (levenshtein <= 6 && epTitle.length > levenshtein) {
- matches.push({
- episode: ep,
- levenshtein
- })
- }
- }
+ fuse.search(searchTitle).forEach((match) => {
+ matches.push({
+ episode: match.item
+ })
})
- return matches.sort((a, b) => a.levenshtein - b.levenshtein)
+ return matches
}
From 81640464ba71a917d199a885f86658ea38a9fb5a Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 6 Jun 2025 17:05:07 -0500
Subject: [PATCH 058/121] Update cleanDatabase to remove duplicate
mediaProgresses
---
server/Database.js | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/server/Database.js b/server/Database.js
index 52827e3f..2413a269 100644
--- a/server/Database.js
+++ b/server/Database.js
@@ -765,6 +765,15 @@ class Database {
if (badSessionsRemoved > 0) {
Logger.warn(`Removed ${badSessionsRemoved} sessions that were 3 seconds or less`)
}
+
+ // Remove mediaProgresses with duplicate mediaItemId (remove the oldest updatedAt)
+ const [duplicateMediaProgresses] = await this.sequelize.query(`SELECT id, mediaItemId FROM mediaProgresses WHERE (mediaItemId, updatedAt) IN (SELECT mediaItemId, MIN(updatedAt) FROM mediaProgresses GROUP BY mediaItemId HAVING COUNT(*) > 1)`)
+ for (const duplicateMediaProgress of duplicateMediaProgresses) {
+ Logger.warn(`Found duplicate mediaProgress for mediaItem "${duplicateMediaProgress.mediaItemId}" - removing it`)
+ await this.mediaProgressModel.destroy({
+ where: { id: duplicateMediaProgress.id }
+ })
+ }
}
async createTextSearchQuery(query) {
From 0c5d05d3199c3aea64769a1f5f2c1535d8b3c9c6 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 7 Jun 2025 17:10:23 -0500
Subject: [PATCH 059/121] Fix chapter table on audiobook tools page uneven
column widths
---
client/pages/audiobook/_id/manage.vue | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/client/pages/audiobook/_id/manage.vue b/client/pages/audiobook/_id/manage.vue
index 37734934..40672af2 100644
--- a/client/pages/audiobook/_id/manage.vue
+++ b/client/pages/audiobook/_id/manage.vue
@@ -28,14 +28,14 @@
-
{{ $strings.LabelMetaTag }}
-
{{ $strings.LabelValue }}
+
{{ $strings.LabelMetaTag }}
+
{{ $strings.LabelValue }}
-
{{ key }}
-
+
{{ key }}
+
{{ value }}
@@ -45,18 +45,18 @@
{{ $strings.LabelChapterTitle }}
-
{{ $strings.LabelStart }}
-
{{ $strings.LabelEnd }}
+
{{ $strings.LabelStart }}
+
{{ $strings.LabelEnd }}
{{ $strings.MessageNoChapters }}
{{ chapter.title }}
-
+
{{ $secondsToTimestamp(chapter.start) }}
-
+
{{ $secondsToTimestamp(chapter.end) }}
From c8cea4e6afc3628d53124cf9cd5a9a867cc71ad2 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sun, 8 Jun 2025 17:28:19 -0500
Subject: [PATCH 060/121] Update book library sort by author to use title as
secondary sort #4380
---
server/utils/queries/libraryItemsBookFilters.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js
index 42c39120..73d36c58 100644
--- a/server/utils/queries/libraryItemsBookFilters.js
+++ b/server/utils/queries/libraryItemsBookFilters.js
@@ -264,9 +264,15 @@ module.exports = {
} else if (sortBy === 'media.metadata.publishedYear') {
return [[Sequelize.literal(`CAST(\`book\`.\`publishedYear\` AS INTEGER)`), dir]]
} else if (sortBy === 'media.metadata.authorNameLF') {
- return [[Sequelize.literal('`libraryItem`.`authorNamesLastFirst` COLLATE NOCASE'), dir]]
+ return [
+ [Sequelize.literal('`libraryItem`.`authorNamesLastFirst` COLLATE NOCASE'), dir],
+ [Sequelize.literal('`libraryItem`.`title` COLLATE NOCASE'), dir]
+ ]
} else if (sortBy === 'media.metadata.authorName') {
- return [[Sequelize.literal('`libraryItem`.`authorNamesFirstLast` COLLATE NOCASE'), dir]]
+ return [
+ [Sequelize.literal('`libraryItem`.`authorNamesFirstLast` COLLATE NOCASE'), dir],
+ [Sequelize.literal('`libraryItem`.`title` COLLATE NOCASE'), dir]
+ ]
} else if (sortBy === 'media.metadata.title') {
if (collapseseries) {
return [[Sequelize.literal('display_title COLLATE NOCASE'), dir]]
From 7122756e585a2d4213f1af2a347f287d8b6e2473 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 9 Jun 2025 15:51:14 -0500
Subject: [PATCH 061/121] Update notification description grammar
---
client/strings/en-us.json | 2 +-
server/utils/notifications.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index afecd671..25c8268b 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -919,7 +919,7 @@
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
"NotificationOnRSSFeedDisabledDescription": "Triggered when an RSS feed is disabled due to too many failed attempts",
- "NotificationOnRSSFeedFailedDescription": "Triggered when an RSS feed request/update fails, but gets not disabled",
+ "NotificationOnRSSFeedFailedDescription": "Triggered when an RSS feed request/update fails, but is not disabled",
"NotificationOnTestDescription": "Event for testing the notification system",
"PlaceholderNewCollection": "New collection name",
"PlaceholderNewFolderPath": "New folder path",
diff --git a/server/utils/notifications.js b/server/utils/notifications.js
index 1b9612b9..304b4ffe 100644
--- a/server/utils/notifications.js
+++ b/server/utils/notifications.js
@@ -63,7 +63,7 @@ module.exports.notificationData = {
{
name: 'onRSSFeedFailed',
requiresLibrary: true,
- description: 'Triggered when an RSS feed request/update fails, but gets not disabled',
+ description: 'Triggered when an RSS feed request/update fails, but is not disabled',
descriptionKey: 'NotificationOnRSSFeedFailedDescription',
variables: ['feedUrl', 'numFailed', 'title'],
defaults: {
From 5e2bb0b12ccacaca5d231e4a5a3bd9ffb111a9df Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 9 Jun 2025 16:21:05 -0500
Subject: [PATCH 062/121] Fix notification js docs and update
description/defaults
---
client/strings/en-us.json | 4 ++--
server/managers/NotificationManager.js | 24 ++++++++++++------------
server/utils/notifications.js | 12 ++++++------
3 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 25c8268b..f6288912 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -918,8 +918,8 @@
"NotificationOnBackupCompletedDescription": "Triggered when a backup is completed",
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
- "NotificationOnRSSFeedDisabledDescription": "Triggered when an RSS feed is disabled due to too many failed attempts",
- "NotificationOnRSSFeedFailedDescription": "Triggered when an RSS feed request/update fails, but is not disabled",
+ "NotificationOnRSSFeedDisabledDescription": "Triggered when automatic episode downloads are disabled due to too many failed attempts",
+ "NotificationOnRSSFeedFailedDescription": "Triggered when the RSS feed request fails for an automatic episode download",
"NotificationOnTestDescription": "Event for testing the notification system",
"PlaceholderNewCollection": "New collection name",
"PlaceholderNewFolderPath": "New folder path",
diff --git a/server/managers/NotificationManager.js b/server/managers/NotificationManager.js
index 4f7072be..567da38e 100644
--- a/server/managers/NotificationManager.js
+++ b/server/managers/NotificationManager.js
@@ -72,11 +72,11 @@ class NotificationManager {
}
/**
- * Handles RSS feed updates
- * @param feedUrl
- * @param numFailed
- * @param title
- * @returns {Promise}
+ * Handles scheduled episode download RSS feed request failed
+ *
+ * @param {string} feedUrl
+ * @param {number} numFailed
+ * @param {string} title
*/
async onRSSFeedFailed(feedUrl, numFailed, title) {
if (!Database.notificationSettings.isUseable) return
@@ -86,7 +86,7 @@ class NotificationManager {
return
}
- Logger.debug(`[NotificationManager] onRSSFeedFailed: RSS feed update failed for ${feedUrl}`)
+ Logger.debug(`[NotificationManager] onRSSFeedFailed: RSS feed request failed for ${feedUrl}`)
const eventData = {
feedUrl: feedUrl,
numFailed: numFailed || 0,
@@ -96,11 +96,11 @@ class NotificationManager {
}
/**
- * Handles RSS feed being disabled due to too many failed updates
- * @param feedUrl
- * @param numFailed
- * @param title
- * @returns {Promise}
+ * Handles scheduled episode downloads disabled due to too many failed attempts
+ *
+ * @param {string} feedUrl
+ * @param {number} numFailed
+ * @param {string} title
*/
async onRSSFeedDisabled(feedUrl, numFailed, title) {
if (!Database.notificationSettings.isUseable) return
@@ -110,7 +110,7 @@ class NotificationManager {
return
}
- Logger.debug(`[NotificationManager] onRSSFeedDisabled: RSS feed disabled due to ${numFailed} failed updates for ${feedUrl}`)
+ Logger.debug(`[NotificationManager] onRSSFeedDisabled: Podcast scheduled episode download disabled due to ${numFailed} failed requests for ${feedUrl}`)
const eventData = {
feedUrl: feedUrl,
numFailed: numFailed || 0,
diff --git a/server/utils/notifications.js b/server/utils/notifications.js
index 304b4ffe..700d3c38 100644
--- a/server/utils/notifications.js
+++ b/server/utils/notifications.js
@@ -63,12 +63,12 @@ module.exports.notificationData = {
{
name: 'onRSSFeedFailed',
requiresLibrary: true,
- description: 'Triggered when an RSS feed request/update fails, but is not disabled',
+ description: 'Triggered when the RSS feed request fails for an automatic episode download',
descriptionKey: 'NotificationOnRSSFeedFailedDescription',
variables: ['feedUrl', 'numFailed', 'title'],
defaults: {
- title: 'RSS Feed Update Failed',
- body: 'Failed to update RSS feed for {{title}}.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
+ title: 'RSS Feed Request Failed',
+ body: 'Failed to request RSS feed for {{title}}.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
},
testData: {
title: 'Test RSS Feed',
@@ -79,12 +79,12 @@ module.exports.notificationData = {
{
name: 'onRSSFeedDisabled',
requiresLibrary: true,
- description: 'Triggered when an RSS feed is disabled due to too many failed attempts',
+ description: 'Triggered when automatic episode downloads are disabled due to too many failed attempts',
descriptionKey: 'NotificationOnRSSFeedDisabledDescription',
variables: ['feedUrl', 'numFailed', 'title'],
defaults: {
- title: 'RSS Feed Disabled',
- body: 'RSS feed for {{title}} has been disabled due to too many failed updates.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
+ title: 'Podcast Episode Download Schedule Disabled',
+ body: 'Automatic episode downloads for {{title}} have been disabled due to too many failed RSS feed requests.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
},
testData: {
title: 'Test RSS Feed',
From 0135b3560c1760cf582db0a040c7f2e0f93810a6 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Tue, 10 Jun 2025 17:02:42 -0500
Subject: [PATCH 063/121] Fix filesystem pathexists path join
---
server/controllers/FileSystemController.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js
index 7629f9ee..edfd869c 100644
--- a/server/controllers/FileSystemController.js
+++ b/server/controllers/FileSystemController.js
@@ -89,7 +89,6 @@ class FileSystemController {
}
const { directory, folderPath } = req.body
-
if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') {
Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`)
return res.status(400).json({
@@ -109,7 +108,8 @@ class FileSystemController {
return res.sendStatus(404)
}
- const filepath = Path.posix.join(libraryFolder.path, directory)
+ const filepath = Path.join(libraryFolder.path, directory)
+
// Ensure filepath is inside library folder (prevents directory traversal)
if (!filepath.startsWith(libraryFolder.path)) {
Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`)
From a6179942074c0990f9d4d9f0f88f111d3584a238 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Wed, 11 Jun 2025 08:12:23 +0200
Subject: [PATCH 064/121] added isbn
---
server/providers/Audible.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/server/providers/Audible.js b/server/providers/Audible.js
index e6816082..18879e91 100644
--- a/server/providers/Audible.js
+++ b/server/providers/Audible.js
@@ -41,7 +41,7 @@ class Audible {
}
cleanResult(item) {
- const { title, subtitle, asin, authors, narrators, publisherName, summary, releaseDate, image, genres, seriesPrimary, seriesSecondary, language, runtimeLengthMin, formatType } = item
+ const { title, subtitle, asin, authors, narrators, publisherName, summary, releaseDate, image, genres, seriesPrimary, seriesSecondary, language, runtimeLengthMin, formatType, isbn } = item
const series = []
if (seriesPrimary) {
@@ -70,6 +70,7 @@ class Audible {
description: summary || null,
cover: image,
asin,
+ isbn,
genres: genresFiltered.length ? genresFiltered : null,
tags: tagsFiltered.length ? tagsFiltered.join(', ') : null,
series: series.length ? series : null,
From aac01d6d9a2d1943d88daf5ce90587052dd5208e Mon Sep 17 00:00:00 2001
From: advplyr
Date: Wed, 11 Jun 2025 16:04:18 -0500
Subject: [PATCH 065/121] Update pathexists endpoint to check user has access
to library
---
server/controllers/FileSystemController.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js
index edfd869c..39663d23 100644
--- a/server/controllers/FileSystemController.js
+++ b/server/controllers/FileSystemController.js
@@ -108,6 +108,11 @@ class FileSystemController {
return res.sendStatus(404)
}
+ if (!req.user.checkCanAccessLibrary(libraryFolder.libraryId)) {
+ Logger.error(`[FileSystemController] User "${req.user.username}" attempting to check path exists for library "${libraryFolder.libraryId}" without access`)
+ return res.sendStatus(403)
+ }
+
const filepath = Path.join(libraryFolder.path, directory)
// Ensure filepath is inside library folder (prevents directory traversal)
From a6f10ca48e923e4418adbb486881ad3942c8771a Mon Sep 17 00:00:00 2001
From: advplyr
Date: Wed, 11 Jun 2025 16:14:51 -0500
Subject: [PATCH 066/121] Update upload endpoint to check user has access to
library
---
server/controllers/MiscController.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js
index 0e5ad141..c779bdd6 100644
--- a/server/controllers/MiscController.js
+++ b/server/controllers/MiscController.js
@@ -59,6 +59,12 @@ class MiscController {
if (!library) {
return res.status(404).send('Library not found')
}
+
+ if (!req.user.checkCanAccessLibrary(library.id)) {
+ Logger.error(`[MiscController] User "${req.user.username}" attempting to upload to library "${library.id}" without access`)
+ return res.sendStatus(403)
+ }
+
const folder = library.libraryFolders.find((fold) => fold.id === folderId)
if (!folder) {
return res.status(404).send('Folder not found')
From 22f6e86a12bd4a52f5cbddd76ea043cae3e3f3d0 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Wed, 11 Jun 2025 16:37:07 -0500
Subject: [PATCH 067/121] Fix pathexists filepath back to posix
---
server/controllers/FileSystemController.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js
index 39663d23..4b0a94b3 100644
--- a/server/controllers/FileSystemController.js
+++ b/server/controllers/FileSystemController.js
@@ -113,7 +113,8 @@ class FileSystemController {
return res.sendStatus(403)
}
- const filepath = Path.join(libraryFolder.path, directory)
+ let filepath = Path.join(libraryFolder.path, directory)
+ filepath = fileUtils.filePathToPOSIX(filepath)
// Ensure filepath is inside library folder (prevents directory traversal)
if (!filepath.startsWith(libraryFolder.path)) {
From b45085d2d6ca6b38253cd38f4c397a5eb715225d Mon Sep 17 00:00:00 2001
From: advplyr
Date: Thu, 12 Jun 2025 17:19:24 -0500
Subject: [PATCH 068/121] Update podcast episode download user agent to fix
#4401
---
server/utils/ffmpegHelpers.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js
index 9c88ef71..11c53929 100644
--- a/server/utils/ffmpegHelpers.js
+++ b/server/utils/ffmpegHelpers.js
@@ -108,7 +108,7 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => {
method: 'GET',
responseType: 'stream',
headers: {
- 'User-Agent': 'audiobookshelf (+https://audiobookshelf.org)'
+ 'User-Agent': 'audiobookshelf (+https://audiobookshelf.org; like iTMS)'
},
timeout: global.PodcastDownloadTimeout
}).catch((error) => {
From 777a055fcd4ec5b4f9f1912f30a4c68b51a6b402 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Thu, 12 Jun 2025 17:31:12 -0500
Subject: [PATCH 069/121] Update podcast episode downloads to have a fallback
user agent string
---
server/utils/ffmpegHelpers.js | 45 +++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 12 deletions(-)
diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js
index 11c53929..357a2084 100644
--- a/server/utils/ffmpegHelpers.js
+++ b/server/utils/ffmpegHelpers.js
@@ -103,18 +103,39 @@ module.exports.resizeImage = resizeImage
*/
module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => {
return new Promise(async (resolve) => {
- const response = await axios({
- url: podcastEpisodeDownload.url,
- method: 'GET',
- responseType: 'stream',
- headers: {
- 'User-Agent': 'audiobookshelf (+https://audiobookshelf.org; like iTMS)'
- },
- timeout: global.PodcastDownloadTimeout
- }).catch((error) => {
- Logger.error(`[ffmpegHelpers] Failed to download podcast episode with url "${podcastEpisodeDownload.url}"`, error)
- return null
- })
+ // Some podcasts fail due to user agent strings
+ // See: https://github.com/advplyr/audiobookshelf/issues/3246 (requires iTMS user agent)
+ // See: https://github.com/advplyr/audiobookshelf/issues/4401 (requires no iTMS user agent)
+ const userAgents = ['audiobookshelf (+https://audiobookshelf.org; like iTMS)', 'audiobookshelf (+https://audiobookshelf.org)']
+
+ let response = null
+ let lastError = null
+
+ for (const userAgent of userAgents) {
+ try {
+ response = await axios({
+ url: podcastEpisodeDownload.url,
+ method: 'GET',
+ responseType: 'stream',
+ headers: {
+ 'User-Agent': userAgent
+ },
+ timeout: global.PodcastDownloadTimeout
+ })
+
+ Logger.debug(`[ffmpegHelpers] Successfully connected with User-Agent: ${userAgent}`)
+ break
+ } catch (error) {
+ lastError = error
+ Logger.warn(`[ffmpegHelpers] Failed to download podcast episode with User-Agent "${userAgent}" for url "${podcastEpisodeDownload.url}"`, error.message)
+
+ // If this is the last attempt, log the full error
+ if (userAgent === userAgents[userAgents.length - 1]) {
+ Logger.error(`[ffmpegHelpers] All User-Agent attempts failed for url "${podcastEpisodeDownload.url}"`, lastError)
+ }
+ }
+ }
+
if (!response) {
return resolve({
success: false
From 6d0d1415e44b7ec4fe75806332a398ffda368821 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 13 Jun 2025 17:23:24 -0500
Subject: [PATCH 070/121] Add fuse.basic.min.js in libs instead of full npm
package, use lower threshold for quick matching
---
package-lock.json | 9 ---------
package.json | 1 -
server/libs/fusejs/index.js | 13 +++++++++++++
server/scanner/Scanner.js | 2 +-
server/utils/podcastUtils.js | 13 +++++++------
5 files changed, 21 insertions(+), 17 deletions(-)
create mode 100644 server/libs/fusejs/index.js
diff --git a/package-lock.json b/package-lock.json
index 9147d9e5..a8074794 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,7 +13,6 @@
"cookie-parser": "^1.4.6",
"express": "^4.17.1",
"express-session": "^1.17.3",
- "fuse.js": "^7.1.0",
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"lru-cache": "^10.0.3",
@@ -2106,14 +2105,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/fuse.js": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
- "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
diff --git a/package.json b/package.json
index 2a77ec87..d4831736 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,6 @@
"cookie-parser": "^1.4.6",
"express": "^4.17.1",
"express-session": "^1.17.3",
- "fuse.js": "^7.1.0",
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"lru-cache": "^10.0.3",
diff --git a/server/libs/fusejs/index.js b/server/libs/fusejs/index.js
new file mode 100644
index 00000000..23d7f045
--- /dev/null
+++ b/server/libs/fusejs/index.js
@@ -0,0 +1,13 @@
+/**
+ * Source: https://github.com/krisk/Fuse/blob/main/dist/fuse.basic.min.js
+ */
+
+/**
+ * Fuse.js v7.1.0 - Lightweight fuzzy-search (http://fusejs.io)
+ *
+ * Copyright (c) 2025 Kiro Risk (http://kiro.me)
+ * All Rights Reserved. Apache Software License 2.0
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:{},n=t.getFn,u=void 0===n?M.getFn:n,i=t.fieldNormWeight,o=void 0===i?M.fieldNormWeight:i;r(this,e),this.norm=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:3,n=new Map,r=Math.pow(10,t);return{get:function(t){var u=t.match(w).length;if(n.has(u))return n.get(u);var i=1/Math.pow(u,.5*e),o=parseFloat(Math.round(i*r)/r);return n.set(u,o),o},clear:function(){n.clear()}}}(o,3),this.getFn=u,this.isCreated=!1,this.setIndexRecords()}return i(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,d(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();d(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,u=void 0===r?M.getFn:r,i=n.fieldNormWeight,o=void 0===i?M.fieldNormWeight:i,a=new x({getFn:u,fieldNormWeight:o});return a.setKeys(e.map(B)),a.setSources(t),a.create(),a}function S(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,u=t.currentLocation,i=void 0===u?0:u,o=t.expectedLocation,a=void 0===o?0:o,c=t.distance,s=void 0===c?M.distance:c,h=t.ignoreLocation,l=void 0===h?M.ignoreLocation:h,f=r/e.length;if(l)return f;var d=Math.abs(a-i);return s?f+d/s:d?1:f}var _=32;function O(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},u=r.location,i=void 0===u?M.location:u,o=r.distance,a=void 0===o?M.distance:o,c=r.threshold,s=void 0===c?M.threshold:c,h=r.findAllMatches,l=void 0===h?M.findAllMatches:h,f=r.minMatchCharLength,d=void 0===f?M.minMatchCharLength:f,v=r.includeMatches,g=void 0===v?M.includeMatches:v,A=r.ignoreLocation,y=void 0===A?M.ignoreLocation:A;if(t.length>_)throw new Error("Pattern length exceeds max of ".concat(_,"."));for(var p,m=t.length,C=e.length,F=Math.max(0,Math.min(i,C)),E=s,B=F,D=d>1||g,b=D?Array(C):[];(p=e.indexOf(t,B))>-1;){var k=S(t,{currentLocation:p,expectedLocation:F,distance:a,ignoreLocation:y});if(E=Math.min(k,E),B=p+m,D)for(var w=0;w=W;$-=1){var K=$-1,J=n[e.charAt(K)];if(D&&(b[K]=+!!J),T[$]=(T[$+1]<<1|1)&J,I&&(T[$]|=(x[$+1]|x[$])<<1|1|x[$+1]),T[$]&j&&(L=S(t,{errors:I,currentLocation:K,expectedLocation:F,distance:a,ignoreLocation:y}))<=E){if(E=L,(B=K)<=F)break;W=Math.max(1,2*F-B)}}if(S(t,{errors:I+1,currentLocation:F,expectedLocation:F,distance:a,ignoreLocation:y})>E)break;x=T}var R={isMatch:B>=0,score:Math.max(.001,L)};if(D){var U=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:M.minMatchCharLength,n=[],r=-1,u=-1,i=0,o=e.length;i=t&&n.push([r,u]),r=-1)}return e[i-1]&&i-r>=t&&n.push([r,i-1]),n}(b,d);U.length?g&&(R.indices=U):R.isMatch=!1}return R}function j(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},i=u.location,o=void 0===i?M.location:i,a=u.threshold,c=void 0===a?M.threshold:a,s=u.distance,h=void 0===s?M.distance:s,l=u.includeMatches,f=void 0===l?M.includeMatches:l,d=u.findAllMatches,v=void 0===d?M.findAllMatches:d,g=u.minMatchCharLength,A=void 0===g?M.minMatchCharLength:g,y=u.isCaseSensitive,p=void 0===y?M.isCaseSensitive:y,m=u.ignoreDiacritics,C=void 0===m?M.ignoreDiacritics:m,F=u.ignoreLocation,E=void 0===F?M.ignoreLocation:F;if(r(this,e),this.options={location:o,threshold:c,distance:h,includeMatches:f,findAllMatches:v,minMatchCharLength:A,isCaseSensitive:p,ignoreDiacritics:C,ignoreLocation:E},t=p?t:t.toLowerCase(),t=C?I(t):t,this.pattern=t,this.chunks=[],this.pattern.length){var B=function(e,t){n.chunks.push({pattern:e,alphabet:j(e),startIndex:t})},D=this.pattern.length;if(D>_){for(var b=0,k=D%_,w=D-k;b-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function T(e,t){t.score=e.score}var $=function(){function e(n){var u=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2?arguments[2]:void 0;if(r(this,e),this.options=t(t({},M),u),this.options.useExtendedSearch)throw new Error("Extended search is not available");this._keyStore=new E(this.options.keys),this.setCollection(n,i)}return i(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof x))throw new Error("Incorrect 'index' type");this._myIndex=t||L(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}},{key:"add",value:function(e){A(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{}).limit,n=void 0===t?-1:t,r=this.options,u=r.includeMatches,i=r.includeScore,o=r.shouldSort,a=r.sortFn,c=r.ignoreFieldNorm,s=d(e)?d(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return function(e,t){var n=t.ignoreFieldNorm,r=void 0===n?M.ignoreFieldNorm:n;e.forEach((function(e){var t=1;e.matches.forEach((function(e){var n=e.key,u=e.norm,i=e.score,o=n?n.weight:null;t*=Math.pow(0===i&&o?Number.EPSILON:i,(o||1)*(r?1:u))})),e.score=t}))}(s,{ignoreFieldNorm:c}),o&&s.sort(a),v(n)&&n>-1&&(s=s.slice(0,n)),function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,u=void 0===r?M.includeMatches:r,i=n.includeScore,o=void 0===i?M.includeScore:i,a=[];return u&&a.push(z),o&&a.push(T),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}(s,this._docs,{includeMatches:u,includeScore:i})}},{key:"_searchStringList",value:function(e){var t=W(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,u=e.i,i=e.n;if(A(n)){var o=t.searchIn(n),a=o.isMatch,c=o.score,s=o.indices;a&&r.push({item:n,idx:u,matches:[{score:c,value:n,norm:i,indices:s}]})}})),r}},{key:"_searchLogical",value:function(e){throw new Error("Logical search is not available")}},{key:"_searchObjectList",value:function(e){var t=this,n=W(e,this.options),r=this._myIndex,u=r.keys,i=r.records,o=[];return i.forEach((function(e){var r=e.$,i=e.i;if(A(r)){var c=[];u.forEach((function(e,u){c.push.apply(c,a(t._findMatches({key:e,value:r[u],searcher:n})))})),c.length&&o.push({idx:i,item:r,matches:c})}})),o}},{key:"_findMatches",value:function(e){var t=e.key,n=e.value,r=e.searcher;if(!A(n))return[];var u=[];if(h(n))n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(A(n)){var a=r.searchIn(n),c=a.isMatch,s=a.score,h=a.indices;c&&u.push({score:s,key:t,value:n,idx:i,norm:o,indices:h})}}));else{var i=n.v,o=n.n,a=r.searchIn(i),c=a.isMatch,s=a.score,l=a.indices;c&&u.push({score:s,key:t,value:i,norm:o,indices:l})}return u}}]),e}();return $.version="7.1.0",$.createIndex=L,$.parseIndex=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?M.getFn:n,u=t.fieldNormWeight,i=void 0===u?M.fieldNormWeight:u,o=e.keys,a=e.records,c=new x({getFn:r,fieldNormWeight:i});return c.setKeys(o),c.setIndexRecords(a),c},$.config=M,$},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Fuse=t();
\ No newline at end of file
diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js
index 1e2751ed..206068cc 100644
--- a/server/scanner/Scanner.js
+++ b/server/scanner/Scanner.js
@@ -370,7 +370,7 @@ class Scanner {
let numEpisodesUpdated = 0
for (const episode of episodesToQuickMatch) {
- const episodeMatches = findMatchingEpisodesInFeed(feed, episode.title)
+ const episodeMatches = findMatchingEpisodesInFeed(feed, episode.title, 0.1)
if (episodeMatches?.length) {
const wasUpdated = await this.updateEpisodeWithMatch(episode, episodeMatches[0].episode, options)
if (wasUpdated) numEpisodesUpdated++
diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js
index 74a71cc1..12469160 100644
--- a/server/utils/podcastUtils.js
+++ b/server/utils/podcastUtils.js
@@ -1,9 +1,9 @@
const axios = require('axios')
const ssrfFilter = require('ssrf-req-filter')
const Logger = require('../Logger')
-const { xmlToJSON, levenshteinDistance, timestampToSeconds } = require('./index')
+const { xmlToJSON, timestampToSeconds } = require('./index')
const htmlSanitizer = require('../utils/htmlSanitizer')
-const Fuse = require('fuse.js')
+const Fuse = require('../libs/fusejs')
/**
* @typedef RssPodcastChapter
@@ -421,19 +421,20 @@ module.exports.findMatchingEpisodes = async (feedUrl, searchTitle) => {
*
* @param {RssPodcast} feed
* @param {string} searchTitle
+ * @param {number} [threshold=0.4] - 0.0 for perfect match, 1.0 for match anything
* @returns {Array<{ episode: RssPodcastEpisode }>}
*/
-module.exports.findMatchingEpisodesInFeed = (feed, searchTitle) => {
+module.exports.findMatchingEpisodesInFeed = (feed, searchTitle, threshold = 0.4) => {
if (!feed?.episodes) {
return null
}
const fuseOptions = {
ignoreDiacritics: true,
- threshold: 0.4, // default 0.6 return too many matches
+ threshold,
keys: [
- {name: 'title', weight: 0.7}, // prefer match in title
- {name: 'subtitle', weight: 0.3}
+ { name: 'title', weight: 0.7 }, // prefer match in title
+ { name: 'subtitle', weight: 0.3 }
]
}
const fuse = new Fuse(feed.episodes, fuseOptions)
From accd5d10968eae8ccc47afe20ad71ab04d8cc62c Mon Sep 17 00:00:00 2001
From: Arieh Kellermann
Date: Mon, 2 Jun 2025 17:51:07 +0200
Subject: [PATCH 071/121] Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/de.json b/client/strings/de.json
index 55b33d8a..98fdba33 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -858,7 +858,7 @@
"MessageSelected": "{0} ausgewählt",
"MessageSeriesSequenceCannotContainSpaces": "Serie Abfolge kann keine Leerzeichen enthalten",
"MessageServerCouldNotBeReached": "Server kann nicht erreicht werden",
- "MessageSetChaptersFromTracksDescription": "Kaitelerstellung basiert auf den existierenden einzelnen Audiodateien. Pro existierende Audiodatei wird 1 Kapitel erstellt, wobei deren Kapitelname aus dem Audiodateinamen extrahiert wird",
+ "MessageSetChaptersFromTracksDescription": "Kapitelerstellung basiert auf den existierenden einzelnen Audiodateien. Pro existierende Audiodatei wird 1 Kapitel erstellt, wobei deren Kapitelname aus dem Audiodateinamen extrahiert wird",
"MessageShareExpirationWillBe": "Läuft am {0} ab",
"MessageShareExpiresIn": "Läuft in {0} ab",
"MessageShareURLWillBe": "Der Freigabe Link wird {0} sein",
From 204ac4f204000769436387c19e743698476779b0 Mon Sep 17 00:00:00 2001
From: Anders Norman
Date: Sun, 1 Jun 2025 20:27:08 +0200
Subject: [PATCH 072/121] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?=
=?UTF-8?q?an=20Bokm=C3=A5l)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently translated at 92.6% (1024 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
---
client/strings/no.json | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/client/strings/no.json b/client/strings/no.json
index 642701ef..773d8d1f 100644
--- a/client/strings/no.json
+++ b/client/strings/no.json
@@ -8,7 +8,7 @@
"ButtonAddYourFirstLibrary": "Legg til ditt første bibliotek",
"ButtonApply": "Bruk",
"ButtonApplyChapters": "Bruk kapittel",
- "ButtonAuthors": "Forfatter",
+ "ButtonAuthors": "Forfattere",
"ButtonBack": "Tilbake",
"ButtonBrowseForFolder": "Bla gjennom mappe",
"ButtonCancel": "Avbryt",
@@ -175,6 +175,7 @@
"HeaderPlaylist": "Spilleliste",
"HeaderPlaylistItems": "Spillelisteelement",
"HeaderPodcastsToAdd": "Podcaster å legge til",
+ "HeaderPresets": "Forhåndsinnstillinger",
"HeaderPreviewCover": "Forhåndsvis omslag",
"HeaderRSSFeedGeneral": "RSS Detailer",
"HeaderRSSFeedIsOpen": "RSS Feed er åpen",
@@ -217,6 +218,7 @@
"LabelAccountTypeAdmin": "Administrator",
"LabelAccountTypeGuest": "Gjest",
"LabelAccountTypeUser": "Bruker",
+ "LabelActivities": "Aktiviteter",
"LabelActivity": "Aktivitet",
"LabelAddToCollection": "Legg til i samling",
"LabelAddToCollectionBatch": "Legg {0} bøker til samling",
@@ -226,6 +228,7 @@
"LabelAddedDate": "La til {0}",
"LabelAdminUsersOnly": "Kun administratorer",
"LabelAll": "Alle",
+ "LabelAllEpisodesDownloaded": "Alle nedlastede episoder",
"LabelAllUsers": "Alle brukere",
"LabelAllUsersExcludingGuests": "Alle brukere bortsett fra gjester",
"LabelAllUsersIncludingGuests": "Alle brukere inkludert gjester",
@@ -281,6 +284,7 @@
"LabelContinueSeries": "Fortsett serier",
"LabelCover": "Omslag",
"LabelCoverImageURL": "Omslagsbilde URL",
+ "LabelCoverProvider": "Tilbyder av omslagsbilde",
"LabelCreatedAt": "Dato opprettet",
"LabelCronExpression": "Cron uttrykk",
"LabelCurrent": "Nåværende",
@@ -389,6 +393,7 @@
"LabelIntervalEvery6Hours": "Hver 6. timer",
"LabelIntervalEveryDay": "Hver dag",
"LabelIntervalEveryHour": "Hver time",
+ "LabelIntervalEveryMinute": "Hvert minutt",
"LabelInvert": "Inverter",
"LabelItem": "Enhet",
"LabelJumpBackwardAmount": "Hopp bakover med",
@@ -464,6 +469,7 @@
"LabelNotificationsMaxQueueSizeHelp": "Hendelser er begrenset til avfyre én gang per sekund. Hendelser blir ignorert om køen er full. Dette forhindrer overflod av varslinger.",
"LabelNumberOfBooks": "Antall bøker",
"LabelNumberOfEpisodes": "Antall episoder",
+ "LabelOpenIDAdvancedPermsClaimDescription": "Navnet på OpenID claim'et som inneholder avanserte tilganger for brukerhandlinger i applikasjonen som vil brukes for ikke-administratorroller (hvis konfigurert). Hvis claim'et mangler fra responsen, nektes tilgang til ABS. Hvis en enkelt opsjon mangler, blir behandlet som false. Påse at identitetstilbyderens claim stemmer overens med den forventede strukturen:",
"LabelOpenIDClaims": "La følge valg være tomme for å slå av avanserte gruppe og tillatelser. Gruppen \"Bruker\" vil da også automatisk legges til.",
"LabelOpenRSSFeed": "Åpne RSS Feed",
"LabelOverwrite": "Overskriv",
@@ -521,6 +527,7 @@
"LabelReleaseDate": "Utgivelsesdato",
"LabelRemoveAllMetadataAbs": "Fjern alle metadata.abs filer",
"LabelRemoveAllMetadataJson": "Fjern alle metadata.json filer",
+ "LabelRemoveAudibleBranding": "Fjern Audible inn- og utledning fra kapitler",
"LabelRemoveCover": "Fjern omslag",
"LabelRemoveMetadataFile": "Fjern metadata-filer fra biblioteks-mapper",
"LabelRemoveMetadataFileHelp": "Fjern alle metadata.json og metadata.abs i alle {0} mappene.",
@@ -550,6 +557,8 @@
"LabelSettingsBookshelfViewHelp": "Skeuomorf design med hyller av ved",
"LabelSettingsChromecastSupport": "Chromecast støtte",
"LabelSettingsDateFormat": "Dato Format",
+ "LabelSettingsEnableWatcher": "Skann biblioteker automatisk for endringer",
+ "LabelSettingsEnableWatcherForLibrary": "Skann bibliotek automatisk for endringer",
"LabelSettingsEnableWatcherHelp": "Aktiverer automatisk opprettelse/oppdatering av enheter når filendringer er oppdaget. *Krever restart av server*",
"LabelSettingsEpubsAllowScriptedContent": "Tillat scripting i innholdet i ebub-bøker",
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillat epub-filer å kjøre script. Det er anbefalt å slå av denne innstillingen med mindre du stoler på kilden til epub-filene.",
@@ -593,6 +602,7 @@
"LabelSlug": "Slug",
"LabelSortAscending": "Stigende",
"LabelSortDescending": "Synkende",
+ "LabelSortPubDate": "Sorter etter publiseringsdato",
"LabelStart": "Start",
"LabelStartTime": "Start Tid",
"LabelStarted": "Startet",
@@ -693,6 +703,8 @@
"LabelYourProgress": "Din fremgang",
"MessageAddToPlayerQueue": "Legg til i kø",
"MessageAppriseDescription": "For å bruke denne funksjonen trenger du en instans av Apprise API kjørende eller et API som håndterer disse forespørslene. Apprise API URL skal være hele URL-en til varslingen, f.eks., hvis din API-instans er på http://192.168.1.1:8337 så skal du bruke http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "Påse at du bruker ASIN fra den riktige Audible-regionen, ikke Amazon.",
+ "MessageAuthenticationOIDCChangesRestart": "Etter å ha lagret, start serveren din på nytt for at OIDC-endringene skal tre i kraft.",
"MessageBackupsDescription": "Sikkerhetskopier inkluderer, brukerfremgang, detaljer om bibliotekgjenstander, tjener instillinger og bilder lagret under /metadata/items og /metadata/authors. Sikkerhetskopier vil ikke inkludere filer som er lagret i bibliotek mappene.",
"MessageBackupsLocationEditNote": "Viktig: Endring av mappen for sikkerhetskopi hverken endrer eller flytter eksisterende sikkerhetskopier!",
"MessageBackupsLocationNoEditNote": "NB: Mappen for sikkerhetskopi settes i en miljøvariabel og kan ikke endres her.",
From c2809808c3626cee3e8888378bb23ae65922dc64 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: Wed, 4 Jun 2025 11:11:22 +0200
Subject: [PATCH 073/121] Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1105 of 1105 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, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/uk.json b/client/strings/uk.json
index 51e5f69e..25ea4368 100644
--- a/client/strings/uk.json
+++ b/client/strings/uk.json
@@ -514,7 +514,7 @@
"LabelPublishers": "Видавці",
"LabelRSSFeedCustomOwnerEmail": "Користувацька електронна адреса власника",
"LabelRSSFeedCustomOwnerName": "Користувацьке ім'я власника",
- "LabelRSSFeedOpen": "RSS-канал відкрито",
+ "LabelRSSFeedOpen": "RSS-канал відкритий",
"LabelRSSFeedPreventIndexing": "Запобігати індексації",
"LabelRSSFeedSlug": "Назва RSS-каналу",
"LabelRSSFeedURL": "Адреса RSS-каналу",
From 5315f65023345e5f0cc5fc11f30ddc9aeec03c3f Mon Sep 17 00:00:00 2001
From: thehijacker
Date: Fri, 6 Jun 2025 08:41:26 +0200
Subject: [PATCH 074/121] Translated using Weblate (Slovenian)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
---
client/strings/sl.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/sl.json b/client/strings/sl.json
index 2f028403..43039624 100644
--- a/client/strings/sl.json
+++ b/client/strings/sl.json
@@ -514,7 +514,7 @@
"LabelPublishers": "Izdajatelji",
"LabelRSSFeedCustomOwnerEmail": "E-pošta lastnika po meri",
"LabelRSSFeedCustomOwnerName": "Ime lastnika po meri",
- "LabelRSSFeedOpen": "Odprt vir RSS",
+ "LabelRSSFeedOpen": "RSS vir je odprt",
"LabelRSSFeedPreventIndexing": "Prepreči indeksiranje",
"LabelRSSFeedSlug": "Slug RSS vira",
"LabelRSSFeedURL": "URL vira RSS",
From fe99a269bc30031ce7773e61724679a8b1fa3058 Mon Sep 17 00:00:00 2001
From: max grakov
Date: Sat, 7 Jun 2025 16:11:37 +0200
Subject: [PATCH 075/121] Translated using Weblate (Russian)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
---
client/strings/ru.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/strings/ru.json b/client/strings/ru.json
index 07a786a1..056245ac 100644
--- a/client/strings/ru.json
+++ b/client/strings/ru.json
@@ -348,7 +348,7 @@
"LabelExpandSubSeries": "Развернуть подсерию",
"LabelExplicit": "18+",
"LabelExplicitChecked": "18+ (отмечено)",
- "LabelExplicitUnchecked": "Не явно (не отмечено)",
+ "LabelExplicitUnchecked": "+18 (не отмечено)",
"LabelExportOPML": "Экспорт OPML",
"LabelFeedURL": "URL канала",
"LabelFetchingMetadata": "Извлечение метаданных",
@@ -514,7 +514,7 @@
"LabelPublishers": "Издатели",
"LabelRSSFeedCustomOwnerEmail": "Пользовательский Email владельца",
"LabelRSSFeedCustomOwnerName": "Пользовательское Имя владельца",
- "LabelRSSFeedOpen": "Открыть RSS-канал",
+ "LabelRSSFeedOpen": "Открыть RSS-ленту",
"LabelRSSFeedPreventIndexing": "Запретить индексирование",
"LabelRSSFeedSlug": "Встроить RSS-канал",
"LabelRSSFeedURL": "URL RSS-канала",
From 76e7616439bed69ca161b47438ae2384e7d8d061 Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Sat, 7 Jun 2025 16:07:45 +0200
Subject: [PATCH 076/121] Translated using Weblate (Arabic)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index a1383047..123cc343 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -514,7 +514,7 @@
"LabelPublishers": "الناشرون",
"LabelRSSFeedCustomOwnerEmail": "البريد الالكتروني المخصص للمالك",
"LabelRSSFeedCustomOwnerName": "الاسم المخصص للمالك",
- "LabelRSSFeedOpen": "فتح تغذية RSS",
+ "LabelRSSFeedOpen": "موجز RSS مفتوح",
"LabelRSSFeedPreventIndexing": "منع الفهرسة",
"LabelRSSFeedSlug": "اسم تعريف تغذية RSS",
"LabelRSSFeedURL": "رابط تغذية RSS",
From d0af82e71aa35fefc080ecaf183d77a9dcab278d Mon Sep 17 00:00:00 2001
From: Grzegorz Orlowski
Date: Sun, 8 Jun 2025 23:01:37 +0200
Subject: [PATCH 077/121] Translated using Weblate (Polish)
Currently translated at 76.9% (850 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
---
client/strings/pl.json | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/client/strings/pl.json b/client/strings/pl.json
index e1822755..b9d7cfe3 100644
--- a/client/strings/pl.json
+++ b/client/strings/pl.json
@@ -329,7 +329,9 @@
"LabelEpisode": "Odcinek",
"LabelEpisodeTitle": "Tytuł odcinka",
"LabelEpisodeType": "Typ odcinka",
+ "LabelEpisodeUrlFromRssFeed": "Adres URL odcinka z kanału RSS",
"LabelEpisodes": "Epizody",
+ "LabelEpisodic": "Epizodyczny",
"LabelExample": "Przykład",
"LabelExpandSeries": "Rozwiń serie",
"LabelExpandSubSeries": "Rozwiń podserie",
@@ -357,6 +359,7 @@
"LabelFontScale": "Rozmiar czcionki",
"LabelFontStrikethrough": "Przekreślony",
"LabelFormat": "Format",
+ "LabelFull": "Pełny",
"LabelGenre": "Gatunek",
"LabelGenres": "Gatunki",
"LabelHardDeleteFile": "Usuń trwale plik",
@@ -381,6 +384,7 @@
"LabelIntervalEvery6Hours": "Co 6 godzin",
"LabelIntervalEveryDay": "Każdego dnia",
"LabelIntervalEveryHour": "Każdej godziny",
+ "LabelIntervalEveryMinute": "Co minutę",
"LabelInvert": "Inversja",
"LabelItem": "Pozycja",
"LabelJumpBackwardAmount": "Przeskocz do tyłu o:",
@@ -412,6 +416,9 @@
"LabelLowestPriority": "Najniższy priorytet",
"LabelMatchExistingUsersBy": "Dopasuje istniejących użytkowników poprzez",
"LabelMatchExistingUsersByDescription": "Służy do łączenia istniejących użytkowników. Po połączeniu użytkownicy zostaną dopasowani za pomocą unikalnego identyfikatora od dostawcy SSO",
+ "LabelMaxEpisodesToDownload": "Maksymalna liczba odcinków do pobrania. Użyj 0, aby wyłączyć ograniczenie.",
+ "LabelMaxEpisodesToKeep": "Maksymalna liczba odcinków do zachowania",
+ "LabelMaxEpisodesToKeepHelp": "Wartość 0 wyłącza maksymalny limit. Po automatycznym pobraniu nowego odcinka, najstarszy odcinek zostanie usunięty, jeśli masz ich więcej niż X. Spowoduje to usunięcie tylko 1 odcinka na nowe pobieranie.",
"LabelMediaPlayer": "Odtwarzacz",
"LabelMediaType": "Typ mediów",
"LabelMetaTag": "Tag",
@@ -424,6 +431,7 @@
"LabelMissingEbook": "Nie posiada ebooka",
"LabelMissingSupplementaryEbook": "Nie posiada dodatkowego ebooka",
"LabelMobileRedirectURIs": "Dozwolone URI przekierowań mobilnych",
+ "LabelMobileRedirectURIsDescription": "To jest biała lista prawidłowych adresów URI przekierowań dla aplikacji mobilnych. Domyślny adres to audiobookshelf://oauth, który można usunąć lub dodać inne adresy URI w celu integracji z aplikacjami innych firm. Użycie gwiazdki (*) jako jedynego wpisu zezwala na dowolny URI.",
"LabelMore": "Więcej",
"LabelMoreInfo": "Więcej informacji",
"LabelName": "Nazwa",
@@ -453,12 +461,14 @@
"LabelNumberOfEpisodes": "# Odcinków",
"LabelOpenRSSFeed": "Otwórz kanał RSS",
"LabelOverwrite": "Nadpisz",
+ "LabelPaginationPageXOfY": "Strona {0} z {1}",
"LabelPassword": "Hasło",
"LabelPath": "Ścieżka",
"LabelPermanent": "Stałe",
"LabelPermissionsAccessAllLibraries": "Ma dostęp do wszystkich bibliotek",
"LabelPermissionsAccessAllTags": "Ma dostęp do wszystkich tagów",
"LabelPermissionsAccessExplicitContent": "Ma dostęp do treści oznacznych jako nieprzyzwoite",
+ "LabelPermissionsCreateEreader": "Możliwość stworzenia czytnika e-booków",
"LabelPermissionsDelete": "Ma możliwość usuwania",
"LabelPermissionsDownload": "Ma możliwość pobierania",
"LabelPermissionsUpdate": "Ma możliwość aktualizowania",
@@ -466,16 +476,22 @@
"LabelPersonalYearReview": "Podsumowanie twojego roku ({0})",
"LabelPhotoPathURL": "Scieżka/URL do zdjęcia",
"LabelPlayMethod": "Metoda odtwarzania",
+ "LabelPlayerChapterNumberMarker": "{0} z {1}",
"LabelPlaylists": "Listy odtwarzania",
+ "LabelPodcast": "Podcast",
"LabelPodcastSearchRegion": "Obszar wyszukiwania podcastów",
+ "LabelPodcastType": "Typ podcastu",
"LabelPodcasts": "Podcasty",
+ "LabelPort": "Port",
"LabelPrefixesToIgnore": "Ignorowane prefiksy (wielkość liter nie ma znaczenia)",
"LabelPreventIndexing": "Zapobiega indeksowaniu przez iTunes i Google",
"LabelPrimaryEbook": "Główny ebook",
"LabelProgress": "Postęp",
"LabelProvider": "Dostawca",
+ "LabelProviderAuthorizationValue": "Wartość nagłówka autoryzacji",
"LabelPubDate": "Data publikacji",
"LabelPublishYear": "Rok publikacji",
+ "LabelPublishedDate": "Opublikowano {0}",
"LabelPublisher": "Wydawca",
"LabelPublishers": "Wydawcy",
"LabelRSSFeedOpen": "RSS Feed otwarty",
@@ -490,15 +506,22 @@
"LabelRecentlyAdded": "Niedawno dodane",
"LabelRecommended": "Polecane",
"LabelRedo": "Wycofaj",
+ "LabelRegion": "Region",
"LabelReleaseDate": "Data wydania",
+ "LabelRemoveAllMetadataAbs": "Usuń wszystkie pliki metadata.abs",
+ "LabelRemoveAllMetadataJson": "Usuń wszystkie pliki metadata.json",
"LabelRemoveCover": "Usuń okładkę",
+ "LabelRemoveMetadataFile": "Usuń pliki metadanych z folderów biblioteki",
+ "LabelRemoveMetadataFileHelp": "Usuń wszystkie pliki metadata.json i metadata.abs z {0} folderów.",
"LabelRowsPerPage": "Wierszy na stronę",
"LabelSearchTerm": "Wyszukiwanie frazy",
"LabelSearchTitle": "Wyszukaj tytuł",
"LabelSearchTitleOrASIN": "Szukaj tytuł lub ASIN",
"LabelSeason": "Sezon",
+ "LabelSeasonNumber": "Sezon #{0}",
"LabelSelectAll": "Wybierz wszystko",
"LabelSelectAllEpisodes": "Wybierz wszystkie odcinki",
+ "LabelSelectEpisodesShowing": "Wybierz {0} wyświetlanych odcinków",
"LabelSelectUsers": "Wybór użytkowników",
"LabelSendEbookToDevice": "Wyślij ebook do...",
"LabelSequence": "Kolejność",
@@ -513,6 +536,8 @@
"LabelSettingsBookshelfViewHelp": "Widok półki z książkami",
"LabelSettingsChromecastSupport": "Wsparcie Chromecast",
"LabelSettingsDateFormat": "Format daty",
+ "LabelSettingsEnableWatcher": "Automatyczne skanowanie bibliotek w poszukiwaniu zmian",
+ "LabelSettingsEnableWatcherForLibrary": "Automatyczne skanowanie biblioteki w poszukiwaniu zmian",
"LabelSettingsEnableWatcherHelp": "Włącza automatyczne dodawanie/aktualizację pozycji gdy wykryte zostaną zmiany w plikach. Wymaga restartu serwera",
"LabelSettingsEpubsAllowScriptedContent": "Zezwalanie na skrypty w plikach epub",
"LabelSettingsEpubsAllowScriptedContentHelp": "Zezwala plikom epub na wykonywanie skryptów. Zaleca się mieć to ustawienie wyłączone, chyba że ma się zaufanie do źródła plików epub.",
@@ -524,6 +549,8 @@
"LabelSettingsHideSingleBookSeriesHelp": "Serie, które posiadają tylko jedną książkę, nie będą pokazywane na stronie z seriami i na stronie domowej z półkami.",
"LabelSettingsHomePageBookshelfView": "Widok półki z książkami na stronie głównej",
"LabelSettingsLibraryBookshelfView": "Widok półki z książkami na stronie biblioteki",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "Oznacz element multimedialny jako ukończony, gdy",
+ "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Pomiń poprzednie książki przy kontynuacji serii",
"LabelSettingsParseSubtitles": "Przetwarzaj podtytuły",
"LabelSettingsParseSubtitlesHelp": "Opcja pozwala na pobranie podtytułu z nazwy folderu z audiobookiem. Podtytuł musi być rozdzielony za pomocą separatora \" - \" Przykład: \"Book Title - A Subtitle Here\" podtytuł \"A Subtitle Here\"",
"LabelSettingsPreferMatchedMetadata": "Preferowanie dopasowanych metadanych",
@@ -547,6 +574,9 @@
"LabelShowSubtitles": "Pokaż Napisy",
"LabelSize": "Rozmiar",
"LabelSleepTimer": "Wyłącznik czasowy",
+ "LabelSortAscending": "Rosnąco",
+ "LabelSortDescending": "Malejąco",
+ "LabelSortPubDate": "Sortuj według daty publikacji",
"LabelStart": "Rozpocznij",
"LabelStartTime": "Czas rozpoczęcia",
"LabelStarted": "Rozpoczęty",
@@ -568,14 +598,21 @@
"LabelStatsWeekListening": "Tydzień słuchania",
"LabelSubtitle": "Podtytuł",
"LabelSupportedFileTypes": "Obsługiwane typy plików",
+ "LabelTag": "Znacznik",
"LabelTags": "Tagi",
"LabelTagsAccessibleToUser": "Tagi dostępne dla użytkownika",
+ "LabelTagsNotAccessibleToUser": "Znaczniki niedostępne dla użytkownika",
+ "LabelTasks": "Uruchomione zadania",
+ "LabelTextEditorLink": "Link",
+ "LabelTextEditorNumberedList": "Lista numerowana",
+ "LabelTextEditorUnlink": "Usuń link",
"LabelThemeDark": "Ciemny",
"LabelThemeLight": "Jasny",
"LabelTimeDurationXHours": "{0} godzin",
"LabelTimeDurationXMinutes": "{0} minuty",
"LabelTimeDurationXSeconds": "{0} sekundy",
"LabelTimeInMinutes": "Czas w minutach",
+ "LabelTimeLeft": "pozostało {0}",
"LabelTimeListened": "Czas odtwarzania",
"LabelTimeListenedToday": "Czas odtwarzania dzisiaj",
"LabelTimeRemaining": "Pozostało {0}",
@@ -583,6 +620,7 @@
"LabelTitle": "Tytuł",
"LabelToolsEmbedMetadata": "Załącz metadane",
"LabelToolsEmbedMetadataDescription": "Załącz metadane do plików audio (okładkę oraz znaczniki rozdziałów).",
+ "LabelToolsM4bEncoder": "Enkoder M4B",
"LabelToolsMakeM4b": "Generuj plik M4B",
"LabelToolsMakeM4bDescription": "Tworzy plik w formacie .M4B, który zawiera metadane, okładkę oraz rozdziały.",
"LabelToolsSplitM4b": "Podziel plik .M4B na pliki .MP3",
@@ -595,12 +633,14 @@
"LabelType": "Typ",
"LabelUndo": "Wycofaj",
"LabelUnknown": "Nieznany",
+ "LabelUnknownPublishDate": "Nieznana data publikacji",
"LabelUpdateCover": "Zaktalizuj odkładkę",
"LabelUpdateCoverHelp": "Umożliwienie nadpisania istniejących okładek dla wybranych książek w przypadku znalezienia dopasowania",
"LabelUpdateDetails": "Zaktualizuj szczegóły",
"LabelUpdateDetailsHelp": "Umożliwienie nadpisania istniejących szczegółów dla wybranych książek w przypadku znalezienia dopasowania",
"LabelUpdatedAt": "Zaktualizowano",
"LabelUploaderDragAndDrop": "Przeciągnij i puść foldery lub pliki",
+ "LabelUploaderDragAndDropFilesOnly": "Przeciągnij i upuść pliki",
"LabelUploaderDropFiles": "Puść pliki",
"LabelUploaderItemFetchMetadataHelp": "Automatycznie pobierz tytuł, autora i serie",
"LabelUseChapterTrack": "Użyj ścieżki rozdziału",
From 1c6f99b876d4f36957eeeb0b5a05e196392bf849 Mon Sep 17 00:00:00 2001
From: Plazec
Date: Mon, 9 Jun 2025 18:29:55 +0200
Subject: [PATCH 078/121] Translated using Weblate (Czech)
Currently translated at 99.7% (1102 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
---
client/strings/cs.json | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/client/strings/cs.json b/client/strings/cs.json
index b17075b0..fca6bf33 100644
--- a/client/strings/cs.json
+++ b/client/strings/cs.json
@@ -154,7 +154,7 @@
"HeaderListeningSessions": "Poslechové relace",
"HeaderListeningStats": "Statistiky poslechu",
"HeaderLogin": "Přihlásit",
- "HeaderLogs": "Záznamy",
+ "HeaderLogs": "Logy",
"HeaderManageGenres": "Spravovat žánry",
"HeaderManageTags": "Spravovat štítky",
"HeaderMapDetails": "Podrobnosti mapování",
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Seznam skladeb",
"HeaderPlaylistItems": "Položky seznamu přehrávání",
"HeaderPodcastsToAdd": "Podcasty k přidání",
+ "HeaderPresets": "Předvolba",
"HeaderPreviewCover": "Náhled obálky",
"HeaderRSSFeedGeneral": "Podrobnosti o RSS",
"HeaderRSSFeedIsOpen": "Informační kanál RSS je otevřený",
@@ -513,9 +514,9 @@
"LabelPublishers": "Vydavatelé",
"LabelRSSFeedCustomOwnerEmail": "Vlastní e-mail vlastníka",
"LabelRSSFeedCustomOwnerName": "Vlastní jméno vlastníka",
- "LabelRSSFeedOpen": "Otevření RSS kanálu",
+ "LabelRSSFeedOpen": "RSS kanál otevřen",
"LabelRSSFeedPreventIndexing": "Zabránit indexování",
- "LabelRSSFeedSlug": "RSS kanál Slug",
+ "LabelRSSFeedSlug": "Klíčové slovo kanálu RSS",
"LabelRSSFeedURL": "URL RSS kanálu",
"LabelRandomly": "Náhodně",
"LabelReAddSeriesToContinueListening": "Znovu přidat sérii k pokračování poslechu",
@@ -530,6 +531,7 @@
"LabelReleaseDate": "Datum vydání",
"LabelRemoveAllMetadataAbs": "Odebrat všechny soubory metadata.abs",
"LabelRemoveAllMetadataJson": "Smazat všechny soubory metadata.json",
+ "LabelRemoveAudibleBranding": "Odebrat úvod a závěr Audible z kapitol",
"LabelRemoveCover": "Odstranit obálku",
"LabelRemoveMetadataFile": "Odstranit soubory metadat ve složkách položek knihovny",
"LabelRemoveMetadataFileHelp": "Odstraníte všechny soubory metadata.json a metadata.abs ve svých složkách {0}.",
@@ -549,7 +551,7 @@
"LabelSeries": "Série",
"LabelSeriesName": "Název série",
"LabelSeriesProgress": "Průběh série",
- "LabelServerLogLevel": "Úroveň protokolu serveru",
+ "LabelServerLogLevel": "Úroveň Logování serveru",
"LabelServerYearReview": "Přehled roku na serveru ({0})",
"LabelSetEbookAsPrimary": "Nastavit jako primární",
"LabelSetEbookAsSupplementary": "Nastavit jako doplňkové",
@@ -706,6 +708,7 @@
"MessageAddToPlayerQueue": "Přidat do fronty přehrávače",
"MessageAppriseDescription": "Abyste mohli používat tuto funkci, musíte mít spuštěnou instanci Apprise API nebo API, které bude zpracovávat stejné požadavky. Adresa URL API Apprise by měla být úplná URL cesta pro odeslání oznámení, např. pokud je vaše instance API obsluhována na adrese http://192.168.1.1:8337 pak byste měli zadat http://192.168.1.1:8337/notify.",
"MessageAsinCheck": "Ujistěte se, že používáte ASIN ze správného regionu Audible a ne z Amazonu.",
+ "MessageAuthenticationOIDCChangesRestart": "Po uložení restartujte server, aby se změny OIDC použily.",
"MessageBackupsDescription": "Zálohy zahrnují uživatele, průběh uživatele, podrobnosti o položkách knihovny, nastavení serveru a obrázky uložené v /metadata/items a /metadata/authors. Zálohy ne zahrnují všechny soubory uložené ve složkách knihovny.",
"MessageBackupsLocationEditNote": "Poznámka: Změna umístění záloh nepřesune ani nezmění existující zálohy",
"MessageBackupsLocationNoEditNote": "Poznámka: Umístění záloh je nastavené z proměnných prostředí a nelze zde změnit.",
@@ -787,7 +790,7 @@
"MessageJoinUsOn": "Přidejte se k nám",
"MessageLoading": "Načítá se...",
"MessageLoadingFolders": "Načítám složky...",
- "MessageLogsDescription": "Protokoly se ukládají do souborů JSON v /metadata/logs. Protokoly o pádech jsou uloženy v /metadata/logs/crash_logs.txt.",
+ "MessageLogsDescription": "Logy se ukládají do souborů JSON v /metadata/logs. Logy o pádech jsou uloženy v /metadata/logs/crash_logs.txt.",
"MessageM4BFailed": "M4B se nezdařil!",
"MessageM4BFinished": "M4B dokončen!",
"MessageMapChapterTitles": "Mapování názvů kapitol ke stávajícím kapitolám audioknihy bez úpravy časových razítek",
@@ -811,11 +814,11 @@
"MessageNoEpisodes": "Žádné epizody",
"MessageNoFoldersAvailable": "Nejsou k dispozici žádné složky",
"MessageNoGenres": "Žádné žánry",
- "MessageNoIssues": "Žádné výtisk",
+ "MessageNoIssues": "Žádné problémy",
"MessageNoItems": "Žádné položky",
"MessageNoItemsFound": "Nebyly nalezeny žádné položky",
"MessageNoListeningSessions": "Žádné poslechové relace",
- "MessageNoLogs": "Žádné protokoly",
+ "MessageNoLogs": "Žádné logy",
"MessageNoMediaProgress": "Žádný průběh médií",
"MessageNoNotifications": "Žádná oznámení",
"MessageNoPodcastFeed": "Neplatný podcast: Žádný kanál",
@@ -853,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Spusť každý {0} v {1}",
"MessageSearchResultsFor": "Výsledky hledání pro",
"MessageSelected": "{0} vybráno",
+ "MessageSeriesSequenceCannotContainSpaces": "Sekvence série nesmí obsahovat mezery",
"MessageServerCouldNotBeReached": "Server je nedostupný",
"MessageSetChaptersFromTracksDescription": "Nastavit kapitoly jako kapitolu a název kapitoly jako název zvukového souboru",
"MessageShareExpirationWillBe": "Expiruje {0}",
@@ -971,6 +975,8 @@
"ToastCachePurgeFailed": "Nepodařilo se vyčistit mezipaměť",
"ToastCachePurgeSuccess": "Vyrovnávací paměť úspěšně vyčištěna",
"ToastChaptersHaveErrors": "Kapitoly obsahují chyby",
+ "ToastChaptersInvalidShiftAmountLast": "Nesprávná délka posunu. Čas začátku poslední kapitoly by přesáhl dobu trvání této audioknihy.",
+ "ToastChaptersInvalidShiftAmountStart": "Nesprávná délka posunu. První kapitola by měla nulovou nebo zápornou délku a byla by přepsána druhou kapitolou. Zvětšete počáteční délku druhé kapitoly.",
"ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy",
"ToastChaptersRemoved": "Kapitoly odstraněny",
"ToastChaptersUpdated": "Kapitola aktualizována",
@@ -1091,7 +1097,7 @@
"ToastUnlinkOpenIdFailed": "Chyba při odpárování uživatele z OpenID",
"ToastUnlinkOpenIdSuccess": "Uživatel odpárován z uživatele z OpenID",
"ToastUploaderFilepathExistsError": "Soubor \"{0}\" na serveru již existuje",
- "ToastUploaderItemExistsInSubdirectoryError": "Položka \"{0}\" používá podsložku nahrávané cesty.",
+ "ToastUploaderItemExistsInSubdirectoryError": "Položka \"{0}\" používá podadresář cesty pro nahrání.",
"ToastUserDeleteFailed": "Nepodařilo se smazat uživatele",
"ToastUserDeleteSuccess": "Uživatel smazán",
"ToastUserPasswordChangeSuccess": "Heslo bylo změněno úspěšně",
From c5e5e516afd89a64501b4efb84c4b62db8147539 Mon Sep 17 00:00:00 2001
From: David Havndrup Munch
Date: Mon, 9 Jun 2025 09:48:23 +0200
Subject: [PATCH 079/121] Translated using Weblate (Danish)
Currently translated at 98.9% (1093 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
---
client/strings/da.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/da.json b/client/strings/da.json
index 06342342..8ceb50ee 100644
--- a/client/strings/da.json
+++ b/client/strings/da.json
@@ -513,7 +513,7 @@
"LabelPublishers": "Forlag",
"LabelRSSFeedCustomOwnerEmail": "Brugerdefineret ejerens e-mail",
"LabelRSSFeedCustomOwnerName": "Brugerdefineret ejerens navn",
- "LabelRSSFeedOpen": "Åben RSS-feed",
+ "LabelRSSFeedOpen": "RSS-feed åbent",
"LabelRSSFeedPreventIndexing": "Forhindrer indeksering",
"LabelRSSFeedSlug": "RSS-feed-slug",
"LabelRSSFeedURL": "RSS-feed-URL",
From 398d04fc08cc984ed29042ab2fe083763f40c471 Mon Sep 17 00:00:00 2001
From: Daniel Schosser
Date: Mon, 9 Jun 2025 09:02:28 +0200
Subject: [PATCH 080/121] Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/de.json b/client/strings/de.json
index 98fdba33..4efe72e9 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -514,7 +514,7 @@
"LabelPublishers": "Herausgeber",
"LabelRSSFeedCustomOwnerEmail": "Benutzerdefinierte Eigentümer-E-Mail",
"LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers",
- "LabelRSSFeedOpen": "RSS Feed offen",
+ "LabelRSSFeedOpen": "RSS Feed öffnen",
"LabelRSSFeedPreventIndexing": "Indizierung verhindern",
"LabelRSSFeedSlug": "RSS-Feed-Schlagwort",
"LabelRSSFeedURL": "RSS-Feed-URL",
From 387a3d05b44afdccfc764d2426725d477fc5d453 Mon Sep 17 00:00:00 2001
From: Rekentek
Date: Mon, 9 Jun 2025 12:06:52 +0200
Subject: [PATCH 081/121] Translated using Weblate (Dutch)
Currently translated at 98.5% (1089 of 1105 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
---
client/strings/nl.json | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/client/strings/nl.json b/client/strings/nl.json
index 955ba86b..fd1a52c2 100644
--- a/client/strings/nl.json
+++ b/client/strings/nl.json
@@ -394,6 +394,7 @@
"LabelIntervalEvery6Hours": "Iedere 6 uur",
"LabelIntervalEveryDay": "Iedere dag",
"LabelIntervalEveryHour": "Ieder uur",
+ "LabelIntervalEveryMinute": "Elke minuut",
"LabelInvert": "Omdraaien",
"LabelItem": "Onderdeel",
"LabelJumpBackwardAmount": "Terugspoelen hoeveelheid",
@@ -424,7 +425,7 @@
"LabelLookForNewEpisodesAfterDate": "Zoek naar nieuwe afleveringen na deze datum",
"LabelLowestPriority": "Laagste Prioriteit",
"LabelMatchExistingUsersBy": "Bestaande gebruikers matchen op",
- "LabelMatchExistingUsersByDescription": "Wordt gebruikt om bestaande gebruikers te verbinden. Zodra ze verbonden zijn, worden gebruikers gekoppeld aan een unieke id van uw SSO-provider.",
+ "LabelMatchExistingUsersByDescription": "Wordt gebruikt om bestaande gebruikers te verbinden. Zodra ze verbonden zijn, worden gebruikers gekoppeld aan een unieke id van uw SSO-provider",
"LabelMaxEpisodesToDownload": "Maximale # afleveringen om te downloaden. Gebruik 0 voor ongelimiteerd.",
"LabelMaxEpisodesToDownloadPerCheck": "Maximale # nieuwe afleveringen om te downloaden per check",
"LabelMaxEpisodesToKeep": "Maximale # afleveringen om te houden",
@@ -529,6 +530,7 @@
"LabelReleaseDate": "Verschijningsdatum",
"LabelRemoveAllMetadataAbs": "Verwijder alle metadata.abs bestanden",
"LabelRemoveAllMetadataJson": "Verwijder alle metadata.json bestanden",
+ "LabelRemoveAudibleBranding": "Verwijder Audible intro en outro uit hoofdstukken",
"LabelRemoveCover": "Verwijder cover",
"LabelRemoveMetadataFile": "Verwijder metadata bestanden in bibliotheek item folders",
"LabelRemoveMetadataFileHelp": "Verwijder alle metadata.json en metadata.abs bestanden in uw {0} folders.",
@@ -558,6 +560,8 @@
"LabelSettingsBookshelfViewHelp": "Skeumorphisch design met houten planken",
"LabelSettingsChromecastSupport": "Chromecast ondersteuning",
"LabelSettingsDateFormat": "Datum format",
+ "LabelSettingsEnableWatcher": "Bibliotheken automatisch scannen op wijzigingen",
+ "LabelSettingsEnableWatcherForLibrary": "Bibliotheek automatisch scannen op wijzigingen",
"LabelSettingsEnableWatcherHelp": "Zorgt voor het automatisch toevoegen/bijwerken van onderdelen als bestandswijzigingen worden gedetecteerd. *Vereist herstarten van server",
"LabelSettingsEpubsAllowScriptedContent": "Sta scripted content toe in epubs",
"LabelSettingsEpubsAllowScriptedContentHelp": "Sta toe dat epub-bestanden scripts uitvoeren. Het wordt aanbevolen om deze instelling uitgeschakeld te houden, tenzij u de bron van de epub-bestanden vertrouwt.",
@@ -719,6 +723,7 @@
"MessageChapterErrorStartGteDuration": "Ongeldig: starttijd moet kleiner zijn dan duur van audioboek",
"MessageChapterErrorStartLtPrev": "Ongeldig: starttijd moet be groter zijn dan of equal aan starttijd van vorig hoofdstuk",
"MessageChapterStartIsAfter": "Start van hoofdstuk is na het einde van je audioboek",
+ "MessageChaptersNotFound": "Hoofdstukken niet gevonden",
"MessageCheckingCron": "Cron aan het checken...",
"MessageConfirmCloseFeed": "Ben je zeker dat je deze feed wil sluiten?",
"MessageConfirmDeleteBackup": "Weet je zeker dat je de backup voor {0} wil verwijderen?",
@@ -775,6 +780,7 @@
"MessageForceReScanDescription": "zal alle bestanden opnieuw scannen als een verse scan. Audiobestanden ID3-tags, OPF-bestanden en textbestanden zullen als nieuw worden gescand.",
"MessageImportantNotice": "Belangrijke opmerking!",
"MessageInsertChapterBelow": "Hoofdstuk hieronder invoegen",
+ "MessageInvalidAsin": "Ongeldige ASIN",
"MessageItemsSelected": "{0} onderdelen geselecteerd",
"MessageItemsUpdated": "{0} onderdelen bijgewerkt",
"MessageJoinUsOn": "Doe mee op",
@@ -808,7 +814,7 @@
"MessageNoItems": "Geen onderdelen",
"MessageNoItemsFound": "Geen onderdelen gevonden",
"MessageNoListeningSessions": "Geen luistersessies",
- "MessageNoLogs": "Geen logs",
+ "MessageNoLogs": "Geen logbestanden",
"MessageNoMediaProgress": "Geen mediavoortgang",
"MessageNoNotifications": "Geen notificaties",
"MessageNoPodcastFeed": "Ongeldige podcast: Geen Feed",
From def93d18ec3eb48d426983415a3f594e69285209 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dawid=20Ku=C5=BAnicki?=
Date: Mon, 9 Jun 2025 07:22:12 +0200
Subject: [PATCH 082/121] Translated using Weblate (Polish)
Currently translated at 76.9% (850 of 1105 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 b9d7cfe3..7248ae96 100644
--- a/client/strings/pl.json
+++ b/client/strings/pl.json
@@ -494,7 +494,7 @@
"LabelPublishedDate": "Opublikowano {0}",
"LabelPublisher": "Wydawca",
"LabelPublishers": "Wydawcy",
- "LabelRSSFeedOpen": "RSS Feed otwarty",
+ "LabelRSSFeedOpen": "Otwarty Kanał RSS",
"LabelRSSFeedPreventIndexing": "Zapobiegaj indeksowaniu",
"LabelRSSFeedURL": "URL kanały RSS",
"LabelRandomly": "Losowo",
From 9841826e10b090f77c3fd31b74c7c7eedbcf1d53 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: Tue, 10 Jun 2025 07:17:34 +0200
Subject: [PATCH 083/121] Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1107 of 1107 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 25ea4368..56b049da 100644
--- a/client/strings/uk.json
+++ b/client/strings/uk.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Запускається після завершення резервного копіювання",
"NotificationOnBackupFailedDescription": "Срабатывает при збої резервного копіювання",
"NotificationOnEpisodeDownloadedDescription": "Запускається при автоматичному завантаженні епізоду подкасту",
+ "NotificationOnRSSFeedDisabledDescription": "Активується, коли автоматичне завантаження епізодів вимкнено через занадто багато невдалих спроб",
+ "NotificationOnRSSFeedFailedDescription": "Активується, коли запит RSS-каналу не вдається виконати для автоматичного завантаження епізоду",
"NotificationOnTestDescription": "Подія для тестування системи сповіщень",
"PlaceholderNewCollection": "Нова назва добірки",
"PlaceholderNewFolderPath": "Новий шлях до теки",
From 3e44ee6f505fb7389feb0681aabd4201a8ddfa01 Mon Sep 17 00:00:00 2001
From: ugyes
Date: Tue, 10 Jun 2025 21:42:31 +0200
Subject: [PATCH 084/121] Translated using Weblate (Hungarian)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
---
client/strings/hu.json | 59 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 55 insertions(+), 4 deletions(-)
diff --git a/client/strings/hu.json b/client/strings/hu.json
index 16772328..cb7bf070 100644
--- a/client/strings/hu.json
+++ b/client/strings/hu.json
@@ -11,7 +11,7 @@
"ButtonAuthors": "Szerzők",
"ButtonBack": "Vissza",
"ButtonBatchEditPopulateFromExisting": "Létezőből feltöltés",
- "ButtonBatchEditPopulateMapDetails": "",
+ "ButtonBatchEditPopulateMapDetails": "A térkép részleteinek feltöltése",
"ButtonBrowseForFolder": "Mappa keresése",
"ButtonCancel": "Mégse",
"ButtonCancelEncode": "Kódolás megszakítása",
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Lejátszási lista",
"HeaderPlaylistItems": "Lejátszási lista elemek",
"HeaderPodcastsToAdd": "Hozzáadandó podcastok",
+ "HeaderPresets": "Alapbeállítások",
"HeaderPreviewCover": "Borító előnézete",
"HeaderRSSFeedGeneral": "RSS részletek",
"HeaderRSSFeedIsOpen": "RSS hírcsatorna nyitva van",
@@ -219,6 +220,7 @@
"LabelAccountTypeAdmin": "Adminisztrátor",
"LabelAccountTypeGuest": "Vendég",
"LabelAccountTypeUser": "Felhasználó",
+ "LabelActivities": "Tevékenységek",
"LabelActivity": "Tevékenység",
"LabelAddToCollection": "Hozzáadás a gyűjteményhez",
"LabelAddToCollectionBatch": "{0} könyv hozzáadása a gyűjteményhez",
@@ -228,6 +230,7 @@
"LabelAddedDate": "{0} Hozzáadva",
"LabelAdminUsersOnly": "Csak admin felhasználók",
"LabelAll": "Összes",
+ "LabelAllEpisodesDownloaded": "Minden epizód letöltve",
"LabelAllUsers": "Minden felhasználó",
"LabelAllUsersExcludingGuests": "Minden felhasználó, vendégek kivételével",
"LabelAllUsersIncludingGuests": "Minden felhasználó, beleértve a vendégeket is",
@@ -251,7 +254,7 @@
"LabelBackToUser": "Vissza a felhasználóhoz",
"LabelBackupAudioFiles": "Audiófájlok biztonsági mentése",
"LabelBackupLocation": "Biztonsági másolat helye",
- "LabelBackupsEnableAutomaticBackups": "Automatikus biztonsági másolatok engedélyezése",
+ "LabelBackupsEnableAutomaticBackups": "Automatikus biztonsági másolatok",
"LabelBackupsEnableAutomaticBackupsHelp": "Biztonsági másolatok mentése a /metadata/backups mappába",
"LabelBackupsMaxBackupSize": "Maximális biztonsági másolat méret (GB-ban) (0-tól végtelenig)",
"LabelBackupsMaxBackupSizeHelp": "A rossz konfiguráció elleni védelem érdekében a biztonsági másolatok meghiúsulnak, ha meghaladják a beállított méretet.",
@@ -283,6 +286,7 @@
"LabelContinueSeries": "Sorozat folytatása",
"LabelCover": "Borító",
"LabelCoverImageURL": "Borítókép URL",
+ "LabelCoverProvider": "Borító Szolgáltató",
"LabelCreatedAt": "Létrehozás ideje",
"LabelCronExpression": "Cron kifejezés",
"LabelCurrent": "Jelenlegi",
@@ -391,7 +395,8 @@
"LabelIntervalEvery6Hours": "Minden 6 órában",
"LabelIntervalEveryDay": "Minden nap",
"LabelIntervalEveryHour": "Minden órában",
- "LabelInvert": "Megfordítás",
+ "LabelIntervalEveryMinute": "Minden percben",
+ "LabelInvert": "Inverz",
"LabelItem": "Elem",
"LabelJumpBackwardAmount": "Visszafelé ugrás mennyisége",
"LabelJumpForwardAmount": "Előre ugrás mennyisége",
@@ -486,6 +491,7 @@
"LabelPersonalYearReview": "Az éved összefoglalása ({0})",
"LabelPhotoPathURL": "Fénykép útvonal/URL",
"LabelPlayMethod": "Lejátszási módszer",
+ "LabelPlaybackRateIncrementDecrement": "Lejátszási sebesség növelés/csökkentés értéke",
"LabelPlayerChapterNumberMarker": "{0} a {1} -ből",
"LabelPlaylists": "Lejátszási listák",
"LabelPodcast": "Podcast",
@@ -508,7 +514,7 @@
"LabelPublishers": "Kiadók",
"LabelRSSFeedCustomOwnerEmail": "Egyéni tulajdonos e-mail",
"LabelRSSFeedCustomOwnerName": "Egyéni tulajdonos neve",
- "LabelRSSFeedOpen": "RSS hírcsatorna nyitva",
+ "LabelRSSFeedOpen": "RSS-hírcsatorna nyitva",
"LabelRSSFeedPreventIndexing": "Indexelés megakadályozása",
"LabelRSSFeedSlug": "RSS hírcsatorna slug",
"LabelRSSFeedURL": "RSS hírcsatorna URL",
@@ -525,6 +531,7 @@
"LabelReleaseDate": "Megjelenés dátuma",
"LabelRemoveAllMetadataAbs": "Az összes metadata.abs fájl eltávolítása",
"LabelRemoveAllMetadataJson": "Az összes metadata.json fájl eltávolítása",
+ "LabelRemoveAudibleBranding": "Audible intro és outro eltávolítása a fejezetekből",
"LabelRemoveCover": "Borító eltávolítása",
"LabelRemoveMetadataFile": "Metaadatfájlok eltávolítása a könyvtár elemek mappáiból",
"LabelRemoveMetadataFileHelp": "A metadata.json és metadata.abs fájlokat eltávolítása a {0} mappáidból.",
@@ -554,6 +561,8 @@
"LabelSettingsBookshelfViewHelp": "Skeuomorfikus dizájn fa polcokkal",
"LabelSettingsChromecastSupport": "Chromecast támogatás",
"LabelSettingsDateFormat": "Dátumformátum",
+ "LabelSettingsEnableWatcher": "Változások automatikus vizsgálata a könyvtárakban",
+ "LabelSettingsEnableWatcherForLibrary": "Változások automatikus vizsgálata a könyvtárban",
"LabelSettingsEnableWatcherHelp": "Engedélyezi az automatikus elem hozzáadás/frissítés funkciót, amikor fájlváltozásokat észlel. *Szerver újraindítása szükséges",
"LabelSettingsEpubsAllowScriptedContent": "Szkriptelt tartalmak engedélyezése epub-okban",
"LabelSettingsEpubsAllowScriptedContentHelp": "Megengedi, hogy az epub fájlok szkripteket hajtsanak végre. Ezt a beállítást kikapcsolva ajánlott tartani, kivéve, ha megbízik az epub fájlok forrásában.",
@@ -597,6 +606,7 @@
"LabelSlug": "Rövid cím",
"LabelSortAscending": "Emelkedő",
"LabelSortDescending": "Csökkenő",
+ "LabelSortPubDate": "Rendezés megjelenés dátuma szerint",
"LabelStart": "Kezdés",
"LabelStartTime": "Kezdési idő",
"LabelStarted": "Elkezdődött",
@@ -697,12 +707,17 @@
"LabelYourProgress": "Haladásod",
"MessageAddToPlayerQueue": "Hozzáadás a lejátszó sorhoz",
"MessageAppriseDescription": "Ennek a funkció használatához futtatnia kell egy Apprise API példányt vagy egy olyan API-t, amely kezeli ezeket a kéréseket. Az Apprise API URL-nek a teljes URL útvonalat kell tartalmaznia az értesítés elküldéséhez, például, ha az API példánya a http://192.168.1.1:8337 címen szolgáltatva, akkor http://192.168.1.1:8337/notify értéket kell megadnia.",
+ "MessageAsinCheck": "Győződjön meg róla, hogy az ASIN-t a megfelelő Audible régióból használja, nem az Amazonból.",
+ "MessageAuthenticationOIDCChangesRestart": "A mentés után indítsa újra a szervert az OIDC módosítások alkalmazásához.",
"MessageBackupsDescription": "A biztonsági másolatok tartalmazzák a felhasználókat, a felhasználói haladást, a könyvtári elem részleteit, a szerver beállításait és a képeket, amelyek a /metadata/items és /metadata/authors mappákban vannak tárolva. A biztonsági másolatok nem tartalmazzák a könyvtári mappákban tárolt fájlokat.",
"MessageBackupsLocationEditNote": "Megjegyzés: A biztonsági mentés helyének frissítése nem mozgatja vagy módosítja a meglévő biztonsági mentéseket",
"MessageBackupsLocationNoEditNote": "Megjegyzés: A biztonsági mentés helye egy környezeti változóval van beállítva, és itt nem módosítható.",
"MessageBackupsLocationPathEmpty": "A biztonsági mentés helyének elérési útvonala nem lehet üres",
+ "MessageBatchEditPopulateMapDetailsAllHelp": "Az engedélyezett mezők feltöltése az összes elem adatával. A több értéket tartalmazó mezők összevonásra kerülnek",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "A térkép engedélyezett adatmezőinek feltöltése ezen elem adataival",
"MessageBatchQuickMatchDescription": "A Gyors egyeztetés megpróbálja hozzáadni a hiányzó borítókat és metaadatokat a kiválasztott elemekhez. Engedélyezze az alábbi opciókat, hogy a Gyors egyeztetés felülírhassa a meglévő borítókat és/vagy metaadatokat.",
"MessageBookshelfNoCollections": "Még nem készített gyűjteményeket",
+ "MessageBookshelfNoCollectionsHelp": "A gyűjtemények nyilvánosak. Minden, a könyvtárhoz hozzáféréssel rendelkező felhasználó láthatja őket.",
"MessageBookshelfNoRSSFeeds": "Nincsenek nyitott RSS hírcsatornák",
"MessageBookshelfNoResultsForFilter": "Nincs eredmény a \"{0}: {1}\" szűrőre",
"MessageBookshelfNoResultsForQuery": "Nincs eredmény a lekérdezéshez",
@@ -712,6 +727,7 @@
"MessageChapterErrorStartGteDuration": "Érvénytelen kezdési idő, kevesebbnek kell lennie, mint a hangoskönyv időtartama",
"MessageChapterErrorStartLtPrev": "Érvénytelen kezdési idő, nagyobbnak kell lennie, mint az előző fejezet kezdési ideje",
"MessageChapterStartIsAfter": "A fejezet kezdete a hangoskönyv végét követi",
+ "MessageChaptersNotFound": "Fejezetek nem találhatók",
"MessageCheckingCron": "Cron ellenőrzése...",
"MessageConfirmCloseFeed": "Biztosan be szeretné zárni ezt a hírcsatornát?",
"MessageConfirmDeleteBackup": "Biztosan törölni szeretné a(z) {0} biztonsági másolatot?",
@@ -768,6 +784,7 @@
"MessageForceReScanDescription": "minden fájlt újra szkennel, mint egy friss szkennelés. Az audiofájlok ID3 címkéi, OPF fájlok és szövegfájlok újként lesznek szkennelve.",
"MessageImportantNotice": "Fontos közlemény!",
"MessageInsertChapterBelow": "Fejezet beszúrása alulra",
+ "MessageInvalidAsin": "Érvénytelen ASIN",
"MessageItemsSelected": "{0} kiválasztott elem",
"MessageItemsUpdated": "{0} frissített elem",
"MessageJoinUsOn": "Csatlakozzon hozzánk a",
@@ -813,6 +830,7 @@
"MessageNoTasksRunning": "Nincsenek futó feladatok",
"MessageNoUpdatesWereNecessary": "Nem volt szükség frissítésekre",
"MessageNoUserPlaylists": "Nincsenek felhasználói lejátszási listák",
+ "MessageNoUserPlaylistsHelp": "A lejátszási listák személyesek. Csak az a felhasználó láthatja őket, aki létrehozta őket.",
"MessageNotYetImplemented": "Még nem implementált",
"MessageOpmlPreviewNote": "Megjegyzés: Ez egy előnézeti kép az elemzett OPML fájlról. A podcast tényleges címe az RSS hírcsatornából származik.",
"MessageOr": "vagy",
@@ -835,8 +853,10 @@
"MessageRestoreBackupConfirm": "Biztosan vissza szeretné állítani a biztonsági másolatot, amely ekkor készült:",
"MessageRestoreBackupWarning": "A biztonsági mentés visszaállítása felülírja az egész adatbázist, amely a /config mappában található, valamint a borítóképeket a /metadata/items és /metadata/authors mappákban.
A biztonsági mentések nem módosítják a könyvtár mappáiban található fájlokat. Ha engedélyezte a szerverbeállításokat a borítóképek és a metaadatok könyvtármappákban való tárolására, akkor ezek nem kerülnek biztonsági mentésre vagy felülírásra.
A szerver használó összes kliens automatikusan frissül.",
"MessageScheduleLibraryScanNote": "A legtöbb felhasználó számára ajánlott ezt a funkciót kikapcsolva hagyni, és engedélyezni a mappafigyelő beállítást. A mappafigyelő automatikusan észleli a könyvtári mappák változásait. A mappafigyelő nem működik minden fájlrendszernél (mint például az NFS), ezért helyette ütemezett könyvtárellenőrzéseket lehet használni.",
+ "MessageScheduleRunEveryWeekdayAtTime": "Futás minden {1} óra {0}-kor",
"MessageSearchResultsFor": "Keresési eredmények",
"MessageSelected": "{0} kiválasztva",
+ "MessageSeriesSequenceCannotContainSpaces": "Sorozat sorrend nem tartalmazhat szóközt",
"MessageServerCouldNotBeReached": "A szervert nem lehet elérni",
"MessageSetChaptersFromTracksDescription": "Fejezetek beállítása minden egyes hangfájlt egy fejezetként használva, és a fejezet címét a hangfájl neveként",
"MessageShareExpirationWillBe": "A lejárat: {0}",
@@ -861,6 +881,7 @@
"MessageTaskNoFilesToScan": "Nincs beolvasandó fájl",
"MessageTaskOpmlImport": "OPML import",
"MessageTaskOpmlImportDescription": "Podcastok létrehozása {0} RSS hírcsatornából",
+ "MessageTaskOpmlImportFeed": "OPML import hírcsatorna",
"MessageTaskOpmlImportFeedDescription": "RSS feed „{0}” importálása",
"MessageTaskOpmlImportFeedFailed": "Nem sikerült letölteni a podcast feedet",
"MessageTaskOpmlImportFeedPodcastDescription": "„{0}” podcast létrehozása",
@@ -869,6 +890,7 @@
"MessageTaskOpmlImportFinished": "{0} podcast hozzáadva",
"MessageTaskOpmlParseFailed": "Az OPML fájl elemzése nem sikerült",
"MessageTaskOpmlParseFastFail": "Érvénytelen OPML fájl: tag nem található VAGY nem találtak taget",
+ "MessageTaskOpmlParseNoneFound": "Nem található feed az OPML fájlban",
"MessageTaskScanItemsAdded": "{0} hozzáadva",
"MessageTaskScanItemsMissing": "{0} hiányzik",
"MessageTaskScanItemsUpdated": "{0} frissítve",
@@ -896,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "A biztonsági mentés befejezésekor aktiválódik",
"NotificationOnBackupFailedDescription": "A biztonsági mentés sikertelensége esetén aktiválódik",
"NotificationOnEpisodeDownloadedDescription": "Egy podcast epizód automatikus letöltésekor aktiválódik",
+ "NotificationOnRSSFeedDisabledDescription": "Akkor lép működésbe, ha az automatikus epizódletöltés a túl sok sikertelen próbálkozás miatt letiltásra kerül",
+ "NotificationOnRSSFeedFailedDescription": "Akkor aktiválódik, ha az RSS feed kérés sikertelen az automatikus epizódletöltésnél",
"NotificationOnTestDescription": "Esemény az értesítési rendszer teszteléséhez",
"PlaceholderNewCollection": "Új gyűjtemény neve",
"PlaceholderNewFolderPath": "Új mappa útvonala",
@@ -940,8 +964,11 @@
"ToastBackupRestoreFailed": "A biztonsági mentés visszaállítása sikertelen",
"ToastBackupUploadFailed": "A biztonsági mentés feltöltése sikertelen",
"ToastBackupUploadSuccess": "Biztonsági mentés feltöltve",
+ "ToastBatchApplyDetailsToItemsSuccess": "Tételekre alkalmazott részletek",
"ToastBatchDeleteFailed": "A tömeges törlés nem sikerült",
"ToastBatchDeleteSuccess": "Sikeres tömeges törlés",
+ "ToastBatchQuickMatchFailed": "Tömeges Gyors Egyeztetés sikertelen!",
+ "ToastBatchQuickMatchStarted": "{0} könyv Tömeges Gyors Egyeztetése elkezdődött!",
"ToastBatchUpdateFailed": "Kötegelt frissítés sikertelen",
"ToastBatchUpdateSuccess": "Kötegelt frissítés sikeres",
"ToastBookmarkCreateFailed": "Könyvjelző létrehozása sikertelen",
@@ -950,9 +977,12 @@
"ToastCachePurgeFailed": "A gyorsítótár törlése sikertelen",
"ToastCachePurgeSuccess": "A gyorsítótár sikeresen törölve",
"ToastChaptersHaveErrors": "A fejezetek hibákat tartalmaznak",
+ "ToastChaptersInvalidShiftAmountLast": "Érvénytelen eltolási érték. Az utolsó fejezet kezdési időpontja túlnyúlna a hangoskönyv időtartamán.",
+ "ToastChaptersInvalidShiftAmountStart": "Érvénytelen eltolási érték. Az első fejezet hossza nulla vagy negatív lenne, és a második fejezet felülírná. Növelje a második fejezet kezdő időtartamát.",
"ToastChaptersMustHaveTitles": "A fejezeteknek címekkel kell rendelkezniük",
"ToastChaptersRemoved": "Fejezetek eltávolítva",
"ToastChaptersUpdated": "Fejezetek frissítve",
+ "ToastCollectionItemsAddFailed": "A tétel(ek) hozzáadása gyűjteményhez sikertelen",
"ToastCollectionRemoveSuccess": "Gyűjtemény eltávolítva",
"ToastCollectionUpdateSuccess": "Gyűjtemény frissítve",
"ToastCoverUpdateFailed": "A borító frissítése nem sikerült",
@@ -967,6 +997,7 @@
"ToastEncodeCancelFailed": "A kódolás törlése sikertelen volt",
"ToastEncodeCancelSucces": "Kódolás törölve",
"ToastEpisodeDownloadQueueClearFailed": "Nem sikerült törölni a várólistát",
+ "ToastEpisodeDownloadQueueClearSuccess": "Epizód letöltési várólista törölve",
"ToastEpisodeUpdateSuccess": "{0} epizód frissítve",
"ToastErrorCannotShare": "Ezen az eszközön nem lehet natívan megosztani",
"ToastFailedToLoadData": "Sikertelen adatbetöltés",
@@ -974,6 +1005,7 @@
"ToastFailedToShare": "Nem sikerült megosztani",
"ToastFailedToUpdate": "Nem sikerült frissíteni",
"ToastInvalidImageUrl": "Érvénytelen a kép URL címe",
+ "ToastInvalidMaxEpisodesToDownload": "A letölthető epizódok száma érvénytelen",
"ToastInvalidUrl": "Érvénytelen URL",
"ToastItemCoverUpdateSuccess": "Elem borítója frissítve",
"ToastItemDeletedFailed": "Nem sikerült törölni az elemet",
@@ -1011,8 +1043,11 @@
"ToastNoUpdatesNecessary": "Nincs szükség frissítésre",
"ToastNotificationCreateFailed": "Értesítés létrehozása sikertelen",
"ToastNotificationDeleteFailed": "Értesítés törlése sikertelen",
+ "ToastNotificationFailedMaximum": "A sikertelen kísérletek maximális száma >= 0 kell, hogy legyen",
+ "ToastNotificationQueueMaximum": "Az értesítési sor maximális száma >= 0 kell, hogy legyen",
"ToastNotificationSettingsUpdateSuccess": "Értesítési beállítások frissítve",
"ToastNotificationTestTriggerFailed": "Nem sikerült a tesztértesítést elindítani",
+ "ToastNotificationTestTriggerSuccess": "Kiváltott tesztértesítés",
"ToastNotificationUpdateSuccess": "Értesítés frissítve",
"ToastPlaylistCreateFailed": "Lejátszási lista létrehozása sikertelen",
"ToastPlaylistCreateSuccess": "Lejátszási lista létrehozva",
@@ -1020,6 +1055,7 @@
"ToastPlaylistUpdateSuccess": "Lejátszási lista frissítve",
"ToastPodcastCreateFailed": "Podcast létrehozása sikertelen",
"ToastPodcastCreateSuccess": "A podcast sikeresen létrehozva",
+ "ToastPodcastGetFeedFailed": "Nem sikerült podcast feedet kapni",
"ToastPodcastNoEpisodesInFeed": "Nincsenek epizódok az RSS hírcsatornában",
"ToastPodcastNoRssFeed": "A podcastnak nincs RSS-hírcsatornája",
"ToastProgressIsNotBeingSynced": "Az előrehaladás nem szinkronizálódik, a lejátszás újraindul",
@@ -1032,10 +1068,18 @@
"ToastRemoveFailed": "Sikertelen eltávolítás",
"ToastRemoveItemFromCollectionFailed": "Tétel eltávolítása a gyűjteményből sikertelen",
"ToastRemoveItemFromCollectionSuccess": "Tétel eltávolítva a gyűjteményből",
+ "ToastRemoveItemsWithIssuesFailed": "Nem sikerült eltávolítani a hibás könyvtárelemeket",
+ "ToastRemoveItemsWithIssuesSuccess": "Hibás könyvtárelemek eltávolítva",
"ToastRenameFailed": "Sikertelen átnevezés",
+ "ToastRescanFailed": "Sikertelen újrakeresés a következőnél: {0}",
+ "ToastRescanRemoved": "A teljes újrabeolvasás befejezve, elem eltávolítva",
+ "ToastRescanUpToDate": "A teljes újrabeolvasás befejezve, elem naprakész volt",
+ "ToastRescanUpdated": "A teljes újrabeolvasás befejezve, elem frissítve",
+ "ToastScanFailed": "Nem sikerült beolvasni a könyvtárelemet",
"ToastSelectAtLeastOneUser": "Válasszon legalább egy felhasználót",
"ToastSendEbookToDeviceFailed": "E-könyv küldése az eszközre sikertelen",
"ToastSendEbookToDeviceSuccess": "E-könyv elküldve az eszközre \"{0}\"",
+ "ToastSeriesSubmitFailedSameName": "Nem lehet két azonos nevű sorozatot hozzáadni",
"ToastSeriesUpdateFailed": "Sorozat frissítése sikertelen",
"ToastSeriesUpdateSuccess": "Sorozat frissítése sikeres",
"ToastServerSettingsUpdateSuccess": "Szerver beállítások frissítve",
@@ -1043,6 +1087,8 @@
"ToastSessionDeleteFailed": "Munkamenet törlése sikertelen",
"ToastSessionDeleteSuccess": "Munkamenet törölve",
"ToastSleepTimerDone": "Alvásidőzítő kész... zZzzZZz",
+ "ToastSlugMustChange": "A Slug érvénytelen karaktereket tartalmaz",
+ "ToastSlugRequired": "Slug szükséges",
"ToastSocketConnected": "Socket csatlakoztatva",
"ToastSocketDisconnected": "Socket lecsatlakoztatva",
"ToastSocketFailedToConnect": "A Socket csatlakoztatása sikertelen",
@@ -1050,9 +1096,14 @@
"ToastSortingPrefixesUpdateSuccess": "Rendezési előtagok frissítése ({0} elem)",
"ToastTitleRequired": "A cím kötelező",
"ToastUnknownError": "Ismeretlen hiba",
+ "ToastUnlinkOpenIdFailed": "Nem sikerült leválasztani a felhasználót az OpenID-ről",
+ "ToastUnlinkOpenIdSuccess": "Felhasználó leválasztva az OpenID-ről",
+ "ToastUploaderFilepathExistsError": "A \"{0}\" fájl elérési útja már létezik a szerveren",
+ "ToastUploaderItemExistsInSubdirectoryError": "A „{0}” elem a feltöltési útvonal egy alkönyvtárát használja.",
"ToastUserDeleteFailed": "Felhasználó törlése sikertelen",
"ToastUserDeleteSuccess": "Felhasználó törölve",
"ToastUserPasswordChangeSuccess": "Jelszó sikeresen megváltoztatva",
+ "ToastUserPasswordMismatch": "A jelszavak nem egyeznek",
"ToastUserPasswordMustChange": "Az új jelszó nem egyezik a régi jelszóval",
"ToastUserRootRequireName": "Egy root felhasználónevet kell megadnia"
}
From 52196afd99370ac563eb2757427874495da29c49 Mon Sep 17 00:00:00 2001
From: Ivan Smoliakov
Date: Tue, 10 Jun 2025 22:39:50 +0200
Subject: [PATCH 085/121] Translated using Weblate (Russian)
Currently translated at 100.0% (1107 of 1107 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, 2 insertions(+)
diff --git a/client/strings/ru.json b/client/strings/ru.json
index 056245ac..54ed4abe 100644
--- a/client/strings/ru.json
+++ b/client/strings/ru.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Запускается при завершении резервного копирования",
"NotificationOnBackupFailedDescription": "Срабатывает при сбое резервного копирования",
"NotificationOnEpisodeDownloadedDescription": "Запускается при автоматической загрузке эпизода подкаста",
+ "NotificationOnRSSFeedDisabledDescription": "Срабатывает, когда автоматическая загрузка эпизодов отключена из-за слишком большого количества неудачных попыток",
+ "NotificationOnRSSFeedFailedDescription": "Срабатывает при сбое запроса RSS-канала на автоматическую загрузку эпизода",
"NotificationOnTestDescription": "Событие для тестирования системы оповещения",
"PlaceholderNewCollection": "Новое имя коллекции",
"PlaceholderNewFolderPath": "Путь к новой папке",
From 06d3bd76a81cbae2be06afc20a85d0696e2620f0 Mon Sep 17 00:00:00 2001
From: Usama Khalil
Date: Wed, 11 Jun 2025 17:03:46 +0200
Subject: [PATCH 086/121] Translated using Weblate (Arabic)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
---
client/strings/ar.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/ar.json b/client/strings/ar.json
index 123cc343..b04d94b9 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "يتم تشغيله عند اكتمال النسخ الاحتياطي",
"NotificationOnBackupFailedDescription": "يتم تشغيله عند فشل النسخ الاحتياطي",
"NotificationOnEpisodeDownloadedDescription": "يتم تشغيله عند تنزيل حلقة بودكاست تلقائيًا",
+ "NotificationOnRSSFeedDisabledDescription": "يتم تشغيله عندما يتم تعطيل تنزيلات الحلقة التلقائية بسبب الكثير من المحاولات الفاشلة",
+ "NotificationOnRSSFeedFailedDescription": "يتم تشغيله عند فشل طلب تغذية RSS في تنزيل حلقة تلقائية",
"NotificationOnTestDescription": "حدث لاختبار نظام الإشعارات",
"PlaceholderNewCollection": "اسم المجموعة الجديدة",
"PlaceholderNewFolderPath": "مسار المجلد الجديد",
From 5123f7d240beb7961899befaf052b8562eeac026 Mon Sep 17 00:00:00 2001
From: thehijacker
Date: Thu, 12 Jun 2025 13:19:05 +0200
Subject: [PATCH 087/121] Translated using Weblate (Slovenian)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
---
client/strings/sl.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/sl.json b/client/strings/sl.json
index 43039624..81e80003 100644
--- a/client/strings/sl.json
+++ b/client/strings/sl.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Sproži se, ko je varnostno kopiranje končano",
"NotificationOnBackupFailedDescription": "Sproži se, ko varnostno kopiranje ne uspe",
"NotificationOnEpisodeDownloadedDescription": "Sproži se, ko se epizoda podcasta samodejno prenese",
+ "NotificationOnRSSFeedDisabledDescription": "Sproži se, ko so samodejni prenosi epizod onemogočeni zaradi preveč neuspelih poskusov",
+ "NotificationOnRSSFeedFailedDescription": "Sproži se, ko zahteva za vir RSS za samodejni prenos epizode ne uspe",
"NotificationOnTestDescription": "Dogodek za testiranje sistema obveščanja",
"PlaceholderNewCollection": "Novo ime zbirke",
"PlaceholderNewFolderPath": "Pot nove mape",
From 35e196238a5d2ba13de77fe9abbb78141feb311c Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 14 Jun 2025 17:18:53 -0500
Subject: [PATCH 088/121] Version bump v2.25.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 23ec14a9..007681ff 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf-client",
- "version": "2.24.0",
+ "version": "2.25.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf-client",
- "version": "2.24.0",
+ "version": "2.25.0",
"license": "ISC",
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
diff --git a/client/package.json b/client/package.json
index 7985cd78..606baacf 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
- "version": "2.24.0",
+ "version": "2.25.0",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast client",
"main": "index.js",
diff --git a/package-lock.json b/package-lock.json
index a8074794..e8bc7eb3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf",
- "version": "2.24.0",
+ "version": "2.25.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf",
- "version": "2.24.0",
+ "version": "2.25.0",
"license": "GPL-3.0",
"dependencies": {
"axios": "^0.27.2",
diff --git a/package.json b/package.json
index d4831736..d31b7a70 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
- "version": "2.24.0",
+ "version": "2.25.0",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
From b515c6c7460a65b74b1d8e76f88c5fd0abbc0e5d Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 14 Jun 2025 17:56:35 -0500
Subject: [PATCH 089/121] Remove mediaProgresses duplicate check
---
server/Database.js | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/server/Database.js b/server/Database.js
index 2413a269..eb384f25 100644
--- a/server/Database.js
+++ b/server/Database.js
@@ -767,13 +767,13 @@ class Database {
}
// Remove mediaProgresses with duplicate mediaItemId (remove the oldest updatedAt)
- const [duplicateMediaProgresses] = await this.sequelize.query(`SELECT id, mediaItemId FROM mediaProgresses WHERE (mediaItemId, updatedAt) IN (SELECT mediaItemId, MIN(updatedAt) FROM mediaProgresses GROUP BY mediaItemId HAVING COUNT(*) > 1)`)
- for (const duplicateMediaProgress of duplicateMediaProgresses) {
- Logger.warn(`Found duplicate mediaProgress for mediaItem "${duplicateMediaProgress.mediaItemId}" - removing it`)
- await this.mediaProgressModel.destroy({
- where: { id: duplicateMediaProgress.id }
- })
- }
+ // const [duplicateMediaProgresses] = await this.sequelize.query(`SELECT id, mediaItemId FROM mediaProgresses WHERE (mediaItemId, userId, updatedAt) IN (SELECT mediaItemId, userId, MIN(updatedAt) FROM mediaProgresses GROUP BY mediaItemId, userId HAVING COUNT(*) > 1)`)
+ // for (const duplicateMediaProgress of duplicateMediaProgresses) {
+ // Logger.warn(`Found duplicate mediaProgress for mediaItem "${duplicateMediaProgress.mediaItemId}" - removing it`)
+ // await this.mediaProgressModel.destroy({
+ // where: { id: duplicateMediaProgress.id }
+ // })
+ // }
}
async createTextSearchQuery(query) {
From f3f5f3b9bd540d311a6ab0a99b9317a5142755ea Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 14 Jun 2025 17:57:19 -0500
Subject: [PATCH 090/121] Version bump v2.25.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 007681ff..db21b43f 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf-client",
- "version": "2.25.0",
+ "version": "2.25.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf-client",
- "version": "2.25.0",
+ "version": "2.25.1",
"license": "ISC",
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
diff --git a/client/package.json b/client/package.json
index 606baacf..e8823f1b 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
- "version": "2.25.0",
+ "version": "2.25.1",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast client",
"main": "index.js",
diff --git a/package-lock.json b/package-lock.json
index e8bc7eb3..d44ea79b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf",
- "version": "2.25.0",
+ "version": "2.25.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf",
- "version": "2.25.0",
+ "version": "2.25.1",
"license": "GPL-3.0",
"dependencies": {
"axios": "^0.27.2",
diff --git a/package.json b/package.json
index d31b7a70..6229a18b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
- "version": "2.25.0",
+ "version": "2.25.1",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
From 693dc00fa30365bb3a96c87c2a1c38564ead4457 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sun, 15 Jun 2025 17:21:47 -0500
Subject: [PATCH 091/121] Update local session sync logs to help debug sync
errors
---
server/managers/PlaybackSessionManager.js | 8 ++++----
server/models/MediaProgress.js | 5 +++--
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/server/managers/PlaybackSessionManager.js b/server/managers/PlaybackSessionManager.js
index 39bd2e8f..fb12feda 100644
--- a/server/managers/PlaybackSessionManager.js
+++ b/server/managers/PlaybackSessionManager.js
@@ -107,7 +107,7 @@ class PlaybackSessionManager {
const syncResults = []
for (const sessionJson of sessions) {
- Logger.info(`[PlaybackSessionManager] Syncing local session "${sessionJson.displayTitle}" (${sessionJson.id})`)
+ Logger.info(`[PlaybackSessionManager] Syncing local session "${sessionJson.displayTitle}" (${sessionJson.id}) (updatedAt: ${sessionJson.updatedAt})`)
const result = await this.syncLocalSession(user, sessionJson, deviceInfo)
syncResults.push(result)
}
@@ -230,9 +230,9 @@ class PlaybackSessionManager {
let userProgressForItem = user.getMediaProgress(mediaItemId)
if (userProgressForItem) {
if (userProgressForItem.updatedAt.valueOf() > session.updatedAt) {
- Logger.debug(`[PlaybackSessionManager] Not updating progress for "${session.displayTitle}" because it has been updated more recently`)
+ Logger.info(`[PlaybackSessionManager] Not updating progress for "${session.displayTitle}" because it has been updated more recently (${userProgressForItem.updatedAt.valueOf()} > ${session.updatedAt}) (incoming currentTime: ${session.currentTime}) (current currentTime: ${userProgressForItem.currentTime})`)
} else {
- Logger.debug(`[PlaybackSessionManager] Updating progress for "${session.displayTitle}" with current time ${session.currentTime} (previously ${userProgressForItem.currentTime})`)
+ Logger.info(`[PlaybackSessionManager] Updating progress for "${session.displayTitle}" with current time ${session.currentTime} (previously ${userProgressForItem.currentTime})`)
const updateResponse = await user.createUpdateMediaProgressFromPayload({
libraryItemId: libraryItem.id,
episodeId: session.episodeId,
@@ -246,7 +246,7 @@ class PlaybackSessionManager {
}
}
} else {
- Logger.debug(`[PlaybackSessionManager] Creating new media progress for media item "${session.displayTitle}"`)
+ Logger.info(`[PlaybackSessionManager] Creating new media progress for media item "${session.displayTitle}"`)
const updateResponse = await user.createUpdateMediaProgressFromPayload({
libraryItemId: libraryItem.id,
episodeId: session.episodeId,
diff --git a/server/models/MediaProgress.js b/server/models/MediaProgress.js
index af3cbc5f..1638a3e4 100644
--- a/server/models/MediaProgress.js
+++ b/server/models/MediaProgress.js
@@ -222,13 +222,13 @@ class MediaProgress extends Model {
const markAsFinishedPercentComplete = Number(progressPayload.markAsFinishedPercentComplete) / 100
shouldMarkAsFinished = markAsFinishedPercentComplete < this.progress
if (shouldMarkAsFinished) {
- Logger.debug(`[MediaProgress] Marking media progress as finished because progress (${this.progress}) is greater than ${markAsFinishedPercentComplete}`)
+ Logger.info(`[MediaProgress] Marking media progress as finished because progress (${this.progress}) is greater than ${markAsFinishedPercentComplete} (media item ${this.mediaItemId})`)
}
} else {
const markAsFinishedTimeRemaining = isNullOrNaN(progressPayload.markAsFinishedTimeRemaining) ? 10 : Number(progressPayload.markAsFinishedTimeRemaining)
shouldMarkAsFinished = timeRemaining < markAsFinishedTimeRemaining
if (shouldMarkAsFinished) {
- Logger.debug(`[MediaProgress] Marking media progress as finished because time remaining (${timeRemaining}) is less than ${markAsFinishedTimeRemaining} seconds`)
+ Logger.info(`[MediaProgress] Marking media progress as finished because time remaining (${timeRemaining}) is less than ${markAsFinishedTimeRemaining} seconds (media item ${this.mediaItemId})`)
}
}
}
@@ -246,6 +246,7 @@ class MediaProgress extends Model {
// For local sync
if (progressPayload.lastUpdate) {
this.updatedAt = progressPayload.lastUpdate
+ Logger.info(`[MediaProgress] Manually setting updatedAt to ${this.updatedAt} (media item ${this.mediaItemId})`)
this.changed('updatedAt', true)
}
From ba4061e5a406c03bffe178232a0a5f7172649ceb Mon Sep 17 00:00:00 2001
From: Hadrien Patte
Date: Mon, 16 Jun 2025 23:03:02 +0200
Subject: [PATCH 092/121] Make `NUSQLITE3_PATH` build arg configurable
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index f9c46117..816bdd3c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -57,7 +57,7 @@ WORKDIR /app
# Copy compiled frontend and server from build stages
COPY --from=build-client /client/dist /app/client/dist
COPY --from=build-server /server /app
-COPY --from=build-server /usr/local/lib/nusqlite3 /usr/local/lib/nusqlite3
+COPY --from=build-server ${NUSQLITE3_PATH} ${NUSQLITE3_PATH}
EXPOSE 80
From 1f7be58124dce737ac252d08a7eefaa6990a6077 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 16 Jun 2025 17:50:53 -0500
Subject: [PATCH 093/121] Fix database cleanup query pulling duplicate
mediaProgresses
---
server/Database.js | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/server/Database.js b/server/Database.js
index eb384f25..a260e89f 100644
--- a/server/Database.js
+++ b/server/Database.js
@@ -766,14 +766,25 @@ class Database {
Logger.warn(`Removed ${badSessionsRemoved} sessions that were 3 seconds or less`)
}
- // Remove mediaProgresses with duplicate mediaItemId (remove the oldest updatedAt)
- // const [duplicateMediaProgresses] = await this.sequelize.query(`SELECT id, mediaItemId FROM mediaProgresses WHERE (mediaItemId, userId, updatedAt) IN (SELECT mediaItemId, userId, MIN(updatedAt) FROM mediaProgresses GROUP BY mediaItemId, userId HAVING COUNT(*) > 1)`)
- // for (const duplicateMediaProgress of duplicateMediaProgresses) {
- // Logger.warn(`Found duplicate mediaProgress for mediaItem "${duplicateMediaProgress.mediaItemId}" - removing it`)
- // await this.mediaProgressModel.destroy({
- // where: { id: duplicateMediaProgress.id }
- // })
- // }
+ // Remove mediaProgresses with duplicate mediaItemId (remove the oldest updatedAt or if updatedAt is the same, remove arbitrary one)
+ const [duplicateMediaProgresses] = await this.sequelize.query(`SELECT mp1.id, mp1.mediaItemId
+FROM mediaProgresses mp1
+WHERE EXISTS (
+ SELECT 1
+ FROM mediaProgresses mp2
+ WHERE mp2.mediaItemId = mp1.mediaItemId
+ AND mp2.userId = mp1.userId
+ AND (
+ mp2.updatedAt > mp1.updatedAt
+ OR (mp2.updatedAt = mp1.updatedAt AND mp2.id < mp1.id)
+ )
+)`)
+ for (const duplicateMediaProgress of duplicateMediaProgresses) {
+ Logger.warn(`Found duplicate mediaProgress for mediaItem "${duplicateMediaProgress.mediaItemId}" - removing it`)
+ await this.mediaProgressModel.destroy({
+ where: { id: duplicateMediaProgress.id }
+ })
+ }
}
async createTextSearchQuery(query) {
From 9d4303ef7b48f6b1854d09c8ee4bc01c2e0867b8 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Tue, 17 Jun 2025 16:25:30 -0500
Subject: [PATCH 094/121] Update book library secondary title sort to use title
ignore prefixes #4414
---
.../utils/queries/libraryItemsBookFilters.js | 28 +++++++++----------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js
index 73d36c58..7fa5eb41 100644
--- a/server/utils/queries/libraryItemsBookFilters.js
+++ b/server/utils/queries/libraryItemsBookFilters.js
@@ -251,6 +251,15 @@ module.exports = {
*/
getOrder(sortBy, sortDesc, collapseseries) {
const dir = sortDesc ? 'DESC' : 'ASC'
+
+ const getTitleOrder = () => {
+ if (global.ServerSettings.sortingIgnorePrefix) {
+ return [Sequelize.literal('`libraryItem`.`titleIgnorePrefix` COLLATE NOCASE'), dir]
+ } else {
+ return [Sequelize.literal('`libraryItem`.`title` COLLATE NOCASE'), dir]
+ }
+ }
+
if (sortBy === 'addedAt') {
return [[Sequelize.literal('libraryItem.createdAt'), dir]]
} else if (sortBy === 'size') {
@@ -264,25 +273,16 @@ module.exports = {
} else if (sortBy === 'media.metadata.publishedYear') {
return [[Sequelize.literal(`CAST(\`book\`.\`publishedYear\` AS INTEGER)`), dir]]
} else if (sortBy === 'media.metadata.authorNameLF') {
- return [
- [Sequelize.literal('`libraryItem`.`authorNamesLastFirst` COLLATE NOCASE'), dir],
- [Sequelize.literal('`libraryItem`.`title` COLLATE NOCASE'), dir]
- ]
+ // Sort by author name last first, secondary sort by title
+ return [[Sequelize.literal('`libraryItem`.`authorNamesLastFirst` COLLATE NOCASE'), dir], getTitleOrder()]
} else if (sortBy === 'media.metadata.authorName') {
- return [
- [Sequelize.literal('`libraryItem`.`authorNamesFirstLast` COLLATE NOCASE'), dir],
- [Sequelize.literal('`libraryItem`.`title` COLLATE NOCASE'), dir]
- ]
+ // Sort by author name first last, secondary sort by title
+ return [[Sequelize.literal('`libraryItem`.`authorNamesFirstLast` COLLATE NOCASE'), dir], getTitleOrder()]
} else if (sortBy === 'media.metadata.title') {
if (collapseseries) {
return [[Sequelize.literal('display_title COLLATE NOCASE'), dir]]
}
-
- if (global.ServerSettings.sortingIgnorePrefix) {
- return [[Sequelize.literal('`libraryItem`.`titleIgnorePrefix` COLLATE NOCASE'), dir]]
- } else {
- return [[Sequelize.literal('`libraryItem`.`title` COLLATE NOCASE'), dir]]
- }
+ return [getTitleOrder()]
} else if (sortBy === 'sequence') {
const nullDir = sortDesc ? 'DESC NULLS FIRST' : 'ASC NULLS LAST'
return [[Sequelize.literal(`CAST(\`series.bookSeries.sequence\` AS FLOAT) ${nullDir}`)]]
From 9bf8d7de11b969bb7ece861d0321db33d1b016c9 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Tue, 17 Jun 2025 17:21:21 -0500
Subject: [PATCH 095/121] Fix server crash when FantLab provider request times
out #4410
---
server/providers/FantLab.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/server/providers/FantLab.js b/server/providers/FantLab.js
index dd9f60cc..f33934ca 100644
--- a/server/providers/FantLab.js
+++ b/server/providers/FantLab.js
@@ -52,9 +52,7 @@ class FantLab {
return []
})
- return Promise.all(items.map(async (item) => await this.getWork(item, timeout))).then((resArray) => {
- return resArray.filter((res) => res)
- })
+ return Promise.all(items.map(async (item) => await this.getWork(item, timeout))).then((resArray) => resArray.filter(Boolean))
}
/**
@@ -83,6 +81,10 @@ class FantLab {
return null
})
+ if (!bookData) {
+ return null
+ }
+
return this.cleanBookData(bookData, timeout)
}
From b8849677de53859aa7f49d944984d7de06890e73 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Wed, 18 Jun 2025 17:20:36 -0500
Subject: [PATCH 096/121] Episode view modal makes timestamps in description
clickable
---
.../components/modals/podcast/ViewEpisode.vue | 40 +++++++++++++++++--
1 file changed, 37 insertions(+), 3 deletions(-)
diff --git a/client/components/modals/podcast/ViewEpisode.vue b/client/components/modals/podcast/ViewEpisode.vue
index 5a520ef4..b4358c5d 100644
--- a/client/components/modals/podcast/ViewEpisode.vue
+++ b/client/components/modals/podcast/ViewEpisode.vue
@@ -16,7 +16,7 @@
diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js
index 12469160..b62e024f 100644
--- a/server/utils/podcastUtils.js
+++ b/server/utils/podcastUtils.js
@@ -25,6 +25,7 @@ const Fuse = require('../libs/fusejs')
* @property {string} episode
* @property {string} author
* @property {string} duration
+ * @property {number|null} durationSeconds - Parsed from duration string if duration is valid
* @property {string} explicit
* @property {number} publishedAt - Unix timestamp
* @property {{ url: string, type?: string, length?: string }} enclosure
@@ -217,8 +218,9 @@ function extractEpisodeData(item) {
})
// Extract psc:chapters if duration is set
- let episodeDuration = !isNaN(episode.duration) ? timestampToSeconds(episode.duration) : null
- if (item['psc:chapters']?.[0]?.['psc:chapter']?.length && episodeDuration) {
+ episode.durationSeconds = episode.duration ? timestampToSeconds(episode.duration) : null
+
+ if (item['psc:chapters']?.[0]?.['psc:chapter']?.length && episode.durationSeconds) {
// Example chapter:
// {"id":0,"start":0,"end":43.004286,"title":"chapter 1"}
@@ -244,7 +246,7 @@ function extractEpisodeData(item) {
} else {
episode.chapters = cleanedChapters.map((chapter, index) => {
const nextChapter = cleanedChapters[index + 1]
- const end = nextChapter ? nextChapter.start : episodeDuration
+ const end = nextChapter ? nextChapter.start : episode.durationSeconds
return {
id: chapter.id,
title: chapter.title,
@@ -273,6 +275,7 @@ function cleanEpisodeData(data) {
episode: data.episode || '',
author: data.author || '',
duration: data.duration || '',
+ durationSeconds: data.durationSeconds || null,
explicit: data.explicit || '',
publishedAt,
enclosure: data.enclosure,
From bb4eec935515f5fe945b970ec2cef2b3355a4e2d Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Sat, 21 Jun 2025 12:02:44 +0200
Subject: [PATCH 099/121] add explicit
---
client/components/controls/LibraryFilterSelect.vue | 5 +++++
server/utils/queries/libraryItemsBookFilters.js | 2 ++
2 files changed, 7 insertions(+)
diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue
index f5eec41a..7bfa1fe8 100644
--- a/client/components/controls/LibraryFilterSelect.vue
+++ b/client/components/controls/LibraryFilterSelect.vue
@@ -228,6 +228,11 @@ export default {
value: 'abridged',
sublist: false
},
+ {
+ text: this.$strings.LabelExplicit,
+ value: 'explicit',
+ sublist: false
+ },
{
text: this.$strings.ButtonIssues,
value: 'issues',
diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js
index 7fa5eb41..ded712cf 100644
--- a/server/utils/queries/libraryItemsBookFilters.js
+++ b/server/utils/queries/libraryItemsBookFilters.js
@@ -186,6 +186,8 @@ module.exports = {
mediaWhere['$series.id$'] = null
} else if (group === 'abridged') {
mediaWhere['abridged'] = true
+ } else if (group === 'explicit') {
+ mediaWhere['explicit'] = true
} else if (['genres', 'tags', 'narrators'].includes(group)) {
mediaWhere[group] = Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(${group}) WHERE json_valid(${group}) AND json_each.value = :filterValue)`), {
[Sequelize.Op.gte]: 1
From 5336d0525e7bd46150369f50e08d06e1145a55b3 Mon Sep 17 00:00:00 2001
From: Vito0912 <86927734+Vito0912@users.noreply.github.com>
Date: Sat, 21 Jun 2025 12:29:54 +0200
Subject: [PATCH 100/121] add explicit to podcasts
---
client/components/controls/LibraryFilterSelect.vue | 5 +++++
server/utils/queries/libraryItemsPodcastFilters.js | 2 ++
2 files changed, 7 insertions(+)
diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue
index 7bfa1fe8..32bb9a86 100644
--- a/client/components/controls/LibraryFilterSelect.vue
+++ b/client/components/controls/LibraryFilterSelect.vue
@@ -277,6 +277,11 @@ export default {
value: 'languages',
sublist: true
},
+ {
+ text: this.$strings.LabelExplicit,
+ value: 'explicit',
+ sublist: false
+ },
{
text: this.$strings.ButtonIssues,
value: 'issues',
diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js
index 33bac28f..8bb5dc11 100644
--- a/server/utils/queries/libraryItemsPodcastFilters.js
+++ b/server/utils/queries/libraryItemsPodcastFilters.js
@@ -59,6 +59,8 @@ module.exports = {
replacements.filterValue = value
} else if (group === 'languages') {
mediaWhere['language'] = value
+ } else if (group === 'explicit') {
+ mediaWhere['explicit'] = true
}
return {
From af684e6a69bf4c0d58f2f3c019e9788d6e20876c Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 21 Jun 2025 17:01:13 -0500
Subject: [PATCH 101/121] Explicit library filter not shown for users without
permission
---
.../controls/LibraryFilterSelect.vue | 34 +++++++++++++------
client/store/user.js | 3 ++
2 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue
index 32bb9a86..62a9b803 100644
--- a/client/components/controls/LibraryFilterSelect.vue
+++ b/client/components/controls/LibraryFilterSelect.vue
@@ -94,6 +94,9 @@ export default {
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
+ userCanAccessExplicitContent() {
+ return this.$store.getters['user/getUserCanAccessExplicitContent']
+ },
libraryMediaType() {
return this.$store.getters['libraries/getCurrentLibraryMediaType']
},
@@ -228,11 +231,6 @@ export default {
value: 'abridged',
sublist: false
},
- {
- text: this.$strings.LabelExplicit,
- value: 'explicit',
- sublist: false
- },
{
text: this.$strings.ButtonIssues,
value: 'issues',
@@ -244,6 +242,15 @@ export default {
sublist: false
}
]
+
+ if (this.userCanAccessExplicitContent) {
+ items.push({
+ text: this.$strings.LabelExplicit,
+ value: 'explicit',
+ sublist: false
+ })
+ }
+
if (this.userIsAdminOrUp) {
items.push({
text: this.$strings.LabelShareOpen,
@@ -254,7 +261,7 @@ export default {
return items
},
podcastItems() {
- return [
+ const items = [
{
text: this.$strings.LabelAll,
value: 'all'
@@ -277,11 +284,6 @@ export default {
value: 'languages',
sublist: true
},
- {
- text: this.$strings.LabelExplicit,
- value: 'explicit',
- sublist: false
- },
{
text: this.$strings.ButtonIssues,
value: 'issues',
@@ -293,6 +295,16 @@ export default {
sublist: false
}
]
+
+ if (this.userCanAccessExplicitContent) {
+ items.push({
+ text: this.$strings.LabelExplicit,
+ value: 'explicit',
+ sublist: false
+ })
+ }
+
+ return items
},
selectItems() {
if (this.isSeries) return this.seriesItems
diff --git a/client/store/user.js b/client/store/user.js
index d5aad19b..41e12cad 100644
--- a/client/store/user.js
+++ b/client/store/user.js
@@ -58,6 +58,9 @@ export const getters = {
getUserCanAccessAllLibraries: (state) => {
return !!state.user?.permissions?.accessAllLibraries
},
+ getUserCanAccessExplicitContent: (state) => {
+ return !!state.user?.permissions?.accessExplicitContent
+ },
getLibrariesAccessible: (state, getters) => {
if (!state.user) return []
if (getters.getUserCanAccessAllLibraries) return []
From a992400d6abd9ca96e9b637e987ec46e4fbc5a08 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 23 Jun 2025 16:56:08 -0500
Subject: [PATCH 102/121] Add ENV REACT_CLIENT_PATH to target a Nextjs frontend
instead of Nuxt
---
index.js | 1 +
server/Auth.js | 13 ++++++++-
server/Server.js | 74 ++++++++++++++++++++++++++++--------------------
3 files changed, 57 insertions(+), 31 deletions(-)
diff --git a/index.js b/index.js
index 2839c238..7fcd90fc 100644
--- a/index.js
+++ b/index.js
@@ -28,6 +28,7 @@ if (isDev) {
if (devEnv.SkipBinariesCheck) process.env.SKIP_BINARIES_CHECK = '1'
if (devEnv.AllowIframe) process.env.ALLOW_IFRAME = '1'
if (devEnv.BackupPath) process.env.BACKUP_PATH = devEnv.BackupPath
+ if (devEnv.ReactClientPath) process.env.REACT_CLIENT_PATH = devEnv.ReactClientPath
process.env.SOURCE = 'local'
process.env.ROUTER_BASE_PATH = devEnv.RouterBasePath ?? '/audiobookshelf'
}
diff --git a/server/Auth.js b/server/Auth.js
index ba72947a..4e76ee33 100644
--- a/server/Auth.js
+++ b/server/Auth.js
@@ -442,7 +442,17 @@ class Auth {
// Local strategy login route (takes username and password)
router.post('/login', passport.authenticate('local'), async (req, res) => {
// return the user login response json if the login was successfull
- res.json(await this.getUserLoginResponsePayload(req.user))
+ const userResponse = await this.getUserLoginResponsePayload(req.user)
+
+ // Experimental Next.js client uses bearer token in cookies
+ res.cookie('auth_token', userResponse.user.token, {
+ httpOnly: true,
+ secure: req.secure || req.get('x-forwarded-proto') === 'https',
+ sameSite: 'strict',
+ maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days
+ })
+
+ res.json(userResponse)
})
// openid strategy login route (this redirects to the configured openid login provider)
@@ -718,6 +728,7 @@ class Auth {
const authMethod = req.cookies.auth_method
res.clearCookie('auth_method')
+ res.clearCookie('auth_token')
let logoutUrl = null
diff --git a/server/Server.js b/server/Server.js
index 5c6f3c16..22a53a3a 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -220,6 +220,7 @@ class Server {
async start() {
Logger.info('=== Starting Server ===')
+
this.initProcessEventListeners()
await this.init()
@@ -281,6 +282,7 @@ class Server {
await this.auth.initPassportJs()
const router = express.Router()
+
// if RouterBasePath is set, modify all requests to include the base path
app.use((req, res, next) => {
const urlStartsWithRouterBasePath = req.url.startsWith(global.RouterBasePath)
@@ -313,10 +315,6 @@ class Server {
router.use('/hls', this.hlsRouter.router)
router.use('/public', this.publicRouter.router)
- // Static path to generated nuxt
- const distPath = Path.join(global.appRoot, '/client/dist')
- router.use(express.static(distPath))
-
// Static folder
router.use(express.static(Path.join(global.appRoot, 'static')))
@@ -336,32 +334,6 @@ class Server {
// Auth routes
await this.auth.initAuthRoutes(router)
- // Client dynamic routes
- const dynamicRoutes = [
- '/item/:id',
- '/author/:id',
- '/audiobook/:id/chapters',
- '/audiobook/:id/edit',
- '/audiobook/:id/manage',
- '/library/:library',
- '/library/:library/search',
- '/library/:library/bookshelf/:id?',
- '/library/:library/authors',
- '/library/:library/narrators',
- '/library/:library/stats',
- '/library/:library/series/:id?',
- '/library/:library/podcast/search',
- '/library/:library/podcast/latest',
- '/library/:library/podcast/download-queue',
- '/config/users/:id',
- '/config/users/:id/sessions',
- '/config/item-metadata-utils/:id',
- '/collection/:id',
- '/playlist/:id',
- '/share/:slug'
- ]
- dynamicRoutes.forEach((route) => router.get(route, (req, res) => res.sendFile(Path.join(distPath, 'index.html'))))
-
router.post('/init', (req, res) => {
if (Database.hasRootUser) {
Logger.error(`[Server] attempt to init server when server already has a root user`)
@@ -392,6 +364,48 @@ class Server {
})
router.get('/healthcheck', (req, res) => res.sendStatus(200))
+ const ReactClientPath = process.env.REACT_CLIENT_PATH
+ if (!ReactClientPath) {
+ // Static path to generated nuxt
+ const distPath = Path.join(global.appRoot, '/client/dist')
+ router.use(express.static(distPath))
+
+ // Client dynamic routes
+ const dynamicRoutes = [
+ '/item/:id',
+ '/author/:id',
+ '/audiobook/:id/chapters',
+ '/audiobook/:id/edit',
+ '/audiobook/:id/manage',
+ '/library/:library',
+ '/library/:library/search',
+ '/library/:library/bookshelf/:id?',
+ '/library/:library/authors',
+ '/library/:library/narrators',
+ '/library/:library/stats',
+ '/library/:library/series/:id?',
+ '/library/:library/podcast/search',
+ '/library/:library/podcast/latest',
+ '/library/:library/podcast/download-queue',
+ '/config/users/:id',
+ '/config/users/:id/sessions',
+ '/config/item-metadata-utils/:id',
+ '/collection/:id',
+ '/playlist/:id',
+ '/share/:slug'
+ ]
+ dynamicRoutes.forEach((route) => router.get(route, (req, res) => res.sendFile(Path.join(distPath, 'index.html'))))
+ } else {
+ // This is for using the experimental Next.js client
+ Logger.info(`Using React client at ${ReactClientPath}`)
+ const nextPath = Path.join(ReactClientPath, 'node_modules/next')
+ const next = require(nextPath)
+ const nextApp = next({ dev: Logger.isDev, dir: ReactClientPath })
+ const handle = nextApp.getRequestHandler()
+ await nextApp.prepare()
+ router.get('*', (req, res) => handle(req, res))
+ }
+
const unixSocketPrefix = 'unix/'
if (this.Host?.startsWith(unixSocketPrefix)) {
const sockPath = this.Host.slice(unixSocketPrefix.length)
From e384863148df101c6bd25798e3c9f71cd7357a98 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Thu, 26 Jun 2025 17:21:58 -0500
Subject: [PATCH 103/121] Add support for running in production with dev.js
config, node index --prod-with-dev-env
---
index.js | 6 ++++--
package.json | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/index.js b/index.js
index 7fcd90fc..7379322e 100644
--- a/index.js
+++ b/index.js
@@ -4,7 +4,9 @@ const optionDefinitions = [
{ name: 'port', alias: 'p', type: String },
{ name: 'host', alias: 'h', type: String },
{ name: 'source', alias: 's', type: String },
- { name: 'dev', alias: 'd', type: Boolean }
+ { name: 'dev', alias: 'd', type: Boolean },
+ // Run in production mode and use dev.js config
+ { name: 'prod-with-dev-env', alias: 'r', type: Boolean }
]
const commandLineArgs = require('./server/libs/commandLineArgs')
@@ -17,7 +19,7 @@ const server = require('./server/Server')
global.appRoot = __dirname
const isDev = process.env.NODE_ENV !== 'production'
-if (isDev) {
+if (isDev || options['prod-with-dev-env']) {
const devEnv = require('./dev').config
if (devEnv.Port) process.env.PORT = devEnv.Port
if (devEnv.ConfigPath) process.env.CONFIG_PATH = devEnv.ConfigPath
diff --git a/package.json b/package.json
index 6229a18b..2fd1a87e 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"scripts": {
"dev": "nodemon --watch server index.js -- --dev",
"start": "node index.js",
+ "start-dev": "node index.js --prod-with-dev-env",
"client": "cd client && npm ci && npm run generate",
"prod": "npm run client && npm ci && node index.js",
"build-win": "npm run client && pkg -t node20-win-x64 -o ./dist/win/audiobookshelf -C GZip .",
From b13b80e0119657e6ceef51e5d54650ebdd4b636e Mon Sep 17 00:00:00 2001
From: petr-prikryl
Date: Mon, 16 Jun 2025 11:54:44 +0200
Subject: [PATCH 104/121] Translated using Weblate (Czech)
Currently translated at 99.9% (1106 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
---
client/strings/cs.json | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/client/strings/cs.json b/client/strings/cs.json
index fca6bf33..3ee9b087 100644
--- a/client/strings/cs.json
+++ b/client/strings/cs.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Spuštěno po dokončení zálohování",
"NotificationOnBackupFailedDescription": "Spuštěno pokud zálohování selže",
"NotificationOnEpisodeDownloadedDescription": "Spuštěno při automatickém stažení epizody podcastu",
+ "NotificationOnRSSFeedDisabledDescription": "Aktivováno když je automatické stahování pozastaveno z důvodu příliš mnoho neůspěšných pokusů",
+ "NotificationOnRSSFeedFailedDescription": "Aktivováno když selže RSS kanál pro stahování epizod",
"NotificationOnTestDescription": "Akce pro otestování upozorňovacího systému",
"PlaceholderNewCollection": "Nový název kolekce",
"PlaceholderNewFolderPath": "Nová cesta ke složce",
@@ -962,7 +964,7 @@
"ToastBackupRestoreFailed": "Nepodařilo se obnovit zálohu",
"ToastBackupUploadFailed": "Nepodařilo se nahrát zálohu",
"ToastBackupUploadSuccess": "Záloha nahrána",
- "ToastBatchApplyDetailsToItemsSuccess": "Detaily aplikované na položky",
+ "ToastBatchApplyDetailsToItemsSuccess": "Detaily byly aplikované na položky",
"ToastBatchDeleteFailed": "Hromadné smazání selhalo",
"ToastBatchDeleteSuccess": "Hromadné smazání proběhlo úspěšně",
"ToastBatchQuickMatchFailed": "Rychlá schoda dávky se nezdařila!",
@@ -976,7 +978,7 @@
"ToastCachePurgeSuccess": "Vyrovnávací paměť úspěšně vyčištěna",
"ToastChaptersHaveErrors": "Kapitoly obsahují chyby",
"ToastChaptersInvalidShiftAmountLast": "Nesprávná délka posunu. Čas začátku poslední kapitoly by přesáhl dobu trvání této audioknihy.",
- "ToastChaptersInvalidShiftAmountStart": "Nesprávná délka posunu. První kapitola by měla nulovou nebo zápornou délku a byla by přepsána druhou kapitolou. Zvětšete počáteční délku druhé kapitoly.",
+ "ToastChaptersInvalidShiftAmountStart": "Nesprávná délka posunu. První kapitola by měla nulovou nebo zápornou délku a byla by přepsána druhou kapitolou. Zvětšete čas začátku druhé kapitoly.",
"ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy",
"ToastChaptersRemoved": "Kapitoly odstraněny",
"ToastChaptersUpdated": "Kapitola aktualizována",
From dd74d0a7267671748c939777e8318398a8c97d03 Mon Sep 17 00:00:00 2001
From: David Havndrup Munch
Date: Mon, 16 Jun 2025 08:07:46 +0200
Subject: [PATCH 105/121] Translated using Weblate (Danish)
Currently translated at 98.9% (1095 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
---
client/strings/da.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/da.json b/client/strings/da.json
index 8ceb50ee..1f390bd3 100644
--- a/client/strings/da.json
+++ b/client/strings/da.json
@@ -910,6 +910,8 @@
"NotificationOnBackupCompletedDescription": "Udløst når backup er færdig",
"NotificationOnBackupFailedDescription": "Udløst når backup fejler",
"NotificationOnEpisodeDownloadedDescription": "Udløst når et podcast afsnit er automatisk downloadet",
+ "NotificationOnRSSFeedDisabledDescription": "Aktiveret når automatiske episode-downloads er slået fra, på grund af for mange forsøg",
+ "NotificationOnRSSFeedFailedDescription": "Aktiveret når anmodning om RSS-feedet fejler for en automatisk episode-download",
"NotificationOnTestDescription": "Event for test af notifikationssystemet",
"PlaceholderNewCollection": "Nyt samlingnavn",
"PlaceholderNewFolderPath": "Ny mappes sti",
From fbf482d6b6f49f8bf2e1837f099f90ea77633514 Mon Sep 17 00:00:00 2001
From: B0rax
Date: Tue, 17 Jun 2025 20:33:27 +0200
Subject: [PATCH 106/121] Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/client/strings/de.json b/client/strings/de.json
index 4efe72e9..3a516108 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -708,7 +708,7 @@
"MessageAddToPlayerQueue": "Zur Abspielwarteliste hinzufügen",
"MessageAppriseDescription": "Um diese Funktion nutzen zu können, musst du eine Instanz von Apprise API laufen haben oder eine API verwenden welche dieselbe Anfragen bearbeiten kann. Die Apprise API Url muss der vollständige URL-Pfad sein, an den die Benachrichtigung gesendet werden soll, z.B. wenn Ihre API-Instanz unter http://192.168.1.1:8337 läuft, würdest du http://192.168.1.1:8337/notify eingeben.",
"MessageAsinCheck": "Stellen Sie sicher, dass Sie die ASIN aus der richtigen Audible Region verwenden, nicht Amazon.",
- "MessageAuthenticationOIDCChangesRestart": "Nach dem Speichern muß der Server neugestartet werden um die OIDC Änderungen zu übernehmen.",
+ "MessageAuthenticationOIDCChangesRestart": "Nach dem Speichern muss der Server neugestartet werden um die OIDC Änderungen zu übernehmen.",
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in /metadata/items & /metadata/authors gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
"MessageBackupsLocationEditNote": "Hinweis: Durch das Aktualisieren des Backup-Speicherorts werden vorhandene Sicherungen nicht verschoben oder geändert",
"MessageBackupsLocationNoEditNote": "Hinweis: Der Sicherungsspeicherort wird über eine Umgebungsvariable festgelegt und kann hier nicht geändert werden.",
@@ -918,6 +918,7 @@
"NotificationOnBackupCompletedDescription": "Wird ausgeführt wenn ein Backup erstellt wurde",
"NotificationOnBackupFailedDescription": "Wird ausgeführt wenn ein Backup fehlgeschlagen ist",
"NotificationOnEpisodeDownloadedDescription": "Wird ausgeführt wenn eine Podcast Folge automatisch heruntergeladen wird",
+ "NotificationOnRSSFeedDisabledDescription": "Wird ausgeführt wenn automatische Downloads von Episoden wegen zu vielen fehlgeschlagenen Versuchen deaktiviert sind",
"NotificationOnTestDescription": "Wird ausgeführt wenn das Benachrichtigungssystem getestet wird",
"PlaceholderNewCollection": "Neuer Sammlungsname",
"PlaceholderNewFolderPath": "Neuer Ordnerpfad",
From b9969c78a68d614713e7628c0b914ba90dc5cfdf Mon Sep 17 00:00:00 2001
From: Stefan Ha
Date: Tue, 17 Jun 2025 20:30:48 +0200
Subject: [PATCH 107/121] Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/de.json b/client/strings/de.json
index 3a516108..19e2394c 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -852,7 +852,7 @@
"MessageResetChaptersConfirm": "Kapitel und vorgenommenen Änderungen werden zurückgesetzt und rückgängig gemacht! Bist du dir sicher?",
"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.",
+ "MessageScheduleLibraryScanNote": "Für die meisten Anwender wird empfohlen, diese Funktion deaktiviert und die Ordnerüberwachung aktiviert zu lassen. Die Ordnerüberwachung wird Änderungen in den Bibliotheksordnern automatisch erkennen. Die Ordnerüberwachung funktioniert nicht mit allen Dateisystemen (wie NFS), hier kann stattdessen die automatischen Bibliothekssuchen verwendet werden.",
"MessageScheduleRunEveryWeekdayAtTime": "Immer {0} um {1} ausführen",
"MessageSearchResultsFor": "Suchergebnisse für",
"MessageSelected": "{0} ausgewählt",
From d614373c6471f3364d4c05b3f762f1038acb389e Mon Sep 17 00:00:00 2001
From: Daniel Schosser
Date: Mon, 16 Jun 2025 08:45:17 +0200
Subject: [PATCH 108/121] Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/strings/de.json b/client/strings/de.json
index 19e2394c..9ff3f133 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -919,6 +919,7 @@
"NotificationOnBackupFailedDescription": "Wird ausgeführt wenn ein Backup fehlgeschlagen ist",
"NotificationOnEpisodeDownloadedDescription": "Wird ausgeführt wenn eine Podcast Folge automatisch heruntergeladen wird",
"NotificationOnRSSFeedDisabledDescription": "Wird ausgeführt wenn automatische Downloads von Episoden wegen zu vielen fehlgeschlagenen Versuchen deaktiviert sind",
+ "NotificationOnRSSFeedFailedDescription": "Wird ausgelöst, wenn die RSS-Feed-Anforderung für einen automatischen Episoden-Download fehlschlägt",
"NotificationOnTestDescription": "Wird ausgeführt wenn das Benachrichtigungssystem getestet wird",
"PlaceholderNewCollection": "Neuer Sammlungsname",
"PlaceholderNewFolderPath": "Neuer Ordnerpfad",
From cf6f49ce7592229ffec9b48a4ed1583a782a71b4 Mon Sep 17 00:00:00 2001
From: biuklija
Date: Mon, 16 Jun 2025 15:45:55 +0200
Subject: [PATCH 109/121] Translated using Weblate (Croatian)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
---
client/strings/hr.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/strings/hr.json b/client/strings/hr.json
index c4715ff3..267d8a67 100644
--- a/client/strings/hr.json
+++ b/client/strings/hr.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Pokreće se po završetku sigurnosnog kopiranja",
"NotificationOnBackupFailedDescription": "Pokreće se kada sigurnosno kopiranje ne uspije",
"NotificationOnEpisodeDownloadedDescription": "Pokreće se kada se nastavak podcasta automatski preuzme",
+ "NotificationOnRSSFeedDisabledDescription": "Pokreće se kada su automatska preuzimanja nastavaka onemogućena zbog previše neuspjelih pokušaja",
+ "NotificationOnRSSFeedFailedDescription": "Pokreće se u slučaju pogreške pri pokušaju automatskog preuzimanja nastavka s RSS izvora",
"NotificationOnTestDescription": "Događaj za testiranje sustava obavijesti",
"PlaceholderNewCollection": "Ime nove zbirke",
"PlaceholderNewFolderPath": "Nova putanja mape",
From 35bb77c9c24e1d0637a9fe79069ac1d2967b7c2a Mon Sep 17 00:00:00 2001
From: Mathias Franco
Date: Mon, 16 Jun 2025 09:45:06 +0200
Subject: [PATCH 110/121] Translated using Weblate (Dutch)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
---
client/strings/nl.json | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/client/strings/nl.json b/client/strings/nl.json
index fd1a52c2..c20efa12 100644
--- a/client/strings/nl.json
+++ b/client/strings/nl.json
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Afspeellijst",
"HeaderPlaylistItems": "Onderdelen in afspeellijst",
"HeaderPodcastsToAdd": "Toe te voegen podcasts",
+ "HeaderPresets": "Voorinstellingen",
"HeaderPreviewCover": "Preview cover",
"HeaderRSSFeedGeneral": "RSS-details",
"HeaderRSSFeedIsOpen": "RSS-feed is open",
@@ -513,7 +514,7 @@
"LabelPublishers": "Uitgevers",
"LabelRSSFeedCustomOwnerEmail": "Aangepast e-mailadres eigenaar",
"LabelRSSFeedCustomOwnerName": "Aangepaste naam eigenaar",
- "LabelRSSFeedOpen": "RSS-feed open",
+ "LabelRSSFeedOpen": "RSS Feed Open",
"LabelRSSFeedPreventIndexing": "Voorkom indexering",
"LabelRSSFeedSlug": "RSS-feed slug",
"LabelRSSFeedURL": "RSS-feed URL",
@@ -605,6 +606,7 @@
"LabelSlug": "Slak",
"LabelSortAscending": "Oplopend",
"LabelSortDescending": "Aflopend",
+ "LabelSortPubDate": "Sorteer Pub Datum",
"LabelStart": "Start",
"LabelStartTime": "Starttijd",
"LabelStarted": "Gestart",
@@ -705,6 +707,8 @@
"LabelYourProgress": "Je voortgang",
"MessageAddToPlayerQueue": "Toevoegen aan wachtrij",
"MessageAppriseDescription": "Om deze functie te gebruiken heb je een draaiende instantie van Apprise API nodig of een api die dezelfde requests afhandelt. De Apprise API Url moet het volledige URL-pad zijn om de notificatie te verzenden, b.v., als je API-instantie draait op http://192.168.1.1:8337 dan zou je http://192.168.1.1:8337/notify gebruiken.",
+ "MessageAsinCheck": "Zorg ervoor dat u de ASIN van de juiste Audible-regio gebruikt, niet die van Amazon.",
+ "MessageAuthenticationOIDCChangesRestart": "Start uw server opnieuw op nadat u het opslaan hebt uitgevoerd, om de OIDC-wijzigingen toe te passen.",
"MessageBackupsDescription": "Back-ups omvatten gebruikers, gebruikers' voortgang, bibliotheekonderdeeldetails, serverinstellingen en afbeeldingen bewaard in /metadata/items & /metadata/authors. Back-ups bevatten niet de bestanden bewaard in je bibliotheekmappen.",
"MessageBackupsLocationEditNote": "Let op: het bijwerken van de back-uplocatie zal bestaande back-ups niet verplaatsen of wijzigen",
"MessageBackupsLocationNoEditNote": "Let op: De back-uplocatie wordt ingesteld via een omgevingsvariabele en kan hier niet worden gewijzigd.",
@@ -781,8 +785,8 @@
"MessageImportantNotice": "Belangrijke opmerking!",
"MessageInsertChapterBelow": "Hoofdstuk hieronder invoegen",
"MessageInvalidAsin": "Ongeldige ASIN",
- "MessageItemsSelected": "{0} onderdelen geselecteerd",
- "MessageItemsUpdated": "{0} onderdelen bijgewerkt",
+ "MessageItemsSelected": "{0} items geselecteerd",
+ "MessageItemsUpdated": "{0} items bijgewerkt",
"MessageJoinUsOn": "Doe mee op",
"MessageLoading": "Aan het laden...",
"MessageLoadingFolders": "Mappen aan het laden...",
@@ -849,8 +853,10 @@
"MessageRestoreBackupConfirm": "Weet je zeker dat je wil herstellen met behulp van de back-up gemaakt op",
"MessageRestoreBackupWarning": "Herstellen met een back-up zal de volledige database in /config en de covers in /metadata/items & /metadata/authors overschrijven.
Back-ups wijzigen geen bestanden in je bibliotheekmappen. Als je de serverinstelling gebruikt om covers en metadata in je bibliotheekmappen te bewaren dan worden deze niet geback-upt of overschreven.
Alle clients die van je server gebruik maken zullen automatisch worden ververst.",
"MessageScheduleLibraryScanNote": "Voor de meeste gebruikers is het raadzaam om deze functie uitgeschakeld te laten en de folder watcher-instelling ingeschakeld te houden. De folder watcher detecteert automatisch wijzigingen in uw bibliotheekmappen. De folder watcher werkt niet voor elk bestandssysteem (zoals NFS), dus geplande bibliotheekscans kunnen in plaats daarvan worden gebruikt.",
+ "MessageScheduleRunEveryWeekdayAtTime": "Elke {0} uitvoeren op {1}",
"MessageSearchResultsFor": "Zoekresultaten voor",
"MessageSelected": "{0} geselecteerd",
+ "MessageSeriesSequenceCannotContainSpaces": "Serievolgorde mag geen spaties bevatten",
"MessageServerCouldNotBeReached": "Server niet bereikbaar",
"MessageSetChaptersFromTracksDescription": "Stel hoofdstukken in met ieder audiobestand als een hoofdstuk en de audiobestandsnaam als hoofdstuktitel",
"MessageShareExpirationWillBe": "Vervaldatum is {0}",
@@ -912,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Wordt geactiveerd wanneer een back-up is voltooid",
"NotificationOnBackupFailedDescription": "Wordt geactiveerd wanneer een back-up mislukt",
"NotificationOnEpisodeDownloadedDescription": "Wordt geactiveerd wanneer een podcastaflevering automatisch wordt gedownload",
+ "NotificationOnRSSFeedDisabledDescription": "Wordt geactiveerd wanneer automatische afleveringsdownloads zijn uitgeschakeld vanwege te veel mislukte pogingen",
+ "NotificationOnRSSFeedFailedDescription": "Getriggerd wanneer de RSS feed aanvraag faalt voor een automatische aflevering download",
"NotificationOnTestDescription": "Event voor het testen van het notificatiesysteem",
"PlaceholderNewCollection": "Nieuwe naam collectie",
"PlaceholderNewFolderPath": "Nieuwe locatie map",
@@ -956,6 +964,7 @@
"ToastBackupRestoreFailed": "Herstellen back-up mislukt",
"ToastBackupUploadFailed": "Uploaden back-up mislukt",
"ToastBackupUploadSuccess": "Back-up geüpload",
+ "ToastBatchApplyDetailsToItemsSuccess": "Details toegepast op items",
"ToastBatchDeleteFailed": "Batch verwijderen mislukt",
"ToastBatchDeleteSuccess": "Batch verwijderen gelukt",
"ToastBatchQuickMatchFailed": "Batch Snel Vergelijken mislukt!",
@@ -968,6 +977,8 @@
"ToastCachePurgeFailed": "Cache wissen is mislukt",
"ToastCachePurgeSuccess": "Cache succesvol verwijderd",
"ToastChaptersHaveErrors": "Hoofdstukken bevatten fouten",
+ "ToastChaptersInvalidShiftAmountLast": "Ongeldige shift-tijd. De starttijd van het laatste hoofdstuk zou langer zijn dan de duur van dit audioboek.",
+ "ToastChaptersInvalidShiftAmountStart": "Ongeldige shift-lengte. Het eerste hoofdstuk zou nul of een negatieve lengte hebben en zou worden overschreven door het tweede hoofdstuk. Verleng de startduur van het tweede hoofdstuk.",
"ToastChaptersMustHaveTitles": "Hoofdstukken moeten titels hebben",
"ToastChaptersRemoved": "Hoofdstukken verwijderd",
"ToastChaptersUpdated": "Hoofdstukken bijgewerkt",
@@ -1068,6 +1079,7 @@
"ToastSelectAtLeastOneUser": "Selecteer ten minste een gebruiker",
"ToastSendEbookToDeviceFailed": "Ebook naar apparaat sturen mislukt",
"ToastSendEbookToDeviceSuccess": "Ebook verstuurd naar apparaat \"{0}\"",
+ "ToastSeriesSubmitFailedSameName": "Kan niet twee series met dezelfde naam toevoegen",
"ToastSeriesUpdateFailed": "Bijwerken serie mislukt",
"ToastSeriesUpdateSuccess": "Bijwerken serie gelukt",
"ToastServerSettingsUpdateSuccess": "Server instellingen bijgewerkt",
@@ -1086,6 +1098,8 @@
"ToastUnknownError": "Onbekende fout",
"ToastUnlinkOpenIdFailed": "Gebruiker ontkoppelen van OpenID mislukt",
"ToastUnlinkOpenIdSuccess": "Gebruiker ontkoppeld van OpenID",
+ "ToastUploaderFilepathExistsError": "Bestandspad \"{0}\" bestaat al op de server",
+ "ToastUploaderItemExistsInSubdirectoryError": "Item \"{0}\" gebruikt een submap van het uploadpad.",
"ToastUserDeleteFailed": "Verwijderen gebruiker mislukt",
"ToastUserDeleteSuccess": "Gebruiker verwijderd",
"ToastUserPasswordChangeSuccess": "Wachtwoord succesvol gewijzigd",
From 9722674072d0ab96b65efcee79de5d2af374c179 Mon Sep 17 00:00:00 2001
From: SunSpring
Date: Tue, 17 Jun 2025 04:53:29 +0200
Subject: [PATCH 111/121] Translated using Weblate (Chinese (Simplified Han
script))
Currently translated at 100.0% (1107 of 1107 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, 2 insertions(+)
diff --git a/client/strings/zh-cn.json b/client/strings/zh-cn.json
index 0f9f4a99..0b6c3422 100644
--- a/client/strings/zh-cn.json
+++ b/client/strings/zh-cn.json
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "备份完成时触发",
"NotificationOnBackupFailedDescription": "备份失败时触发",
"NotificationOnEpisodeDownloadedDescription": "当播客节目自动下载时触发",
+ "NotificationOnRSSFeedDisabledDescription": "由于尝试失败次数过多而导致剧集自动下载被禁用时触发",
+ "NotificationOnRSSFeedFailedDescription": "当 RSS 源请求自动下载剧集失败时触发",
"NotificationOnTestDescription": "测试通知系统的事件",
"PlaceholderNewCollection": "输入收藏夹名称",
"PlaceholderNewFolderPath": "输入文件夹路径",
From 4462d32e980e8bc62df3d4a67da496494b38a67b Mon Sep 17 00:00:00 2001
From: burghy86
Date: Wed, 18 Jun 2025 10:57:36 +0200
Subject: [PATCH 112/121] Translated using Weblate (Italian)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
---
client/strings/it.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/client/strings/it.json b/client/strings/it.json
index 41c06175..77e4faee 100644
--- a/client/strings/it.json
+++ b/client/strings/it.json
@@ -514,7 +514,7 @@
"LabelPublishers": "Editori",
"LabelRSSFeedCustomOwnerEmail": "E-mail del proprietario personalizzato",
"LabelRSSFeedCustomOwnerName": "Nome del proprietario personalizzato",
- "LabelRSSFeedOpen": "Flusso RSS aperto",
+ "LabelRSSFeedOpen": "Feed RSS aperto",
"LabelRSSFeedPreventIndexing": "Impedisci l'indicizzazione",
"LabelRSSFeedSlug": "Parole chiave del flusso RSS",
"LabelRSSFeedURL": "URL del flusso RSS",
@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Attivato al completamento di un backup",
"NotificationOnBackupFailedDescription": "Attivato quando un backup fallisce",
"NotificationOnEpisodeDownloadedDescription": "Attivato quando un episodio di podcast viene scaricato automaticamente",
+ "NotificationOnRSSFeedDisabledDescription": "Attivato quando i download automatici degli episodi vengono disabilitati a causa di troppi tentativi falliti",
+ "NotificationOnRSSFeedFailedDescription": "Attivato quando la richiesta del feed RSS per il download automatico di un episodio fallisce",
"NotificationOnTestDescription": "test il sistema di notifica",
"PlaceholderNewCollection": "Nome Nuova Raccolta",
"PlaceholderNewFolderPath": "Nuovo Percorso Cartella",
From 276a179446a3adfe978872fbe6051c2925d59a1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20F=C3=B6rster?=
Date: Fri, 20 Jun 2025 17:02:12 +0200
Subject: [PATCH 113/121] Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
---
client/strings/de.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/strings/de.json b/client/strings/de.json
index 9ff3f133..04de453e 100644
--- a/client/strings/de.json
+++ b/client/strings/de.json
@@ -53,7 +53,7 @@
"ButtonNext": "Vor",
"ButtonNextChapter": "Nächstes Kapitel",
"ButtonNextItemInQueue": "Das nächste Element in der Warteschlange",
- "ButtonOk": "Einverstanden",
+ "ButtonOk": "Ok",
"ButtonOpenFeed": "Feed öffnen",
"ButtonOpenManager": "Manager öffnen",
"ButtonPause": "Pausieren",
From 34315d4c10affd3029b4c54a0c558fecf2edba62 Mon Sep 17 00:00:00 2001
From: Dan Johansen
Date: Mon, 23 Jun 2025 11:58:47 +0200
Subject: [PATCH 114/121] Translated using Weblate (Danish)
Currently translated at 99.7% (1104 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
---
client/strings/da.json | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/client/strings/da.json b/client/strings/da.json
index 1f390bd3..342529f7 100644
--- a/client/strings/da.json
+++ b/client/strings/da.json
@@ -177,6 +177,7 @@
"HeaderPlaylist": "Afspilningsliste",
"HeaderPlaylistItems": "Afspilningsliste Elementer",
"HeaderPodcastsToAdd": "Podcasts til Tilføjelse",
+ "HeaderPresets": "Forudindstillinger",
"HeaderPreviewCover": "Forhåndsvis Omslag",
"HeaderRSSFeedGeneral": "RSS Detaljer",
"HeaderRSSFeedIsOpen": "RSS Feed er Åben",
@@ -530,6 +531,7 @@
"LabelReleaseDate": "Udgivelsesdato",
"LabelRemoveAllMetadataAbs": "Fjern alle metadata.abs filer",
"LabelRemoveAllMetadataJson": "Fjern alle metadata.json filer",
+ "LabelRemoveAudibleBranding": "Fjern Audible intro og outro fra kapitler",
"LabelRemoveCover": "Fjern omslag",
"LabelRemoveMetadataFile": "Fjern alle metadata filer i biblioteksmapper",
"LabelRemoveMetadataFileHelp": "Fjern alle metadata.json og metadata.abs filer i dine {0} mapper.",
@@ -604,6 +606,7 @@
"LabelSlug": "Snegl",
"LabelSortAscending": "Stigende",
"LabelSortDescending": "Faldende",
+ "LabelSortPubDate": "Sortér Pub Dato",
"LabelStart": "Start",
"LabelStartTime": "Starttid",
"LabelStarted": "Startet",
@@ -704,6 +707,8 @@
"LabelYourProgress": "Din fremgang",
"MessageAddToPlayerQueue": "Tilføj til afspilningskø",
"MessageAppriseDescription": "For at bruge denne funktion skal du have en instans af Apprise API kørende eller en API, der håndterer de samme anmodninger. Apprise API-webadressen skal være den fulde URL-sti for at sende underretningen, f.eks. hvis din API-instans er tilgængelig på http://192.168.1.1:8337, så skal du bruge http://192.168.1.1:8337/notify.",
+ "MessageAsinCheck": "Sikr dig at du bruger ASIN fra den korrekte Audible region, ikke Amazon.",
+ "MessageAuthenticationOIDCChangesRestart": "Genstart sin server efter du har gemt for at bekræfte OIDC ændringer.",
"MessageBackupsDescription": "Backups inkluderer brugere, brugerfremskridt, biblioteksvareoplysninger, serverindstillinger og billeder gemt i /metadata/items og /metadata/authors. Backups inkluderer ikke nogen filer gemt i dine biblioteksmapper.",
"MessageBackupsLocationEditNote": "Note: Opdatering af backup sti vil ikke fjerne eller modificere eksisterende backups",
"MessageBackupsLocationNoEditNote": "Note: Backup sti er sat igennem miljøvariabel og kan ikke ændres her.",
@@ -722,6 +727,7 @@
"MessageChapterErrorStartGteDuration": "Ugyldig starttid skal være mindre end lydbogens varighed",
"MessageChapterErrorStartLtPrev": "Ugyldig starttid skal være større end eller lig med den foregående kapitels starttid",
"MessageChapterStartIsAfter": "Kapitelstarten er efter slutningen af din lydbog",
+ "MessageChaptersNotFound": "Kapitler ikke fundet",
"MessageCheckingCron": "Tjekker cron...",
"MessageConfirmCloseFeed": "Er du sikker på, at du vil lukke dette feed?",
"MessageConfirmDeleteBackup": "Er du sikker på, at du vil slette backup for {0}?",
@@ -778,6 +784,7 @@
"MessageForceReScanDescription": "vil scanne alle filer igen som en frisk scanning. Lydfilens ID3-tags, OPF-filer og tekstfiler scannes som nye.",
"MessageImportantNotice": "Vigtig besked!",
"MessageInsertChapterBelow": "Indsæt kapitel nedenfor",
+ "MessageInvalidAsin": "Ugyldig ASIN",
"MessageItemsSelected": "{0} elementer valgt",
"MessageItemsUpdated": "{0} elementer opdateret",
"MessageJoinUsOn": "Deltag i os på",
@@ -849,6 +856,7 @@
"MessageScheduleRunEveryWeekdayAtTime": "Kør hvert {0} af {1}",
"MessageSearchResultsFor": "Søgeresultater for",
"MessageSelected": "{0} valgt",
+ "MessageSeriesSequenceCannotContainSpaces": "Serie sekvens kan ikke indeholde mellemrum",
"MessageServerCouldNotBeReached": "Serveren kunne ikke nås",
"MessageSetChaptersFromTracksDescription": "Indstil kapitler ved at bruge hver lydfil som et kapitel og kapiteloverskrift som lydfilnavn",
"MessageShareExpirationWillBe": "Udløb vil være {0}",
@@ -956,6 +964,7 @@
"ToastBackupRestoreFailed": "Mislykkedes gendannelse af sikkerhedskopi",
"ToastBackupUploadFailed": "Mislykkedes upload af sikkerhedskopi",
"ToastBackupUploadSuccess": "Sikkerhedskopi uploadet",
+ "ToastBatchApplyDetailsToItemsSuccess": "Detaljer bekræftet på element",
"ToastBatchDeleteFailed": "Batch slet fejlede",
"ToastBatchDeleteSuccess": "Batch slet succes",
"ToastBatchQuickMatchFailed": "Batch Hurtig Match fejlede!",
From d102065d025dda59f8d515ba53f9fe8114b98891 Mon Sep 17 00:00:00 2001
From: Eigen_art
Date: Tue, 24 Jun 2025 23:22:03 +0200
Subject: [PATCH 115/121] Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1107 of 1107 strings)
Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
---
client/strings/uk.json | 230 ++++++++++++++++++++---------------------
1 file changed, 115 insertions(+), 115 deletions(-)
diff --git a/client/strings/uk.json b/client/strings/uk.json
index 56b049da..70cb4bb3 100644
--- a/client/strings/uk.json
+++ b/client/strings/uk.json
@@ -3,7 +3,7 @@
"ButtonAddChapters": "Додати глави",
"ButtonAddDevice": "Додати пристрій",
"ButtonAddLibrary": "Додати бібліотеку",
- "ButtonAddPodcasts": "Додати подкаст",
+ "ButtonAddPodcasts": "Додати подкасти",
"ButtonAddUser": "Додати користувача",
"ButtonAddYourFirstLibrary": "Додайте вашу першу бібліотеку",
"ButtonApply": "Застосувати",
@@ -16,7 +16,7 @@
"ButtonCancel": "Скасувати",
"ButtonCancelEncode": "Скасувати кодування",
"ButtonChangeRootPassword": "Змінити кореневий пароль",
- "ButtonCheckAndDownloadNewEpisodes": "Перевірити та завантажити нові епізоди",
+ "ButtonCheckAndDownloadNewEpisodes": "Перевірити та скачати нові епізоди",
"ButtonChooseAFolder": "Обрати теку",
"ButtonChooseFiles": "Обрати файли",
"ButtonClearFilter": "Очистити фільтр",
@@ -32,8 +32,8 @@
"ButtonEditChapters": "Редагувати глави",
"ButtonEditPodcast": "Редагувати подкаст",
"ButtonEnable": "Увімкнути",
- "ButtonFireAndFail": "Вогонь і невдача",
- "ButtonFireOnTest": "Випробування на вогнестійкість",
+ "ButtonFireAndFail": "Виконати і завершити з помилкою",
+ "ButtonFireOnTest": "Виконати подію onTest",
"ButtonForceReScan": "Примусово сканувати",
"ButtonFullPath": "Повний шлях",
"ButtonHide": "Приховати",
@@ -57,7 +57,7 @@
"ButtonOpenFeed": "Відкрити стрічку",
"ButtonOpenManager": "Відкрити менеджер",
"ButtonPause": "Пауза",
- "ButtonPlay": "Слухати",
+ "ButtonPlay": "Відтворити",
"ButtonPlayAll": "Відтворити все",
"ButtonPlaying": "Відтворюється",
"ButtonPlaylists": "Списки відтворення",
@@ -86,7 +86,7 @@
"ButtonResetToDefault": "Скинути до стандартних",
"ButtonRestore": "Відновити",
"ButtonSave": "Зберегти",
- "ButtonSaveAndClose": "Зберегти та закрити",
+ "ButtonSaveAndClose": "Зберегти і закрити",
"ButtonSaveTracklist": "Зберегти порядок",
"ButtonScan": "Сканувати",
"ButtonScanLibrary": "Сканувати бібліотеку",
@@ -103,7 +103,7 @@
"ButtonStartMetadataEmbed": "Почати вбудування метаданих",
"ButtonStats": "Статистика",
"ButtonSubmit": "Надіслати",
- "ButtonTest": "Перевірити",
+ "ButtonTest": "Тест",
"ButtonUnlinkOpenId": "Вимкнути OpenID",
"ButtonUpload": "Завантажити",
"ButtonUploadBackup": "Завантажити резервну копію",
@@ -115,7 +115,7 @@
"ButtonYes": "Так",
"ErrorUploadFetchMetadataAPI": "Помилка при отриманні метаданих",
"ErrorUploadFetchMetadataNoResults": "Не вдалося отримати метадані — спробуйте оновити заголовок та/або автора",
- "ErrorUploadLacksTitle": "Назва обов'язкова",
+ "ErrorUploadLacksTitle": "Потрібна назва",
"HeaderAccount": "Профіль",
"HeaderAddCustomMetadataProvider": "Додати користувацький постачальник метаданих",
"HeaderAdvanced": "Розширені",
@@ -130,11 +130,11 @@
"HeaderCollection": "Добірка",
"HeaderCollectionItems": "Елементи добірки",
"HeaderCover": "Обкладинка",
- "HeaderCurrentDownloads": "Поточні завантаження",
+ "HeaderCurrentDownloads": "Поточні скачування",
"HeaderCustomMessageOnLogin": "Повідомлення при вході",
"HeaderCustomMetadataProviders": "Постачальники метаданих",
"HeaderDetails": "Подробиці",
- "HeaderDownloadQueue": "Черга завантажень",
+ "HeaderDownloadQueue": "Черга скачувань",
"HeaderEbookFiles": "Файли електронних книг",
"HeaderEmail": "Електронна пошта",
"HeaderEmailSettings": "Налаштування електронної пошти",
@@ -152,13 +152,13 @@
"HeaderLibraryFiles": "Файли бібліотеки",
"HeaderLibraryStats": "Статистика бібліотеки",
"HeaderListeningSessions": "Сеанси прослуховування",
- "HeaderListeningStats": "Статистика відтворення",
+ "HeaderListeningStats": "Статистика прослуховування",
"HeaderLogin": "Вхід",
"HeaderLogs": "Журнал",
"HeaderManageGenres": "Керувати жанрами",
"HeaderManageTags": "Керувати мітками",
"HeaderMapDetails": "Призначити подробиці",
- "HeaderMatch": "Пошук",
+ "HeaderMatch": "Допасуй",
"HeaderMetadataOrderOfPrecedence": "Порядок метаданих",
"HeaderMetadataToEmbed": "Вбудувати метадані",
"HeaderNewAccount": "Новий профіль",
@@ -176,7 +176,7 @@
"HeaderPlayerSettings": "Налаштування програвача",
"HeaderPlaylist": "Список відтворення",
"HeaderPlaylistItems": "Елементи списку відтворення",
- "HeaderPodcastsToAdd": "Додати подкасти",
+ "HeaderPodcastsToAdd": "Подкасти для додання",
"HeaderPresets": "Пресети",
"HeaderPreviewCover": "Попередній перегляд",
"HeaderRSSFeedGeneral": "Подробиці RSS",
@@ -186,7 +186,7 @@
"HeaderRemoveEpisodes": "Видалити епізодів: {0}",
"HeaderSavedMediaProgress": "Збережений прогрес медіа",
"HeaderSchedule": "Розклад",
- "HeaderScheduleEpisodeDownloads": "Запланувати автоматичне завантаження епізодів",
+ "HeaderScheduleEpisodeDownloads": "Запланувати автоматичне скачування епізодів",
"HeaderScheduleLibraryScans": "Розклад автосканування бібліотеки",
"HeaderSession": "Сеанс",
"HeaderSetBackupSchedule": "Встановити розклад резервного копіювання",
@@ -223,21 +223,21 @@
"LabelActivities": "Діяльність",
"LabelActivity": "Активність",
"LabelAddToCollection": "Додати у добірку",
- "LabelAddToCollectionBatch": "Додати книги до добірки: {0}",
+ "LabelAddToCollectionBatch": "Додати {0} книг до добірки",
"LabelAddToPlaylist": "Додати до списку відтворення",
- "LabelAddToPlaylistBatch": "Додано елементів у список відтворення: {0}",
+ "LabelAddToPlaylistBatch": "Додати {0} елементів до списку відтворення",
"LabelAddedAt": "Дата додавання",
"LabelAddedDate": "Додано {0}",
"LabelAdminUsersOnly": "Тільки для адміністраторів",
"LabelAll": "Усе",
- "LabelAllEpisodesDownloaded": "Усі серії завантажено",
+ "LabelAllEpisodesDownloaded": "Усі епізоди скачано",
"LabelAllUsers": "Усі користувачі",
"LabelAllUsersExcludingGuests": "Усі, крім гостей",
"LabelAllUsersIncludingGuests": "Усі, включно з гостями",
"LabelAlreadyInYourLibrary": "Вже у вашій бібліотеці",
"LabelApiToken": "Токен API",
"LabelAppend": "Додати",
- "LabelAudioBitrate": "Бітрейт аудіо (напр. 128k)",
+ "LabelAudioBitrate": "Бітрейт аудіо (наприклад, 128k)",
"LabelAudioChannels": "Канали аудіо (1 або 2)",
"LabelAudioCodec": "Аудіокодек",
"LabelAuthor": "Автор",
@@ -256,18 +256,18 @@
"LabelBackupLocation": "Розташування резервних копій",
"LabelBackupsEnableAutomaticBackups": "Автоматичне резервне копіювання",
"LabelBackupsEnableAutomaticBackupsHelp": "Резервні копії збережено у /metadata/backups",
- "LabelBackupsMaxBackupSize": "Максимальний розмір резервної копії (у ГБ) (0 — необмежене)",
+ "LabelBackupsMaxBackupSize": "Максимальний розмір резервної копії (у ГБ) (0 — без обмежень)",
"LabelBackupsMaxBackupSizeHelp": "У якості захисту від неправильного налаштування, резервну копію не буде збережено, якщо її розмір перевищуватиме вказаний.",
"LabelBackupsNumberToKeep": "Кількість резервних копій",
- "LabelBackupsNumberToKeepHelp": "Лиш 1 резервну копію буде видалено за раз, тож якщо їх багато, то вам варто видалити їх вручну.",
+ "LabelBackupsNumberToKeepHelp": "Видаляється лише 1 резервна копія за раз, тому якщо у вас більше копій, видаліть їх вручну.",
"LabelBitrate": "Бітрейт",
"LabelBonus": "Бонус",
- "LabelBooks": "Книги",
+ "LabelBooks": "Книг",
"LabelButtonText": "Текст кнопки",
"LabelByAuthor": "від {0}",
"LabelChangePassword": "Змінити пароль",
"LabelChannels": "Канали",
- "LabelChapterCount": "{0} Глав",
+ "LabelChapterCount": "{0} глав",
"LabelChapterTitle": "Назва глави",
"LabelChapters": "Глави",
"LabelChaptersFound": "глав знайдено",
@@ -304,9 +304,9 @@
"LabelDiscFromFilename": "Диск за назвою файлу",
"LabelDiscFromMetadata": "Диск за метаданими",
"LabelDiscover": "Огляд",
- "LabelDownload": "Завантажити",
- "LabelDownloadNEpisodes": "Завантажити епізодів: {0}",
- "LabelDownloadable": "Можна завантажити",
+ "LabelDownload": "Скачати",
+ "LabelDownloadNEpisodes": "Скачати {0} епізодів",
+ "LabelDownloadable": "Можна скачати",
"LabelDuration": "Тривалість",
"LabelDurationComparisonExactMatch": "(повний збіг)",
"LabelDurationComparisonLonger": "(на {0} довше)",
@@ -353,9 +353,9 @@
"LabelFeedURL": "Адреса стрічки",
"LabelFetchingMetadata": "Отримання метаданих",
"LabelFile": "Файл",
- "LabelFileBirthtime": "Дата створення",
+ "LabelFileBirthtime": "Дата створення файлу",
"LabelFileBornDate": "Народився {0}",
- "LabelFileModified": "Дата змінення",
+ "LabelFileModified": "Дата зміни файлу",
"LabelFileModifiedDate": "Змінено {0}",
"LabelFilename": "Ім'я файлу",
"LabelFilterByUser": "Фільтрувати за користувачем",
@@ -395,7 +395,7 @@
"LabelIntervalEvery6Hours": "Кожні 6 годин",
"LabelIntervalEveryDay": "Щодня",
"LabelIntervalEveryHour": "Щогодини",
- "LabelIntervalEveryMinute": "Кожну хвилину",
+ "LabelIntervalEveryMinute": "Щохвилини",
"LabelInvert": "Інвертувати",
"LabelItem": "Елемент",
"LabelJumpBackwardAmount": "Час переходу назад",
@@ -427,10 +427,10 @@
"LabelLowestPriority": "Найнижчий пріоритет",
"LabelMatchExistingUsersBy": "Шукати наявних користувачів за",
"LabelMatchExistingUsersByDescription": "Використовується для підключення наявних користувачів. Після підключення користувач отримає унікальний id від вашого сервісу SSO",
- "LabelMaxEpisodesToDownload": "Максимальна кількість епізодів для завантаження. Використовуйте 0 для необмеженої кількості.",
- "LabelMaxEpisodesToDownloadPerCheck": "Максимальна кількість нових епізодів для завантаження за перевірку",
+ "LabelMaxEpisodesToDownload": "Максимальна кількість епізодів для скачування. Використовуйте 0 для необмеженої кількості.",
+ "LabelMaxEpisodesToDownloadPerCheck": "Максимальна кількість нових епізодів для скачування за перевірку",
"LabelMaxEpisodesToKeep": "Максимальна кількість епізодів для зберігання",
- "LabelMaxEpisodesToKeepHelp": "Значення 0 не встановлює обмеження. Після автоматичного завантаження нового епізоду, буде видалено найстаріший епізод, якщо у вас більше ніж X епізодів. Видаляється лише 1 епізод за одне нове завантаження.",
+ "LabelMaxEpisodesToKeepHelp": "Значення 0 — без обмежень. Після автоматичного завантаження нового епізоду буде видалено найстаріший, якщо їх більше X. Видаляється лише 1 епізод за одне нове завантаження.",
"LabelMediaPlayer": "Програвач медіа",
"LabelMediaType": "Тип медіа",
"LabelMetaTag": "Метатег",
@@ -485,7 +485,7 @@
"LabelPermissionsAccessExplicitContent": "Доступ до відвертого вмісту",
"LabelPermissionsCreateEreader": "Можна створити читалку",
"LabelPermissionsDelete": "Може видаляти",
- "LabelPermissionsDownload": "Може завантажувати",
+ "LabelPermissionsDownload": "Може скачувати",
"LabelPermissionsUpdate": "Може оновлювати",
"LabelPermissionsUpload": "Може завантажувати",
"LabelPersonalYearReview": "Ваші підсумки року ({0})",
@@ -542,8 +542,8 @@
"LabelSeason": "Сезон",
"LabelSeasonNumber": "Сезон #{0}",
"LabelSelectAll": "Вибрати все",
- "LabelSelectAllEpisodes": "Вибрати всі серії",
- "LabelSelectEpisodesShowing": "Обрати показані епізоди: {0}",
+ "LabelSelectAllEpisodes": "Вибрати всі епізоди",
+ "LabelSelectEpisodesShowing": "Вибрати {0} показаних епізодів",
"LabelSelectUsers": "Вибрати користувачів",
"LabelSendEbookToDevice": "Надіслати електронну книгу на...",
"LabelSequence": "Послідовність",
@@ -595,7 +595,7 @@
"LabelSettingsStoreMetadataWithItemHelp": "За замовчуванням файли метаданих зберігаються у /metadata/items. Цей параметр увімкне збереження метаданих у теці елемента бібліотеки",
"LabelSettingsTimeFormat": "Формат часу",
"LabelShare": "Поділитися",
- "LabelShareDownloadableHelp": "Дозволяє користувачам із посиланням для спільного доступу завантажувати zip-файл елемента бібліотеки.",
+ "LabelShareDownloadableHelp": "Дозволяє користувачам із посиланням для спільного доступу скачування zip-файлу елемента бібліотеки.",
"LabelShareOpen": "Поділитися відкрито",
"LabelShareURL": "Поділитися URL",
"LabelShowAll": "Показати все",
@@ -714,19 +714,19 @@
"MessageBackupsLocationNoEditNote": "Примітка: розташування резервної копії встановлюється за допомогою змінної середовища та не може бути змінене тут.",
"MessageBackupsLocationPathEmpty": "Шлях розташування резервної копії не може бути порожнім",
"MessageBatchEditPopulateMapDetailsAllHelp": "Заповнити увімкнені поля даними з усіх елементів. Поля з кількома значеннями буде об’єднано",
- "MessageBatchEditPopulateMapDetailsItemHelp": "Заповніть увімкнені поля деталей карти даними з цього елемента",
+ "MessageBatchEditPopulateMapDetailsItemHelp": "Заповнити увімкнені поля деталізації даними з цього елемента",
"MessageBatchQuickMatchDescription": "Швидкий пошук спробує знайти відсутні обкладинки та метадані обраних елементів. Увімкніть налаштування нижче, аби дозволити заміну наявних обкладинок та/або метаданих під час швидкого пошуку.",
- "MessageBookshelfNoCollections": "Ви не створили жодної добірки",
+ "MessageBookshelfNoCollections": "Ви ще не створили жодної добірки",
"MessageBookshelfNoCollectionsHelp": "Колекції публічні. Їх можуть бачити всі користувачі, які мають доступ до бібліотеки.",
"MessageBookshelfNoRSSFeeds": "Немає відкритих RSS-каналів",
"MessageBookshelfNoResultsForFilter": "Немає результатів з фільтром \"{0}: {1}\"",
"MessageBookshelfNoResultsForQuery": "Немає результатів за запитом",
- "MessageBookshelfNoSeries": "Серії відсутні",
- "MessageChapterEndIsAfter": "Кінець глави знаходиться після закінчення книги",
- "MessageChapterErrorFirstNotZero": "Перша глава мусить починатися з 0",
- "MessageChapterErrorStartGteDuration": "Час початку мусить бути меншим за тривалість аудіокниги",
- "MessageChapterErrorStartLtPrev": "Неприпустимий час початку, має бути більшим за час початку попередньої глави",
- "MessageChapterStartIsAfter": "Початок глави знаходиться після закінчення книги",
+ "MessageBookshelfNoSeries": "У вас немає серій",
+ "MessageChapterEndIsAfter": "Кінець глави після завершення аудіокниги",
+ "MessageChapterErrorFirstNotZero": "Перша глава повинна починатися з 0",
+ "MessageChapterErrorStartGteDuration": "Час початку має бути меншим за тривалість аудіокниги",
+ "MessageChapterErrorStartLtPrev": "Час початку має бути більшим або рівним часу початку попередньої глави",
+ "MessageChapterStartIsAfter": "Початок глави після завершення аудіокниги",
"MessageChaptersNotFound": "Розділи не знайдені",
"MessageCheckingCron": "Перевірка планувальника...",
"MessageConfirmCloseFeed": "Ви дійсно бажаєте закрити цей канал?",
@@ -734,25 +734,25 @@
"MessageConfirmDeleteDevice": "Ви впевнені, що хочете видалити пристрій для читання \"{0}\"?",
"MessageConfirmDeleteFile": "Файл буде видалено з вашої файлової системи. Ви впевнені?",
"MessageConfirmDeleteLibrary": "Ви дійсно бажаєте назавжди видалити бібліотеку \"{0}\"?",
- "MessageConfirmDeleteLibraryItem": "Елемент бібліотеки буде видалено з бази даних та вашої файлової системи. Ви впевнені?",
- "MessageConfirmDeleteLibraryItems": "З бази даних та вашої файлової системи будуть видалені елементи бібліотеки: {0}. Ви впевнені?",
+ "MessageConfirmDeleteLibraryItem": "Елемент бібліотеки буде видалено з бази даних і файлової системи. Ви впевнені?",
+ "MessageConfirmDeleteLibraryItems": "Буде видалено {0} елементів бібліотеки з бази даних і файлової системи. Ви впевнені?",
"MessageConfirmDeleteMetadataProvider": "Ви впевнені, що хочете видалити користувацького постачальника метаданих \"{0}\"?",
"MessageConfirmDeleteNotification": "Ви впевнені, що хочете видалити це сповіщення?",
"MessageConfirmDeleteSession": "Ви дійсно бажаєте видалити цей сеанс?",
- "MessageConfirmEmbedMetadataInAudioFiles": "Ви впевнені, що хочете вставити метадані в {0} аудіофайлів?",
+ "MessageConfirmEmbedMetadataInAudioFiles": "Ви впевнені, що хочете вбудувати метадані у {0} аудіофайлів?",
"MessageConfirmForceReScan": "Ви дійсно бажаєте примусово пересканувати?",
- "MessageConfirmMarkAllEpisodesFinished": "Ви дійсно бажаєте позначити усі епізоди завершеними?",
- "MessageConfirmMarkAllEpisodesNotFinished": "Ви дійсно бажаєте позначити усі епізоди незавершеними?",
+ "MessageConfirmMarkAllEpisodesFinished": "Ви впевнені, що хочете позначити всі епізоди завершеними?",
+ "MessageConfirmMarkAllEpisodesNotFinished": "Ви впевнені, що хочете позначити всі епізоди незавершеними?",
"MessageConfirmMarkItemFinished": "Ви впевнені, що хочете позначити \"{0}\" як завершене?",
"MessageConfirmMarkItemNotFinished": "Ви впевнені, що хочете позначити \"{0}\" як незавершене?",
"MessageConfirmMarkSeriesFinished": "Ви дійсно бажаєте позначити усі книги серії завершеними?",
"MessageConfirmMarkSeriesNotFinished": "Ви дійсно бажаєте позначити всі книги серії незавершеними?",
"MessageConfirmNotificationTestTrigger": "Активувати це сповіщення з тестовими даними?",
- "MessageConfirmPurgeCache": "Очищення кешу видалить усю теку /metadata/cache.
Ви дійсно бажаєте видалити теку кешу?",
- "MessageConfirmPurgeItemsCache": "Очищення кешу елементів видалить усю теку /metadata/cache/items. Ви певні?",
- "MessageConfirmQuickEmbed": "Увага! Швидке вбудування не створює резервних копій ваших аудіо. Переконайтеся, що маєте копію ваших файлів.
Продовжити?",
- "MessageConfirmQuickMatchEpisodes": "При виявленні співпадінь інформація про епізоди швидкого пошуку буде перезаписана. Будуть оновлені тільки несуперечливі епізоди. Ви впевнені?",
- "MessageConfirmReScanLibraryItems": "Ви дійсно бажаєте пересканувати елементи: {0}?",
+ "MessageConfirmPurgeCache": "Очищення кешу видалить всю теку /metadata/cache.
Ви впевнені, що хочете видалити теку кешу?",
+ "MessageConfirmPurgeItemsCache": "Очищення кешу елементів видалить всю теку /metadata/cache/items. Ви впевнені?",
+ "MessageConfirmQuickEmbed": "Увага! Швидке вбудовування не створює резервних копій ваших аудіофайлів. Переконайтеся, що маєте резервну копію.
Продовжити?",
+ "MessageConfirmQuickMatchEpisodes": "Швидке співставлення епізодів перезапише подробиці, якщо знайдено відповідність. Оновлюються лише невідповідні епізоди. Ви впевнені?",
+ "MessageConfirmReScanLibraryItems": "Ви впевнені, що хочете пересканувати {0} елементів?",
"MessageConfirmRemoveAllChapters": "Ви дійсно бажаєте видалити усі глави?",
"MessageConfirmRemoveAuthor": "Ви дійсно бажаєте видалити автора \"{0}\"?",
"MessageConfirmRemoveCollection": "Ви дійсно бажаєте видалити добірку \"{0}\"?",
@@ -761,44 +761,44 @@
"MessageConfirmRemoveListeningSessions": "Ви дійсно бажаєте видалити сеанси прослуховування: {0}?",
"MessageConfirmRemoveMetadataFiles": "Ви впевнені, що хочете видалити всі файли metadata.{0} у папках елементів вашої бібліотеки?",
"MessageConfirmRemoveNarrator": "Ви дійсно бажаєте видалити читця \"{0}\"?",
- "MessageConfirmRemovePlaylist": "Ви дійсно бажаєте видалити список відтворення \"{0}\"?",
- "MessageConfirmRenameGenre": "Ви дійсно бажаєте замінити жанр \"{0}\" на \"{1}\" для усіх елементів?",
- "MessageConfirmRenameGenreMergeNote": "Примітка: такий жанр вже існує, тож їх буде об'єднано.",
- "MessageConfirmRenameGenreWarning": "Увага! Вже існує схожий жанр у іншому регістрі \"{0}\".",
- "MessageConfirmRenameTag": "Ви дійсно бажаєте замінити мітку \"{0}\" на \"{1}\" для усіх елементів?",
- "MessageConfirmRenameTagMergeNote": "Примітка: така мітка вже існує, тож їх буде об'єднано.",
- "MessageConfirmRenameTagWarning": "Увага! Вже існує схожа мітка у іншому регістрі \"{0}\".",
+ "MessageConfirmRemovePlaylist": "Ви дійсно бажаєте видалити ваш список відтворення \"{0}\"?",
+ "MessageConfirmRenameGenre": "Ви впевнені, що хочете перейменувати жанр \"{0}\" на \"{1}\" для всіх елементів?",
+ "MessageConfirmRenameGenreMergeNote": "Примітка: Такий жанр вже існує, тому вони будуть об'єднані.",
+ "MessageConfirmRenameGenreWarning": "Увага! Схожий жанр з іншом регістром вже існує \"{0}\".",
+ "MessageConfirmRenameTag": "Ви впевнені, що хочете перейменувати мітку \"{0}\" на \"{1}\" для всіх елементів?",
+ "MessageConfirmRenameTagMergeNote": "Примітка: Така мітка вже існує, тому вони будуть об'єднані.",
+ "MessageConfirmRenameTagWarning": "Увага! Схожа мітка з іншою регістром вже існує \"{0}\".",
"MessageConfirmResetProgress": "Ви впевнені, що хочете скинути свій прогрес?",
"MessageConfirmSendEbookToDevice": "Ви дійсно хочете відправити на пристрій \"{2}\" електроні книги: {0}, \"{1}\"?",
"MessageConfirmUnlinkOpenId": "Ви впевнені, що хочете відв'язати цього користувача від OpenID?",
"MessageDaysListenedInTheLastYear": "{0} днів, прослуханих за останній рік",
- "MessageDownloadingEpisode": "Завантаження епізоду",
+ "MessageDownloadingEpisode": "Скачування епізоду",
"MessageDragFilesIntoTrackOrder": "Перетягніть файли до правильного порядку",
"MessageEmbedFailed": "Не вдалося вбудувати!",
- "MessageEmbedFinished": "Вбудовано!",
- "MessageEmbedQueue": "В черзі на вбудовування метаданих ({0} в черзі)",
- "MessageEpisodesQueuedForDownload": "Епізодів у черзі завантаження: {0}",
+ "MessageEmbedFinished": "Вбудовування завершено!",
+ "MessageEmbedQueue": "У черзі на вбудовування метаданих ({0} у черзі)",
+ "MessageEpisodesQueuedForDownload": "{0} епізод(ів) у черзі на завантаження",
"MessageEreaderDevices": "Аби гарантувати отримання електронних книг, вам може знадобитися додати вказану вище адресу електронної пошти як правильного відправника на кожному з пристроїв зі списку нижче.",
"MessageFeedURLWillBe": "URL-адреса каналу буде {0}",
"MessageFetching": "Отримання...",
- "MessageForceReScanDescription": "Просканує усі файли заново, неначе вперше. ID3-мітки, файли OPF та текстові файли будуть проскановані як нові.",
+ "MessageForceReScanDescription": "Просканує всі файли заново, як при першому скануванні. ID3-мітки, OPF-файли та текстові файли будуть проскановані як нові.",
"MessageImportantNotice": "Важливе повідомлення!",
"MessageInsertChapterBelow": "Введіть главу нижче",
"MessageInvalidAsin": "Невірний ASIN",
- "MessageItemsSelected": "Вибрано елементів: {0}",
- "MessageItemsUpdated": "Оновлено елементів: {0}",
+ "MessageItemsSelected": "Вибрано {0} елементів",
+ "MessageItemsUpdated": "Оновлено {0} елементів",
"MessageJoinUsOn": "Приєднуйтесь до",
"MessageLoading": "Завантаження...",
- "MessageLoadingFolders": "Завантаження тек...",
+ "MessageLoadingFolders": "Завантаження папок...",
"MessageLogsDescription": "Журнали зберігаються у /metadata/logs як JSON-файли. Журнали збоїв зберігаються у /metadata/logs/crash_logs.txt.",
"MessageM4BFailed": "Помилка M4B!",
"MessageM4BFinished": "M4B створено!",
- "MessageMapChapterTitles": "Встановіть назви глав вашої аудіокниги без визначення налаштувань тривалості",
+ "MessageMapChapterTitles": "Встановіть назви глав вашої аудіокниги без зміни часових міток",
"MessageMarkAllEpisodesFinished": "Позначити всі епізоди завершеними",
"MessageMarkAllEpisodesNotFinished": "Позначити всі епізоди незавершеними",
- "MessageMarkAsFinished": "Позначити завершеним",
- "MessageMarkAsNotFinished": "Позначити незавершеним",
- "MessageMatchBooksDescription": "Спробує віднайти книгу у вказаному джерелі пошуку та встановити подробиці та обкладинку, яких бракує. Не перезаписує подробиці.",
+ "MessageMarkAsFinished": "Позначити як завершене",
+ "MessageMarkAsNotFinished": "Позначити як незавершене",
+ "MessageMatchBooksDescription": "Спробує знайти книги у бібліотеці у вибраному джерелі пошуку та заповнити порожні подробиці й обкладинку. Не перезаписує подробиці.",
"MessageNoAudioTracks": "Аудіодоріжки відсутні",
"MessageNoAuthors": "Автори відсутні",
"MessageNoBackups": "Резервні копії відсутні",
@@ -808,8 +808,8 @@
"MessageNoCoversFound": "Обкладинок не знайдено",
"MessageNoDescription": "Без опису",
"MessageNoDevices": "Немає пристроїв",
- "MessageNoDownloadsInProgress": "Немає активних завантажень",
- "MessageNoDownloadsQueued": "Немає завантажень у черзі",
+ "MessageNoDownloadsInProgress": "Немає активних скачувань",
+ "MessageNoDownloadsQueued": "Немає скачувань у черзі",
"MessageNoEpisodeMatchesFound": "Відповідних епізодів не знайдено",
"MessageNoEpisodes": "Епізоди відсутні",
"MessageNoFoldersAvailable": "Немає доступних тек",
@@ -818,21 +818,21 @@
"MessageNoItems": "Елементи відсутні",
"MessageNoItemsFound": "Елементів не знайдено",
"MessageNoListeningSessions": "Сеанси прослуховування відсутні",
- "MessageNoLogs": "Немає журнали",
+ "MessageNoLogs": "Немає журналів",
"MessageNoMediaProgress": "Прогрес відсутній",
"MessageNoNotifications": "Сповіщення відсутні",
- "MessageNoPodcastFeed": "Невірний подкаст: Немає каналу",
+ "MessageNoPodcastFeed": "Некоректний подкаст: немає каналу",
"MessageNoPodcastsFound": "Подкастів не знайдено",
"MessageNoResults": "Немає результатів",
"MessageNoSearchResultsFor": "Немає результатів пошуку для \"{0}\"",
- "MessageNoSeries": "Без серії",
- "MessageNoTags": "Без міток",
+ "MessageNoSeries": "Немає серій",
+ "MessageNoTags": "Немає міток",
"MessageNoTasksRunning": "Немає активних завдань",
- "MessageNoUpdatesWereNecessary": "Оновлень не потрібно",
+ "MessageNoUpdatesWereNecessary": "Оновлення не потрібні",
"MessageNoUserPlaylists": "У вас немає списків відтворення",
- "MessageNoUserPlaylistsHelp": "Списки відтворення приватні. Лише користувач, який їх створює, може бачити їх.",
+ "MessageNoUserPlaylistsHelp": "Списки відтворення приватні. Лише користувач, який їх створив, може їх бачити.",
"MessageNotYetImplemented": "Ще не реалізовано",
- "MessageOpmlPreviewNote": "Примітка: це попередній перегляд OPML-файлу. Актуальна назва подкасту буде завантажена з RSS-каналу.",
+ "MessageOpmlPreviewNote": "Примітка: це попередній перегляд OPML-файлу. Актуальна назва подкасту буде взята з RSS-каналу.",
"MessageOr": "або",
"MessagePauseChapter": "Призупинити відтворення глави",
"MessagePlayChapter": "Слухати початок глави",
@@ -841,7 +841,7 @@
"MessagePodcastHasNoRSSFeedForMatching": "Подкаст не має RSS-каналу для пошуку",
"MessagePodcastSearchField": "Введіть пошуковий запит або URL RSS-стрічки",
"MessageQuickEmbedInProgress": "Швидке вбудовування в процесі",
- "MessageQuickEmbedQueue": "В черзі на швидке вбудовування ({0} в черзі)",
+ "MessageQuickEmbedQueue": "У черзі на швидке вбудовування ({0} в черзі)",
"MessageQuickMatchAllEpisodes": "Швидке співставлення всіх епізодів",
"MessageQuickMatchDescription": "Заповнити відсутні подробиці та обкладинку першим результатом пошуку '{0}'. Не перезаписує подробиці, якщо не увімкнено параметр \"Надавати перевагу віднайденим метаданим\".",
"MessageRemoveChapter": "Видалити главу",
@@ -849,9 +849,9 @@
"MessageRemoveFromPlayerQueue": "Вилучити з черги відтворення",
"MessageRemoveUserWarning": "Ви дійсно бажаєте назавжди видалити користувача \"{0}\"?",
"MessageReportBugsAndContribute": "Повідомляйте про помилки, пропонуйте функції та долучайтеся на",
- "MessageResetChaptersConfirm": "Ви дійсно бажаєте скинути глави та скасувати внесені зміни?",
- "MessageRestoreBackupConfirm": "Ви дійсно бажаєте відновити резервну копію від",
- "MessageRestoreBackupWarning": "Відновлення резервної копії перезапише всю базу даних, розташовану в /config, і зображення обкладинок в /metadata/items та /metadata/authors.
Резервні копії не змінюють жодних файлів у теках бібліотеки. Якщо у налаштуваннях сервера увімкнено збереження обкладинок і метаданих у теках бібліотеки, вони не створюються під час резервного копіювання і не перезаписуються..
Всі клієнти, що користуються вашим сервером, будуть автоматично оновлені.",
+ "MessageResetChaptersConfirm": "Ви впевнені, що хочете скинути глави та скасувати внесені зміни?",
+ "MessageRestoreBackupConfirm": "Ви впевнені, що хочете відновити резервну копію, створену",
+ "MessageRestoreBackupWarning": "Відновлення резервної копії перезапише всю базу даних у /config і зображення обкладинок у /metadata/items та /metadata/authors.
Резервні копії не змінюють файли у теках бібліотеки. Якщо у налаштуваннях сервера увімкнено збереження обкладинок і метаданих у теках бібліотеки, вони не створюються під час резервного копіювання і не перезаписуються.
Всі клієнти, що користуються вашим сервером, будуть автоматично оновлені.",
"MessageScheduleLibraryScanNote": "Для більшості користувачів рекомендується залишити цю функцію вимкненою та залишити параметр перегляду папок увімкненим. Засіб спостереження за папками автоматично виявить зміни в папках вашої бібліотеки. Засіб спостереження за папками не працює для кожної файлової системи (наприклад, NFS), тому замість нього можна використовувати сканування бібліотек за розкладом.",
"MessageScheduleRunEveryWeekdayAtTime": "Запуск кожні {0} о {1}",
"MessageSearchResultsFor": "Результати пошуку для",
@@ -860,12 +860,12 @@
"MessageServerCouldNotBeReached": "Не вдалося підключитися до сервера",
"MessageSetChaptersFromTracksDescription": "Створити глави з аудіодоріжок, встановивши назви файлів за заголовки",
"MessageShareExpirationWillBe": "Термін сплине за {0}",
- "MessageShareExpiresIn": "Сплине за {0}",
- "MessageShareURLWillBe": "Поширюваний URL - {0}",
+ "MessageShareExpiresIn": "Спливає через {0}",
+ "MessageShareURLWillBe": "URL для спільного доступу — {0}",
"MessageStartPlaybackAtTime": "Почати відтворення \"{0}\" з {1}?",
"MessageTaskAudioFileNotWritable": "Аудіофайл \"{0}\" недоступний для запису",
- "MessageTaskCanceledByUser": "Задача скасована користувачем",
- "MessageTaskDownloadingEpisodeDescription": "Завантаження епізоду \"{0}\"",
+ "MessageTaskCanceledByUser": "Завдання скасовано користувачем",
+ "MessageTaskDownloadingEpisodeDescription": "Скачування епізоду \"{0}\"",
"MessageTaskEmbeddingMetadata": "Вбудовування метаданих",
"MessageTaskEmbeddingMetadataDescription": "Вбудовування метаданих у аудіокнигу \"{0}\"",
"MessageTaskEncodingM4b": "Кодування M4B",
@@ -880,19 +880,19 @@
"MessageTaskMatchingBooksInLibrary": "Відповідність книг у бібліотеці \"{0}\"",
"MessageTaskNoFilesToScan": "Немає файлів для сканування",
"MessageTaskOpmlImport": "Імпорт OPML",
- "MessageTaskOpmlImportDescription": "Створення подкастів з {0} RSS-стрічок",
- "MessageTaskOpmlImportFeed": "Канал імпорту OPML",
+ "MessageTaskOpmlImportDescription": "Створення подкастів з {0} RSS-каналів",
+ "MessageTaskOpmlImportFeed": "Імпорт RSS-каналу OPML",
"MessageTaskOpmlImportFeedDescription": "Імпорт RSS-каналу \"{0}\"",
- "MessageTaskOpmlImportFeedFailed": "Не вдалося отримати подкаст-стрічку",
+ "MessageTaskOpmlImportFeedFailed": "Не вдалося отримати подкаст-канал",
"MessageTaskOpmlImportFeedPodcastDescription": "Створення подкасту \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Подкаст вже існує за цим шляхом",
"MessageTaskOpmlImportFeedPodcastFailed": "Не вдалося створити подкаст",
"MessageTaskOpmlImportFinished": "Додано {0} подкастів",
- "MessageTaskOpmlParseFailed": "Не вдалося розібрати файл OPML",
- "MessageTaskOpmlParseFastFail": "Невірний файл OPML: не знайдено тег або тег ",
- "MessageTaskOpmlParseNoneFound": "У файлі OPML не знайдено жодного канала",
+ "MessageTaskOpmlParseFailed": "Не вдалося розібрати OPML-файл",
+ "MessageTaskOpmlParseFastFail": "Некоректний OPML-файл: не знайдено тег або ",
+ "MessageTaskOpmlParseNoneFound": "У OPML-файлі не знайдено жодного каналу",
"MessageTaskScanItemsAdded": "{0} додано",
- "MessageTaskScanItemsMissing": "{0} відсутній",
+ "MessageTaskScanItemsMissing": "{0} відсутні",
"MessageTaskScanItemsUpdated": "{0} оновлено",
"MessageTaskScanNoChangesNeeded": "Змін не потрібно",
"MessageTaskScanningFileChanges": "Сканування змін файлів у \"{0}\"",
@@ -902,24 +902,24 @@
"MessageUploaderItemFailed": "Не вдалося завантажити",
"MessageUploaderItemSuccess": "Успішно завантажено!",
"MessageUploading": "Завантаження...",
- "MessageValidCronExpression": "Допустима команда cron",
- "MessageWatcherIsDisabledGlobally": "Спостерігача вимкнено в налаштуваннях сервера",
+ "MessageValidCronExpression": "Коректний cron-вираз",
+ "MessageWatcherIsDisabledGlobally": "Спостерігача вимкнено у глобальних налаштуваннях сервера",
"MessageXLibraryIsEmpty": "Бібліотека {0} порожня!",
- "MessageYourAudiobookDurationIsLonger": "Тривалість вашої аудіокниги довша за віднайдену",
- "MessageYourAudiobookDurationIsShorter": "Тривалість вашої аудіокниги коротша за віднайдену",
- "NoteChangeRootPassword": "Тільки користувач root — єдиний, хто може мати порожній пароль",
- "NoteChapterEditorTimes": "Примітка: Перша глава мусить починатися з 0:00, а час початку останньої глави не може бути більшим за зазначену тривалість аудіокниги.",
- "NoteFolderPicker": "Примітка: вже обрані теки не буде показано",
- "NoteRSSFeedPodcastAppsHttps": "Попередження: Більшість додатків подкастів вимагатимуть використання протоколу HTTPS від RSS-каналу",
- "NoteRSSFeedPodcastAppsPubDate": "Попередження: 1 або більше ваших епізодів не мають дати публікації. Деякі додатки подкастів вимагають це.",
- "NoteUploaderFoldersWithMediaFiles": "Теки з медіафайлами буде оброблено як окремі елементи бібліотеки.",
- "NoteUploaderOnlyAudioFiles": "Якщо завантажувати лише аудіофайли, то кожен файл буде оброблено як окрему книгу.",
- "NoteUploaderUnsupportedFiles": "Непідтримувані файли пропущено. Під час вибору або перетягування теки, файли, що знаходяться поза текою, пропускаються.",
- "NotificationOnBackupCompletedDescription": "Запускається після завершення резервного копіювання",
- "NotificationOnBackupFailedDescription": "Срабатывает при збої резервного копіювання",
- "NotificationOnEpisodeDownloadedDescription": "Запускається при автоматичному завантаженні епізоду подкасту",
- "NotificationOnRSSFeedDisabledDescription": "Активується, коли автоматичне завантаження епізодів вимкнено через занадто багато невдалих спроб",
- "NotificationOnRSSFeedFailedDescription": "Активується, коли запит RSS-каналу не вдається виконати для автоматичного завантаження епізоду",
+ "MessageYourAudiobookDurationIsLonger": "Тривалість вашої аудіокниги більша за знайдену",
+ "MessageYourAudiobookDurationIsShorter": "Тривалість вашої аудіокниги менша за знайдену",
+ "NoteChangeRootPassword": "Тільки користувач root може мати порожній пароль",
+ "NoteChapterEditorTimes": "Примітка: Перша глава повинна починатися з 0:00, а час початку останньої глави не може перевищувати тривалість цієї аудіокниги.",
+ "NoteFolderPicker": "Примітка: вже додані папки не відображаються",
+ "NoteRSSFeedPodcastAppsHttps": "Попередження: більшість додатків подкастів вимагають використання HTTPS для RSS-каналу",
+ "NoteRSSFeedPodcastAppsPubDate": "Попередження: один або більше ваших епізодів не мають дати публікації. Деякі додатки подкастів цього вимагають.",
+ "NoteUploaderFoldersWithMediaFiles": "Теки з медіафайлами обробляються як окремі елементи бібліотеки.",
+ "NoteUploaderOnlyAudioFiles": "Якщо завантажувати лише аудіофайли, кожен файл буде окремою аудіокнигою.",
+ "NoteUploaderUnsupportedFiles": "Непідтримувані файли ігноруються. При виборі або перетягуванні теки, файли поза теками елементів ігноруються.",
+ "NotificationOnBackupCompletedDescription": "Виконується після завершення резервного копіювання",
+ "NotificationOnBackupFailedDescription": "Виконується при помилці резервного копіювання",
+ "NotificationOnEpisodeDownloadedDescription": "Виконується при автоматичному завантаженні епізоду подкасту",
+ "NotificationOnRSSFeedDisabledDescription": "Виконується, коли автоматичне завантаження епізодів вимкнено через забагато невдалих спроб",
+ "NotificationOnRSSFeedFailedDescription": "Виконується, коли запит RSS-каналу не вдається для автоматичного завантаження епізоду",
"NotificationOnTestDescription": "Подія для тестування системи сповіщень",
"PlaceholderNewCollection": "Нова назва добірки",
"PlaceholderNewFolderPath": "Новий шлях до теки",
@@ -997,7 +997,7 @@
"ToastEncodeCancelFailed": "Не вдалося скасувати кодування",
"ToastEncodeCancelSucces": "Кодування скасовано",
"ToastEpisodeDownloadQueueClearFailed": "Не вдалося очистити чергу",
- "ToastEpisodeDownloadQueueClearSuccess": "Чергу на завантаження епізодів очищено",
+ "ToastEpisodeDownloadQueueClearSuccess": "Чергу на скачування епізодів очищено",
"ToastEpisodeUpdateSuccess": "{0} епізодів оновлено",
"ToastErrorCannotShare": "Не можна типово поширити на цей пристрій",
"ToastFailedToLoadData": "Не вдалося завантажити дані",
@@ -1005,7 +1005,7 @@
"ToastFailedToShare": "Не вдалося поділитися",
"ToastFailedToUpdate": "Не вдалося оновити",
"ToastInvalidImageUrl": "Невірний URL зображення",
- "ToastInvalidMaxEpisodesToDownload": "Невірна кількість епізодів для завантаження",
+ "ToastInvalidMaxEpisodesToDownload": "Невірна кількість епізодів для скачування",
"ToastInvalidUrl": "Невірний URL",
"ToastItemCoverUpdateSuccess": "Обкладинку елемента оновлено",
"ToastItemDeletedFailed": "Не вдалося видалити елемент",
From 293851d9310dca2fb1807ed610aefae1208e491f Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 30 Jun 2025 17:49:05 -0500
Subject: [PATCH 116/121] Fix missing translation in remove podcast episode
modal #4434
---
client/components/modals/podcast/RemoveEpisode.vue | 2 +-
client/strings/en-us.json | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/client/components/modals/podcast/RemoveEpisode.vue b/client/components/modals/podcast/RemoveEpisode.vue
index 38dd71cf..b2cebe84 100644
--- a/client/components/modals/podcast/RemoveEpisode.vue
+++ b/client/components/modals/podcast/RemoveEpisode.vue
@@ -11,7 +11,7 @@
{{ $getString('MessageConfirmRemoveEpisode', [episodeTitle]) }}