Compare commits

...

23 Commits

Author SHA1 Message Date
advplyr f850db23fe Version bump v2.17.2 2024-11-21 15:24:45 -06:00
advplyr 5f81010f6a Merge pull request #3631 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-11-21 15:17:42 -06:00
burghy86 daf2493f50 Translated using Weblate (Italian)
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-11-21 22:05:10 +01:00
DR 57222f3611 Translated using Weblate (Hebrew)
Currently translated at 72.8% (780 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2024-11-21 22:05:10 +01:00
Mohamad Dahhan 62b185979e Translated using Weblate (Arabic)
Currently translated at 14.2% (153 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2024-11-21 22:05:10 +01:00
DR ebcc85acc4 Translated using Weblate (Hebrew)
Currently translated at 70.5% (756 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2024-11-21 22:05:10 +01:00
advplyr 33a7ba4acd Merge pull request #3632 from sevenlayercookie/master
on iOS, do not restrict file types for upload
2024-11-21 15:05:05 -06:00
advplyr 1d4e6993fc Upload page UI updates for mobile 2024-11-21 14:56:43 -06:00
advplyr 784b761629 Fix:Unable to edit series sequence #3636 2024-11-21 14:19:40 -06:00
Harrison Rose 268fb2ce9a on iOS, hide UI on upload page related to folder selection (since iOS Webkit does not support folder selection) 2024-11-21 04:43:03 +00:00
Harrison Rose fc5f35b388 on iOS, do not restrict file types for upload 2024-11-21 02:06:53 +00:00
advplyr ff026a06bb Fix v2.17.0 migration to ensure mediaItemShares table exists 2024-11-20 16:48:09 -06:00
advplyr b148a57c98 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-11-19 16:48:09 -06:00
advplyr ee6e2d2983 Update:Persist podcast episode table sort and filter options in local storage #1321 2024-11-19 16:48:05 -06:00
advplyr ea3a6fd75e Merge pull request #3603 from nichwall/pr_template
PR Template
2024-11-19 16:15:29 -06:00
advplyr 22f85d3af9 Version bump v2.17.1 2024-11-18 08:02:46 -06:00
advplyr 75f4c2ee99 Merge pull request #3626 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-11-18 08:01:58 -06:00
thehijacker dd3467efa2 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-11-18 15:00:12 +01:00
Clara Papke 4adb15c11b Translated using Weblate (German)
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-11-18 15:00:12 +01:00
advplyr a5e38d1473 Fix:Error adding new series if a series has a null title #3622 2024-11-18 07:59:02 -06:00
advplyr 778256ca16 Fix:Server crash on new libraries when getting filter data #3623 2024-11-18 07:42:24 -06:00
Nicholas Wallace d5fbc1d455 Add: statement about workflows passing 2024-11-17 12:22:15 -07:00
Nicholas Wallace 0d54b57151 Add: PR template 2024-11-11 21:20:53 -07:00
19 changed files with 164 additions and 62 deletions
+33
View File
@@ -0,0 +1,33 @@
<!--
For Work In Progress Pull Requests, please use the Draft PR feature,
see https://github.blog/2019-02-14-introducing-draft-pull-requests/ for further details.
If you do not follow this template, the PR may be closed without review.
Please ensure all checks pass.
If you are a new contributor, the workflows will need to be manually approved before they run.
-->
## Brief summary
<!-- Please provide a brief summary of what your PR attempts to achieve. -->
## Which issue is fixed?
<!-- Which issue number does this PR fix? Ex: "Fixes #1234" -->
## In-depth Description
<!--
Describe your solution in more depth.
How does it work? Why is this the best solution?
Does it solve a problem that affects multiple users or is this an edge case for your setup?
-->
## How have you tested this?
<!-- Please describe in detail with reproducible steps how you tested your changes. -->
## Screenshots
<!-- If your PR includes any changes to the web client, please include screenshots or a short video from before and after your changes. -->
@@ -25,7 +25,6 @@
</template> </template>
</div> </div>
</div> </div>
<!-- <p v-if="!episodes.length" class="py-4 text-center text-lg">{{ $strings.MessageNoEpisodes }}</p> -->
<div v-if="episodes.length" class="w-full py-3 mx-auto flex"> <div v-if="episodes.length" class="w-full py-3 mx-auto flex">
<form @submit.prevent="submit" class="flex flex-grow"> <form @submit.prevent="submit" class="flex flex-grow">
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="flex-grow mr-2 text-sm md:text-base" /> <ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="flex-grow mr-2 text-sm md:text-base" />
@@ -515,6 +514,10 @@ export default {
} }
}, },
filterSortChanged() { filterSortChanged() {
// Save filterKey and sortKey to local storage
localStorage.setItem('podcastEpisodesFilter', this.filterKey)
localStorage.setItem('podcastEpisodesSortBy', this.sortKey + (this.sortDesc ? '-desc' : ''))
this.init() this.init()
}, },
refresh() { refresh() {
@@ -537,6 +540,11 @@ export default {
} }
}, },
mounted() { mounted() {
this.filterKey = localStorage.getItem('podcastEpisodesFilter') || 'incomplete'
const sortBy = localStorage.getItem('podcastEpisodesSortBy') || 'publishedAt-desc'
this.sortKey = sortBy.split('-')[0]
this.sortDesc = sortBy.split('-')[1] === 'desc'
this.episodesCopy = this.episodes.map((ep) => ({ ...ep })) this.episodesCopy = this.episodes.map((ep) => ({ ...ep }))
this.initListeners() this.initListeners()
this.init() this.init()
@@ -71,8 +71,6 @@ export default {
this.showSeriesForm = true this.showSeriesForm = true
}, },
submitSeriesForm() { submitSeriesForm() {
console.log('submit series form', this.value, this.selectedSeries)
if (!this.selectedSeries.name) { if (!this.selectedSeries.name) {
this.$toast.error('Must enter a series') this.$toast.error('Must enter a series')
return return
+16 -16
View File
@@ -28,10 +28,8 @@ export default {
var validOtherFiles = [] var validOtherFiles = []
var ignoredFiles = [] var ignoredFiles = []
files.forEach((file) => { files.forEach((file) => {
// var filetype = this.checkFileType(file.name)
if (!file.filetype) ignoredFiles.push(file) if (!file.filetype) ignoredFiles.push(file)
else { else {
// file.filetype = filetype
if (file.filetype === 'audio' || (file.filetype === 'ebook' && mediaType === 'book')) validItemFiles.push(file) if (file.filetype === 'audio' || (file.filetype === 'ebook' && mediaType === 'book')) validItemFiles.push(file)
else validOtherFiles.push(file) else validOtherFiles.push(file)
} }
@@ -165,7 +163,7 @@ export default {
var firstBookPath = Path.dirname(firstBookFile.filepath) var firstBookPath = Path.dirname(firstBookFile.filepath)
var dirs = firstBookPath.split('/').filter(d => !!d && d !== '.') var dirs = firstBookPath.split('/').filter((d) => !!d && d !== '.')
if (dirs.length) { if (dirs.length) {
audiobook.title = dirs.pop() audiobook.title = dirs.pop()
if (dirs.length > 1) { if (dirs.length > 1) {
@@ -189,7 +187,7 @@ export default {
var firstAudioFile = podcast.itemFiles[0] var firstAudioFile = podcast.itemFiles[0]
if (!firstAudioFile.filepath) return podcast // No path if (!firstAudioFile.filepath) return podcast // No path
var firstPath = Path.dirname(firstAudioFile.filepath) var firstPath = Path.dirname(firstAudioFile.filepath)
var dirs = firstPath.split('/').filter(d => !!d && d !== '.') var dirs = firstPath.split('/').filter((d) => !!d && d !== '.')
if (dirs.length) { if (dirs.length) {
podcast.title = dirs.length > 1 ? dirs[1] : dirs[0] podcast.title = dirs.length > 1 ? dirs[1] : dirs[0]
} else { } else {
@@ -212,13 +210,15 @@ export default {
} }
var ignoredFiles = itemData.ignoredFiles var ignoredFiles = itemData.ignoredFiles
var index = 1 var index = 1
var items = itemData.items.filter((ab) => { var items = itemData.items
if (!ab.itemFiles.length) { .filter((ab) => {
if (ab.otherFiles.length) ignoredFiles = ignoredFiles.concat(ab.otherFiles) if (!ab.itemFiles.length) {
if (ab.ignoredFiles.length) ignoredFiles = ignoredFiles.concat(ab.ignoredFiles) if (ab.otherFiles.length) ignoredFiles = ignoredFiles.concat(ab.otherFiles)
} if (ab.ignoredFiles.length) ignoredFiles = ignoredFiles.concat(ab.ignoredFiles)
return ab.itemFiles.length }
}).map(ab => this.cleanItem(ab, mediaType, index++)) return ab.itemFiles.length
})
.map((ab) => this.cleanItem(ab, mediaType, index++))
return { return {
items, items,
ignoredFiles ignoredFiles
@@ -259,7 +259,7 @@ export default {
otherFiles.forEach((file) => { otherFiles.forEach((file) => {
var dir = Path.dirname(file.filepath) var dir = Path.dirname(file.filepath)
var findItem = Object.values(itemMap).find(b => dir.startsWith(b.path)) var findItem = Object.values(itemMap).find((b) => dir.startsWith(b.path))
if (findItem) { if (findItem) {
findItem.otherFiles.push(file) findItem.otherFiles.push(file)
} else { } else {
@@ -270,18 +270,18 @@ export default {
var items = [] var items = []
var index = 1 var index = 1
// If book media type and all files are audio files then treat each one as an audiobook // If book media type and all files are audio files then treat each one as an audiobook
if (itemMap[''] && !otherFiles.length && mediaType === 'book' && !itemMap[''].itemFiles.some(f => f.filetype !== 'audio')) { if (itemMap[''] && !otherFiles.length && mediaType === 'book' && !itemMap[''].itemFiles.some((f) => f.filetype !== 'audio')) {
items = itemMap[''].itemFiles.map((audioFile) => { items = itemMap[''].itemFiles.map((audioFile) => {
return this.cleanItem({ itemFiles: [audioFile], otherFiles: [], ignoredFiles: [] }, mediaType, index++) return this.cleanItem({ itemFiles: [audioFile], otherFiles: [], ignoredFiles: [] }, mediaType, index++)
}) })
} else { } else {
items = Object.values(itemMap).map(i => this.cleanItem(i, mediaType, index++)) items = Object.values(itemMap).map((i) => this.cleanItem(i, mediaType, index++))
} }
return { return {
items, items,
ignoredFiles: ignoredFiles ignoredFiles: ignoredFiles
} }
}, }
} }
} }
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.17.0", "version": "2.17.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.17.0", "version": "2.17.2",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.13.6", "@nuxtjs/axios": "^5.13.6",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.17.0", "version": "2.17.2",
"buildNumber": 1, "buildNumber": 1,
"description": "Self-hosted audiobook and podcast client", "description": "Self-hosted audiobook and podcast client",
"main": "index.js", "main": "index.js",
+16 -12
View File
@@ -1,20 +1,20 @@
<template> <template>
<div id="page-wrapper" class="page p-0 sm:p-6 overflow-y-auto" :class="streamLibraryItem ? 'streaming' : ''"> <div id="page-wrapper" class="page p-1 sm:p-6 overflow-y-auto" :class="streamLibraryItem ? 'streaming' : ''">
<div class="w-full max-w-6xl mx-auto"> <div class="w-full max-w-6xl mx-auto">
<!-- Library & folder picker --> <!-- Library & folder picker -->
<div class="flex my-6 -mx-2"> <div class="flex flex-wrap my-6 md:-mx-2">
<div class="w-1/5 px-2"> <div class="w-full md:w-1/5 px-2">
<ui-dropdown v-model="selectedLibraryId" :items="libraryItems" :label="$strings.LabelLibrary" :disabled="!!items.length" @input="libraryChanged" /> <ui-dropdown v-model="selectedLibraryId" :items="libraryItems" :label="$strings.LabelLibrary" :disabled="!!items.length" @input="libraryChanged" />
</div> </div>
<div class="w-3/5 px-2"> <div class="w-full md:w-3/5 px-2">
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="!selectedLibraryId || !!items.length" :label="$strings.LabelFolder" /> <ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="!selectedLibraryId || !!items.length" :label="$strings.LabelFolder" />
</div> </div>
<div class="w-1/5 px-2"> <div class="w-full md:w-1/5 px-2">
<ui-text-input-with-label :value="selectedLibraryMediaType" readonly :label="$strings.LabelMediaType" /> <ui-text-input-with-label :value="selectedLibraryMediaType" readonly :label="$strings.LabelMediaType" />
</div> </div>
</div> </div>
<div v-if="!selectedLibraryIsPodcast" class="flex items-center mb-6"> <div v-if="!selectedLibraryIsPodcast" class="flex items-center mb-6 px-2 md:px-0">
<label class="flex cursor-pointer pt-4"> <label class="flex cursor-pointer pt-4">
<ui-toggle-switch v-model="fetchMetadata.enabled" class="inline-flex" /> <ui-toggle-switch v-model="fetchMetadata.enabled" class="inline-flex" />
<span class="pl-2 text-base">{{ $strings.LabelAutoFetchMetadata }}</span> <span class="pl-2 text-base">{{ $strings.LabelAutoFetchMetadata }}</span>
@@ -33,13 +33,13 @@
</widgets-alert> </widgets-alert>
<!-- Picker display --> <!-- Picker display -->
<div v-if="!items.length && !ignoredFiles.length" class="w-full mx-auto border border-white border-opacity-20 px-12 pt-12 pb-4 my-12 relative" :class="isDragging ? 'bg-primary bg-opacity-40' : 'border-dashed'"> <div v-if="!items.length && !ignoredFiles.length" class="w-full mx-auto border border-white border-opacity-20 px-4 md:px-12 pt-12 pb-4 my-12 relative" :class="isDragging ? 'bg-primary bg-opacity-40' : 'border-dashed'">
<p class="text-2xl text-center">{{ isDragging ? $strings.LabelUploaderDropFiles : $strings.LabelUploaderDragAndDrop }}</p> <p class="text-2xl text-center">{{ isDragging ? $strings.LabelUploaderDropFiles : isIOS ? $strings.LabelUploaderDragAndDropFilesOnly : $strings.LabelUploaderDragAndDrop }}</p>
<p class="text-center text-sm my-5">{{ $strings.MessageOr }}</p> <p class="text-center text-sm my-5">{{ $strings.MessageOr }}</p>
<div class="w-full max-w-xl mx-auto"> <div class="w-full max-w-xl mx-auto">
<div class="flex"> <div class="flex">
<ui-btn class="w-full mx-1" @click="openFilePicker">{{ $strings.ButtonChooseFiles }}</ui-btn> <ui-btn class="w-full mx-1" @click="openFilePicker">{{ $strings.ButtonChooseFiles }}</ui-btn>
<ui-btn class="w-full mx-1" @click="openFolderPicker">{{ $strings.ButtonChooseAFolder }}</ui-btn> <ui-btn v-if="!isIOS" class="w-full mx-1" @click="openFolderPicker">{{ $strings.ButtonChooseAFolder }} </ui-btn>
</div> </div>
</div> </div>
<div class="pt-8 text-center"> <div class="pt-8 text-center">
@@ -48,7 +48,7 @@
</p> </p>
<p class="text-sm text-white text-opacity-70"> <p class="text-sm text-white text-opacity-70">
{{ $strings.NoteUploaderFoldersWithMediaFiles }} <span v-if="selectedLibraryMediaType === 'book'">{{ $strings.NoteUploaderOnlyAudioFiles }}</span> <span v-if="!isIOS">{{ $strings.NoteUploaderFoldersWithMediaFiles }}</span> <span v-if="selectedLibraryMediaType === 'book'">{{ $strings.NoteUploaderOnlyAudioFiles }}</span>
</p> </p>
</div> </div>
</div> </div>
@@ -84,8 +84,8 @@
</div> </div>
</div> </div>
<input ref="fileInput" type="file" multiple :accept="inputAccept" class="hidden" @change="inputChanged" /> <input ref="fileInput" type="file" multiple :accept="isIOS ? '' : inputAccept" class="hidden" @change="inputChanged" />
<input ref="fileFolderInput" type="file" webkitdirectory multiple :accept="inputAccept" class="hidden" @change="inputChanged" /> <input ref="fileFolderInput" type="file" webkitdirectory multiple :accept="inputAccept" class="hidden" @change="inputChanged" v-if="!isIOS" />
</div> </div>
</template> </template>
@@ -127,6 +127,10 @@ export default {
}) })
return extensions return extensions
}, },
isIOS() {
const ua = window.navigator.userAgent
return /iPad|iPhone|iPod/.test(ua) && !window.MSStream
},
streamLibraryItem() { streamLibraryItem() {
return this.$store.state.streamLibraryItem return this.$store.state.streamLibraryItem
}, },
+26 -1
View File
@@ -127,5 +127,30 @@
"HeaderCollectionItems": "عناصر المجموعة", "HeaderCollectionItems": "عناصر المجموعة",
"HeaderCover": "الغلاف", "HeaderCover": "الغلاف",
"HeaderCurrentDownloads": "التنزيلات الجارية", "HeaderCurrentDownloads": "التنزيلات الجارية",
"HeaderCustomMessageOnLogin": "رسالة مخصصة عند تسجيل الدخول" "HeaderCustomMessageOnLogin": "رسالة مخصصة عند تسجيل الدخول",
"HeaderCustomMetadataProviders": "مقدمو البيانات الوصفية المخصصة",
"HeaderDetails": "التفاصيل",
"HeaderDownloadQueue": "تنزيل قائمة الانتظار",
"HeaderEbookFiles": "ملفات الكتب الإلكترونية",
"HeaderEmail": "البريد الإلكتروني",
"HeaderEmailSettings": "إعدادات البريد الإلكتروني",
"HeaderEpisodes": "الحلقات",
"HeaderEreaderDevices": "أجهزة قراءة الكتب الإلكترونية",
"HeaderEreaderSettings": "إعدادات القارئ الإلكتروني",
"HeaderFiles": "ملفات",
"HeaderFindChapters": "البحث عن الفصول",
"HeaderIgnoredFiles": "الملفات المتجاهلة",
"HeaderItemFiles": "ملفات العنصر",
"HeaderItemMetadataUtils": "بيانات تعريف العنصر",
"HeaderLastListeningSession": "آخر جلسة استماع",
"HeaderLatestEpisodes": "أحدث الحلقات",
"HeaderLibraries": "المكتبات",
"HeaderLibraryFiles": "ملفات المكتبة",
"HeaderLibraryStats": "إحصائيات المكتبة",
"HeaderListeningSessions": "جلسات الاستماع",
"HeaderListeningStats": "جلسات الاستماع",
"HeaderLogin": "تسجيل الدخول",
"HeaderLogs": "السجلات",
"HeaderManageGenres": "إدارة الانواع",
"HeaderManageTags": "إدارة العلامات"
} }
+6 -5
View File
@@ -71,8 +71,8 @@
"ButtonQuickMatch": "Schnellabgleich", "ButtonQuickMatch": "Schnellabgleich",
"ButtonReScan": "Neu scannen", "ButtonReScan": "Neu scannen",
"ButtonRead": "Lesen", "ButtonRead": "Lesen",
"ButtonReadLess": "Weniger anzeigen", "ButtonReadLess": "weniger Anzeigen",
"ButtonReadMore": "Mehr anzeigen", "ButtonReadMore": "Mehr Anzeigen",
"ButtonRefresh": "Neu Laden", "ButtonRefresh": "Neu Laden",
"ButtonRemove": "Entfernen", "ButtonRemove": "Entfernen",
"ButtonRemoveAll": "Alles entfernen", "ButtonRemoveAll": "Alles entfernen",
@@ -220,7 +220,7 @@
"LabelAddToPlaylist": "Zur Wiedergabeliste hinzufügen", "LabelAddToPlaylist": "Zur Wiedergabeliste hinzufügen",
"LabelAddToPlaylistBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Wiedergabeliste hinzu", "LabelAddToPlaylistBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Wiedergabeliste hinzu",
"LabelAddedAt": "Hinzugefügt am", "LabelAddedAt": "Hinzugefügt am",
"LabelAddedDate": "Hinzugefügt {0}", "LabelAddedDate": "{0} Hinzugefügt",
"LabelAdminUsersOnly": "Nur Admin Benutzer", "LabelAdminUsersOnly": "Nur Admin Benutzer",
"LabelAll": "Alle", "LabelAll": "Alle",
"LabelAllUsers": "Alle Benutzer", "LabelAllUsers": "Alle Benutzer",
@@ -534,6 +534,7 @@
"LabelSelectUsers": "Benutzer auswählen", "LabelSelectUsers": "Benutzer auswählen",
"LabelSendEbookToDevice": "E-Buch senden an …", "LabelSendEbookToDevice": "E-Buch senden an …",
"LabelSequence": "Reihenfolge", "LabelSequence": "Reihenfolge",
"LabelSerial": "fortlaufend",
"LabelSeries": "Serien", "LabelSeries": "Serien",
"LabelSeriesName": "Serienname", "LabelSeriesName": "Serienname",
"LabelSeriesProgress": "Serienfortschritt", "LabelSeriesProgress": "Serienfortschritt",
@@ -680,8 +681,8 @@
"LabelWeekdaysToRun": "Wochentage für die Ausführung", "LabelWeekdaysToRun": "Wochentage für die Ausführung",
"LabelXBooks": "{0} Bücher", "LabelXBooks": "{0} Bücher",
"LabelXItems": "{0} Medien", "LabelXItems": "{0} Medien",
"LabelYearReviewHide": "Verstecke Jahr in Übersicht", "LabelYearReviewHide": "Jahresrückblick verbergen",
"LabelYearReviewShow": "Zeige Jahr in Übersicht", "LabelYearReviewShow": "Jahresrückblick anzeigen",
"LabelYourAudiobookDuration": "Laufzeit deines Mediums", "LabelYourAudiobookDuration": "Laufzeit deines Mediums",
"LabelYourBookmarks": "Lesezeichen", "LabelYourBookmarks": "Lesezeichen",
"LabelYourPlaylists": "Eigene Wiedergabelisten", "LabelYourPlaylists": "Eigene Wiedergabelisten",
+1
View File
@@ -663,6 +663,7 @@
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located", "LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
"LabelUpdatedAt": "Updated At", "LabelUpdatedAt": "Updated At",
"LabelUploaderDragAndDrop": "Drag & drop files or folders", "LabelUploaderDragAndDrop": "Drag & drop files or folders",
"LabelUploaderDragAndDropFilesOnly": "Drag & drop files",
"LabelUploaderDropFiles": "Drop files", "LabelUploaderDropFiles": "Drop files",
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series", "LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
"LabelUseAdvancedOptions": "Use Advanced Options", "LabelUseAdvancedOptions": "Use Advanced Options",
+24 -1
View File
@@ -18,7 +18,8 @@
"ButtonChooseAFolder": "בחר תיקייה", "ButtonChooseAFolder": "בחר תיקייה",
"ButtonChooseFiles": "בחר קבצים", "ButtonChooseFiles": "בחר קבצים",
"ButtonClearFilter": "נקה סינון", "ButtonClearFilter": "נקה סינון",
"ButtonCloseFeed": "סגור פיד", "ButtonCloseFeed": "סגור ערוץ",
"ButtonCloseSession": "סגור סשן פתוח",
"ButtonCollections": "אוספים", "ButtonCollections": "אוספים",
"ButtonConfigureScanner": "הגדר סורק", "ButtonConfigureScanner": "הגדר סורק",
"ButtonCreate": "צור", "ButtonCreate": "צור",
@@ -28,6 +29,7 @@
"ButtonEdit": "ערוך", "ButtonEdit": "ערוך",
"ButtonEditChapters": "ערוך פרקים", "ButtonEditChapters": "ערוך פרקים",
"ButtonEditPodcast": "ערוך פודקאסט", "ButtonEditPodcast": "ערוך פודקאסט",
"ButtonEnable": "הפעל",
"ButtonForceReScan": "סרוק מחדש בכוח", "ButtonForceReScan": "סרוק מחדש בכוח",
"ButtonFullPath": "נתיב מלא", "ButtonFullPath": "נתיב מלא",
"ButtonHide": "הסתר", "ButtonHide": "הסתר",
@@ -46,19 +48,24 @@
"ButtonNevermind": "לא משנה", "ButtonNevermind": "לא משנה",
"ButtonNext": "הבא", "ButtonNext": "הבא",
"ButtonNextChapter": "פרק הבא", "ButtonNextChapter": "פרק הבא",
"ButtonNextItemInQueue": "פריט הבא בתור",
"ButtonOk": "אישור", "ButtonOk": "אישור",
"ButtonOpenFeed": "פתח פיד", "ButtonOpenFeed": "פתח פיד",
"ButtonOpenManager": "פתח מנהל", "ButtonOpenManager": "פתח מנהל",
"ButtonPause": "השהה", "ButtonPause": "השהה",
"ButtonPlay": "נגן", "ButtonPlay": "נגן",
"ButtonPlayAll": "נגן הכל",
"ButtonPlaying": "מנגן", "ButtonPlaying": "מנגן",
"ButtonPlaylists": "רשימות השמעה", "ButtonPlaylists": "רשימות השמעה",
"ButtonPrevious": "קודם", "ButtonPrevious": "קודם",
"ButtonPreviousChapter": "פרק קודם", "ButtonPreviousChapter": "פרק קודם",
"ButtonProbeAudioFile": "בדוק קובץ אודיו",
"ButtonPurgeAllCache": "נקה את כל המטמון", "ButtonPurgeAllCache": "נקה את כל המטמון",
"ButtonPurgeItemsCache": "נקה את מטמון הפריטים", "ButtonPurgeItemsCache": "נקה את מטמון הפריטים",
"ButtonQueueAddItem": "הוסף לתור", "ButtonQueueAddItem": "הוסף לתור",
"ButtonQueueRemoveItem": "הסר מהתור", "ButtonQueueRemoveItem": "הסר מהתור",
"ButtonQuickEmbed": "הטמעה מהירה",
"ButtonQuickEmbedMetadata": "הטמעת מטא נתונים מהירה",
"ButtonQuickMatch": "התאמה מהירה", "ButtonQuickMatch": "התאמה מהירה",
"ButtonReScan": "סרוק מחדש", "ButtonReScan": "סרוק מחדש",
"ButtonRead": "קרא", "ButtonRead": "קרא",
@@ -88,8 +95,10 @@
"ButtonShow": "הצג", "ButtonShow": "הצג",
"ButtonStartM4BEncode": "התחל קידוד M4B", "ButtonStartM4BEncode": "התחל קידוד M4B",
"ButtonStartMetadataEmbed": "התחל הטמעת מטא-נתונים", "ButtonStartMetadataEmbed": "התחל הטמעת מטא-נתונים",
"ButtonStats": "סטטיסטיקות",
"ButtonSubmit": "שלח", "ButtonSubmit": "שלח",
"ButtonTest": "בדיקה", "ButtonTest": "בדיקה",
"ButtonUnlinkOpenId": "נתק OpenID",
"ButtonUpload": "העלה", "ButtonUpload": "העלה",
"ButtonUploadBackup": "העלה גיבוי", "ButtonUploadBackup": "העלה גיבוי",
"ButtonUploadCover": "העלה כריכה", "ButtonUploadCover": "העלה כריכה",
@@ -102,6 +111,7 @@
"ErrorUploadFetchMetadataNoResults": "לא ניתן לשלוף מטא-נתונים - נסה לעדכן כותרת ו/או יוצר", "ErrorUploadFetchMetadataNoResults": "לא ניתן לשלוף מטא-נתונים - נסה לעדכן כותרת ו/או יוצר",
"ErrorUploadLacksTitle": "חובה לתת כותרת", "ErrorUploadLacksTitle": "חובה לתת כותרת",
"HeaderAccount": "חשבון", "HeaderAccount": "חשבון",
"HeaderAddCustomMetadataProvider": "הוסף ספק מטא-נתונים מותאם אישית",
"HeaderAdvanced": "מתקדם", "HeaderAdvanced": "מתקדם",
"HeaderAppriseNotificationSettings": "הגדרות התראות של Apprise", "HeaderAppriseNotificationSettings": "הגדרות התראות של Apprise",
"HeaderAudioTracks": "רצועות קול", "HeaderAudioTracks": "רצועות קול",
@@ -147,13 +157,17 @@
"HeaderMetadataToEmbed": "מטא-נתונים להטמעה", "HeaderMetadataToEmbed": "מטא-נתונים להטמעה",
"HeaderNewAccount": "חשבון חדש", "HeaderNewAccount": "חשבון חדש",
"HeaderNewLibrary": "ספרייה חדשה", "HeaderNewLibrary": "ספרייה חדשה",
"HeaderNotificationCreate": "צור התראה",
"HeaderNotificationUpdate": "עדכון התראה",
"HeaderNotifications": "התראות", "HeaderNotifications": "התראות",
"HeaderOpenIDConnectAuthentication": "אימות OpenID Connect", "HeaderOpenIDConnectAuthentication": "אימות OpenID Connect",
"HeaderOpenListeningSessions": "פתח הפעלות האזנה",
"HeaderOpenRSSFeed": "פתח ערוץ RSS", "HeaderOpenRSSFeed": "פתח ערוץ RSS",
"HeaderOtherFiles": "קבצים אחרים", "HeaderOtherFiles": "קבצים אחרים",
"HeaderPasswordAuthentication": "אימות סיסמה", "HeaderPasswordAuthentication": "אימות סיסמה",
"HeaderPermissions": "הרשאות", "HeaderPermissions": "הרשאות",
"HeaderPlayerQueue": "תור ניגון", "HeaderPlayerQueue": "תור ניגון",
"HeaderPlayerSettings": "הגדרות נגן",
"HeaderPlaylist": "רשימת השמעה", "HeaderPlaylist": "רשימת השמעה",
"HeaderPlaylistItems": "פריטי רשימת השמעה", "HeaderPlaylistItems": "פריטי רשימת השמעה",
"HeaderPodcastsToAdd": "פודקאסטים להוספה", "HeaderPodcastsToAdd": "פודקאסטים להוספה",
@@ -165,6 +179,7 @@
"HeaderRemoveEpisodes": "הסר {0} פרקים", "HeaderRemoveEpisodes": "הסר {0} פרקים",
"HeaderSavedMediaProgress": "התקדמות מדיה שמורה", "HeaderSavedMediaProgress": "התקדמות מדיה שמורה",
"HeaderSchedule": "תיזמון", "HeaderSchedule": "תיזמון",
"HeaderScheduleEpisodeDownloads": "תזמן הורדת פרקים אוטומטית",
"HeaderScheduleLibraryScans": "קבע סריקות ספרייה אוטומטיות", "HeaderScheduleLibraryScans": "קבע סריקות ספרייה אוטומטיות",
"HeaderSession": "הפעלה", "HeaderSession": "הפעלה",
"HeaderSetBackupSchedule": "קבע לוח זמנים לגיבוי", "HeaderSetBackupSchedule": "קבע לוח זמנים לגיבוי",
@@ -190,6 +205,9 @@
"HeaderYearReview": "שנת {0} בסקירה", "HeaderYearReview": "שנת {0} בסקירה",
"HeaderYourStats": "הסטטיסטיקות שלך", "HeaderYourStats": "הסטטיסטיקות שלך",
"LabelAbridged": "מקוצר", "LabelAbridged": "מקוצר",
"LabelAbridgedChecked": "מקוצר (מסומן)",
"LabelAbridgedUnchecked": "בלתי מקוצר (לא מסומן)",
"LabelAccessibleBy": "נגיש על ידי",
"LabelAccountType": "סוג חשבון", "LabelAccountType": "סוג חשבון",
"LabelAccountTypeAdmin": "מנהל", "LabelAccountTypeAdmin": "מנהל",
"LabelAccountTypeGuest": "אורח", "LabelAccountTypeGuest": "אורח",
@@ -200,13 +218,18 @@
"LabelAddToPlaylist": "הוסף לרשימת השמעה", "LabelAddToPlaylist": "הוסף לרשימת השמעה",
"LabelAddToPlaylistBatch": "הוסף {0} פריטים לרשימת השמעה", "LabelAddToPlaylistBatch": "הוסף {0} פריטים לרשימת השמעה",
"LabelAddedAt": "נוסף בתאריך", "LabelAddedAt": "נוסף בתאריך",
"LabelAddedDate": "נוסף ב-{0}",
"LabelAdminUsersOnly": "רק מנהלים", "LabelAdminUsersOnly": "רק מנהלים",
"LabelAll": "הכל", "LabelAll": "הכל",
"LabelAllUsers": "כל המשתמשים", "LabelAllUsers": "כל המשתמשים",
"LabelAllUsersExcludingGuests": "כל המשתמשים, ללא אורחים", "LabelAllUsersExcludingGuests": "כל המשתמשים, ללא אורחים",
"LabelAllUsersIncludingGuests": "כל המשתמשים כולל אורחים", "LabelAllUsersIncludingGuests": "כל המשתמשים כולל אורחים",
"LabelAlreadyInYourLibrary": "כבר קיים בספרייה שלך", "LabelAlreadyInYourLibrary": "כבר קיים בספרייה שלך",
"LabelApiToken": "טוקן API",
"LabelAppend": "הוסף לסוף", "LabelAppend": "הוסף לסוף",
"LabelAudioBitrate": "קצב סיביות (לדוגמא 128k)",
"LabelAudioChannels": "ערוצי קול (1 או 2)",
"LabelAudioCodec": "קידוד קול",
"LabelAuthor": "יוצר", "LabelAuthor": "יוצר",
"LabelAuthorFirstLast": "יוצר (שם פרטי שם משפחה)", "LabelAuthorFirstLast": "יוצר (שם פרטי שם משפחה)",
"LabelAuthorLastFirst": "יוצר (שם משפחה, שם פרטי)", "LabelAuthorLastFirst": "יוצר (שם משפחה, שם פרטי)",
+7 -7
View File
@@ -66,13 +66,13 @@
"ButtonPurgeItemsCache": "Elimina la Cache selezionata", "ButtonPurgeItemsCache": "Elimina la Cache selezionata",
"ButtonQueueAddItem": "Aggiungi alla Coda", "ButtonQueueAddItem": "Aggiungi alla Coda",
"ButtonQueueRemoveItem": "Rimuovi dalla Coda", "ButtonQueueRemoveItem": "Rimuovi dalla Coda",
"ButtonQuickEmbed": "Quick Embed", "ButtonQuickEmbed": "Incorporazione Rapida",
"ButtonQuickEmbedMetadata": "Incorporamento rapido Metadati", "ButtonQuickEmbedMetadata": "Incorporamento rapido Metadati",
"ButtonQuickMatch": "Controlla Metadata Auto", "ButtonQuickMatch": "Controlla Metadata Auto",
"ButtonReScan": "Ri-scansiona", "ButtonReScan": "Ri-scansiona",
"ButtonRead": "Leggi", "ButtonRead": "Leggi",
"ButtonReadLess": "Leggi di Meno", "ButtonReadLess": "Riduci",
"ButtonReadMore": "Leggi di Più", "ButtonReadMore": "Espandi",
"ButtonRefresh": "Aggiorna", "ButtonRefresh": "Aggiorna",
"ButtonRemove": "Rimuovi", "ButtonRemove": "Rimuovi",
"ButtonRemoveAll": "Rimuovi Tutto", "ButtonRemoveAll": "Rimuovi Tutto",
@@ -220,7 +220,7 @@
"LabelAddToPlaylist": "Aggiungi alla playlist", "LabelAddToPlaylist": "Aggiungi alla playlist",
"LabelAddToPlaylistBatch": "Aggiungi {0} file alla Playlist", "LabelAddToPlaylistBatch": "Aggiungi {0} file alla Playlist",
"LabelAddedAt": "Aggiunto il", "LabelAddedAt": "Aggiunto il",
"LabelAddedDate": "{0} aggiunti", "LabelAddedDate": "Aggiunti {0}",
"LabelAdminUsersOnly": "Solo utenti Amministratori", "LabelAdminUsersOnly": "Solo utenti Amministratori",
"LabelAll": "Tutti", "LabelAll": "Tutti",
"LabelAllUsers": "Tutti gli Utenti", "LabelAllUsers": "Tutti gli Utenti",
@@ -495,7 +495,7 @@
"LabelProviderAuthorizationValue": "Authorization Header Value", "LabelProviderAuthorizationValue": "Authorization Header Value",
"LabelPubDate": "Data di pubblicazione", "LabelPubDate": "Data di pubblicazione",
"LabelPublishYear": "Anno di pubblicazione", "LabelPublishYear": "Anno di pubblicazione",
"LabelPublishedDate": "{0} pubblicati", "LabelPublishedDate": "Pubblicati {0}",
"LabelPublishedDecade": "Decennio di pubblicazione", "LabelPublishedDecade": "Decennio di pubblicazione",
"LabelPublishedDecades": "Decenni di pubblicazione", "LabelPublishedDecades": "Decenni di pubblicazione",
"LabelPublisher": "Editore", "LabelPublisher": "Editore",
@@ -682,7 +682,7 @@
"LabelXBooks": "{0} libri", "LabelXBooks": "{0} libri",
"LabelXItems": "{0} oggetti", "LabelXItems": "{0} oggetti",
"LabelYearReviewHide": "Nascondi Anno in rassegna", "LabelYearReviewHide": "Nascondi Anno in rassegna",
"LabelYearReviewShow": "Vedi Anno in rassegna", "LabelYearReviewShow": "Mostra Anno in rassegna",
"LabelYourAudiobookDuration": "La durata dell'audiolibro", "LabelYourAudiobookDuration": "La durata dell'audiolibro",
"LabelYourBookmarks": "I tuoi preferiti", "LabelYourBookmarks": "I tuoi preferiti",
"LabelYourPlaylists": "le tue Playlist", "LabelYourPlaylists": "le tue Playlist",
@@ -779,7 +779,7 @@
"MessageNoBackups": "Nessun Backup", "MessageNoBackups": "Nessun Backup",
"MessageNoBookmarks": "Nessun preferito", "MessageNoBookmarks": "Nessun preferito",
"MessageNoChapters": "Nessun capitolo", "MessageNoChapters": "Nessun capitolo",
"MessageNoCollections": "Nessuna Raccolta", "MessageNoCollections": "Nessuna Collezione",
"MessageNoCoversFound": "Nessuna Cover Trovata", "MessageNoCoversFound": "Nessuna Cover Trovata",
"MessageNoDescription": "Nessuna descrizione", "MessageNoDescription": "Nessuna descrizione",
"MessageNoDevices": "nessun dispositivo", "MessageNoDevices": "nessun dispositivo",
+2 -2
View File
@@ -495,7 +495,7 @@
"LabelProviderAuthorizationValue": "Vrednost glave avtorizacije", "LabelProviderAuthorizationValue": "Vrednost glave avtorizacije",
"LabelPubDate": "Datum objave", "LabelPubDate": "Datum objave",
"LabelPublishYear": "Leto izdaje", "LabelPublishYear": "Leto izdaje",
"LabelPublishedDate": "Izdano {0}", "LabelPublishedDate": "Objavljeno {0}",
"LabelPublishedDecade": "Desetletje izdaje", "LabelPublishedDecade": "Desetletje izdaje",
"LabelPublishedDecades": "Desetletja izdaje", "LabelPublishedDecades": "Desetletja izdaje",
"LabelPublisher": "Izdajatelj", "LabelPublisher": "Izdajatelj",
@@ -682,7 +682,7 @@
"LabelXBooks": "{0} knjig", "LabelXBooks": "{0} knjig",
"LabelXItems": "{0} elementov", "LabelXItems": "{0} elementov",
"LabelYearReviewHide": "Skrij pregled leta", "LabelYearReviewHide": "Skrij pregled leta",
"LabelYearReviewShow": "Poglej pregled leta", "LabelYearReviewShow": "Poglej si pregled leta",
"LabelYourAudiobookDuration": "Trajanje tvojih zvočnih knjig", "LabelYourAudiobookDuration": "Trajanje tvojih zvočnih knjig",
"LabelYourBookmarks": "Tvoji zaznamki", "LabelYourBookmarks": "Tvoji zaznamki",
"LabelYourPlaylists": "Tvoje seznami predvajanj", "LabelYourPlaylists": "Tvoje seznami predvajanj",
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.17.0", "version": "2.17.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.17.0", "version": "2.17.2",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"axios": "^0.27.2", "axios": "^0.27.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.17.0", "version": "2.17.2",
"buildNumber": 1, "buildNumber": 1,
"description": "Self-hosted audiobook and podcast server", "description": "Self-hosted audiobook and podcast server",
"main": "index.js", "main": "index.js",
@@ -27,10 +27,14 @@ async function up({ context: { queryInterface, logger } }) {
type: 'UUID' type: 'UUID'
}) })
logger.info('[2.17.0 migration] Changing mediaItemShares.mediaItemId column to UUID') if (await queryInterface.tableExists('mediaItemShares')) {
await queryInterface.changeColumn('mediaItemShares', 'mediaItemId', { logger.info('[2.17.0 migration] Changing mediaItemShares.mediaItemId column to UUID')
type: 'UUID' await queryInterface.changeColumn('mediaItemShares', 'mediaItemId', {
}) type: 'UUID'
})
} else {
logger.info('[2.17.0 migration] mediaItemShares table does not exist, skipping column change')
}
logger.info('[2.17.0 migration] Changing playbackSessions.mediaItemId column to UUID') logger.info('[2.17.0 migration] Changing playbackSessions.mediaItemId column to UUID')
await queryInterface.changeColumn('playbackSessions', 'mediaItemId', { await queryInterface.changeColumn('playbackSessions', 'mediaItemId', {
+1 -1
View File
@@ -479,7 +479,7 @@ class LibraryItem extends Model {
{ {
model: this.sequelize.models.series, model: this.sequelize.models.series,
through: { through: {
attributes: ['sequence'] attributes: ['id', 'sequence']
} }
} }
], ],
+6 -1
View File
@@ -29,7 +29,12 @@ class BookMetadata {
this.subtitle = metadata.subtitle this.subtitle = metadata.subtitle
this.authors = metadata.authors?.map ? metadata.authors.map((a) => ({ ...a })) : [] this.authors = metadata.authors?.map ? metadata.authors.map((a) => ({ ...a })) : []
this.narrators = metadata.narrators ? [...metadata.narrators].filter((n) => n) : [] this.narrators = metadata.narrators ? [...metadata.narrators].filter((n) => n) : []
this.series = metadata.series?.map ? metadata.series.map((s) => ({ ...s })) : [] this.series = metadata.series?.map
? metadata.series.map((s) => ({
...s,
name: s.name || 'No Title'
}))
: []
this.genres = metadata.genres ? [...metadata.genres] : [] this.genres = metadata.genres ? [...metadata.genres] : []
this.publishedYear = metadata.publishedYear || null this.publishedYear = metadata.publishedYear || null
this.publishedDate = metadata.publishedDate || null this.publishedDate = metadata.publishedDate || null
+3 -3
View File
@@ -510,7 +510,7 @@ module.exports = {
// If nothing has changed, check if the number of podcasts in // If nothing has changed, check if the number of podcasts in
// library is still the same as prior check before updating cache creation time // library is still the same as prior check before updating cache creation time
if (podcastCountFromDatabase === Database.libraryFilterData[libraryId].podcastCount) { if (podcastCountFromDatabase === Database.libraryFilterData[libraryId]?.podcastCount) {
Logger.debug(`Filter data for ${libraryId} has not changed, returning cached data and updating cache time after ${((Date.now() - start) / 1000).toFixed(2)}s`) Logger.debug(`Filter data for ${libraryId} has not changed, returning cached data and updating cache time after ${((Date.now() - start) / 1000).toFixed(2)}s`)
Database.libraryFilterData[libraryId].loadedAt = Date.now() Database.libraryFilterData[libraryId].loadedAt = Date.now()
return cachedFilterData return cachedFilterData
@@ -613,7 +613,7 @@ module.exports = {
if (changedBooks + changedSeries + changedAuthors === 0) { if (changedBooks + changedSeries + changedAuthors === 0) {
// If nothing has changed, check if the number of authors, series, and books // If nothing has changed, check if the number of authors, series, and books
// matches the prior check before updating cache creation time // matches the prior check before updating cache creation time
if (bookCountFromDatabase === Database.libraryFilterData[libraryId].bookCount && seriesCountFromDatabase === Database.libraryFilterData[libraryId].seriesCount && authorCountFromDatabase === Database.libraryFilterData[libraryId].authorCount) { if (bookCountFromDatabase === Database.libraryFilterData[libraryId]?.bookCount && seriesCountFromDatabase === Database.libraryFilterData[libraryId]?.seriesCount && authorCountFromDatabase === Database.libraryFilterData[libraryId].authorCount) {
Logger.debug(`Filter data for ${libraryId} has not changed, returning cached data and updating cache time after ${((Date.now() - start) / 1000).toFixed(2)}s`) Logger.debug(`Filter data for ${libraryId} has not changed, returning cached data and updating cache time after ${((Date.now() - start) / 1000).toFixed(2)}s`)
Database.libraryFilterData[libraryId].loadedAt = Date.now() Database.libraryFilterData[libraryId].loadedAt = Date.now()
return cachedFilterData return cachedFilterData
@@ -662,7 +662,7 @@ module.exports = {
}, },
attributes: ['id', 'name'] attributes: ['id', 'name']
}) })
series.forEach((s) => data.series.push({ id: s.id, name: s.name })) series.forEach((s) => data.series.push({ id: s.id, name: s.name || 'No Title' }))
const authors = await Database.authorModel.findAll({ const authors = await Database.authorModel.findAll({
where: { where: {