Compare commits

..

40 Commits

Author SHA1 Message Date
advplyr bc1b99efd6 Fix socket events check user permissions for library items #4199 2025-04-12 17:39:51 -05:00
advplyr 26309019e7 Merge pull request #4195 from advplyr/fix_podcast_episode_scanner_promise
Fix podcast re-scan promise
2025-04-10 17:54:26 -05:00
advplyr b47d7b734d Update podcast scanner to remove media progress and episodes from playlist 2025-04-10 17:51:42 -05:00
advplyr 62194b8781 Fix podcast re-scan promise 2025-04-10 17:39:41 -05:00
advplyr 7c114a051a Set podcast episode audio file index to 1 for scanned in episodes 2025-04-09 08:33:00 -05:00
advplyr 26c0c89b94 Update podcast latest page to show latest 50 episodes #3343 2025-04-08 17:39:24 -05:00
advplyr c81071a7b3 Fix item page text overlap on details #4187 2025-04-07 17:19:48 -05:00
advplyr 31f48edcc3 Update close feed button on rss feeds table to be consistent with other UI 2025-04-06 17:56:17 -05:00
advplyr f7109a055c Fix rss feeds table overflow with long slugs 2025-04-06 17:50:39 -05:00
advplyr 9d0e7759e0 Merge pull request #4158 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-04-04 17:24:59 -05:00
peter cerny d15ccbd2fc Translated using Weblate (Slovak)
Currently translated at 50.7% (556 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:31 +02:00
Coxe cfeb1743df Translated using Weblate (Danish)
Currently translated at 99.8% (1093 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-04-05 00:17:31 +02:00
peter cerny a7e0330b06 Translated using Weblate (Slovak)
Currently translated at 43.2% (474 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:30 +02:00
biuklija 5f69e83d46 Translated using Weblate (Croatian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-04-05 00:17:30 +02:00
peter cerny fdde62896f Translated using Weblate (Slovak)
Currently translated at 41.9% (459 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:29 +02:00
thehijacker f1909d0fc7 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-04-05 00:17:28 +02:00
Mikkel Dupont Olesen 28225618fd Translated using Weblate (Danish)
Currently translated at 99.2% (1087 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-04-05 00:17:27 +02:00
Marc 1a562a5f23 Translated using Weblate (German)
Currently translated at 99.6% (1091 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-04-05 00:17:27 +02:00
peter cerny bfbbcba160 Translated using Weblate (Slovak)
Currently translated at 29.5% (324 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:26 +02:00
Jan-Eric Myhrgren 21e4d17ef3 Translated using Weblate (Swedish)
Currently translated at 94.8% (1039 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-04-05 00:17:25 +02:00
Augusto Massini Pinto 87faebc7d9 Translated using Weblate (Portuguese (Brazil))
Currently translated at 71.4% (782 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-04-05 00:17:25 +02:00
Adolfo Jayme Barrientos 5d868d1355 Translated using Weblate (Spanish)
Currently translated at 99.4% (1089 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-04-05 00:17:24 +02:00
Adolfo Jayme Barrientos ac0fd41740 Translated using Weblate (Catalan)
Currently translated at 94.2% (1032 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-04-05 00:17:24 +02:00
Adolfo Jayme Barrientos 1202e95b66 Translated using Weblate (Spanish)
Currently translated at 99.4% (1089 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-04-05 00:17:23 +02:00
Simple16 c05cf9718b Translated using Weblate (Russian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-04-05 00:17:22 +02:00
Adolfo Jayme Barrientos d8f1e43e85 Translated using Weblate (Spanish)
Currently translated at 99.2% (1087 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-04-05 00:17:22 +02:00
Adolfo Jayme Barrientos ed7e87b168 Translated using Weblate (Catalan)
Currently translated at 94.1% (1031 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-04-05 00:17:21 +02:00
Martin Balko 4ca2c9e97f Translated using Weblate (Slovak)
Currently translated at 28.5% (313 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:20 +02:00
Kabika82 01dd1c0615 Translated using Weblate (Hungarian)
Currently translated at 95.9% (1051 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-04-05 00:17:20 +02:00
Martin Balko cd387b8fed Translated using Weblate (Slovak)
Currently translated at 27.7% (304 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:19 +02:00
SunSpring c85cd69152 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-04-05 00:17:18 +02:00
Максим Горпиніч 9e9b52a252 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-04-05 00:17:18 +02:00
advplyr 292d5783a9 Update player queue button on item page to be to the right of the read button to keep consistent spacing 2025-04-04 17:17:06 -05:00
advplyr c7faafd0f3 Fix author order when scanning in multi-author books by setting bookAuthor.createdAt when bulk creating #4177 2025-04-03 17:50:10 -05:00
advplyr ca7388b14e Fix download podcast update library item size #4180 2025-04-02 17:35:57 -05:00
advplyr ddf2ca3670 Update item image in audio player when updated on item #4025 2025-04-01 17:32:21 -05:00
advplyr 96825c3c2b Update feedepisode psc customElement 2025-03-31 17:59:16 -05:00
advplyr 6ed66fea16 Update podcast rss feed parser to use psc chapters on episodes 2025-03-31 17:57:39 -05:00
advplyr ddcda197b4 Fix manage, chapters edit tracks and library stats page not setting the current library properly #4170 2025-03-30 17:27:36 -05:00
advplyr 8bea5d83f5 Merge pull request #4168 from advplyr/new_stats_controller
Create new StatsController and move year in review stats endpoint
2025-03-29 17:44:25 -05:00
40 changed files with 737 additions and 158 deletions
@@ -156,7 +156,7 @@ export default {
return this.mediaMetadata.authors || [] return this.mediaMetadata.authors || []
}, },
libraryId() { libraryId() {
return this.streamLibraryItem ? this.streamLibraryItem.libraryId : null return this.streamLibraryItem?.libraryId || null
}, },
totalDurationPretty() { totalDurationPretty() {
// Adjusted by playback rate // Adjusted by playback rate
@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<div v-if="narrators?.length" class="flex py-0.5 mt-4"> <div v-if="narrators?.length" class="flex py-0.5 mt-4">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelNarrators }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelNarrators }}</span>
</div> </div>
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis"> <div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
@@ -12,7 +12,7 @@
</div> </div>
</div> </div>
<div v-if="publishedYear" role="paragraph" class="flex py-0.5"> <div v-if="publishedYear" role="paragraph" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublishYear }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublishYear }}</span>
</div> </div>
<div> <div>
@@ -20,7 +20,7 @@
</div> </div>
</div> </div>
<div v-if="publisher" role="paragraph" class="flex py-0.5"> <div v-if="publisher" role="paragraph" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublisher }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublisher }}</span>
</div> </div>
<div> <div>
@@ -28,7 +28,7 @@
</div> </div>
</div> </div>
<div v-if="podcastType" role="paragraph" class="flex py-0.5"> <div v-if="podcastType" role="paragraph" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span>
</div> </div>
<div class="capitalize"> <div class="capitalize">
@@ -36,7 +36,7 @@
</div> </div>
</div> </div>
<div class="flex py-0.5" v-if="genres.length"> <div class="flex py-0.5" v-if="genres.length">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelGenres }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelGenres }}</span>
</div> </div>
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis"> <div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
@@ -47,7 +47,7 @@
</div> </div>
</div> </div>
<div class="flex py-0.5" v-if="tags.length"> <div class="flex py-0.5" v-if="tags.length">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelTags }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelTags }}</span>
</div> </div>
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis"> <div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
@@ -58,7 +58,7 @@
</div> </div>
</div> </div>
<div v-if="language" class="flex py-0.5"> <div v-if="language" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelLanguage }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelLanguage }}</span>
</div> </div>
<div> <div>
@@ -66,7 +66,7 @@
</div> </div>
</div> </div>
<div v-if="tracks.length || (isPodcast && totalPodcastDuration)" role="paragraph" class="flex py-0.5"> <div v-if="tracks.length || (isPodcast && totalPodcastDuration)" role="paragraph" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelDuration }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
</div> </div>
<div> <div>
@@ -74,7 +74,7 @@
</div> </div>
</div> </div>
<div role="paragraph" class="flex py-0.5"> <div role="paragraph" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32"> <div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelSize }}</span> <span class="text-white/60 uppercase text-sm">{{ $strings.LabelSize }}</span>
</div> </div>
<div> <div>
+2 -2
View File
@@ -26,9 +26,9 @@
<span class="material-symbols text-2xl text-error">error_outline</span> <span class="material-symbols text-2xl text-error">error_outline</span>
</ui-tooltip> </ui-tooltip>
<button aria-label="Download Backup" class="inline-flex material-symbols text-xl mx-1 mt-1 text-white/70 hover:text-white/100" @click.stop="downloadBackup(backup)">download</button> <button aria-label="Download backup" class="inline-flex material-symbols text-xl mx-1 mt-1 text-white/70 hover:text-white/100" @click.stop="downloadBackup(backup)">download</button>
<button aria-label="Delete Backup" class="inline-flex material-symbols text-xl mx-1 text-white/70 hover:text-error" @click.stop="deleteBackupClick(backup)">delete</button> <button aria-label="Delete backup" class="inline-flex material-symbols text-xl mx-1 text-white/70 hover:text-error" @click.stop="deleteBackupClick(backup)">delete</button>
</div> </div>
</td> </td>
</tr> </tr>
+4 -1
View File
@@ -183,7 +183,7 @@ export default {
this.$store.commit('libraries/updateFilterDataWithItem', libraryItem) this.$store.commit('libraries/updateFilterDataWithItem', libraryItem)
}, },
libraryItemUpdated(libraryItem) { libraryItemUpdated(libraryItem) {
if (this.$store.state.selectedLibraryItem && this.$store.state.selectedLibraryItem.id === libraryItem.id) { if (this.$store.state.selectedLibraryItem?.id === libraryItem.id) {
this.$store.commit('setSelectedLibraryItem', libraryItem) this.$store.commit('setSelectedLibraryItem', libraryItem)
if (this.$store.state.globals.selectedEpisode && libraryItem.mediaType === 'podcast') { if (this.$store.state.globals.selectedEpisode && libraryItem.mediaType === 'podcast') {
const episode = libraryItem.media.episodes.find((ep) => ep.id === this.$store.state.globals.selectedEpisode.id) const episode = libraryItem.media.episodes.find((ep) => ep.id === this.$store.state.globals.selectedEpisode.id)
@@ -192,6 +192,9 @@ export default {
} }
} }
} }
if (this.$store.state.streamLibraryItem?.id === libraryItem.id) {
this.$store.commit('updateStreamLibraryItem', libraryItem)
}
this.$eventBus.$emit(`${libraryItem.id}_updated`, libraryItem) this.$eventBus.$emit(`${libraryItem.id}_updated`, libraryItem)
this.$store.commit('libraries/updateFilterDataWithItem', libraryItem) this.$store.commit('libraries/updateFilterDataWithItem', libraryItem)
}, },
+5
View File
@@ -221,6 +221,11 @@ export default {
return redirect('/') return redirect('/')
} }
// Fetch and set library if this items library does not match the current
if (store.state.libraries.currentLibraryId !== libraryItem.libraryId || !store.state.libraries.filterData) {
await store.dispatch('libraries/fetch', libraryItem.libraryId)
}
var previousRoute = from ? from.fullPath : null var previousRoute = from ? from.fullPath : null
if (from && from.path === '/login') previousRoute = null if (from && from.path === '/login') previousRoute = null
return { return {
+6
View File
@@ -103,6 +103,12 @@ export default {
console.error('No need to edit library item that is 1 file...') console.error('No need to edit library item that is 1 file...')
return redirect('/') return redirect('/')
} }
// Fetch and set library if this items library does not match the current
if (store.state.libraries.currentLibraryId !== libraryItem.libraryId || !store.state.libraries.filterData) {
await store.dispatch('libraries/fetch', libraryItem.libraryId)
}
return { return {
libraryItem, libraryItem,
files: libraryItem.media.audioFiles ? libraryItem.media.audioFiles.map((af) => ({ ...af, include: !af.exclude })) : [] files: libraryItem.media.audioFiles ? libraryItem.media.audioFiles.map((af) => ({ ...af, include: !af.exclude })) : []
+6 -1
View File
@@ -195,10 +195,15 @@ export default {
return redirect('/?error=invalid media type') return redirect('/?error=invalid media type')
} }
if (!libraryItem.media.audioFiles.length) { if (!libraryItem.media.audioFiles.length) {
cnosole.error('No audio files') console.error('No audio files')
return redirect('/?error=no audio files') return redirect('/?error=no audio files')
} }
// Fetch and set library if this items library does not match the current
if (store.state.libraries.currentLibraryId !== libraryItem.libraryId || !store.state.libraries.filterData) {
await store.dispatch('libraries/fetch', libraryItem.libraryId)
}
return { return {
libraryItem libraryItem
} }
+2 -2
View File
@@ -32,7 +32,7 @@
<p class="truncate">{{ feed.meta.title }}</p> <p class="truncate">{{ feed.meta.title }}</p>
</td> </td>
<!-- --> <!-- -->
<td class="hidden xl:table-cell"> <td class="hidden xl:table-cell max-w-48">
<p class="truncate">{{ feed.slug }}</p> <p class="truncate">{{ feed.slug }}</p>
</td> </td>
<!-- --> <!-- -->
@@ -57,7 +57,7 @@
</td> </td>
<!-- --> <!-- -->
<td class="text-center"> <td class="text-center">
<ui-icon-btn icon="delete" class="mx-0.5" :size="7" bg-color="bg-error" outlined @click.stop="deleteFeedClick(feed)" /> <ui-icon-btn icon="delete" class="mx-0.5 text-white/70" borderless :size="7" iconFontSize="1.25rem" outlined @click.stop="deleteFeedClick(feed)" />
</td> </td>
</tr> </tr>
</table> </table>
+4 -4
View File
@@ -91,15 +91,15 @@
{{ isMissing ? $strings.LabelMissing : $strings.LabelIncomplete }} {{ isMissing ? $strings.LabelMissing : $strings.LabelIncomplete }}
</ui-btn> </ui-btn>
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_play'" :bg-color="isQueued ? 'bg-primary' : 'bg-success/60'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
</ui-tooltip>
<ui-btn v-if="showReadButton" color="bg-info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook"> <ui-btn v-if="showReadButton" color="bg-info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
<span class="material-symbols text-2xl -ml-2 pr-2 text-white" aria-hidden="true">auto_stories</span> <span class="material-symbols text-2xl -ml-2 pr-2 text-white" aria-hidden="true">auto_stories</span>
{{ $strings.ButtonRead }} {{ $strings.ButtonRead }}
</ui-btn> </ui-btn>
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_play'" :bg-color="isQueued ? 'bg-primary' : 'bg-success/60'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
</ui-tooltip>
<ui-tooltip v-if="userCanUpdate" :text="$strings.LabelEdit" direction="top"> <ui-tooltip v-if="userCanUpdate" :text="$strings.LabelEdit" direction="top">
<ui-icon-btn icon="&#xe3c9;" outlined class="mx-0.5" :aria-label="$strings.LabelEdit" @click="editClick" /> <ui-icon-btn icon="&#xe3c9;" outlined class="mx-0.5" :aria-label="$strings.LabelEdit" @click="editClick" />
</ui-tooltip> </ui-tooltip>
@@ -249,7 +249,7 @@ export default {
}, },
async loadRecentEpisodes(page = 0) { async loadRecentEpisodes(page = 0) {
this.processing = true this.processing = true
const episodePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/recent-episodes?limit=25&page=${page}`).catch((error) => { const episodePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/recent-episodes?limit=50&page=${page}`).catch((error) => {
console.error('Failed to get recent episodes', error) console.error('Failed to get recent episodes', error)
this.$toast.error(this.$strings.ToastFailedToLoadData) this.$toast.error(this.$strings.ToastFailedToLoadData)
return null return null
+5 -3
View File
@@ -89,14 +89,16 @@
<script> <script>
export default { export default {
asyncData({ redirect, store }) { async asyncData({ redirect, store, params }) {
if (!store.getters['user/getIsAdminOrUp']) { if (!store.getters['user/getIsAdminOrUp']) {
redirect('/') redirect('/')
return return
} }
if (!store.state.libraries.currentLibraryId) { const libraryId = params.library
return redirect('/config') const library = await store.dispatch('libraries/fetch', libraryId)
if (!library) {
return redirect(`/oops?message=Library "${libraryId}" not found`)
} }
return {} return {}
}, },
+4
View File
@@ -171,6 +171,10 @@ export const mutations = {
state.playerQueueItems = payload.queueItems || [] state.playerQueueItems = payload.queueItems || []
} }
}, },
updateStreamLibraryItem(state, libraryItem) {
if (!libraryItem) return
state.streamLibraryItem = libraryItem
},
setIsPlaying(state, isPlaying) { setIsPlaying(state, isPlaying) {
state.streamIsPlaying = isPlaying state.streamIsPlaying = isPlaying
}, },
+8 -5
View File
@@ -117,7 +117,7 @@
"HeaderAccount": "Compte", "HeaderAccount": "Compte",
"HeaderAddCustomMetadataProvider": "Afegeix un proveïdor de metadades personalitzat", "HeaderAddCustomMetadataProvider": "Afegeix un proveïdor de metadades personalitzat",
"HeaderAdvanced": "Avançat", "HeaderAdvanced": "Avançat",
"HeaderAppriseNotificationSettings": "Configuració de Notificacions Apprise", "HeaderAppriseNotificationSettings": "Paràmetres de notificacions Apprise",
"HeaderAudioTracks": "Pistes d'àudio", "HeaderAudioTracks": "Pistes d'àudio",
"HeaderAudiobookTools": "Eines de gestió de fitxers de l'audiollibre", "HeaderAudiobookTools": "Eines de gestió de fitxers de l'audiollibre",
"HeaderAuthentication": "Autenticació", "HeaderAuthentication": "Autenticació",
@@ -133,9 +133,9 @@
"HeaderCustomMetadataProviders": "Proveïdors de metadades personalitzats", "HeaderCustomMetadataProviders": "Proveïdors de metadades personalitzats",
"HeaderDetails": "Detalls", "HeaderDetails": "Detalls",
"HeaderDownloadQueue": "Cua de baixades", "HeaderDownloadQueue": "Cua de baixades",
"HeaderEbookFiles": "Fitxers de Llibres Digitals", "HeaderEbookFiles": "Fitxers de llibres digitals",
"HeaderEmail": "Correu electrònic", "HeaderEmail": "Correu electrònic",
"HeaderEmailSettings": "Configuració de Correu Electrònic", "HeaderEmailSettings": "Paràmetres de correu electrònic",
"HeaderEpisodes": "Episodis", "HeaderEpisodes": "Episodis",
"HeaderEreaderDevices": "Dispositius Ereader", "HeaderEreaderDevices": "Dispositius Ereader",
"HeaderEreaderSettings": "Paràmetres del lector", "HeaderEreaderSettings": "Paràmetres del lector",
@@ -171,7 +171,7 @@
"HeaderPasswordAuthentication": "Autenticació per Contrasenya", "HeaderPasswordAuthentication": "Autenticació per Contrasenya",
"HeaderPermissions": "Permisos", "HeaderPermissions": "Permisos",
"HeaderPlayerQueue": "Cua del Reproductor", "HeaderPlayerQueue": "Cua del Reproductor",
"HeaderPlayerSettings": "Configuració del Reproductor", "HeaderPlayerSettings": "Paràmetres del reproductor",
"HeaderPlaylist": "Llista de Reproducció", "HeaderPlaylist": "Llista de Reproducció",
"HeaderPlaylistItems": "Elements de la Llista de Reproducció", "HeaderPlaylistItems": "Elements de la Llista de Reproducció",
"HeaderPodcastsToAdd": "Pòdcasts a afegir", "HeaderPodcastsToAdd": "Pòdcasts a afegir",
@@ -725,7 +725,8 @@
"MessageConfirmResetProgress": "Estàs segur que vols reiniciar el teu progrés?", "MessageConfirmResetProgress": "Estàs segur que vols reiniciar el teu progrés?",
"MessageConfirmSendEbookToDevice": "Estàs segur que vols enviar {0} ebook(s) \"{1}\" al dispositiu \"{2}\"?", "MessageConfirmSendEbookToDevice": "Estàs segur que vols enviar {0} ebook(s) \"{1}\" al dispositiu \"{2}\"?",
"MessageConfirmUnlinkOpenId": "Estàs segur que vols desvincular aquest usuari d'OpenID?", "MessageConfirmUnlinkOpenId": "Estàs segur que vols desvincular aquest usuari d'OpenID?",
"MessageDownloadingEpisode": "Descarregant capítol", "MessageDaysListenedInTheLastYear": "{0} dies escoltats l'any passat",
"MessageDownloadingEpisode": "S'està baixant l'episodi",
"MessageDragFilesIntoTrackOrder": "Arrossega els fitxers en l'ordre correcte de les pistes", "MessageDragFilesIntoTrackOrder": "Arrossega els fitxers en l'ordre correcte de les pistes",
"MessageEmbedFailed": "Error en incrustar!", "MessageEmbedFailed": "Error en incrustar!",
"MessageEmbedFinished": "Incrustació acabada!", "MessageEmbedFinished": "Incrustació acabada!",
@@ -1026,6 +1027,8 @@
"ToastUnknownError": "Error desconegut", "ToastUnknownError": "Error desconegut",
"ToastUnlinkOpenIdFailed": "Error en desvincular l'usuari d'OpenID", "ToastUnlinkOpenIdFailed": "Error en desvincular l'usuari d'OpenID",
"ToastUnlinkOpenIdSuccess": "Usuari desvinculat d'OpenID", "ToastUnlinkOpenIdSuccess": "Usuari desvinculat d'OpenID",
"ToastUploaderFilepathExistsError": "El camí del fitxer «{0}» ja existeix al servidor",
"ToastUploaderItemExistsInSubdirectoryError": "L'element «{0}» usa un subdirectori del camí de pujada.",
"ToastUserDeleteFailed": "Error en eliminar l'usuari", "ToastUserDeleteFailed": "Error en eliminar l'usuari",
"ToastUserDeleteSuccess": "Usuari eliminat", "ToastUserDeleteSuccess": "Usuari eliminat",
"ToastUserPasswordChangeSuccess": "Contrasenya canviada correctament", "ToastUserPasswordChangeSuccess": "Contrasenya canviada correctament",
+7 -1
View File
@@ -229,6 +229,7 @@
"LabelAddedDate": "Tilføjet {0}", "LabelAddedDate": "Tilføjet {0}",
"LabelAdminUsersOnly": "Kun Administratorer", "LabelAdminUsersOnly": "Kun Administratorer",
"LabelAll": "Alle", "LabelAll": "Alle",
"LabelAllEpisodesDownloaded": "Alle episoder hentet",
"LabelAllUsers": "Alle Brugere", "LabelAllUsers": "Alle Brugere",
"LabelAllUsersExcludingGuests": "Alle bruger eksklusiv gæster", "LabelAllUsersExcludingGuests": "Alle bruger eksklusiv gæster",
"LabelAllUsersIncludingGuests": "Alle bruger inklusiv gæster", "LabelAllUsersIncludingGuests": "Alle bruger inklusiv gæster",
@@ -252,7 +253,7 @@
"LabelBackToUser": "Tilbage til Bruger", "LabelBackToUser": "Tilbage til Bruger",
"LabelBackupAudioFiles": "Sikkerhedskopier lydfiler", "LabelBackupAudioFiles": "Sikkerhedskopier lydfiler",
"LabelBackupLocation": "Backup Placering", "LabelBackupLocation": "Backup Placering",
"LabelBackupsEnableAutomaticBackups": "Aktivér automatisk sikkerhedskopiering", "LabelBackupsEnableAutomaticBackups": "Automatisk sikkerhedskopiering",
"LabelBackupsEnableAutomaticBackupsHelp": "Sikkerhedskopier gemt i /metadata/backups", "LabelBackupsEnableAutomaticBackupsHelp": "Sikkerhedskopier gemt i /metadata/backups",
"LabelBackupsMaxBackupSize": "Maksimal sikkerhedskopistørrelse (i GB) (0 for ubegrænset)", "LabelBackupsMaxBackupSize": "Maksimal sikkerhedskopistørrelse (i GB) (0 for ubegrænset)",
"LabelBackupsMaxBackupSizeHelp": "Som en beskyttelse mod fejlkonfiguration fejler sikkerhedskopier, hvis de overstiger den konfigurerede størrelse.", "LabelBackupsMaxBackupSizeHelp": "Som en beskyttelse mod fejlkonfiguration fejler sikkerhedskopier, hvis de overstiger den konfigurerede størrelse.",
@@ -558,6 +559,8 @@
"LabelSettingsBookshelfViewHelp": "Skeumorfisk design med træhylder", "LabelSettingsBookshelfViewHelp": "Skeumorfisk design med træhylder",
"LabelSettingsChromecastSupport": "Chromecast-understøttelse", "LabelSettingsChromecastSupport": "Chromecast-understøttelse",
"LabelSettingsDateFormat": "Datoformat", "LabelSettingsDateFormat": "Datoformat",
"LabelSettingsEnableWatcher": "Scan automatisk bibliotek for ændringer",
"LabelSettingsEnableWatcherForLibrary": "Scan automatisk bibliotek for ændringer",
"LabelSettingsEnableWatcherHelp": "Aktiverer automatisk tilføjelse/opdatering af elementer, når filændringer registreres. *Kræver servergenstart", "LabelSettingsEnableWatcherHelp": "Aktiverer automatisk tilføjelse/opdatering af elementer, når filændringer registreres. *Kræver servergenstart",
"LabelSettingsEpubsAllowScriptedContent": "Tillad scriptet indhold i epub", "LabelSettingsEpubsAllowScriptedContent": "Tillad scriptet indhold i epub",
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillad epub filer at køre scripts. Det anbefales at holde denne indstilling deaktiveret med mindre du stoler på kilderne af epub filerne.", "LabelSettingsEpubsAllowScriptedContentHelp": "Tillad epub filer at køre scripts. Det anbefales at holde denne indstilling deaktiveret med mindre du stoler på kilderne af epub filerne.",
@@ -1063,6 +1066,7 @@
"ToastSelectAtLeastOneUser": "Vælg mindst en bruger", "ToastSelectAtLeastOneUser": "Vælg mindst en bruger",
"ToastSendEbookToDeviceFailed": "Mislykkedes afsendelse af e-bog til enhed", "ToastSendEbookToDeviceFailed": "Mislykkedes afsendelse af e-bog til enhed",
"ToastSendEbookToDeviceSuccess": "E-bog afsendt til enhed \"{0}\"", "ToastSendEbookToDeviceSuccess": "E-bog afsendt til enhed \"{0}\"",
"ToastSeriesSubmitFailedSameName": "Kan ikke tilføje to serier med samme navn",
"ToastSeriesUpdateFailed": "Mislykkedes opdatering af serie", "ToastSeriesUpdateFailed": "Mislykkedes opdatering af serie",
"ToastSeriesUpdateSuccess": "Serieopdatering lykkedes", "ToastSeriesUpdateSuccess": "Serieopdatering lykkedes",
"ToastServerSettingsUpdateSuccess": "Server indstillinger opdateret", "ToastServerSettingsUpdateSuccess": "Server indstillinger opdateret",
@@ -1081,6 +1085,8 @@
"ToastUnknownError": "Ukendt fejl", "ToastUnknownError": "Ukendt fejl",
"ToastUnlinkOpenIdFailed": "Fejlede i af afkoble bruger fra OpenID", "ToastUnlinkOpenIdFailed": "Fejlede i af afkoble bruger fra OpenID",
"ToastUnlinkOpenIdSuccess": "Bruger afkoblet fra OpenID", "ToastUnlinkOpenIdSuccess": "Bruger afkoblet fra OpenID",
"ToastUploaderFilepathExistsError": "Filsti \"{0}\" findes allerede på serveren",
"ToastUploaderItemExistsInSubdirectoryError": "Genstand \"{0}\" benytter en undermappe af upload stien",
"ToastUserDeleteFailed": "Mislykkedes sletning af bruger", "ToastUserDeleteFailed": "Mislykkedes sletning af bruger",
"ToastUserDeleteSuccess": "Bruger slettet", "ToastUserDeleteSuccess": "Bruger slettet",
"ToastUserPasswordChangeSuccess": "Password ændret", "ToastUserPasswordChangeSuccess": "Password ændret",
+4
View File
@@ -229,6 +229,7 @@
"LabelAddedDate": "{0} Hinzugefügt", "LabelAddedDate": "{0} Hinzugefügt",
"LabelAdminUsersOnly": "Nur Admin Benutzer", "LabelAdminUsersOnly": "Nur Admin Benutzer",
"LabelAll": "Alle", "LabelAll": "Alle",
"LabelAllEpisodesDownloaded": "Alle Episoden heruntergeladen",
"LabelAllUsers": "Alle Benutzer", "LabelAllUsers": "Alle Benutzer",
"LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen", "LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen",
"LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste", "LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste",
@@ -953,6 +954,7 @@
"ToastBackupRestoreFailed": "Sicherung konnte nicht wiederhergestellt werden", "ToastBackupRestoreFailed": "Sicherung konnte nicht wiederhergestellt werden",
"ToastBackupUploadFailed": "Sicherung konnte nicht hochgeladen werden", "ToastBackupUploadFailed": "Sicherung konnte nicht hochgeladen werden",
"ToastBackupUploadSuccess": "Sicherung hochgeladen", "ToastBackupUploadSuccess": "Sicherung hochgeladen",
"ToastBatchApplyDetailsToItemsSuccess": "Details auf Medien anwenden",
"ToastBatchDeleteFailed": "Batch-Löschen fehlgeschlagen", "ToastBatchDeleteFailed": "Batch-Löschen fehlgeschlagen",
"ToastBatchDeleteSuccess": "Batch-Löschung erfolgreich", "ToastBatchDeleteSuccess": "Batch-Löschung erfolgreich",
"ToastBatchQuickMatchFailed": "Batch-Schnellabgleich fehlgeschlagen!", "ToastBatchQuickMatchFailed": "Batch-Schnellabgleich fehlgeschlagen!",
@@ -1065,6 +1067,7 @@
"ToastSelectAtLeastOneUser": "Wähle mindestens einen Benutzer aus", "ToastSelectAtLeastOneUser": "Wähle mindestens einen Benutzer aus",
"ToastSendEbookToDeviceFailed": "E-Buch konnte nicht auf Gerät übertragen werden", "ToastSendEbookToDeviceFailed": "E-Buch konnte nicht auf Gerät übertragen werden",
"ToastSendEbookToDeviceSuccess": "E-Buch an Gerät „{0}“ gesendet", "ToastSendEbookToDeviceSuccess": "E-Buch an Gerät „{0}“ gesendet",
"ToastSeriesSubmitFailedSameName": "Serien mit dem selben Namen können nicht hinzugefügt werden",
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen", "ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
"ToastSeriesUpdateSuccess": "Serien aktualisiert", "ToastSeriesUpdateSuccess": "Serien aktualisiert",
"ToastServerSettingsUpdateSuccess": "Die Server-Einstellungen wurden geupdated", "ToastServerSettingsUpdateSuccess": "Die Server-Einstellungen wurden geupdated",
@@ -1083,6 +1086,7 @@
"ToastUnknownError": "Unbekannter Fehler", "ToastUnknownError": "Unbekannter Fehler",
"ToastUnlinkOpenIdFailed": "Fehler beim entkoppeln des Benutzers von OpenID", "ToastUnlinkOpenIdFailed": "Fehler beim entkoppeln des Benutzers von OpenID",
"ToastUnlinkOpenIdSuccess": "Benutzer entkoppelt von OpenID", "ToastUnlinkOpenIdSuccess": "Benutzer entkoppelt von OpenID",
"ToastUploaderFilepathExistsError": "Dateipfad \"{0}\" ist bereits auf dem Server horhanden",
"ToastUserDeleteFailed": "Benutzer konnte nicht gelöscht werden", "ToastUserDeleteFailed": "Benutzer konnte nicht gelöscht werden",
"ToastUserDeleteSuccess": "Benutzer gelöscht", "ToastUserDeleteSuccess": "Benutzer gelöscht",
"ToastUserPasswordChangeSuccess": "Passwort erfolgreich verändert", "ToastUserPasswordChangeSuccess": "Passwort erfolgreich verändert",
+48 -44
View File
@@ -77,9 +77,9 @@
"ButtonRemove": "Quitar", "ButtonRemove": "Quitar",
"ButtonRemoveAll": "Quitar todo", "ButtonRemoveAll": "Quitar todo",
"ButtonRemoveAllLibraryItems": "Quitar todos los elementos de la biblioteca", "ButtonRemoveAllLibraryItems": "Quitar todos los elementos de la biblioteca",
"ButtonRemoveFromContinueListening": "Remover de Continuar Escuchando", "ButtonRemoveFromContinueListening": "Quitar de Continuar escuchando",
"ButtonRemoveFromContinueReading": "Remover de Continuar Leyendo", "ButtonRemoveFromContinueReading": "Quitar de Continuar leyendo",
"ButtonRemoveSeriesFromContinueSeries": "Remover Serie de Continuar Series", "ButtonRemoveSeriesFromContinueSeries": "Quitar serie de Continuar serie",
"ButtonReset": "Restablecer", "ButtonReset": "Restablecer",
"ButtonResetToDefault": "Restaurar valores predeterminados", "ButtonResetToDefault": "Restaurar valores predeterminados",
"ButtonRestore": "Restaurar", "ButtonRestore": "Restaurar",
@@ -227,6 +227,7 @@
"LabelAddedDate": "{0} Añadido", "LabelAddedDate": "{0} Añadido",
"LabelAdminUsersOnly": "Solamente usuarios administradores", "LabelAdminUsersOnly": "Solamente usuarios administradores",
"LabelAll": "Todos", "LabelAll": "Todos",
"LabelAllEpisodesDownloaded": "Todos los episodios descargados",
"LabelAllUsers": "Todos los usuarios", "LabelAllUsers": "Todos los usuarios",
"LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados", "LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados",
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados", "LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
@@ -436,8 +437,8 @@
"LabelMinute": "Minuto", "LabelMinute": "Minuto",
"LabelMinutes": "Minutos", "LabelMinutes": "Minutos",
"LabelMissing": "Falta", "LabelMissing": "Falta",
"LabelMissingEbook": "No tiene ebook", "LabelMissingEbook": "No tiene libro electrónico",
"LabelMissingSupplementaryEbook": "No tiene ebook suplementario", "LabelMissingSupplementaryEbook": "No tiene libro electrónico suplementario",
"LabelMobileRedirectURIs": "URIs de redirección a móviles permitidos", "LabelMobileRedirectURIs": "URIs de redirección a móviles permitidos",
"LabelMobileRedirectURIsDescription": "Esta es una lista blanca de URI de redireccionamiento válidos para aplicaciones móviles. El predeterminado es <code> audiobookshelf</code> , que puede eliminar o complementar con URI adicionales para la integración de aplicaciones de terceros. Usando un asterisco (<code> *</code> ) como única entrada que permite cualquier URI.", "LabelMobileRedirectURIsDescription": "Esta es una lista blanca de URI de redireccionamiento válidos para aplicaciones móviles. El predeterminado es <code> audiobookshelf</code> , que puede eliminar o complementar con URI adicionales para la integración de aplicaciones de terceros. Usando un asterisco (<code> *</code> ) como única entrada que permite cualquier URI.",
"LabelMore": "Más", "LabelMore": "Más",
@@ -497,7 +498,7 @@
"LabelPort": "Puerto", "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", "LabelPreventIndexing": "Evite que los directorios de pódcast de iTunes y Google indicen su suministro",
"LabelPrimaryEbook": "Ebook principal", "LabelPrimaryEbook": "Libro electrónico principal",
"LabelProgress": "Progreso", "LabelProgress": "Progreso",
"LabelProvider": "Proveedor", "LabelProvider": "Proveedor",
"LabelProviderAuthorizationValue": "Valor del encabezado de autorización", "LabelProviderAuthorizationValue": "Valor del encabezado de autorización",
@@ -713,20 +714,20 @@
"MessageBookshelfNoResultsForQuery": "No hay resultados para la consulta", "MessageBookshelfNoResultsForQuery": "No hay resultados para la consulta",
"MessageBookshelfNoSeries": "No tiene ninguna serie", "MessageBookshelfNoSeries": "No tiene ninguna serie",
"MessageChapterEndIsAfter": "El final del capítulo es después del final de tu audiolibro", "MessageChapterEndIsAfter": "El final del capítulo es después del final de tu audiolibro",
"MessageChapterErrorFirstNotZero": "El primer capitulo debe iniciar en 0", "MessageChapterErrorFirstNotZero": "El primer capítulo debe iniciar en 0",
"MessageChapterErrorStartGteDuration": "El tiempo de inicio no es válido: debe ser inferior a la duración del audiolibro", "MessageChapterErrorStartGteDuration": "El tiempo de inicio no es válido: debe ser inferior a la duración del audiolibro",
"MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válido: debe ser mayor o igual que el tiempo de inicio del capítulo anterior", "MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válido: debe ser mayor o igual que el tiempo de inicio del capítulo anterior",
"MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro", "MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro",
"MessageCheckingCron": "Revisando cron...", "MessageCheckingCron": "Revisando cron...",
"MessageConfirmCloseFeed": "¿Confirma que quiere cerrar este suministro?", "MessageConfirmCloseFeed": "¿Confirma que quiere cerrar este suministro?",
"MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?", "MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?",
"MessageConfirmDeleteDevice": "¿Estás seguro de que deseas eliminar el lector electrónico \"{0}\"?", "MessageConfirmDeleteDevice": "¿Confirma que quiere eliminar el lector electrónico «{0}»?",
"MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?", "MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?",
"MessageConfirmDeleteLibrary": "¿Está seguro de que desea eliminar permanentemente la biblioteca \"{0}\"?", "MessageConfirmDeleteLibrary": "¿Confirma que quiere eliminar permanentemente la biblioteca «{0}»?",
"MessageConfirmDeleteLibraryItem": "Esto eliminará el elemento de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?", "MessageConfirmDeleteLibraryItem": "Esto eliminará el elemento de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?",
"MessageConfirmDeleteLibraryItems": "Esto eliminará {0} elementos de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?", "MessageConfirmDeleteLibraryItems": "Esto eliminará {0} elementos de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?",
"MessageConfirmDeleteMetadataProvider": "¿Estás seguro de que deseas eliminar el proveedor de metadatos personalizado \"{0}\"?", "MessageConfirmDeleteMetadataProvider": "¿Confirma que quiere eliminar el proveedor de metadatos personalizado «{0}»?",
"MessageConfirmDeleteNotification": "¿Estás seguro de que deseas eliminar esta notificación?", "MessageConfirmDeleteNotification": "¿Confirma que quiere eliminar esta notificación?",
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?", "MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
"MessageConfirmEmbedMetadataInAudioFiles": "¿Está seguro de que desea incrustar metadatos en {0} archivos de audio?", "MessageConfirmEmbedMetadataInAudioFiles": "¿Está seguro de que desea incrustar metadatos en {0} archivos de audio?",
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?", "MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
@@ -741,27 +742,27 @@
"MessageConfirmPurgeItemsCache": "Purgar la caché de los elementos eliminará todo el directorio <code>/metadata/cache/items</code>.<br />¿Estás seguro?", "MessageConfirmPurgeItemsCache": "Purgar la caché de los elementos eliminará todo el directorio <code>/metadata/cache/items</code>.<br />¿Estás seguro?",
"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. <br><br>¿Deseas continuar?", "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. <br><br>¿Deseas continuar?",
"MessageConfirmQuickMatchEpisodes": "El reconocimiento rápido de extensiones sobrescribirá los detalles si se encuentra una coincidencia. Se actualizarán las extensiones no reconocidas. ¿Está seguro?", "MessageConfirmQuickMatchEpisodes": "El reconocimiento rápido de extensiones sobrescribirá los detalles si se encuentra una coincidencia. Se actualizarán las extensiones no reconocidas. ¿Está seguro?",
"MessageConfirmReScanLibraryItems": "¿Estás seguro de querer re escanear {0} elemento(s)?", "MessageConfirmReScanLibraryItems": "¿Confirma que quiere volver a analizar {0} elementos?",
"MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?", "MessageConfirmRemoveAllChapters": "¿Confirma que quiere quitar todos los capítulos?",
"MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?", "MessageConfirmRemoveAuthor": "¿Confirma que quiere quitar el autor «{0}»?",
"MessageConfirmRemoveCollection": "¿Está seguro de que desea remover la colección \"{0}\"?", "MessageConfirmRemoveCollection": "¿Confirma que quiere quitar la colección «{0}»?",
"MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?", "MessageConfirmRemoveEpisode": "¿Confirma que quiere quitar el episodio «{0}»?",
"MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?", "MessageConfirmRemoveEpisodes": "¿Confirma que quiere quitar {0} episodios?",
"MessageConfirmRemoveListeningSessions": "¿Está seguro que desea remover {0} sesiones de escuchar?", "MessageConfirmRemoveListeningSessions": "¿Confirma que quiere quitar {0} sesiones de escucha?",
"MessageConfirmRemoveMetadataFiles": "¿Está seguro de que desea eliminar todos los archivos de metadatos.{0} en las carpetas de elementos de su biblioteca?", "MessageConfirmRemoveMetadataFiles": "¿Está seguro de que desea eliminar todos los archivos de metadatos.{0} en las carpetas de elementos de su biblioteca?",
"MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?", "MessageConfirmRemoveNarrator": "¿Confirma que quiere quitar el narrador «{0}»?",
"MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{0}\"?", "MessageConfirmRemovePlaylist": "¿Confirma que quiere quitar la lista de reproducción «{0}»?",
"MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?", "MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
"MessageConfirmRenameGenreMergeNote": "Nota: Este género ya existe, por lo que se fusionarán.", "MessageConfirmRenameGenreMergeNote": "Nota: Este género ya existe, por lo que se fusionarán.",
"MessageConfirmRenameGenreWarning": "Advertencia! Un genero similar ya existe \"{0}\".", "MessageConfirmRenameGenreWarning": "Advertencia! Un genero similar ya existe \"{0}\".",
"MessageConfirmRenameTag": "¿Está seguro de que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?", "MessageConfirmRenameTag": "¿Está seguro de que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?",
"MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe, por lo que se fusionarán.", "MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe, por lo que se fusionarán.",
"MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".", "MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".",
"MessageConfirmResetProgress": "¿Estás seguro de que quieres reiniciar tu progreso?", "MessageConfirmResetProgress": "¿Confirma que quiere restablecer su progreso?",
"MessageConfirmSendEbookToDevice": "¿Está seguro de que enviar {0} ebook(s) \"{1}\" al dispositivo \"{2}\"?", "MessageConfirmSendEbookToDevice": "¿Confirma que quiere enviar el libro electrónico {0} «{1}» al dispositivo «{2}»?",
"MessageConfirmUnlinkOpenId": "¿Estás seguro de que deseas desvincular este usuario de OpenID?", "MessageConfirmUnlinkOpenId": "¿Estás seguro de que deseas desvincular este usuario de OpenID?",
"MessageDaysListenedInTheLastYear": "{0} dies escoltats en l'últim any", "MessageDaysListenedInTheLastYear": "{0} días escuchados el año pasado",
"MessageDownloadingEpisode": "Descargando Capitulo", "MessageDownloadingEpisode": "Descargando episodio",
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas", "MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas",
"MessageEmbedFailed": "¡Error al insertar!", "MessageEmbedFailed": "¡Error al insertar!",
"MessageEmbedFinished": "Incrustación Terminada!", "MessageEmbedFinished": "Incrustación Terminada!",
@@ -810,7 +811,7 @@
"MessageNoMediaProgress": "Multimedia sin Progreso", "MessageNoMediaProgress": "Multimedia sin Progreso",
"MessageNoNotifications": "Ninguna notificación", "MessageNoNotifications": "Ninguna notificación",
"MessageNoPodcastFeed": "Podcast no válido: Sin feed", "MessageNoPodcastFeed": "Podcast no válido: Sin feed",
"MessageNoPodcastsFound": "Ningún podcast encontrado", "MessageNoPodcastsFound": "No se encontró ningún pódcast",
"MessageNoResults": "Sin Resultados", "MessageNoResults": "Sin Resultados",
"MessageNoSearchResultsFor": "La búsqueda «{0}» no produjo ningún resultado", "MessageNoSearchResultsFor": "La búsqueda «{0}» no produjo ningún resultado",
"MessageNoSeries": "Ninguna serie", "MessageNoSeries": "Ninguna serie",
@@ -832,10 +833,10 @@
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)", "MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
"MessageQuickMatchAllEpisodes": "Combina rápidamente todos los episodios", "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": "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.",
"MessageRemoveChapter": "Remover capítulos", "MessageRemoveChapter": "Quitar capítulo",
"MessageRemoveEpisodes": "Remover {0} episodio(s)", "MessageRemoveEpisodes": "Quitar {0} episodio(s)",
"MessageRemoveFromPlayerQueue": "Remover la cola de reproducción", "MessageRemoveFromPlayerQueue": "Quitar de la cola de reproducción",
"MessageRemoveUserWarning": "¿Está seguro de que desea eliminar el usuario \"{0}\"?", "MessageRemoveUserWarning": "¿Confirma que quiere eliminar permanentemente el usuario «{0}»?",
"MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuya en", "MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuya en",
"MessageResetChaptersConfirm": "¿Está seguro de que desea deshacer los cambios y revertir los capítulos a su estado original?", "MessageResetChaptersConfirm": "¿Está seguro de que desea deshacer los cambios y revertir los capítulos a su estado original?",
"MessageRestoreBackupConfirm": "¿Está seguro de que desea para restaurar del respaldo creado en", "MessageRestoreBackupConfirm": "¿Está seguro de que desea para restaurar del respaldo creado en",
@@ -851,7 +852,7 @@
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?", "MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
"MessageTaskAudioFileNotWritable": "El archivo de audio \"{0}\" no se puede grabar", "MessageTaskAudioFileNotWritable": "El archivo de audio \"{0}\" no se puede grabar",
"MessageTaskCanceledByUser": "Tarea cancelada por el usuario", "MessageTaskCanceledByUser": "Tarea cancelada por el usuario",
"MessageTaskDownloadingEpisodeDescription": "Descargando el episodio \"{0}\"", "MessageTaskDownloadingEpisodeDescription": "Descargando el episodio «{0}»",
"MessageTaskEmbeddingMetadata": "Inserción de metadatos", "MessageTaskEmbeddingMetadata": "Inserción de metadatos",
"MessageTaskEmbeddingMetadataDescription": "Inserción de metadatos en el audiolibro \"{0}\"", "MessageTaskEmbeddingMetadataDescription": "Inserción de metadatos en el audiolibro \"{0}\"",
"MessageTaskEncodingM4b": "Codificación M4B", "MessageTaskEncodingM4b": "Codificación M4B",
@@ -863,16 +864,16 @@
"MessageTaskFailedToMergeAudioFiles": "Error al fusionar archivos de audio", "MessageTaskFailedToMergeAudioFiles": "Error al fusionar archivos de audio",
"MessageTaskFailedToMoveM4bFile": "Error al mover el archivo m4b", "MessageTaskFailedToMoveM4bFile": "Error al mover el archivo m4b",
"MessageTaskFailedToWriteMetadataFile": "Error al escribir el archivo de metadatos", "MessageTaskFailedToWriteMetadataFile": "Error al escribir el archivo de metadatos",
"MessageTaskMatchingBooksInLibrary": "Libros coincidentes en la biblioteca \"{0}\"", "MessageTaskMatchingBooksInLibrary": "Libros coincidentes en la biblioteca «{0}»",
"MessageTaskNoFilesToScan": "Sin archivos para escanear", "MessageTaskNoFilesToScan": "Sin archivos para escanear",
"MessageTaskOpmlImport": "Importar OPML", "MessageTaskOpmlImport": "Importar OPML",
"MessageTaskOpmlImportDescription": "Creando pódcast a partir de {0} suministros RSS", "MessageTaskOpmlImportDescription": "Creando pódcast a partir de {0} suministros RSS",
"MessageTaskOpmlImportFeed": "Feed de importación OPML", "MessageTaskOpmlImportFeed": "Feed de importación OPML",
"MessageTaskOpmlImportFeedDescription": "Importando el feed RSS \"{0}\"", "MessageTaskOpmlImportFeedDescription": "Importando el suministro RSS «{0}»",
"MessageTaskOpmlImportFeedFailed": "No se puede obtener el podcast", "MessageTaskOpmlImportFeedFailed": "No se puede obtener el podcast",
"MessageTaskOpmlImportFeedPodcastDescription": "Creando podcast \"{0}\"", "MessageTaskOpmlImportFeedPodcastDescription": "Creando pódcast «{0}»",
"MessageTaskOpmlImportFeedPodcastExists": "Podcast ya existe en la ruta", "MessageTaskOpmlImportFeedPodcastExists": "Podcast ya existe en la ruta",
"MessageTaskOpmlImportFeedPodcastFailed": "Error al crear podcast", "MessageTaskOpmlImportFeedPodcastFailed": "No se pudo crear el pódcast",
"MessageTaskOpmlImportFinished": "Añadido {0} podcasts", "MessageTaskOpmlImportFinished": "Añadido {0} podcasts",
"MessageTaskOpmlParseFailed": "No se pudo analizar el archivo OPML", "MessageTaskOpmlParseFailed": "No se pudo analizar el archivo OPML",
"MessageTaskOpmlParseFastFail": "No se encontró la etiqueta <opml> del archivo OPML no válido O no se encontró la etiqueta <outline>", "MessageTaskOpmlParseFastFail": "No se encontró la etiqueta <opml> del archivo OPML no válido O no se encontró la etiqueta <outline>",
@@ -948,6 +949,7 @@
"ToastBackupRestoreFailed": "Error al restaurar el respaldo", "ToastBackupRestoreFailed": "Error al restaurar el respaldo",
"ToastBackupUploadFailed": "Error al subir el respaldo", "ToastBackupUploadFailed": "Error al subir el respaldo",
"ToastBackupUploadSuccess": "Respaldo cargado", "ToastBackupUploadSuccess": "Respaldo cargado",
"ToastBatchApplyDetailsToItemsSuccess": "Detalles aplicados a los elementos",
"ToastBatchDeleteFailed": "Error al eliminar por lotes", "ToastBatchDeleteFailed": "Error al eliminar por lotes",
"ToastBatchDeleteSuccess": "Borrado por lotes correcto", "ToastBatchDeleteSuccess": "Borrado por lotes correcto",
"ToastBatchQuickMatchFailed": "¡Error en la sincronización rápida por lotes!", "ToastBatchQuickMatchFailed": "¡Error en la sincronización rápida por lotes!",
@@ -964,7 +966,7 @@
"ToastChaptersRemoved": "Capítulos eliminados", "ToastChaptersRemoved": "Capítulos eliminados",
"ToastChaptersUpdated": "Capítulos actualizados", "ToastChaptersUpdated": "Capítulos actualizados",
"ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)", "ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)",
"ToastCollectionRemoveSuccess": "Colección removida", "ToastCollectionRemoveSuccess": "Colección quitada",
"ToastCollectionUpdateSuccess": "Colección actualizada", "ToastCollectionUpdateSuccess": "Colección actualizada",
"ToastCoverUpdateFailed": "Error al actualizar la cubierta", "ToastCoverUpdateFailed": "Error al actualizar la cubierta",
"ToastDateTimeInvalidOrIncomplete": "Fecha y hora inválidas o incompletas", "ToastDateTimeInvalidOrIncomplete": "Fecha y hora inválidas o incompletas",
@@ -998,12 +1000,12 @@
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado", "ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
"ToastItemUpdateSuccess": "Elemento actualizado", "ToastItemUpdateSuccess": "Elemento actualizado",
"ToastLibraryCreateFailed": "Error al crear biblioteca", "ToastLibraryCreateFailed": "Error al crear biblioteca",
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada", "ToastLibraryCreateSuccess": "Se creó la biblioteca «{0}»",
"ToastLibraryDeleteFailed": "Error al eliminar biblioteca", "ToastLibraryDeleteFailed": "Error al eliminar biblioteca",
"ToastLibraryDeleteSuccess": "Biblioteca eliminada", "ToastLibraryDeleteSuccess": "Biblioteca eliminada",
"ToastLibraryScanFailedToStart": "Error al iniciar el escaneo", "ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca", "ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada", "ToastLibraryUpdateSuccess": "Se actualizó la biblioteca «{0}»",
"ToastMatchAllAuthorsFailed": "No se pudo encontrar a todos los autores", "ToastMatchAllAuthorsFailed": "No se pudo encontrar a todos los autores",
"ToastMetadataFilesRemovedError": "Error al eliminar metadatos de {0} archivo(s)", "ToastMetadataFilesRemovedError": "Error al eliminar metadatos de {0} archivo(s)",
"ToastMetadataFilesRemovedNoneFound": "No hay metadatos.{0} archivo(s) encontrado(s) en la biblioteca", "ToastMetadataFilesRemovedNoneFound": "No hay metadatos.{0} archivo(s) encontrado(s) en la biblioteca",
@@ -1013,7 +1015,7 @@
"ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico", "ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico",
"ToastNameRequired": "Nombre obligatorio", "ToastNameRequired": "Nombre obligatorio",
"ToastNewEpisodesFound": "{0} nuevo(s) episodio(s) encontrado(s)", "ToastNewEpisodesFound": "{0} nuevo(s) episodio(s) encontrado(s)",
"ToastNewUserCreatedFailed": "Error al crear la cuenta: \"{0}\"", "ToastNewUserCreatedFailed": "No se pudo crear la cuenta: «{0}»",
"ToastNewUserCreatedSuccess": "Nueva cuenta creada", "ToastNewUserCreatedSuccess": "Nueva cuenta creada",
"ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca", "ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca",
"ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña", "ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña",
@@ -1034,8 +1036,8 @@
"ToastPlaylistCreateSuccess": "Lista de reproducción creada", "ToastPlaylistCreateSuccess": "Lista de reproducción creada",
"ToastPlaylistRemoveSuccess": "Lista de reproducción eliminada", "ToastPlaylistRemoveSuccess": "Lista de reproducción eliminada",
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada", "ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
"ToastPodcastCreateFailed": "Error al crear podcast", "ToastPodcastCreateFailed": "No se pudo crear el pódcast",
"ToastPodcastCreateSuccess": "Podcast creado", "ToastPodcastCreateSuccess": "Se creó el pódcast correctamente",
"ToastPodcastGetFeedFailed": "No se puede obtener el podcast", "ToastPodcastGetFeedFailed": "No se puede obtener el podcast",
"ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS", "ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS",
"ToastPodcastNoRssFeed": "El podcast no tiene feed RSS", "ToastPodcastNoRssFeed": "El podcast no tiene feed RSS",
@@ -1057,9 +1059,9 @@
"ToastRescanUpToDate": "Reescaneado del artículo completo, estaba actualizado", "ToastRescanUpToDate": "Reescaneado del artículo completo, estaba actualizado",
"ToastRescanUpdated": "Reescaneado completado, el artículo ha sido actualizado", "ToastRescanUpdated": "Reescaneado completado, el artículo ha sido actualizado",
"ToastScanFailed": "No se pudo escanear el elemento de la biblioteca", "ToastScanFailed": "No se pudo escanear el elemento de la biblioteca",
"ToastSelectAtLeastOneUser": "Selecciona al menos un usuario", "ToastSelectAtLeastOneUser": "Seleccione al menos un usuario",
"ToastSendEbookToDeviceFailed": "Error al enviar el ebook al dispositivo", "ToastSendEbookToDeviceFailed": "No se pudo enviar el libro electrónico al dispositivo",
"ToastSendEbookToDeviceSuccess": "Ebook enviado al dispositivo \"{0}\"", "ToastSendEbookToDeviceSuccess": "Libro electrónico enviado al dispositivo «{0}»",
"ToastSeriesSubmitFailedSameName": "No se puede añadir dos series con el mismo nombre", "ToastSeriesSubmitFailedSameName": "No se puede añadir dos series con el mismo nombre",
"ToastSeriesUpdateFailed": "Error al actualizar la serie", "ToastSeriesUpdateFailed": "Error al actualizar la serie",
"ToastSeriesUpdateSuccess": "Serie actualizada", "ToastSeriesUpdateSuccess": "Serie actualizada",
@@ -1079,10 +1081,12 @@
"ToastUnknownError": "Error desconocido", "ToastUnknownError": "Error desconocido",
"ToastUnlinkOpenIdFailed": "Error al desvincular el usuario de OpenID", "ToastUnlinkOpenIdFailed": "Error al desvincular el usuario de OpenID",
"ToastUnlinkOpenIdSuccess": "Usuario desvinculado de OpenID", "ToastUnlinkOpenIdSuccess": "Usuario desvinculado de OpenID",
"ToastUploaderFilepathExistsError": "La ruta de archivo «{0}» ya existe en el servidor",
"ToastUploaderItemExistsInSubdirectoryError": "El elemento «{0}» usa un subdirectorio de la ruta de carga.",
"ToastUserDeleteFailed": "Error al eliminar el usuario", "ToastUserDeleteFailed": "Error al eliminar el usuario",
"ToastUserDeleteSuccess": "Usuario eliminado", "ToastUserDeleteSuccess": "Usuario eliminado",
"ToastUserPasswordChangeSuccess": "Contraseña modificada correctamente", "ToastUserPasswordChangeSuccess": "Contraseña modificada correctamente",
"ToastUserPasswordMismatch": "No coinciden las contraseñas", "ToastUserPasswordMismatch": "No coinciden las contraseñas",
"ToastUserPasswordMustChange": "La nueva contraseña no puede ser igual que la anterior", "ToastUserPasswordMustChange": "La nueva contraseña no puede ser igual que la anterior",
"ToastUserRootRequireName": "Debes introducir un nombre de usuario root" "ToastUserRootRequireName": "Debe introducir un nombre de usuario administrativo"
} }
+2
View File
@@ -1086,6 +1086,8 @@
"ToastUnknownError": "Nepoznata pogreška", "ToastUnknownError": "Nepoznata pogreška",
"ToastUnlinkOpenIdFailed": "Uklanjanje OpenID veze korisnika nije uspjelo", "ToastUnlinkOpenIdFailed": "Uklanjanje OpenID veze korisnika nije uspjelo",
"ToastUnlinkOpenIdSuccess": "Korisnik odspojen od OpenID-ja", "ToastUnlinkOpenIdSuccess": "Korisnik odspojen od OpenID-ja",
"ToastUploaderFilepathExistsError": "Putanja \"{0}\" već postoji na poslužitelju",
"ToastUploaderItemExistsInSubdirectoryError": "Stavka \"{0}\" koristi se podmapom u putanje za učitavanje.",
"ToastUserDeleteFailed": "Brisanje korisnika nije uspjelo", "ToastUserDeleteFailed": "Brisanje korisnika nije uspjelo",
"ToastUserDeleteSuccess": "Korisnik izbrisan", "ToastUserDeleteSuccess": "Korisnik izbrisan",
"ToastUserPasswordChangeSuccess": "Zaporka je uspješno promijenjena", "ToastUserPasswordChangeSuccess": "Zaporka je uspješno promijenjena",
+2
View File
@@ -10,6 +10,8 @@
"ButtonApplyChapters": "Fejezetek alkalmazása", "ButtonApplyChapters": "Fejezetek alkalmazása",
"ButtonAuthors": "Szerzők", "ButtonAuthors": "Szerzők",
"ButtonBack": "Vissza", "ButtonBack": "Vissza",
"ButtonBatchEditPopulateFromExisting": "Létezőből feltöltés",
"ButtonBatchEditPopulateMapDetails": "",
"ButtonBrowseForFolder": "Mappa keresése", "ButtonBrowseForFolder": "Mappa keresése",
"ButtonCancel": "Mégse", "ButtonCancel": "Mégse",
"ButtonCancelEncode": "Kódolás megszakítása", "ButtonCancelEncode": "Kódolás megszakítása",
+16
View File
@@ -10,6 +10,8 @@
"ButtonApplyChapters": "Aplicar Capítulos", "ButtonApplyChapters": "Aplicar Capítulos",
"ButtonAuthors": "Autores", "ButtonAuthors": "Autores",
"ButtonBack": "Voltar", "ButtonBack": "Voltar",
"ButtonBatchEditPopulateFromExisting": "Popular de um existente",
"ButtonBatchEditPopulateMapDetails": "Popular mapeamento de detalhes",
"ButtonBrowseForFolder": "Procurar por Pasta", "ButtonBrowseForFolder": "Procurar por Pasta",
"ButtonCancel": "Cancelar", "ButtonCancel": "Cancelar",
"ButtonCancelEncode": "Cancelar Codificação", "ButtonCancelEncode": "Cancelar Codificação",
@@ -19,6 +21,7 @@
"ButtonChooseFiles": "Escolha arquivos", "ButtonChooseFiles": "Escolha arquivos",
"ButtonClearFilter": "Limpar Filtro", "ButtonClearFilter": "Limpar Filtro",
"ButtonCloseFeed": "Fechar Feed", "ButtonCloseFeed": "Fechar Feed",
"ButtonCloseSession": "Fechar Sessão Aberta",
"ButtonCollections": "Coleções", "ButtonCollections": "Coleções",
"ButtonConfigureScanner": "Configurar Verificador", "ButtonConfigureScanner": "Configurar Verificador",
"ButtonCreate": "Criar", "ButtonCreate": "Criar",
@@ -28,6 +31,9 @@
"ButtonEdit": "Editar", "ButtonEdit": "Editar",
"ButtonEditChapters": "Editar Capítulos", "ButtonEditChapters": "Editar Capítulos",
"ButtonEditPodcast": "Editar Podcast", "ButtonEditPodcast": "Editar Podcast",
"ButtonEnable": "Ativar",
"ButtonFireAndFail": "Disparar e Falhar",
"ButtonFireOnTest": "Disparar evento onTest",
"ButtonForceReScan": "Forcar Nova Verificação", "ButtonForceReScan": "Forcar Nova Verificação",
"ButtonFullPath": "Caminho Completo", "ButtonFullPath": "Caminho Completo",
"ButtonHide": "Ocultar", "ButtonHide": "Ocultar",
@@ -37,6 +43,7 @@
"ButtonJumpForward": "Adiantar", "ButtonJumpForward": "Adiantar",
"ButtonLatest": "Mais Recentes", "ButtonLatest": "Mais Recentes",
"ButtonLibrary": "Biblioteca", "ButtonLibrary": "Biblioteca",
"ButtonLogout": "Logout",
"ButtonLookup": "Procurar", "ButtonLookup": "Procurar",
"ButtonManageTracks": "Gerenciar Faixas", "ButtonManageTracks": "Gerenciar Faixas",
"ButtonMapChapterTitles": "Designar Títulos de Capítulos", "ButtonMapChapterTitles": "Designar Títulos de Capítulos",
@@ -45,18 +52,24 @@
"ButtonNevermind": "Cancelar", "ButtonNevermind": "Cancelar",
"ButtonNext": "Próximo", "ButtonNext": "Próximo",
"ButtonNextChapter": "Próximo Capítulo", "ButtonNextChapter": "Próximo Capítulo",
"ButtonNextItemInQueue": "Próximo Item da Fila",
"ButtonOk": "Ok",
"ButtonOpenFeed": "Abrir Feed", "ButtonOpenFeed": "Abrir Feed",
"ButtonOpenManager": "Abrir Gerenciador", "ButtonOpenManager": "Abrir Gerenciador",
"ButtonPause": "Pausar", "ButtonPause": "Pausar",
"ButtonPlay": "Reproduzir", "ButtonPlay": "Reproduzir",
"ButtonPlayAll": "Reproduzir Tudo",
"ButtonPlaying": "Reproduzindo", "ButtonPlaying": "Reproduzindo",
"ButtonPlaylists": "Lista de Reprodução", "ButtonPlaylists": "Lista de Reprodução",
"ButtonPrevious": "Anterior", "ButtonPrevious": "Anterior",
"ButtonPreviousChapter": "Capítulo Anterior", "ButtonPreviousChapter": "Capítulo Anterior",
"ButtonProbeAudioFile": "Sondar Arquivo de Áudio",
"ButtonPurgeAllCache": "Apagar Todo o Cache", "ButtonPurgeAllCache": "Apagar Todo o Cache",
"ButtonPurgeItemsCache": "Apagar o Cache de Itens", "ButtonPurgeItemsCache": "Apagar o Cache de Itens",
"ButtonQueueAddItem": "Adicionar à Lista", "ButtonQueueAddItem": "Adicionar à Lista",
"ButtonQueueRemoveItem": "Remover da Lista", "ButtonQueueRemoveItem": "Remover da Lista",
"ButtonQuickEmbed": "Incorporação Rápida",
"ButtonQuickEmbedMetadata": "Incorporação Rápida de Metadata",
"ButtonQuickMatch": "Consulta rápida", "ButtonQuickMatch": "Consulta rápida",
"ButtonReScan": "Nova Verificação", "ButtonReScan": "Nova Verificação",
"ButtonRead": "Ler", "ButtonRead": "Ler",
@@ -77,6 +90,8 @@
"ButtonSaveTracklist": "Salvar Lista de Faixas", "ButtonSaveTracklist": "Salvar Lista de Faixas",
"ButtonScan": "Verificar", "ButtonScan": "Verificar",
"ButtonScanLibrary": "Verificar Biblioteca", "ButtonScanLibrary": "Verificar Biblioteca",
"ButtonScrollLeft": "Arrastar para Esquerda",
"ButtonScrollRight": "Arrastar para Direita",
"ButtonSearch": "Pesquisar", "ButtonSearch": "Pesquisar",
"ButtonSelectFolderPath": "Selecionar Caminho da Pasta", "ButtonSelectFolderPath": "Selecionar Caminho da Pasta",
"ButtonSeries": "Séries", "ButtonSeries": "Séries",
@@ -196,6 +211,7 @@
"LabelAddToPlaylist": "Adicionar à Lista de Reprodução", "LabelAddToPlaylist": "Adicionar à Lista de Reprodução",
"LabelAddToPlaylistBatch": "Adicionar {0} itens à Lista de Reprodução", "LabelAddToPlaylistBatch": "Adicionar {0} itens à Lista de Reprodução",
"LabelAddedAt": "Acrescentado em", "LabelAddedAt": "Acrescentado em",
"LabelAddedDate": "Adicionado {0}",
"LabelAdminUsersOnly": "Apenas usuários administradores", "LabelAdminUsersOnly": "Apenas usuários administradores",
"LabelAll": "Todos", "LabelAll": "Todos",
"LabelAllUsers": "Todos Usuários", "LabelAllUsers": "Todos Usuários",
+7 -2
View File
@@ -229,6 +229,7 @@
"LabelAddedDate": "Добавлено {0}", "LabelAddedDate": "Добавлено {0}",
"LabelAdminUsersOnly": "Только для пользователей с правами администратора", "LabelAdminUsersOnly": "Только для пользователей с правами администратора",
"LabelAll": "Все", "LabelAll": "Все",
"LabelAllEpisodesDownloaded": "Все эпизоды загружены",
"LabelAllUsers": "Все пользователи", "LabelAllUsers": "Все пользователи",
"LabelAllUsersExcludingGuests": "Все пользователи, кроме гостей", "LabelAllUsersExcludingGuests": "Все пользователи, кроме гостей",
"LabelAllUsersIncludingGuests": "Все пользователи, включая гостей", "LabelAllUsersIncludingGuests": "Все пользователи, включая гостей",
@@ -777,8 +778,8 @@
"MessageForceReScanDescription": "будет сканировать все файлы снова, как свежее сканирование. Теги ID3 аудиофайлов, OPF-файлы и текстовые файлы будут сканироваться как новые.", "MessageForceReScanDescription": "будет сканировать все файлы снова, как свежее сканирование. Теги ID3 аудиофайлов, OPF-файлы и текстовые файлы будут сканироваться как новые.",
"MessageImportantNotice": "Важное замечание!", "MessageImportantNotice": "Важное замечание!",
"MessageInsertChapterBelow": "Вставить главу ниже", "MessageInsertChapterBelow": "Вставить главу ниже",
"MessageItemsSelected": "{0} Элементов выделено", "MessageItemsSelected": "{0} элементов выделено",
"MessageItemsUpdated": "{0} Элементов обновлено", "MessageItemsUpdated": "{0} элементов обновлено",
"MessageJoinUsOn": "Присоединяйтесь к нам в", "MessageJoinUsOn": "Присоединяйтесь к нам в",
"MessageLoading": "Загрузка...", "MessageLoading": "Загрузка...",
"MessageLoadingFolders": "Загрузка каталогов...", "MessageLoadingFolders": "Загрузка каталогов...",
@@ -953,6 +954,7 @@
"ToastBackupRestoreFailed": "Не удалось восстановить из бэкапа", "ToastBackupRestoreFailed": "Не удалось восстановить из бэкапа",
"ToastBackupUploadFailed": "Не удалось загрузить бэкап", "ToastBackupUploadFailed": "Не удалось загрузить бэкап",
"ToastBackupUploadSuccess": "Бэкап загружен", "ToastBackupUploadSuccess": "Бэкап загружен",
"ToastBatchApplyDetailsToItemsSuccess": "Подробности, применяемые к элементам",
"ToastBatchDeleteFailed": "Не удалось выполнить пакетное удаление", "ToastBatchDeleteFailed": "Не удалось выполнить пакетное удаление",
"ToastBatchDeleteSuccess": "Успешное пакетное удаление", "ToastBatchDeleteSuccess": "Успешное пакетное удаление",
"ToastBatchQuickMatchFailed": "Не удалось выполнить пакетное быстрое сопоставление!", "ToastBatchQuickMatchFailed": "Не удалось выполнить пакетное быстрое сопоставление!",
@@ -1065,6 +1067,7 @@
"ToastSelectAtLeastOneUser": "Выберите хотя бы одного пользователя", "ToastSelectAtLeastOneUser": "Выберите хотя бы одного пользователя",
"ToastSendEbookToDeviceFailed": "Не удалось отправить e-книгу на устройство", "ToastSendEbookToDeviceFailed": "Не удалось отправить e-книгу на устройство",
"ToastSendEbookToDeviceSuccess": "E-книга отправлена на устройство \"{0}\"", "ToastSendEbookToDeviceSuccess": "E-книга отправлена на устройство \"{0}\"",
"ToastSeriesSubmitFailedSameName": "Невозможно добавить две серии с одинаковым названием",
"ToastSeriesUpdateFailed": "Не удалось обновить серию", "ToastSeriesUpdateFailed": "Не удалось обновить серию",
"ToastSeriesUpdateSuccess": "Успешное обновление серии", "ToastSeriesUpdateSuccess": "Успешное обновление серии",
"ToastServerSettingsUpdateSuccess": "Обновлены настройки сервера", "ToastServerSettingsUpdateSuccess": "Обновлены настройки сервера",
@@ -1083,6 +1086,8 @@
"ToastUnknownError": "Неизвестная ошибка", "ToastUnknownError": "Неизвестная ошибка",
"ToastUnlinkOpenIdFailed": "Не удалось отвязать пользователя от OpenID", "ToastUnlinkOpenIdFailed": "Не удалось отвязать пользователя от OpenID",
"ToastUnlinkOpenIdSuccess": "Пользователь отвязан от OpenID", "ToastUnlinkOpenIdSuccess": "Пользователь отвязан от OpenID",
"ToastUploaderFilepathExistsError": "Путь к файлу \"{0}\" уже существует на сервере",
"ToastUploaderItemExistsInSubdirectoryError": "Элемент «{0}» использует подкаталог пути загрузки.",
"ToastUserDeleteFailed": "Не удалось удалить пользователя", "ToastUserDeleteFailed": "Не удалось удалить пользователя",
"ToastUserDeleteSuccess": "Пользователь удален", "ToastUserDeleteSuccess": "Пользователь удален",
"ToastUserPasswordChangeSuccess": "Пароль успешно изменен", "ToastUserPasswordChangeSuccess": "Пароль успешно изменен",
+379 -7
View File
@@ -32,7 +32,7 @@
"ButtonEditChapters": "Upraviť kapitoly", "ButtonEditChapters": "Upraviť kapitoly",
"ButtonEditPodcast": "Upraviť podcast", "ButtonEditPodcast": "Upraviť podcast",
"ButtonEnable": "Povoliť", "ButtonEnable": "Povoliť",
"ButtonFireAndFail": "Fire and Fail", "ButtonFireAndFail": "Spustiť a zlyhať",
"ButtonFireOnTest": "Fire onTest udalosť", "ButtonFireOnTest": "Fire onTest udalosť",
"ButtonForceReScan": "Vynútiť preskenovanie", "ButtonForceReScan": "Vynútiť preskenovanie",
"ButtonFullPath": "Zobraziť cestu", "ButtonFullPath": "Zobraziť cestu",
@@ -87,7 +87,7 @@
"ButtonRestore": "Obnoviť zo zálohy", "ButtonRestore": "Obnoviť zo zálohy",
"ButtonSave": "Uložiť", "ButtonSave": "Uložiť",
"ButtonSaveAndClose": "Uložiť a zavrieť", "ButtonSaveAndClose": "Uložiť a zavrieť",
"ButtonSaveTracklist": "Uložiť zoznam", "ButtonSaveTracklist": "Uložiť tracklist",
"ButtonScan": "Skenovať", "ButtonScan": "Skenovať",
"ButtonScanLibrary": "Skenovať knižnicu", "ButtonScanLibrary": "Skenovať knižnicu",
"ButtonScrollLeft": "Doľava", "ButtonScrollLeft": "Doľava",
@@ -107,7 +107,7 @@
"ButtonUnlinkOpenId": "Odhlásiť OpenID", "ButtonUnlinkOpenId": "Odhlásiť OpenID",
"ButtonUpload": "Nahrať", "ButtonUpload": "Nahrať",
"ButtonUploadBackup": "Nahrať zálohu", "ButtonUploadBackup": "Nahrať zálohu",
"ButtonUploadCover": "Nahrať obal", "ButtonUploadCover": "Nahrať prebal",
"ButtonUploadOPMLFile": "Nahrať súbor OPML", "ButtonUploadOPMLFile": "Nahrať súbor OPML",
"ButtonUserDelete": "Odstrániť užívateľa {0}", "ButtonUserDelete": "Odstrániť užívateľa {0}",
"ButtonUserEdit": "Upraviť užívateľa {0}", "ButtonUserEdit": "Upraviť užívateľa {0}",
@@ -129,7 +129,7 @@
"HeaderChooseAFolder": "Vybrať priečinok", "HeaderChooseAFolder": "Vybrať priečinok",
"HeaderCollection": "Zbierka", "HeaderCollection": "Zbierka",
"HeaderCollectionItems": "Položky zbierky", "HeaderCollectionItems": "Položky zbierky",
"HeaderCover": "Obal", "HeaderCover": "Prebal",
"HeaderCurrentDownloads": "Aktuálne sťahovanie", "HeaderCurrentDownloads": "Aktuálne sťahovanie",
"HeaderCustomMessageOnLogin": "Vlastné privítanie pri prihlásení", "HeaderCustomMessageOnLogin": "Vlastné privítanie pri prihlásení",
"HeaderCustomMetadataProviders": "Vlastné zdroje metadát", "HeaderCustomMetadataProviders": "Vlastné zdroje metadát",
@@ -147,7 +147,7 @@
"HeaderItemFiles": "Položka Súbory", "HeaderItemFiles": "Položka Súbory",
"HeaderItemMetadataUtils": "Položka Nástroje metadát", "HeaderItemMetadataUtils": "Položka Nástroje metadát",
"HeaderLastListeningSession": "Posledné pripojenie", "HeaderLastListeningSession": "Posledné pripojenie",
"HeaderLatestEpisodes": "Najnovšie epizódy", "HeaderLatestEpisodes": "Posledné epizódy",
"HeaderLibraries": "Knižnice", "HeaderLibraries": "Knižnice",
"HeaderLibraryFiles": "Súbory knižnice", "HeaderLibraryFiles": "Súbory knižnice",
"HeaderLibraryStats": "Štatistiky knižnice", "HeaderLibraryStats": "Štatistiky knižnice",
@@ -157,6 +157,7 @@
"HeaderLogs": "Záznamy udalostí", "HeaderLogs": "Záznamy udalostí",
"HeaderManageGenres": "Spravovať žánre", "HeaderManageGenres": "Spravovať žánre",
"HeaderManageTags": "Spravovať štítky", "HeaderManageTags": "Spravovať štítky",
"HeaderMapDetails": "Podrobnosti mapovania",
"HeaderMatch": "Spárovať", "HeaderMatch": "Spárovať",
"HeaderMetadataOrderOfPrecedence": "Metadáta pravidiel poradia", "HeaderMetadataOrderOfPrecedence": "Metadáta pravidiel poradia",
"HeaderMetadataToEmbed": "Metadáta na vloženie", "HeaderMetadataToEmbed": "Metadáta na vloženie",
@@ -176,11 +177,382 @@
"HeaderPlaylist": "Playlist", "HeaderPlaylist": "Playlist",
"HeaderPlaylistItems": "Položky playlistu", "HeaderPlaylistItems": "Položky playlistu",
"HeaderPodcastsToAdd": "Podcasty na pridanie", "HeaderPodcastsToAdd": "Podcasty na pridanie",
"HeaderPreviewCover": "Ukážka obalu", "HeaderPreviewCover": "Ukážka prebalu",
"HeaderRSSFeedGeneral": "Detaily RSS", "HeaderRSSFeedGeneral": "Detaily RSS",
"HeaderRSSFeedIsOpen": "RSS zdroj je otvorený", "HeaderRSSFeedIsOpen": "RSS zdroj je otvorený",
"HeaderRSSFeeds": "RSS zdroje", "HeaderRSSFeeds": "RSS zdroje",
"HeaderRemoveEpisode": "Odstrániť epizódu", "HeaderRemoveEpisode": "Odstrániť epizódu",
"HeaderRemoveEpisodes": "Odstrániť {0} epizód", "HeaderRemoveEpisodes": "Odstrániť {0} epizód",
"LabelBackupsNumberToKeepHelp": "Týmto spôsobom odstránite vždy iba jednu zálohu. V prípade, ak chcete odtrániť viacero záloh, mali by ste ich odstrániť manuálne." "HeaderSavedMediaProgress": "Priebeh uložených médií",
"HeaderSchedule": "Plán",
"HeaderScheduleEpisodeDownloads": "Naplánovať automatické sťahovanie epizód",
"HeaderScheduleLibraryScans": "Naplánovanovať automatické skenovanie knižnice",
"HeaderSession": "Relácia",
"HeaderSetBackupSchedule": "Naplánovať zálohovanie",
"HeaderSettings": "Nastavenia",
"HeaderSettingsDisplay": "Zobraziť",
"HeaderSettingsExperimental": "Experimentálne funkcie",
"HeaderSettingsGeneral": "Hlavné",
"HeaderSettingsScanner": "Skener",
"HeaderSettingsWebClient": "Webový klient",
"HeaderSleepTimer": "Časovač spánku",
"HeaderStatsLargestItems": "Najväčšie položky",
"HeaderStatsLongestItems": "Najdlhšie položky (v hodinách)",
"HeaderStatsMinutesListeningChart": "Vypočutých minút (za posledných 7 dní)",
"HeaderStatsRecentSessions": "Nedávne relácie",
"HeaderStatsTop10Authors": "Top 10 autorov",
"HeaderStatsTop5Genres": "Top 5 žánrov",
"HeaderTableOfContents": "Obsah",
"HeaderTools": "Nástroje",
"HeaderUpdateAccount": "Aktualizovať účet",
"HeaderUpdateAuthor": "Aktualizovať autora",
"HeaderUpdateDetails": "Aktualizovať detaily",
"HeaderUpdateLibrary": "Aktualizovať knižnicu",
"HeaderUsers": "Užívatelia",
"HeaderYearReview": "Prehľad roka {0}",
"HeaderYourStats": "Tvoje štatistiky",
"LabelAbridged": "Skrátená verzia",
"LabelAbridgedChecked": "Skrátená verzia (zaškrtnuté)",
"LabelAbridgedUnchecked": "Neskrátená verzia (nezaškrtnuté)",
"LabelAccessibleBy": "Prístupné pre",
"LabelAccountType": "Typ účtu",
"LabelAccountTypeAdmin": "Administrátor",
"LabelAccountTypeGuest": "Hosť",
"LabelAccountTypeUser": "Užívateľ",
"LabelActivities": "Aktivity",
"LabelActivity": "Aktivita",
"LabelAddToCollection": "Pridať do kolekcie",
"LabelAddToCollectionBatch": "Pridať {0} kníh do kolekcie",
"LabelAddToPlaylist": "Pridať do playlistu",
"LabelAddToPlaylistBatch": "Pridať {0} položie do playlistu",
"LabelAddedAt": "Pridané",
"LabelAddedDate": "Pridané {0}",
"LabelAdminUsersOnly": "Iba administrátory",
"LabelAll": "Všetko",
"LabelAllEpisodesDownloaded": "Všetky epizódy stiahnuté",
"LabelAllUsers": "Všetci užívatelia",
"LabelAllUsersExcludingGuests": "Všetci užívatelia okrem hostí",
"LabelAllUsersIncludingGuests": "Všetci užívatelia vrátane hostí",
"LabelAlreadyInYourLibrary": "Už v tvojej knižnici",
"LabelApiToken": "API Token",
"LabelAppend": "Pridať",
"LabelAudioBitrate": "Bitrate audio stopy (napr. 128k)",
"LabelAudioChannels": "Počet kanálov audio stopy (1 alebo 2)",
"LabelAudioCodec": "Kodek audio stopy",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (Meno, Priezvisko)",
"LabelAuthorLastFirst": "Autor (Priezvisko, Meno)",
"LabelAuthors": "Autori",
"LabelAutoDownloadEpisodes": "Automaticky sťahovať epizódy",
"LabelAutoFetchMetadata": "Automaticky načítať metadáta",
"LabelAutoFetchMetadataHelp": "Načíta metadáta pre názov, autra a sériu pre optimalizované nahranie. Dodatočné metadáta môžu byť priradené po nahraní.",
"LabelAutoLaunch": "Automaticky spustiť",
"LabelAutoLaunchDescription": "Presmerovať na poskytovateľa authentifikácie pri otvorení prihlasovacej stránky (manuálny prepis cesty <code>/login?autoLaunch=0</code>)",
"LabelAutoRegister": "Automatická registrácia",
"LabelAutoRegisterDescription": "Automaticky vytvoriť nových užívateľov po prihlásení",
"LabelBackToUser": "Späť na užívateľa",
"LabelBackupAudioFiles": "Zálohovať audio súbory",
"LabelBackupLocation": "Zálohovať lokáciu",
"LabelBackupsEnableAutomaticBackups": "Automatické zálohy",
"LabelBackupsEnableAutomaticBackupsHelp": "Zálohy uložené v /metadata/backups",
"LabelBackupsMaxBackupSize": "Maximálna veľkosť zálohy (v GB) (0 pre neobmedzenú)",
"LabelBackupsMaxBackupSizeHelp": "Ako poistka proti miskonfigurácii, zálohy zlyhajú ak prekročia konfigurovanú veľkosť.",
"LabelBackupsNumberToKeep": "Počet uložených záloh",
"LabelBackupsNumberToKeepHelp": "Týmto spôsobom odstránite vždy iba jednu zálohu. V prípade, ak chcete odtrániť viacero záloh, mali by ste ich odstrániť manuálne.",
"LabelBitrate": "Bitrate",
"LabelBonus": "Bonus",
"LabelBooks": "Knihy",
"LabelButtonText": "Text tlačidla",
"LabelByAuthor": "od {0}",
"LabelChangePassword": "Zmeniť heslo",
"LabelChannels": "Kanály",
"LabelChapterCount": "{0} kapitol",
"LabelChapterTitle": "Názov kapitoly",
"LabelChapters": "Kapitoly",
"LabelChaptersFound": "nájdených kapitol",
"LabelClickForMoreInfo": "Klikni pre viac informácií",
"LabelClickToUseCurrentValue": "Klikni pre použitie aktuálnej hodnoty",
"LabelClosePlayer": "Zavrieť prehrávač",
"LabelCodec": "Kodek",
"LabelCollapseSeries": "Zbaliť série",
"LabelCollapseSubSeries": "Zbaliť podsérie",
"LabelCollection": "Kolekcia",
"LabelCollections": "Kolekcie",
"LabelComplete": "Hotovo",
"LabelConfirmPassword": "Potvrdiť heslo",
"LabelContinueListening": "Pokračovať v počúvaní",
"LabelContinueReading": "Pokračovať v čítaní",
"LabelContinueSeries": "Pokračovať v sérii",
"LabelCover": "Prebal",
"LabelCoverImageURL": "URL obrázku prebalu",
"LabelCoverProvider": "Poskytovateľ prebalu",
"LabelCreatedAt": "Vytvorené",
"LabelCronExpression": "Cron príkaz",
"LabelCurrent": "Aktuálny",
"LabelCurrently": "Aktuálne:",
"LabelCustomCronExpression": "Vlastný Cron príkaz:",
"LabelDatetime": "Dátum a čas",
"LabelDays": "Dni",
"LabelDeleteFromFileSystemCheckbox": "Zmazať zo systému (odškrtni len pre odstránenie z databázy)",
"LabelDescription": "Popis",
"LabelDeselectAll": "Odznačiť všetko",
"LabelDevice": "Zariadenie",
"LabelDeviceInfo": "Informácie o zariadení",
"LabelDeviceIsAvailableTo": "Zariadenie je k dispozícii...",
"LabelDirectory": "Priečinok",
"LabelDiscFromFilename": "Disk z názvu súboru",
"LabelDiscFromMetadata": "Disk z metadát",
"LabelDiscover": "Objaviť",
"LabelDownload": "Stiahnuť",
"LabelDownloadNEpisodes": "Stiahnuť {0} epizód",
"LabelDownloadable": "Dostupné na stiahnutie",
"LabelDuration": "Dĺžka",
"LabelDurationComparisonExactMatch": "(presná zhoda)",
"LabelDurationComparisonLonger": "({0} dlhšie)",
"LabelDurationComparisonShorter": "({0} kratšie)",
"LabelDurationFound": "Nájdená dlžka:",
"LabelEbook": "E-kniha",
"LabelEbooks": "E-knihy",
"LabelEdit": "Upraviť",
"LabelEmail": "E-mail",
"LabelEmailSettingsFromAddress": "Z e-mailu",
"LabelEmailSettingsRejectUnauthorized": "Odmietnuť neautorizované certifikáty",
"LabelEmailSettingsRejectUnauthorizedHelp": "Vypnutie validácie SSL certifikátu môže tvoje pripojenie vystaviť bezpečnostným rizikám, ako napríklad MitM útokom. Vypni túto možnosť len v prípade, že rozumieš dôsledkom a dôveruješ e-mailovému serveru, ku ktorému sa pripájaš.",
"LabelEmailSettingsSecure": "Bezpečné",
"LabelEmailSettingsSecureHelp": "Pri povolení bude na pripojenie k serveru použité TLS. V opačnom prípade je TLS použité iba v prípade, ak server podporuje rozšírenie STARTTLS. Vo väčšine prípadov povoľte túto možnosť, ak sa pripájate cez port 465. V prípadoch, ak používate port 587 alebo 25, túto voľbu nepovoľujte. (prevzaté z nodemailer.com/smtp/#authentification)",
"LabelEmailSettingsTestAddress": "Testovacia adresa",
"LabelEmbeddedCover": "Vložený prebal",
"LabelEnable": "Povoliť",
"LabelEncodingBackupLocation": "Záloha vašich pôvodných zvukových súborov bude uložená v:",
"LabelEncodingChaptersNotEmbedded": "Kapitoly nie sú vkladané do viacstopových audiokníh.",
"LabelEncodingClearItemCache": "Nezabudnite pravidelne vyčistiť vyrovnávaciu pamäť jednotlivých položiek.",
"LabelEncodingFinishedM4B": "Dokončený súbor M4B bude uložený do priečinka audioknihy v:",
"LabelEncodingInfoEmbedded": "Metadáta budú vložené do zvukových stôp v priečinku audioknihy.",
"LabelEncodingStartedNavigation": "Po spustení úlohy môžete túto stránku opustiť.",
"LabelEncodingTimeWarning": "Prekódovanie môže trvať aj 30 minút.",
"LabelEncodingWarningAdvancedSettings": "Pozor: Nemeňte uvedené nastavenia, pokiaľ nie ste dostatočne oboznámený s nastaveniami ffmpeg prekódovania.",
"LabelEncodingWatcherDisabled": "V prípade, ak nemáte povolené automatické sledovanie zmien, bude na konci potrebné audioknihu opätovne preskenovať.",
"LabelEnd": "Ukončiť",
"LabelEndOfChapter": "Koniec kapitoly",
"LabelEpisode": "Epizóda",
"LabelEpisodeNotLinkedToRssFeed": "Epizóda bez RSS zdroja",
"LabelEpisodeNumber": "Epizóda #{0}",
"LabelEpisodeTitle": "Názov epizódy",
"LabelEpisodeType": "Typ epizódy",
"LabelEpisodeUrlFromRssFeed": "URL epizódy z RSS",
"LabelEpisodes": "Epizódy",
"LabelEpisodic": "Epizódny",
"LabelExample": "Príklad",
"LabelExpandSeries": "Rozbaliť série",
"LabelExpandSubSeries": "Rozbaliť podsérie",
"LabelExplicit": "Explicitné",
"LabelExplicitChecked": "Explicitné (zaškrtnuté)",
"LabelExplicitUnchecked": "Ne-explicitné (nezaškrtnuté)",
"LabelExportOPML": "Exportovať OPML",
"LabelFeedURL": "URL zdroja",
"LabelFetchingMetadata": "Sťahovanie metadát",
"LabelFile": "Súbor",
"LabelFileBirthtime": "Čas vytvorenia súboru",
"LabelFileBornDate": "Vytvorené {0}",
"LabelFileModified": "Súbor zmenený",
"LabelFileModifiedDate": "Zmenený {0}",
"LabelFilename": "Názov súboru",
"LabelFilterByUser": "Užívateľský filter",
"LabelFindEpisodes": "Nájsť epizódy",
"LabelFinished": "Ukončené",
"LabelFolder": "Priečinok",
"LabelFolders": "Priečinky",
"LabelFontBold": "Tučné",
"LabelFontBoldness": "Hrúbka písma",
"LabelFontFamily": "Rodina písiem",
"LabelFontItalic": "Kurzíva",
"LabelFontScale": "Veľkosť písma",
"LabelFontStrikethrough": "Preškrtnuté",
"LabelFormat": "Formát",
"LabelFull": "Plné",
"LabelGenre": "Žáner",
"LabelGenres": "Žánre",
"LabelHardDeleteFile": "Nezvratné zmazanie súborov",
"LabelHasEbook": "Má e-knihu",
"LabelHasSupplementaryEbook": "Má doplnkovú e-knihu",
"LabelHideSubtitles": "Skryť titulky",
"LabelHighestPriority": "Najvyššia priorita",
"LabelHost": "Host",
"LabelHour": "Hodina",
"LabelHours": "Hodiny",
"LabelIcon": "Ikona",
"LabelImageURLFromTheWeb": "URL obrázku",
"LabelInProgress": "Prebieha",
"LabelIncludeInTracklist": "Vložiť do tracklistu",
"LabelIncomplete": "Nekompletné",
"LabelInterval": "Interval",
"LabelIntervalCustomDailyWeekly": "Vlastný",
"LabelIntervalEvery12Hours": "Každých 12 hodín",
"LabelIntervalEvery15Minutes": "Každých 15 minút",
"LabelIntervalEvery2Hours": "Každé 2 hodiny",
"LabelIntervalEvery30Minutes": "Každých 30 minút",
"LabelIntervalEvery6Hours": "Každých 6 hodín",
"LabelIntervalEveryDay": "Denne",
"LabelIntervalEveryHour": "Každú hodinu",
"LabelIntervalEveryMinute": "Každú minútu",
"LabelInvert": "Invertne",
"LabelItem": "Položka",
"LabelJumpBackwardAmount": "Posunúť vpred o",
"LabelJumpForwardAmount": "Posunúť dozadu o",
"LabelLanguage": "Jazyk",
"LabelLanguageDefaultServer": "Prednastavený jazyk servera",
"LabelLanguages": "Jazyky",
"LabelLastBookAdded": "Posledná pridaná kniha",
"LabelLastBookUpdated": "Posledná aktualizovaná kniha",
"LabelLastSeen": "Posledne videné",
"LabelLastTime": "Posledný čas",
"LabelLastUpdate": "Posledná aktualizácia",
"LabelLayout": "Rozloženie",
"LabelLayoutSinglePage": "Jedna stránka",
"LabelLayoutSplitPage": "Rozdelená stránka",
"LabelLess": "Menej",
"LabelLibrariesAccessibleToUser": "Knižnice dostupné užívateľovi",
"LabelLibrary": "Knižnica",
"LabelLibraryFilterSublistEmpty": "Žiadne {0}",
"LabelLibraryItem": "Položka knižnice",
"LabelLibraryName": "Názov knižnice",
"LabelLimit": "Limit",
"LabelLineSpacing": "Riadkovanie",
"LabelListenAgain": "Počúvať znova",
"LabelLogLevelDebug": "Ladenie",
"LabelLogLevelInfo": "Informácia",
"LabelLogLevelWarn": "Varovanie",
"LabelLookForNewEpisodesAfterDate": "Hľadať nové epizódy od uvedeného dátumu",
"LabelLowestPriority": "Najnižšia priorita",
"LabelMatchExistingUsersBy": "Vyhľadaj vytvorených užívateľov podľa",
"LabelMatchExistingUsersByDescription": "Používané na pripájanie vytvorených užívateľov. Po pripojení budú užívatelia vyhľadaní na základe jedinečného id poskytnutého Vaším poskytovateľom SSO",
"LabelMaxEpisodesToDownload": "Stiahnuť maximálne # epizód. Pre neobmedzené sťahovanie zadajte 0.",
"LabelMaxEpisodesToDownloadPerCheck": "Pri kontrole stiahnuť maximálne # epizód",
"LabelMaxEpisodesToKeep": "Uchovávať maximálne # epizód",
"LabelMaxEpisodesToKeepHelp": "Hodnota 0 znamená bez limitu. Po automatickom stiahnutí novej epizódy bude najstaršia epizóda zmazaná a ponechaných zostane X epizód. Pri každom stiahnutí 1 epizódy bude vždy zmazaná iba 1 najstaršia epizóda.",
"LabelMediaPlayer": "Prehrávač",
"LabelMediaType": "Typ média",
"LabelMetaTag": "Meta štítok",
"LabelMetaTags": "Meta štítky",
"LabelMetadataOrderOfPrecedenceDescription": "Zdroje metadát s vyššou prioritou prepíšu zdroje metadát s nižšou prioritou",
"LabelMetadataProvider": "Poskytovateľ metadát",
"LabelMinute": "Minúta",
"LabelMinutes": "Minúty",
"LabelMissing": "Chýbajúce",
"LabelMissingEbook": "Nemá e-knihu",
"LabelMissingSupplementaryEbook": "Nemá doplnkovú e-knihu",
"LabelMobileRedirectURIs": "Povolené Mobile Redirect URI",
"LabelMobileRedirectURIsDescription": "Toto je zoznam povolených URI pre mobilné aplikácie. Prednastavená je <code>audiobookshelf://oauth</code>, ktorú však môžete odstrániť alebo nahradiť inou URI pre integráciu aplikácií tretích strán. Použitím hviezdičky (<code>*</code>) povolíte všetky URI.",
"LabelMore": "Viac",
"LabelMoreInfo": "Viac informácií",
"LabelName": "Názov",
"LabelNarrator": "Interpret",
"LabelNarrators": "Interpreti",
"LabelNew": "Nový",
"LabelNewPassword": "Nové heslo",
"LabelNewestAuthors": "Najnovší autori",
"LabelNewestEpisodes": "Najnovšie epizódy",
"LabelNextBackupDate": "Ďalší dátum zálohovania",
"LabelNextScheduledRun": "Ďalší plánovaný beh",
"LabelNoCustomMetadataProviders": "Žiadne vlastné zdroje metadát",
"LabelNoEpisodesSelected": "Neboli vybrané žiadne epizódy",
"LabelNotFinished": "Nedokončené",
"LabelNotStarted": "Nezačaté",
"LabelNotes": "Poznámky",
"LabelNotificationAppriseURL": "URL odkaz(-y) Apprise",
"LabelNotificationAvailableVariables": "Dostupné premenné",
"LabelNotificationBodyTemplate": "Šablóna obsahu",
"LabelNotificationEvent": "Udalosť oznámení",
"LabelNotificationTitleTemplate": "Šablóna názvu",
"LabelNotificationsMaxFailedAttempts": "Maximálny počet neúspešných pokusov",
"LabelNotificationsMaxFailedAttemptsHelp": "Notifikácie sa automaticky vypnú, ak ich odoslanie zlyhá nasledovný počet krát",
"LabelNotificationsMaxQueueSize": "Maximálna dĺžka fronty oznámení",
"LabelNotificationsMaxQueueSizeHelp": "Odosielanie udalostí je ohraničené na jedno oznámenie za sekundu. Novovzniknuté udalosti budú ignorované, ak bude fronta oznámení naplnená. Toto nastavenie zabraňuje nevyžiadanému zahlteniu oznámeniami.",
"LabelNumberOfBooks": "Počet kníh",
"LabelNumberOfEpisodes": "# epizód",
"LabelOpenIDAdvancedPermsClaimDescription": "Názov OpenID predpokladá prítomnosť pokročilých povolení pre užívateľské akcie v rámci aplikácie, ktoré sú aplikovateľné na ne-administrátorské role (<b>ak sú nakonfigurované</b>). Ak potvrdenie takýchto pokročilých povolení nie je v odozve prítomné, prístup do ABS bude automaticky zamietnutý. Ak v odozve chýba len niektoré z očakávaných nastavení, tak bude jeho hodnota automaticky nastavená na <code>false</code>. Uistite sa prosím, že forma odozvy poskytovateľa identity má nasledovnú štruktúru:",
"LabelOpenIDClaims": "Ak ponecháte nasledujúce nastavenia prázdne, pokročilé nastavenia skupín a povolení nebudú aktivované a automaticky bude nastavená skupina 'Užívateľ'.",
"LabelOpenIDGroupClaimDescription": "Pri názve požiadavky OpenID sa predpokladá, že obsahuje zoznam užívateľských skupín. Bežne označovaný ako <code>groups</code>. <b>Ak je správne nakonfigurovaný</b>, aplikácia automaticky pridelí role podľa príslušnosti k užívateľským skupinám pod podmienkou, že sú tieto skupiny v požiadavke nazvané (bez ohľadu na veľkosť písmen) ako 'admin', 'user' alebo 'guest'. Požiadavka musí obsahovať zoznam skupín a ak užívateľ patrí do viacerých skupín, aplikácia mu priradí rolu, ktorá zodpovedá skupine s najvyššími prístupovými právami. Ak sa žiadna z poskytnutých skupín nezhoduje, prístup bude zamietnutý.",
"LabelOpenRSSFeed": "Otvor RSS zdroj",
"LabelOverwrite": "Prepísať",
"LabelPaginationPageXOfY": "Stránka {0} z {1}",
"LabelPassword": "Heslo",
"LabelPath": "Cesta",
"LabelPermanent": "Trvalé",
"LabelPermissionsAccessAllLibraries": "Má prístup do všetkých knižníc",
"LabelPermissionsAccessAllTags": "Má prístup ku všetkým štítkom",
"LabelPermissionsAccessExplicitContent": "Má prístup k explicitnému obsahu",
"LabelPermissionsCreateEreader": "Môže vytvoriť čítačku e-kníh",
"LabelPermissionsDelete": "Môže mazať",
"LabelPermissionsDownload": "Môže sťahovať",
"LabelPermissionsUpdate": "Môže aktualizovať",
"LabelPermissionsUpload": "Môže nahrávať",
"LabelPersonalYearReview": "Váš rok v prehľade ({0})",
"LabelPhotoPathURL": "Cesta/URL fotky",
"LabelPlayMethod": "Metóda prehrávania",
"LabelPlaybackRateIncrementDecrement": "Veľkosť kroku zrýchlenia/spomalenia rýchlosti prehávania",
"LabelPlayerChapterNumberMarker": "{0} z {1}",
"LabelPlaylists": "Playlisty",
"LabelPodcast": "Podcast",
"LabelPodcastSearchRegion": "Región vyhľadávania podcastu",
"LabelPodcastType": "Typ podcastu",
"LabelPodcasts": "Podcasty",
"LabelPort": "Prístav",
"LabelPrefixesToIgnore": "Ignorované predpony (bez ohľadu na veľkosť písmen)",
"LabelPreventIndexing": "Zabráňte indexovaniu Vášho zdroja službami iTunes a Google podcasts directories",
"LabelPrimaryEbook": "Primárny e-book",
"LabelProgress": "Pokrok",
"LabelProvider": "Poskytovateľ",
"LabelProviderAuthorizationValue": "Obsah hlavičky autorizácie",
"LabelPubDate": "Dátum vydania",
"LabelPublishYear": "Rok vydania",
"LabelPublishedDate": "Vydané {0}",
"LabelPublishedDecade": "Dekáda vydania",
"LabelPublishedDecades": "Dekády vydania",
"LabelPublisher": "Vydavateľ",
"LabelPublishers": "Vydavatelia",
"LabelRSSFeedCustomOwnerEmail": "Vlastný e-mail vlastníka",
"LabelRSSFeedCustomOwnerName": "Vlastné meno vlastníka",
"LabelRSSFeedOpen": "RSS zdroj otvorený",
"LabelRSSFeedPreventIndexing": "Zakázať indexovanie",
"LabelRSSFeedSlug": "Slug RSS zdroja",
"LabelRSSFeedURL": "URL RSS zdroja",
"LabelRandomly": "Náhodne",
"LabelReAddSeriesToContinueListening": "Znova pridať série do pokračujúceho počúvania",
"LabelRead": "Čítať",
"LabelReadAgain": "Čítať znova",
"LabelReadEbookWithoutProgress": "Čítať e-knihu bez sledovania pokroku",
"LabelRecentSeries": "Posledné série",
"LabelRecentlyAdded": "Posledné pridané",
"LabelRecommended": "Odporúčané",
"LabelRedo": "Zopakovať",
"LabelRegion": "Región",
"LabelReleaseDate": "Dátum vydania",
"LabelRemoveAllMetadataAbs": "Odstrániť všetky súbory metadata.abs",
"LabelRemoveAllMetadataJson": "Odstrániť všetky súbory metadata.json",
"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.",
"LabelRowsPerPage": "Počet riadkov na stránku",
"LabelSearchTerm": "Hľadaj výraz",
"LabelSearchTitle": "Hľadaj názov",
"LabelSearchTitleOrASIN": "Hľadaj názov alebo ASIN",
"LabelSeason": "Sezóna",
"LabelSeasonNumber": "Sezóna #{0}",
"LabelSelectAll": "Vybrať všetko",
"LabelSelectAllEpisodes": "Vybrať všetky epizódy",
"LabelSelectEpisodesShowing": "Vybrať {0} zobrazených epizód",
"LabelSelectUsers": "Vybrať užívateľov",
"LabelSendEbookToDevice": "Poslať e-knihu do...",
"LabelSequence": "Postupnosť",
"LabelSerial": "Na pokračovanie",
"LabelSeries": "Séria",
"LabelSeriesName": "Názov série",
"LabelSeriesProgress": "Pokrok série",
"LabelServerLogLevel": "Úroveň logovania servera",
"LabelServerYearReview": "Rok servera v prehľade ({0})",
"LabelSetEbookAsPrimary": "Nastaviť ako primárny",
"LabelSetEbookAsSupplementary": "Nastaviť ako doplnkový",
"LabelSettingsAllowIframe": "Povoliť vkladanie do iframe",
"LabelSettingsAudiobooksOnly": "Len audioknihy"
} }
+2
View File
@@ -1086,6 +1086,8 @@
"ToastUnknownError": "Neznana napaka", "ToastUnknownError": "Neznana napaka",
"ToastUnlinkOpenIdFailed": "Prekinitev povezave uporabnika z OpenID ni uspela", "ToastUnlinkOpenIdFailed": "Prekinitev povezave uporabnika z OpenID ni uspela",
"ToastUnlinkOpenIdSuccess": "Uporabnik je prekinil povezavo z OpenID", "ToastUnlinkOpenIdSuccess": "Uporabnik je prekinil povezavo z OpenID",
"ToastUploaderFilepathExistsError": "Datoteka s potjo \"{0}\" že obstaja na strežniku",
"ToastUploaderItemExistsInSubdirectoryError": "Element \"{0}\" uporablja podmapo v poti za nalaganje.",
"ToastUserDeleteFailed": "Brisanje uporabnika ni uspelo", "ToastUserDeleteFailed": "Brisanje uporabnika ni uspelo",
"ToastUserDeleteSuccess": "Uporabnik je bil izbrisan", "ToastUserDeleteSuccess": "Uporabnik je bil izbrisan",
"ToastUserPasswordChangeSuccess": "Geslo je bilo uspešno spremenjeno", "ToastUserPasswordChangeSuccess": "Geslo je bilo uspešno spremenjeno",
+8 -2
View File
@@ -544,7 +544,8 @@
"LabelSettingsBookshelfViewHelp": "Bakgrund med ett utseende liknande en bokhylla i trä", "LabelSettingsBookshelfViewHelp": "Bakgrund med ett utseende liknande en bokhylla i trä",
"LabelSettingsChromecastSupport": "Stöd för Chromecast", "LabelSettingsChromecastSupport": "Stöd för Chromecast",
"LabelSettingsDateFormat": "Datumformat", "LabelSettingsDateFormat": "Datumformat",
"LabelSettingsEnableWatcher": "Automatiskt upptäcka förändringar i biblioteket", "LabelSettingsEnableWatcher": "Upptäck automatiskt förändringar i biblioteket",
"LabelSettingsEnableWatcherForLibrary": "Upptäck automatiskt förändringar i biblioteket",
"LabelSettingsEnableWatcherHelp": "Aktiverar automatik att upptäcka när objekt<br>adderas, uppdateras eller raderas.<br>OBS: Kräver en omstart av servern", "LabelSettingsEnableWatcherHelp": "Aktiverar automatik att upptäcka när objekt<br>adderas, uppdateras eller raderas.<br>OBS: Kräver en omstart av servern",
"LabelSettingsEpubsAllowScriptedContent": "Tillåt e-böcker i epubs-format som innehåller script", "LabelSettingsEpubsAllowScriptedContent": "Tillåt e-böcker i epubs-format som innehåller script",
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillåt att epub-filer får innehålla script.<br>Det rekommenderas att denna inställning är<br>avstängd när du inte litar på källan för epub-filerna.", "LabelSettingsEpubsAllowScriptedContentHelp": "Tillåt att epub-filer får innehålla script.<br>Det rekommenderas att denna inställning är<br>avstängd när du inte litar på källan för epub-filerna.",
@@ -819,7 +820,7 @@
"MessageResetChaptersConfirm": "Är du säker på att du vill återställa alla kapitel och ångra de ändringarna du gjort?", "MessageResetChaptersConfirm": "Är du säker på att du vill återställa alla kapitel och ångra de ändringarna du gjort?",
"MessageRestoreBackupConfirm": "Är du säker på att du vill läsa in säkerhetskopian som skapades den", "MessageRestoreBackupConfirm": "Är du säker på att du vill läsa in säkerhetskopian som skapades den",
"MessageRestoreBackupWarning": "Att återställa en säkerhetskopia kommer att skriva över hela databasen som finns i /config och omslagsbilder i /metadata/items & /metadata/authors.<br /><br />Säkerhetskopior ändrar inte några filer i dina biblioteksmappar. Om du har aktiverat serverinställningar för att lagra omslagskonst och metadata i dina biblioteksmappar säkerhetskopieras eller skrivs de inte över.<br /><br />Alla klienter som använder din server kommer att uppdateras automatiskt.", "MessageRestoreBackupWarning": "Att återställa en säkerhetskopia kommer att skriva över hela databasen som finns i /config och omslagsbilder i /metadata/items & /metadata/authors.<br /><br />Säkerhetskopior ändrar inte några filer i dina biblioteksmappar. Om du har aktiverat serverinställningar för att lagra omslagskonst och metadata i dina biblioteksmappar säkerhetskopieras eller skrivs de inte över.<br /><br />Alla klienter som använder din server kommer att uppdateras automatiskt.",
"MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas att denna funktion ej aktiveras. Istället bör funktionen som automatisk upptäcker ändringar av filerna vara aktiverad. För vissa filsystem (som t.ex. NFS) fungerar inte denna funktion. Då kan schemalagda skanningar av biblioteken användas istället.", "MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas att denna funktion ej aktiveras. Istället bör funktionen som automatisk upptäcker ändringar i biblioteket vara aktiverad. För vissa filsystem (som t.ex. NFS) fungerar inte denna funktion. Då kan schemalagda skanningar av biblioteken användas istället.",
"MessageScheduleRunEveryWeekdayAtTime": "Startar varje {0} klockan {1}", "MessageScheduleRunEveryWeekdayAtTime": "Startar varje {0} klockan {1}",
"MessageSearchResultsFor": "Sökresultat för", "MessageSearchResultsFor": "Sökresultat för",
"MessageSelected": "{0} valda", "MessageSelected": "{0} valda",
@@ -909,6 +910,7 @@
"ToastAuthorUpdateMerged": "Författaren sammanslagen", "ToastAuthorUpdateMerged": "Författaren sammanslagen",
"ToastAuthorUpdateSuccess": "Författaren uppdaterad", "ToastAuthorUpdateSuccess": "Författaren uppdaterad",
"ToastAuthorUpdateSuccessNoImageFound": "Författaren uppdaterad (ingen bild hittad)", "ToastAuthorUpdateSuccessNoImageFound": "Författaren uppdaterad (ingen bild hittad)",
"ToastBackupAppliedSuccess": "Säkerhetskopian är importerad",
"ToastBackupCreateFailed": "Det gick inte att skapa en säkerhetskopia", "ToastBackupCreateFailed": "Det gick inte att skapa en säkerhetskopia",
"ToastBackupCreateSuccess": "Säkerhetskopian har skapats", "ToastBackupCreateSuccess": "Säkerhetskopian har skapats",
"ToastBackupDeleteFailed": "Det gick inte att radera säkerhetskopian", "ToastBackupDeleteFailed": "Det gick inte att radera säkerhetskopian",
@@ -918,6 +920,7 @@
"ToastBackupRestoreFailed": "Det gick inte att återställa säkerhetskopian", "ToastBackupRestoreFailed": "Det gick inte att återställa säkerhetskopian",
"ToastBackupUploadFailed": "Det gick inte att ladda upp säkerhetskopian", "ToastBackupUploadFailed": "Det gick inte att ladda upp säkerhetskopian",
"ToastBackupUploadSuccess": "Säkerhetskopian uppladdad", "ToastBackupUploadSuccess": "Säkerhetskopian uppladdad",
"ToastBatchApplyDetailsToItemsSuccess": "Informationen har adderats till alla objekt",
"ToastBatchQuickMatchStarted": "Snabbmatchning av {0} böcker har påbörjats!", "ToastBatchQuickMatchStarted": "Snabbmatchning av {0} böcker har påbörjats!",
"ToastBatchUpdateFailed": "Batchuppdateringen misslyckades", "ToastBatchUpdateFailed": "Batchuppdateringen misslyckades",
"ToastBatchUpdateSuccess": "Batchuppdateringen lyckades", "ToastBatchUpdateSuccess": "Batchuppdateringen lyckades",
@@ -948,6 +951,8 @@
"ToastEpisodeDownloadQueueClearSuccess": "Kö för nedladdning av avsnitt har tömts", "ToastEpisodeDownloadQueueClearSuccess": "Kö för nedladdning av avsnitt har tömts",
"ToastEpisodeUpdateSuccess": "{0} avsnitt uppdaterades", "ToastEpisodeUpdateSuccess": "{0} avsnitt uppdaterades",
"ToastFailedToLoadData": "Misslyckades med att ladda data", "ToastFailedToLoadData": "Misslyckades med att ladda data",
"ToastFailedToMatch": "Misslyckades med att matcha",
"ToastFailedToShare": "Misslyckades med att dela",
"ToastFailedToUpdate": "Misslyckades med att uppdatera", "ToastFailedToUpdate": "Misslyckades med att uppdatera",
"ToastInvalidImageUrl": "Felaktig URL-adress till omslagsbilden", "ToastInvalidImageUrl": "Felaktig URL-adress till omslagsbilden",
"ToastInvalidMaxEpisodesToDownload": "Ogiltigt maximalt antal avsnitt att ladda ner", "ToastInvalidMaxEpisodesToDownload": "Ogiltigt maximalt antal avsnitt att ladda ner",
@@ -1030,6 +1035,7 @@
"ToastSortingPrefixesUpdateSuccess": "{0} begrepp för sortering har uppdateras", "ToastSortingPrefixesUpdateSuccess": "{0} begrepp för sortering har uppdateras",
"ToastTitleRequired": "En titel måste anges", "ToastTitleRequired": "En titel måste anges",
"ToastUnknownError": "Ett okänt fel inträffade", "ToastUnknownError": "Ett okänt fel inträffade",
"ToastUploaderFilepathExistsError": "En fil med namnet \"{0}\" finns redan på servern",
"ToastUserDeleteFailed": "Misslyckades med att ta bort användaren", "ToastUserDeleteFailed": "Misslyckades med att ta bort användaren",
"ToastUserDeleteSuccess": "Användaren borttagen", "ToastUserDeleteSuccess": "Användaren borttagen",
"ToastUserPasswordChangeSuccess": "Lösenordet har ändrats", "ToastUserPasswordChangeSuccess": "Lösenordet har ändrats",
+2
View File
@@ -1086,6 +1086,8 @@
"ToastUnknownError": "Невідома помилка", "ToastUnknownError": "Невідома помилка",
"ToastUnlinkOpenIdFailed": "Не вдалося відв'язати користувача від OpenID", "ToastUnlinkOpenIdFailed": "Не вдалося відв'язати користувача від OpenID",
"ToastUnlinkOpenIdSuccess": "Користувача відв'язано від OpenID", "ToastUnlinkOpenIdSuccess": "Користувача відв'язано від OpenID",
"ToastUploaderFilepathExistsError": "Шлях до файлу \"{0}\" уже існує на сервері",
"ToastUploaderItemExistsInSubdirectoryError": "Елемент \"{0}\" використовує підкаталог шляху завантаження.",
"ToastUserDeleteFailed": "Не вдалося видалити користувача", "ToastUserDeleteFailed": "Не вдалося видалити користувача",
"ToastUserDeleteSuccess": "Користувача видалено", "ToastUserDeleteSuccess": "Користувача видалено",
"ToastUserPasswordChangeSuccess": "Пароль успішно змінено", "ToastUserPasswordChangeSuccess": "Пароль успішно змінено",
+5
View File
@@ -229,6 +229,7 @@
"LabelAddedDate": "已添加 {0}", "LabelAddedDate": "已添加 {0}",
"LabelAdminUsersOnly": "仅限管理员用户", "LabelAdminUsersOnly": "仅限管理员用户",
"LabelAll": "全部", "LabelAll": "全部",
"LabelAllEpisodesDownloaded": "所有剧集已下载",
"LabelAllUsers": "所有用户", "LabelAllUsers": "所有用户",
"LabelAllUsersExcludingGuests": "除访客外的所有用户", "LabelAllUsersExcludingGuests": "除访客外的所有用户",
"LabelAllUsersIncludingGuests": "包括访客的所有用户", "LabelAllUsersIncludingGuests": "包括访客的所有用户",
@@ -953,6 +954,7 @@
"ToastBackupRestoreFailed": "备份还原失败", "ToastBackupRestoreFailed": "备份还原失败",
"ToastBackupUploadFailed": "上传备份失败", "ToastBackupUploadFailed": "上传备份失败",
"ToastBackupUploadSuccess": "备份已上传", "ToastBackupUploadSuccess": "备份已上传",
"ToastBatchApplyDetailsToItemsSuccess": "应用于项目的详细信息",
"ToastBatchDeleteFailed": "批量删除失败", "ToastBatchDeleteFailed": "批量删除失败",
"ToastBatchDeleteSuccess": "批量删除成功", "ToastBatchDeleteSuccess": "批量删除成功",
"ToastBatchQuickMatchFailed": "批量快速匹配失败!", "ToastBatchQuickMatchFailed": "批量快速匹配失败!",
@@ -1065,6 +1067,7 @@
"ToastSelectAtLeastOneUser": "至少选择一位用户", "ToastSelectAtLeastOneUser": "至少选择一位用户",
"ToastSendEbookToDeviceFailed": "发送电子书到设备失败", "ToastSendEbookToDeviceFailed": "发送电子书到设备失败",
"ToastSendEbookToDeviceSuccess": "电子书已经发送到设备 \"{0}\"", "ToastSendEbookToDeviceSuccess": "电子书已经发送到设备 \"{0}\"",
"ToastSeriesSubmitFailedSameName": "无法添加两个同名系列",
"ToastSeriesUpdateFailed": "更新系列失败", "ToastSeriesUpdateFailed": "更新系列失败",
"ToastSeriesUpdateSuccess": "系列已更新", "ToastSeriesUpdateSuccess": "系列已更新",
"ToastServerSettingsUpdateSuccess": "服务器设置已更新", "ToastServerSettingsUpdateSuccess": "服务器设置已更新",
@@ -1083,6 +1086,8 @@
"ToastUnknownError": "未知错误", "ToastUnknownError": "未知错误",
"ToastUnlinkOpenIdFailed": "无法取消用户与 OpenID 的关联", "ToastUnlinkOpenIdFailed": "无法取消用户与 OpenID 的关联",
"ToastUnlinkOpenIdSuccess": "用户已取消与 OpenID 的关联", "ToastUnlinkOpenIdSuccess": "用户已取消与 OpenID 的关联",
"ToastUploaderFilepathExistsError": "文件路径 \"{0}\" 在服务器上已存在",
"ToastUploaderItemExistsInSubdirectoryError": "项目 \"{0}\" 正在使用上传路径的子目录。",
"ToastUserDeleteFailed": "删除用户失败", "ToastUserDeleteFailed": "删除用户失败",
"ToastUserDeleteSuccess": "用户已删除", "ToastUserDeleteSuccess": "用户已删除",
"ToastUserPasswordChangeSuccess": "密码修改成功", "ToastUserPasswordChangeSuccess": "密码修改成功",
+36
View File
@@ -84,6 +84,42 @@ class SocketAuthority {
} }
} }
/**
* Emits event with library item to all clients that can access the library item
* Note: Emits toOldJSONExpanded()
*
* @param {string} evt
* @param {import('./models/LibraryItem')} libraryItem
*/
libraryItemEmitter(evt, libraryItem) {
for (const socketId in this.clients) {
if (this.clients[socketId].user?.checkCanAccessLibraryItem(libraryItem)) {
this.clients[socketId].socket.emit(evt, libraryItem.toOldJSONExpanded())
}
}
}
/**
* Emits event with library items to all clients that can access the library items
* Note: Emits toOldJSONExpanded()
*
* @param {string} evt
* @param {import('./models/LibraryItem')[]} libraryItems
*/
libraryItemsEmitter(evt, libraryItems) {
for (const socketId in this.clients) {
if (this.clients[socketId].user) {
const libraryItemsAccessibleToUser = libraryItems.filter((li) => this.clients[socketId].user.checkCanAccessLibraryItem(li))
if (libraryItemsAccessibleToUser.length) {
this.clients[socketId].socket.emit(
evt,
libraryItemsAccessibleToUser.map((li) => li.toOldJSONExpanded())
)
}
}
}
}
/** /**
* Closes the Socket.IO server and disconnect all clients * Closes the Socket.IO server and disconnect all clients
* *
+2 -8
View File
@@ -152,10 +152,7 @@ class AuthorController {
for (const libraryItem of libraryItems) { for (const libraryItem of libraryItems) {
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
} }
SocketAuthority.emitter( SocketAuthority.libraryItemsEmitter('items_updated', libraryItems)
'items_updated',
libraryItems.map((li) => li.toOldJSONExpanded())
)
} }
// Remove old author // Remove old author
@@ -210,10 +207,7 @@ class AuthorController {
} }
if (libraryItems.length) { if (libraryItems.length) {
SocketAuthority.emitter( SocketAuthority.libraryItemsEmitter('items_updated', libraryItems)
'items_updated',
libraryItems.map((li) => li.toOldJSONExpanded())
)
} }
} else { } else {
numBooksForAuthor = await Database.bookAuthorModel.getCountForAuthor(req.author.id) numBooksForAuthor = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
+2 -8
View File
@@ -1188,10 +1188,7 @@ class LibraryController {
} }
if (itemsUpdated.length) { if (itemsUpdated.length) {
SocketAuthority.emitter( SocketAuthority.libraryItemsEmitter('items_updated', itemsUpdated)
'items_updated',
itemsUpdated.map((li) => li.toOldJSONExpanded())
)
} }
res.json({ res.json({
@@ -1232,10 +1229,7 @@ class LibraryController {
} }
if (itemsUpdated.length) { if (itemsUpdated.length) {
SocketAuthority.emitter( SocketAuthority.libraryItemsEmitter('items_updated', itemsUpdated)
'items_updated',
itemsUpdated.map((li) => li.toOldJSONExpanded())
)
} }
res.json({ res.json({
+9 -9
View File
@@ -253,7 +253,7 @@ class LibraryItemController {
} }
Logger.debug(`[LibraryItemController] Updated library item media ${req.libraryItem.media.title}`) Logger.debug(`[LibraryItemController] Updated library item media ${req.libraryItem.media.title}`)
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
} }
res.json({ res.json({
updated: hasUpdates, updated: hasUpdates,
@@ -300,7 +300,7 @@ class LibraryItemController {
req.libraryItem.changed('updatedAt', true) req.libraryItem.changed('updatedAt', true)
await req.libraryItem.save() await req.libraryItem.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
res.json({ res.json({
success: true, success: true,
cover: result.cover cover: result.cover
@@ -332,7 +332,7 @@ class LibraryItemController {
req.libraryItem.changed('updatedAt', true) req.libraryItem.changed('updatedAt', true)
await req.libraryItem.save() await req.libraryItem.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
} }
res.json({ res.json({
success: true, success: true,
@@ -358,7 +358,7 @@ class LibraryItemController {
await CacheManager.purgeCoverCache(req.libraryItem.id) await CacheManager.purgeCoverCache(req.libraryItem.id)
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
} }
res.sendStatus(200) res.sendStatus(200)
@@ -485,7 +485,7 @@ class LibraryItemController {
req.libraryItem.media.changed('audioFiles', true) req.libraryItem.media.changed('audioFiles', true)
await req.libraryItem.media.save() await req.libraryItem.media.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
res.json(req.libraryItem.toOldJSON()) res.json(req.libraryItem.toOldJSON())
} }
@@ -663,7 +663,7 @@ class LibraryItemController {
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
Logger.debug(`[LibraryItemController] Updated library item media "${libraryItem.media.title}"`) Logger.debug(`[LibraryItemController] Updated library item media "${libraryItem.media.title}"`)
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
itemsUpdated++ itemsUpdated++
} }
} }
@@ -894,7 +894,7 @@ class LibraryItemController {
await req.libraryItem.saveMetadataFile() await req.libraryItem.saveMetadataFile()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
} }
res.json({ res.json({
@@ -1005,7 +1005,7 @@ class LibraryItemController {
await req.libraryItem.save() await req.libraryItem.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
res.sendStatus(200) res.sendStatus(200)
} }
@@ -1153,7 +1153,7 @@ class LibraryItemController {
await req.libraryItem.save() await req.libraryItem.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
res.sendStatus(200) res.sendStatus(200)
} }
+4 -4
View File
@@ -343,7 +343,7 @@ class MiscController {
}) })
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
numItemsUpdated++ numItemsUpdated++
} }
} }
@@ -386,7 +386,7 @@ class MiscController {
}) })
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
numItemsUpdated++ numItemsUpdated++
} }
@@ -481,7 +481,7 @@ class MiscController {
}) })
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
numItemsUpdated++ numItemsUpdated++
} }
} }
@@ -524,7 +524,7 @@ class MiscController {
}) })
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
numItemsUpdated++ numItemsUpdated++
} }
+4 -4
View File
@@ -161,7 +161,7 @@ class PodcastController {
} }
} }
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_added', newLibraryItem)
res.json(newLibraryItem.toOldJSONExpanded()) res.json(newLibraryItem.toOldJSONExpanded())
@@ -379,7 +379,7 @@ class PodcastController {
const overrideDetails = req.query.override === '1' const overrideDetails = req.query.override === '1'
const episodesUpdated = await Scanner.quickMatchPodcastEpisodes(req.libraryItem, { overrideDetails }) const episodesUpdated = await Scanner.quickMatchPodcastEpisodes(req.libraryItem, { overrideDetails })
if (episodesUpdated) { if (episodesUpdated) {
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
} }
res.json({ res.json({
@@ -418,7 +418,7 @@ class PodcastController {
Logger.info(`[PodcastController] Updated episode "${episode.title}" keys`, episode.changed()) Logger.info(`[PodcastController] Updated episode "${episode.title}" keys`, episode.changed())
await episode.save() await episode.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
} else { } else {
Logger.info(`[PodcastController] No changes to episode "${episode.title}"`) Logger.info(`[PodcastController] No changes to episode "${episode.title}"`)
} }
@@ -504,7 +504,7 @@ class PodcastController {
req.libraryItem.media.numEpisodes = req.libraryItem.media.podcastEpisodes.length req.libraryItem.media.numEpisodes = req.libraryItem.media.podcastEpisodes.length
await req.libraryItem.media.save() await req.libraryItem.media.save()
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
res.json(req.libraryItem.toOldJSON()) res.json(req.libraryItem.toOldJSON())
} }
+12 -4
View File
@@ -211,6 +211,14 @@ class PodcastManager {
const podcastEpisode = await Database.podcastEpisodeModel.createFromRssPodcastEpisode(this.currentDownload.rssPodcastEpisode, libraryItem.media.id, audioFile) const podcastEpisode = await Database.podcastEpisodeModel.createFromRssPodcastEpisode(this.currentDownload.rssPodcastEpisode, libraryItem.media.id, audioFile)
libraryItem.libraryFiles.push(libraryFile.toJSON()) libraryItem.libraryFiles.push(libraryFile.toJSON())
// Re-calculating library item size because this wasnt being updated properly for podcasts in v2.20.0 and below
let libraryItemSize = 0
libraryItem.libraryFiles.forEach((lf) => {
if (lf.metadata.size && !isNaN(lf.metadata.size)) {
libraryItemSize += Number(lf.metadata.size)
}
})
libraryItem.size = libraryItemSize
libraryItem.changed('libraryFiles', true) libraryItem.changed('libraryFiles', true)
libraryItem.media.podcastEpisodes.push(podcastEpisode) libraryItem.media.podcastEpisodes.push(podcastEpisode)
@@ -246,7 +254,7 @@ class PodcastManager {
await libraryItem.media.save() await libraryItem.media.save()
} }
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
const podcastEpisodeExpanded = podcastEpisode.toOldJSONExpanded(libraryItem.id) const podcastEpisodeExpanded = podcastEpisode.toOldJSONExpanded(libraryItem.id)
podcastEpisodeExpanded.libraryItem = libraryItem.toOldJSONExpanded() podcastEpisodeExpanded.libraryItem = libraryItem.toOldJSONExpanded()
SocketAuthority.emitter('episode_added', podcastEpisodeExpanded) SocketAuthority.emitter('episode_added', podcastEpisodeExpanded)
@@ -359,7 +367,7 @@ class PodcastManager {
libraryItem.changed('updatedAt', true) libraryItem.changed('updatedAt', true)
await libraryItem.save() await libraryItem.save()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
return libraryItem.media.autoDownloadEpisodes return libraryItem.media.autoDownloadEpisodes
} }
@@ -417,7 +425,7 @@ class PodcastManager {
libraryItem.changed('updatedAt', true) libraryItem.changed('updatedAt', true)
await libraryItem.save() await libraryItem.save()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
return newEpisodes || [] return newEpisodes || []
} }
@@ -704,7 +712,7 @@ class PodcastManager {
} }
} }
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_added', newLibraryItem)
// Turn on podcast auto download cron if not already on // Turn on podcast auto download cron if not already on
if (newLibraryItem.media.autoDownloadEpisodes) { if (newLibraryItem.media.autoDownloadEpisodes) {
+4
View File
@@ -80,9 +80,13 @@ class PodcastEpisode extends Model {
if (rssPodcastEpisode.guid) { if (rssPodcastEpisode.guid) {
podcastEpisode.extraData.guid = rssPodcastEpisode.guid podcastEpisode.extraData.guid = rssPodcastEpisode.guid
} }
if (audioFile.chapters?.length) { if (audioFile.chapters?.length) {
podcastEpisode.chapters = audioFile.chapters.map((ch) => ({ ...ch })) podcastEpisode.chapters = audioFile.chapters.map((ch) => ({ ...ch }))
} else if (rssPodcastEpisode.chapters?.length) {
podcastEpisode.chapters = rssPodcastEpisode.chapters.map((ch) => ({ ...ch }))
} }
return this.create(podcastEpisode) return this.create(podcastEpisode)
} }
+4
View File
@@ -475,6 +475,8 @@ class BookScanner {
bookAuthors: [], bookAuthors: [],
bookSeries: [] bookSeries: []
} }
const createdAtTimestamp = new Date().getTime()
if (bookMetadata.authors.length) { if (bookMetadata.authors.length) {
for (const authorName of bookMetadata.authors) { for (const authorName of bookMetadata.authors) {
const matchingAuthorId = await Database.getAuthorIdByName(libraryItemData.libraryId, authorName) const matchingAuthorId = await Database.getAuthorIdByName(libraryItemData.libraryId, authorName)
@@ -485,6 +487,8 @@ class BookScanner {
} else { } else {
// New author // New author
bookObject.bookAuthors.push({ bookObject.bookAuthors.push({
// Ensures authors are in a set order
createdAt: createdAtTimestamp + bookObject.bookAuthors.length,
author: { author: {
libraryId: libraryItemData.libraryId, libraryId: libraryItemData.libraryId,
name: authorName, name: authorName,
+1 -1
View File
@@ -64,7 +64,7 @@ class LibraryItemScanner {
const { libraryItem: expandedLibraryItem, wasUpdated } = await this.rescanLibraryItemMedia(libraryItem, libraryItemScanData, library.settings, scanLogger) const { libraryItem: expandedLibraryItem, wasUpdated } = await this.rescanLibraryItemMedia(libraryItem, libraryItemScanData, library.settings, scanLogger)
if (libraryItemDataUpdated || wasUpdated) { if (libraryItemDataUpdated || wasUpdated) {
SocketAuthority.emitter('item_updated', expandedLibraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', expandedLibraryItem)
await this.checkAuthorsAndSeriesRemovedFromBooks(library.id, scanLogger) await this.checkAuthorsAndSeriesRemovedFromBooks(library.id, scanLogger)
+6 -22
View File
@@ -223,11 +223,7 @@ class LibraryScanner {
// Emit item updates in chunks of 10 to client // Emit item updates in chunks of 10 to client
if (libraryItemsUpdated.length === 10) { if (libraryItemsUpdated.length === 10) {
// TODO: Should only emit to clients where library item is accessible SocketAuthority.libraryItemsEmitter('items_updated', libraryItemsUpdated)
SocketAuthority.emitter(
'items_updated',
libraryItemsUpdated.map((li) => li.toOldJSONExpanded())
)
libraryItemsUpdated = [] libraryItemsUpdated = []
} }
@@ -235,11 +231,7 @@ class LibraryScanner {
} }
// Emit item updates to client // Emit item updates to client
if (libraryItemsUpdated.length) { if (libraryItemsUpdated.length) {
// TODO: Should only emit to clients where library item is accessible SocketAuthority.libraryItemsEmitter('items_updated', libraryItemsUpdated)
SocketAuthority.emitter(
'items_updated',
libraryItemsUpdated.map((li) => li.toOldJSONExpanded())
)
} }
// Authors and series that were removed from books should be removed if they are now empty // Authors and series that were removed from books should be removed if they are now empty
@@ -277,11 +269,7 @@ class LibraryScanner {
// Emit new items in chunks of 10 to client // Emit new items in chunks of 10 to client
if (newLibraryItems.length === 10) { if (newLibraryItems.length === 10) {
// TODO: Should only emit to clients where library item is accessible SocketAuthority.libraryItemsEmitter('items_added', newLibraryItems)
SocketAuthority.emitter(
'items_added',
newLibraryItems.map((li) => li.toOldJSONExpanded())
)
newLibraryItems = [] newLibraryItems = []
} }
@@ -289,11 +277,7 @@ class LibraryScanner {
} }
// Emit new items to client // Emit new items to client
if (newLibraryItems.length) { if (newLibraryItems.length) {
// TODO: Should only emit to clients where library item is accessible SocketAuthority.libraryItemsEmitter('items_added', newLibraryItems)
SocketAuthority.emitter(
'items_added',
newLibraryItems.map((li) => li.toOldJSONExpanded())
)
} }
} }
@@ -609,7 +593,7 @@ class LibraryScanner {
Logger.info(`[LibraryScanner] Scanning file update group and library item was deleted "${existingLibraryItem.media.title}" - marking as missing`) Logger.info(`[LibraryScanner] Scanning file update group and library item was deleted "${existingLibraryItem.media.title}" - marking as missing`)
existingLibraryItem.isMissing = true existingLibraryItem.isMissing = true
await existingLibraryItem.save() await existingLibraryItem.save()
SocketAuthority.emitter('item_updated', existingLibraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', existingLibraryItem)
itemGroupingResults[itemDir] = ScanResult.REMOVED itemGroupingResults[itemDir] = ScanResult.REMOVED
continue continue
@@ -643,7 +627,7 @@ class LibraryScanner {
const isSingleMediaItem = isSingleMediaFile(fileUpdateGroup, itemDir) const isSingleMediaItem = isSingleMediaFile(fileUpdateGroup, itemDir)
const newLibraryItem = await LibraryItemScanner.scanPotentialNewLibraryItem(fullPath, library, folder, isSingleMediaItem) const newLibraryItem = await LibraryItemScanner.scanPotentialNewLibraryItem(fullPath, library, folder, isSingleMediaItem)
if (newLibraryItem) { if (newLibraryItem) {
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_added', newLibraryItem)
} }
itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING
} }
+34 -10
View File
@@ -59,17 +59,36 @@ class PodcastScanner {
if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) { if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) {
// Filter out and destroy episodes that were removed // Filter out and destroy episodes that were removed
existingPodcastEpisodes = await Promise.all( const episodesToRemove = []
existingPodcastEpisodes.filter(async (ep) => { existingPodcastEpisodes = existingPodcastEpisodes.filter((ep) => {
if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) { if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) {
libraryScan.addLog(LogLevel.INFO, `Podcast episode "${ep.title}" audio file was removed`) episodesToRemove.push(ep)
// TODO: Should clean up other data linked to this episode return false
await ep.destroy() }
return false return true
})
if (episodesToRemove.length) {
// Remove episodes from playlists and media progress
const episodeIds = episodesToRemove.map((ep) => ep.id)
await Database.playlistModel.removeMediaItemsFromPlaylists(episodeIds)
const mediaProgressRemoved = await Database.mediaProgressModel.destroy({
where: {
mediaItemId: episodeIds
} }
return true
}) })
) if (mediaProgressRemoved) {
libraryScan.addLog(LogLevel.INFO, `Removed ${mediaProgressRemoved} media progress for episodes`)
}
// Remove episodes
await Promise.all(
episodesToRemove.map(async (ep) => {
await ep.destroy()
libraryScan.addLog(LogLevel.INFO, `Podcast episode "${ep.title}" audio file was removed`)
})
)
}
// Update audio files that were modified // Update audio files that were modified
if (libraryItemData.audioLibraryFilesModified.length) { if (libraryItemData.audioLibraryFilesModified.length) {
@@ -113,6 +132,9 @@ class PodcastScanner {
// Create new podcast episodes from new found audio files // Create new podcast episodes from new found audio files
for (const newAudioFile of newAudioFiles) { for (const newAudioFile of newAudioFiles) {
// Podcast episode audio files always have index 1
newAudioFile.index = 1
const newEpisode = { const newEpisode = {
title: newAudioFile.metaTags.tagTitle || newAudioFile.metadata.filenameNoExt, title: newAudioFile.metaTags.tagTitle || newAudioFile.metadata.filenameNoExt,
subtitle: null, subtitle: null,
@@ -136,7 +158,6 @@ class PodcastScanner {
} }
let hasMediaChanges = false let hasMediaChanges = false
if (existingPodcastEpisodes.length !== media.numEpisodes) { if (existingPodcastEpisodes.length !== media.numEpisodes) {
media.numEpisodes = existingPodcastEpisodes.length media.numEpisodes = existingPodcastEpisodes.length
hasMediaChanges = true hasMediaChanges = true
@@ -253,6 +274,9 @@ class PodcastScanner {
// Create podcast episodes from audio files // Create podcast episodes from audio files
for (const audioFile of scannedAudioFiles) { for (const audioFile of scannedAudioFiles) {
// Podcast episode audio files always have index 1
audioFile.index = 1
const newEpisode = { const newEpisode = {
title: audioFile.metaTags.tagTitle || audioFile.metadata.filenameNoExt, title: audioFile.metaTags.tagTitle || audioFile.metadata.filenameNoExt,
subtitle: null, subtitle: null,
+1 -1
View File
@@ -126,7 +126,7 @@ class Scanner {
await libraryItem.saveMetadataFile() await libraryItem.saveMetadataFile()
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded()) SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
} }
return { return {
+26
View File
@@ -243,3 +243,29 @@ module.exports.isValidASIN = (str) => {
if (!str || typeof str !== 'string') return false if (!str || typeof str !== 'string') return false
return /^[A-Z0-9]{10}$/.test(str) return /^[A-Z0-9]{10}$/.test(str)
} }
/**
* Convert timestamp to seconds
* @example "01:00:00" => 3600
* @example "01:00" => 60
* @example "01" => 1
*
* @param {string} timestamp
* @returns {number}
*/
module.exports.timestampToSeconds = (timestamp) => {
if (typeof timestamp !== 'string') {
return null
}
const parts = timestamp.split(':').map(Number)
if (parts.some(isNaN)) {
return null
} else if (parts.length === 1) {
return parts[0]
} else if (parts.length === 2) {
return parts[0] * 60 + parts[1]
} else if (parts.length === 3) {
return parts[0] * 3600 + parts[1] * 60 + parts[2]
}
return null
}
+53 -2
View File
@@ -1,9 +1,17 @@
const axios = require('axios') const axios = require('axios')
const ssrfFilter = require('ssrf-req-filter') const ssrfFilter = require('ssrf-req-filter')
const Logger = require('../Logger') const Logger = require('../Logger')
const { xmlToJSON, levenshteinDistance } = require('./index') const { xmlToJSON, levenshteinDistance, timestampToSeconds } = require('./index')
const htmlSanitizer = require('../utils/htmlSanitizer') const htmlSanitizer = require('../utils/htmlSanitizer')
/**
* @typedef RssPodcastChapter
* @property {number} id
* @property {string} title
* @property {number} start
* @property {number} end
*/
/** /**
* @typedef RssPodcastEpisode * @typedef RssPodcastEpisode
* @property {string} title * @property {string} title
@@ -22,6 +30,7 @@ const htmlSanitizer = require('../utils/htmlSanitizer')
* @property {string} guid * @property {string} guid
* @property {string} chaptersUrl * @property {string} chaptersUrl
* @property {string} chaptersType * @property {string} chaptersType
* @property {RssPodcastChapter[]} chapters
*/ */
/** /**
@@ -205,12 +214,53 @@ function extractEpisodeData(item) {
const cleanKey = key.split(':').pop() const cleanKey = key.split(':').pop()
episode[cleanKey] = extractFirstArrayItemString(item, key) episode[cleanKey] = extractFirstArrayItemString(item, key)
}) })
// 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) {
// Example chapter:
// {"id":0,"start":0,"end":43.004286,"title":"chapter 1"}
const cleanedChapters = item['psc:chapters'][0]['psc:chapter'].map((chapter, index) => {
if (!chapter['$']?.title || !chapter['$']?.start || typeof chapter['$']?.start !== 'string' || typeof chapter['$']?.title !== 'string') {
return null
}
const start = timestampToSeconds(chapter['$'].start)
if (start === null) {
return null
}
return {
id: index,
title: chapter['$'].title,
start
}
})
if (cleanedChapters.some((chapter) => !chapter)) {
Logger.warn(`[podcastUtils] Invalid chapter data for ${episode.enclosure.url}`)
} else {
episode.chapters = cleanedChapters.map((chapter, index) => {
const nextChapter = cleanedChapters[index + 1]
const end = nextChapter ? nextChapter.start : episodeDuration
return {
id: chapter.id,
title: chapter.title,
start: chapter.start,
end
}
})
}
}
return episode return episode
} }
function cleanEpisodeData(data) { function cleanEpisodeData(data) {
const pubJsDate = data.pubDate ? new Date(data.pubDate) : null const pubJsDate = data.pubDate ? new Date(data.pubDate) : null
const publishedAt = pubJsDate && !isNaN(pubJsDate) ? pubJsDate.valueOf() : null const publishedAt = pubJsDate && !isNaN(pubJsDate) ? pubJsDate.valueOf() : null
return { return {
title: data.title, title: data.title,
subtitle: data.subtitle || '', subtitle: data.subtitle || '',
@@ -227,7 +277,8 @@ function cleanEpisodeData(data) {
enclosure: data.enclosure, enclosure: data.enclosure,
guid: data.guid || null, guid: data.guid || null,
chaptersUrl: data.chaptersUrl || null, chaptersUrl: data.chaptersUrl || null,
chaptersType: data.chaptersType || null chaptersType: data.chaptersType || null,
chapters: data.chapters || []
} }
} }