mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-01 16:30:39 +02:00
Compare commits
284 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d80752cc9d | |||
| b764e848c7 | |||
| b037c4e8a3 | |||
| 6ba2360790 | |||
| ca4eb507f0 | |||
| 965b094470 | |||
| 0fe313ecfd | |||
| 35a2f8d44f | |||
| 50797879d5 | |||
| 9327331ee9 | |||
| 1c15007e32 | |||
| 2151ffa114 | |||
| 49ed208a54 | |||
| d668462529 | |||
| f2102a0a23 | |||
| 5efc6b82c1 | |||
| 1e4e9768da | |||
| cc5109c305 | |||
| e858d6a1d5 | |||
| b4cd5d2862 | |||
| 0633a44cfb | |||
| 5748126b83 | |||
| 06375743a3 | |||
| 2a41c186aa | |||
| af51b7254c | |||
| f63dfd769f | |||
| a1512f3174 | |||
| 245751e2ce | |||
| 37001d9425 | |||
| 9d1f51c6ba | |||
| cb234fe1fc | |||
| cb85e0255b | |||
| 61b4cfdab7 | |||
| d2c405c126 | |||
| cbca560f92 | |||
| 2d7b63b4cf | |||
| 217038b085 | |||
| 13dd4edd6a | |||
| a7288b4fbf | |||
| 3020e8104e | |||
| 8fdeeaaf38 | |||
| 42616b59de | |||
| bf16681bea | |||
| 027190b5a4 | |||
| 241c02be30 | |||
| dd87268848 | |||
| f2ac24e623 | |||
| 80e0cac474 | |||
| 37273dd51c | |||
| 926a85fff0 | |||
| 70273ba2ba | |||
| 158cdeed57 | |||
| ba9595a1be | |||
| 347e3ff674 | |||
| 2b6fb46cdb | |||
| 465775bd55 | |||
| 44e82fc454 | |||
| c4963d0de8 | |||
| ff81d70cb1 | |||
| d7a543e143 | |||
| cba547083d | |||
| 47b1d2a2c2 | |||
| abc378954c | |||
| fdf871af17 | |||
| 83fcb0efdc | |||
| 0c43f3d15a | |||
| 88e087d50f | |||
| a9fb6eb8bc | |||
| 08acfdcd24 | |||
| 576eb9106f | |||
| ddd2c0ae4e | |||
| e58d7db03b | |||
| 1cac42aec5 | |||
| f94449a659 | |||
| df6afc957f | |||
| 99ffd3050c | |||
| 69dd82d329 | |||
| 076f71d490 | |||
| 33eae1e03a | |||
| 8a20510cde | |||
| c33b470fca | |||
| 29db5f1990 | |||
| f98f78a5bd | |||
| d258b42e01 | |||
| a6da32430f | |||
| cfae607310 | |||
| 7653e72e88 | |||
| f38b6636e3 | |||
| e42db121ea | |||
| 0adceaa3f0 | |||
| e6db1495ab | |||
| e6e494a92c | |||
| 549f95b259 | |||
| d92626071e | |||
| a7ac82b023 | |||
| 64b78b5822 | |||
| 8ba17db877 | |||
| 6820d9ae4e | |||
| 0bdc2fb05e | |||
| cf5598aeb9 | |||
| 8cf3d648ea | |||
| 212311a980 | |||
| c9522dc25d | |||
| 37af753402 | |||
| d8c5627cf8 | |||
| 4f926b37db | |||
| fefc16bd13 | |||
| 1b1b71a9b6 | |||
| 086532652e | |||
| 4e8b4720a1 | |||
| 4a7ada28fb | |||
| 1710285674 | |||
| a6bb61d998 | |||
| 5ec05dfa84 | |||
| 83e854aa13 | |||
| 634f809159 | |||
| e5cf141834 | |||
| 8610b68d3f | |||
| f3e3bddc94 | |||
| 7ef3284cc5 | |||
| 3494586f77 | |||
| faaf99e6bb | |||
| 1078ba2111 | |||
| 2ad69300f5 | |||
| d2f3fa7fdf | |||
| 64fcb6270b | |||
| 562c30cff4 | |||
| 7108501d24 | |||
| 37eae3406c | |||
| 501dc938e6 | |||
| c5ecd35fe9 | |||
| 7cd8d7f44d | |||
| 567a9a4e58 | |||
| 58f4a0cfbb | |||
| e6c0b697aa | |||
| 35f60d699d | |||
| c219be0970 | |||
| c72ce843fa | |||
| c606059a3a | |||
| 049a8bdc6d | |||
| 9752f744ca | |||
| 4be6fb789c | |||
| afc56e5259 | |||
| d47f8521d5 | |||
| 7f853d426a | |||
| e9008c615d | |||
| 01f081ef5a | |||
| 7ee174e0d5 | |||
| 24439f86e0 | |||
| fbd3ce3b72 | |||
| 96f8b54b51 | |||
| 9c94a78e29 | |||
| a14e3dd137 | |||
| e37673bd67 | |||
| 6aa10d20a1 | |||
| 68a92acb7a | |||
| 8aa7cc9ca5 | |||
| e6c087c3bb | |||
| 39a2097152 | |||
| 6a8003917e | |||
| d5a17ddc8c | |||
| 48bbf0d649 | |||
| 0bc58c254f | |||
| b2d41f0583 | |||
| 0d31d20f0f | |||
| 5154e31c1c | |||
| c67b5e950e | |||
| 8a7b5cc87d | |||
| bb7938f66d | |||
| 5b22e945da | |||
| decde230aa | |||
| 1dec8ae122 | |||
| 8512d5e693 | |||
| bb481ccfb4 | |||
| 12bce48ef5 | |||
| 013c7c776e | |||
| 8f96d20a23 | |||
| 1a8811b69a | |||
| d796849d74 | |||
| 942bd0859f | |||
| 072028c740 | |||
| 0d08aecd56 | |||
| 66b290577c | |||
| 22ad16e11b | |||
| 2f49a08c7d | |||
| fcacda74cb | |||
| fa0c90de70 | |||
| c1197314ac | |||
| 0b31792660 | |||
| 8b95dd65d9 | |||
| 691ed88096 | |||
| 836d772cd4 | |||
| 999ada03d1 | |||
| b35fabbe55 | |||
| 8cd8a157a6 | |||
| 86aece6828 | |||
| f9edadbafd | |||
| 6a388cd4fe | |||
| 9d17e9ff48 | |||
| 662b7d01b8 | |||
| a19bc4b4e4 | |||
| a545aa5c39 | |||
| fa451f362b | |||
| 868659a2f1 | |||
| 8ae62da138 | |||
| bedba39af9 | |||
| 8493e56b11 | |||
| 21c77dccce | |||
| 55164803b0 | |||
| c163f84aec | |||
| 2711b989e1 | |||
| 5c49a8ce6a | |||
| 854f308eae | |||
| 16ba6b53ba | |||
| 0af29a378a | |||
| def34a860b | |||
| f8034e1b78 | |||
| 01fbea02f1 | |||
| 3d9af89e24 | |||
| d430d9f3ed | |||
| 0c24a1e626 | |||
| 1099dbe642 | |||
| 2df3277dcd | |||
| 6ae14213f5 | |||
| 61bd029303 | |||
| 5b09bd8242 | |||
| 703477b157 | |||
| 03ff5d8ae1 | |||
| 220f7ef7cd | |||
| 682a99dd43 | |||
| fac5de582d | |||
| 7cbf9de8ca | |||
| ce213c3d89 | |||
| 32cd0360e6 | |||
| 1ec23a5699 | |||
| 48330f6432 | |||
| 28358debbc | |||
| 54b7ed6117 | |||
| 0cfd2ee63b | |||
| 37a0990741 | |||
| 7a0cd1eb34 | |||
| ac3277da09 | |||
| 65d1e7be56 | |||
| 80685afa7e | |||
| f892453892 | |||
| 422bb8c31c | |||
| 6fb1202c1c | |||
| 4ddd2788f0 | |||
| 8a28029809 | |||
| 423a2129d1 | |||
| a338097514 | |||
| 84b67abb03 | |||
| 5ec8406653 | |||
| b3ce300d32 | |||
| 3f93b93d9e | |||
| e32c83db63 | |||
| 0344a63b48 | |||
| 24923c0009 | |||
| a9036c9738 | |||
| f9f7fbed33 | |||
| 53b5bee736 | |||
| d0b3726905 | |||
| 7a6864507e | |||
| e20563f2e1 | |||
| fea5f8f3d4 | |||
| f9bb529b85 | |||
| 60e348fcc1 | |||
| f194c5be0e | |||
| 47712e63f1 | |||
| 790c1fb34a | |||
| 9cca731acc | |||
| 48f232790a | |||
| 3c55aa5f43 | |||
| 8c1edb30a6 | |||
| 5e64af4448 | |||
| 9f60017cfe | |||
| b6a86d11d2 | |||
| db86bfd63d | |||
| 7ff72a8920 | |||
| 2c4f86d148 | |||
| 1a9f26e804 | |||
| 42f8194bde | |||
| 8634b7058c | |||
| fc276b330a |
@@ -70,6 +70,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
tags: ${{ github.event.inputs.tags || steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
/ffmpeg*
|
||||
/ffprobe*
|
||||
/unicode*
|
||||
/libnusqlite3*
|
||||
|
||||
sw.*
|
||||
.DS_STORE
|
||||
|
||||
+24
-9
@@ -11,20 +11,35 @@ FROM node:20-alpine
|
||||
ENV NODE_ENV=production
|
||||
|
||||
RUN apk update && \
|
||||
apk add --no-cache --update \
|
||||
curl \
|
||||
tzdata \
|
||||
ffmpeg \
|
||||
make \
|
||||
gcompat \
|
||||
python3 \
|
||||
g++ \
|
||||
tini
|
||||
apk add --no-cache --update \
|
||||
curl \
|
||||
tzdata \
|
||||
ffmpeg \
|
||||
make \
|
||||
python3 \
|
||||
g++ \
|
||||
tini \
|
||||
unzip
|
||||
|
||||
COPY --from=build /client/dist /client/dist
|
||||
COPY index.js package* /
|
||||
COPY server server
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
ENV NUSQLITE3_DIR="/usr/local/lib/nusqlite3"
|
||||
ENV NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so"
|
||||
|
||||
RUN case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") \
|
||||
curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-x64.zip" ;; \
|
||||
"linux/arm64") \
|
||||
curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-arm64.zip" ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
|
||||
esac && \
|
||||
unzip /tmp/library.zip -d $NUSQLITE3_DIR && \
|
||||
rm /tmp/library.zip
|
||||
|
||||
RUN npm ci --only=production
|
||||
|
||||
RUN apk del make python3 g++
|
||||
|
||||
@@ -264,7 +264,6 @@ export default {
|
||||
libraryItems.forEach((item) => {
|
||||
let subtitle = ''
|
||||
if (item.mediaType === 'book') subtitle = item.media.metadata.authors.map((au) => au.name).join(', ')
|
||||
else if (item.mediaType === 'music') subtitle = item.media.metadata.artists.join(', ')
|
||||
queueItems.push({
|
||||
libraryItemId: item.id,
|
||||
libraryId: item.libraryId,
|
||||
|
||||
@@ -347,6 +347,13 @@ export default {
|
||||
libraryItemsAdded(libraryItems) {
|
||||
console.log('libraryItems added', libraryItems)
|
||||
|
||||
// First items added to library
|
||||
const isThisLibrary = libraryItems.some((li) => li.libraryId === this.currentLibraryId)
|
||||
if (!this.shelves.length && !this.search && isThisLibrary) {
|
||||
this.fetchCategories()
|
||||
return
|
||||
}
|
||||
|
||||
const recentlyAddedShelf = this.shelves.find((shelf) => shelf.id === 'recently-added')
|
||||
if (!recentlyAddedShelf) return
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<div v-if="shelf.type === 'authors'" class="flex items-center">
|
||||
<template v-for="entity in shelf.entities">
|
||||
<cards-author-card :key="entity.id" :author="entity" @hook:updated="updatedBookCard" class="mx-2e" @edit="editAuthor" />
|
||||
<cards-author-card :key="entity.id" :authorMount="entity" @hook:updated="updatedBookCard" class="mx-2e" @edit="editAuthor" />
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="shelf.type === 'narrators'" class="flex items-center">
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<p v-if="isCollectionsPage" class="text-sm">{{ $strings.ButtonCollections }}</p>
|
||||
<span v-else class="material-symbols text-lg"></span>
|
||||
</nuxt-link>
|
||||
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/authors`" class="flex-grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
|
||||
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="flex-grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
|
||||
<p v-if="isAuthorsPage" class="text-sm">{{ $strings.ButtonAuthors }}</p>
|
||||
<svg v-else class="w-5 h-5" viewBox="0 0 24 24">
|
||||
<path
|
||||
@@ -50,7 +50,7 @@
|
||||
{{ seriesName }}
|
||||
</p>
|
||||
<div class="w-6 h-6 rounded-full bg-black bg-opacity-30 flex items-center justify-center ml-3">
|
||||
<span class="font-mono">{{ numShowing }}</span>
|
||||
<span class="font-mono">{{ $formatNumber(numShowing) }}</span>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
<ui-context-menu-dropdown v-if="!isBatchSelecting && seriesContextMenuItems.length" :items="seriesContextMenuItems" class="mx-px" @action="seriesContextMenuAction" />
|
||||
</template>
|
||||
<!-- library & collections page -->
|
||||
<template v-else-if="page !== 'search' && page !== 'podcast-search' && page !== 'recent-episodes' && !isHome">
|
||||
<p class="hidden md:block">{{ numShowing }} {{ entityName }}</p>
|
||||
<template v-else-if="page !== 'search' && page !== 'podcast-search' && page !== 'recent-episodes' && !isHome && !isAuthorsPage">
|
||||
<p class="hidden md:block">{{ $formatNumber(numShowing) }} {{ entityName }}</p>
|
||||
|
||||
<div class="flex-grow hidden sm:inline-block" />
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<controls-sort-select v-if="isSeriesPage && !isBatchSelecting" v-model="settings.seriesSortBy" :descending.sync="settings.seriesSortDesc" :items="seriesSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateSeriesSort" />
|
||||
|
||||
<!-- issues page remove all button -->
|
||||
<ui-btn v-if="isIssuesFilter && userCanDelete && !isBatchSelecting" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">{{ $strings.ButtonRemoveAll }} {{ numShowing }} {{ entityName }}</ui-btn>
|
||||
<ui-btn v-if="isIssuesFilter && userCanDelete && !isBatchSelecting" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">{{ $strings.ButtonRemoveAll }} {{ $formatNumber(numShowing) }} {{ entityName }}</ui-btn>
|
||||
|
||||
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
|
||||
</template>
|
||||
@@ -92,12 +92,14 @@
|
||||
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
|
||||
</template>
|
||||
<!-- authors page -->
|
||||
<template v-else-if="page === 'authors'">
|
||||
<div class="flex-grow" />
|
||||
<ui-btn v-if="userCanUpdate && authors?.length && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
|
||||
<template v-else-if="isAuthorsPage">
|
||||
<p class="hidden md:block">{{ $formatNumber(numShowing) }} {{ entityName }}</p>
|
||||
|
||||
<div class="flex-grow hidden sm:inline-block" />
|
||||
<ui-btn v-if="userCanUpdate && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
|
||||
|
||||
<!-- author sort select -->
|
||||
<controls-sort-select v-if="authors?.length" v-model="settings.authorSortBy" :descending.sync="settings.authorSortDesc" :items="authorSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateAuthorSort" />
|
||||
<controls-sort-select v-model="settings.authorSortBy" :descending.sync="settings.authorSortDesc" :items="authorSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateAuthorSort" />
|
||||
</template>
|
||||
<!-- home page -->
|
||||
<template v-else-if="isHome">
|
||||
@@ -117,11 +119,7 @@ export default {
|
||||
type: Object,
|
||||
default: () => null
|
||||
},
|
||||
searchQuery: String,
|
||||
authors: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
searchQuery: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -246,9 +244,6 @@ export default {
|
||||
isPodcastLibrary() {
|
||||
return this.currentLibraryMediaType === 'podcast'
|
||||
},
|
||||
isMusicLibrary() {
|
||||
return this.currentLibraryMediaType === 'music'
|
||||
},
|
||||
isLibraryPage() {
|
||||
return this.page === ''
|
||||
},
|
||||
@@ -271,7 +266,7 @@ export default {
|
||||
return this.$route.name === 'library-library-podcast-latest'
|
||||
},
|
||||
isAuthorsPage() {
|
||||
return this.$route.name === 'library-library-authors'
|
||||
return this.page === 'authors'
|
||||
},
|
||||
isAlbumsPage() {
|
||||
return this.page === 'albums'
|
||||
@@ -281,13 +276,13 @@ export default {
|
||||
},
|
||||
entityName() {
|
||||
if (this.isAlbumsPage) return 'Albums'
|
||||
if (this.isMusicLibrary) return 'Tracks'
|
||||
|
||||
if (this.isPodcastLibrary) return this.$strings.LabelPodcasts
|
||||
if (!this.page) return this.$strings.LabelBooks
|
||||
if (this.isSeriesPage) return this.$strings.LabelSeries
|
||||
if (this.isCollectionsPage) return this.$strings.LabelCollections
|
||||
if (this.isPlaylistsPage) return this.$strings.LabelPlaylists
|
||||
if (this.isAuthorsPage) return this.$strings.LabelAuthors
|
||||
return ''
|
||||
},
|
||||
seriesId() {
|
||||
@@ -477,42 +472,54 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to re-add series to continue listening', error)
|
||||
this.$toast.error(this.$strings.ToastItemUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processingSeries = false
|
||||
})
|
||||
},
|
||||
async fetchAllAuthors() {
|
||||
// fetch all authors from the server, in the order that they are currently displayed
|
||||
const response = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/authors?sort=${this.settings.authorSortBy}&desc=${this.settings.authorSortDesc}`)
|
||||
return response.authors
|
||||
},
|
||||
async matchAllAuthors() {
|
||||
this.processingAuthors = true
|
||||
|
||||
for (const author of this.authors) {
|
||||
const payload = {}
|
||||
if (author.asin) payload.asin = author.asin
|
||||
else payload.q = author.name
|
||||
try {
|
||||
const authors = await this.fetchAllAuthors()
|
||||
|
||||
payload.region = 'us'
|
||||
if (this.libraryProvider.startsWith('audible.')) {
|
||||
payload.region = this.libraryProvider.split('.').pop() || 'us'
|
||||
for (const author of authors) {
|
||||
const payload = {}
|
||||
if (author.asin) payload.asin = author.asin
|
||||
else payload.q = author.name
|
||||
|
||||
payload.region = 'us'
|
||||
if (this.libraryProvider.startsWith('audible.')) {
|
||||
payload.region = this.libraryProvider.split('.').pop() || 'us'
|
||||
}
|
||||
|
||||
this.$eventBus.$emit(`searching-author-${author.id}`, true)
|
||||
|
||||
var response = await this.$axios.$post(`/api/authors/${author.id}/match`, payload).catch((error) => {
|
||||
console.error('Failed', error)
|
||||
return null
|
||||
})
|
||||
if (!response) {
|
||||
console.error(`Author ${author.name} not found`)
|
||||
this.$toast.error(this.$getString('ToastAuthorNotFound', [author.name]))
|
||||
} else if (response.updated) {
|
||||
if (response.author.imagePath) console.log(`Author ${response.author.name} was updated`)
|
||||
else console.log(`Author ${response.author.name} was updated (no image found)`)
|
||||
} else {
|
||||
console.log(`No updates were made for Author ${response.author.name}`)
|
||||
}
|
||||
|
||||
this.$eventBus.$emit(`searching-author-${author.id}`, false)
|
||||
}
|
||||
|
||||
this.$eventBus.$emit(`searching-author-${author.id}`, true)
|
||||
|
||||
var response = await this.$axios.$post(`/api/authors/${author.id}/match`, payload).catch((error) => {
|
||||
console.error('Failed', error)
|
||||
return null
|
||||
})
|
||||
if (!response) {
|
||||
console.error(`Author ${author.name} not found`)
|
||||
this.$toast.error(this.$getString('ToastAuthorNotFound', [author.name]))
|
||||
} else if (response.updated) {
|
||||
if (response.author.imagePath) console.log(`Author ${response.author.name} was updated`)
|
||||
else console.log(`Author ${response.author.name} was updated (no image found)`)
|
||||
} else {
|
||||
console.log(`No updates were made for Author ${response.author.name}`)
|
||||
}
|
||||
|
||||
this.$eventBus.$emit(`searching-author-${author.id}`, false)
|
||||
} catch (error) {
|
||||
console.error('Failed to match all authors', error)
|
||||
this.$toast.error(this.$strings.ToastMatchAllAuthorsFailed)
|
||||
}
|
||||
this.processingAuthors = false
|
||||
},
|
||||
|
||||
@@ -91,6 +91,7 @@ export default {
|
||||
if (this.page === 'series') return this.$strings.MessageBookshelfNoSeries
|
||||
if (this.page === 'collections') return this.$strings.MessageBookshelfNoCollections
|
||||
if (this.page === 'playlists') return this.$strings.MessageNoUserPlaylists
|
||||
if (this.page === 'authors') return this.$strings.MessageNoAuthors
|
||||
if (this.hasFilter) {
|
||||
if (this.filterName === 'Issues') return this.$strings.MessageNoIssues
|
||||
else if (this.filterName === 'Feed-open') return this.$strings.MessageBookshelfNoRSSFeeds
|
||||
@@ -111,6 +112,12 @@ export default {
|
||||
seriesFilterBy() {
|
||||
return this.$store.getters['user/getUserSetting']('seriesFilterBy')
|
||||
},
|
||||
authorSortBy() {
|
||||
return this.$store.getters['user/getUserSetting']('authorSortBy')
|
||||
},
|
||||
authorSortDesc() {
|
||||
return !!this.$store.getters['user/getUserSetting']('authorSortDesc')
|
||||
},
|
||||
orderBy() {
|
||||
return this.$store.getters['user/getUserSetting']('orderBy')
|
||||
},
|
||||
@@ -217,6 +224,8 @@ export default {
|
||||
this.$store.commit('globals/setEditCollection', entity)
|
||||
} else if (this.entityName === 'playlists') {
|
||||
this.$store.commit('globals/setEditPlaylist', entity)
|
||||
} else if (this.entityName === 'authors') {
|
||||
this.$store.commit('globals/showEditAuthorModal', entity)
|
||||
}
|
||||
},
|
||||
clearSelectedEntities() {
|
||||
@@ -457,6 +466,9 @@ export default {
|
||||
if (this.collapseBookSeries) {
|
||||
searchParams.set('collapseseries', 1)
|
||||
}
|
||||
} else if (this.page === 'authors') {
|
||||
searchParams.set('sort', this.authorSortBy)
|
||||
searchParams.set('desc', this.authorSortDesc ? 1 : 0)
|
||||
} else {
|
||||
if (this.filterBy && this.filterBy !== 'all') {
|
||||
searchParams.set('filter', this.filterBy)
|
||||
@@ -601,6 +613,34 @@ export default {
|
||||
this.executeRebuild()
|
||||
}
|
||||
},
|
||||
authorAdded(author) {
|
||||
if (this.entityName !== 'authors') return
|
||||
console.log(`[LazyBookshelf] authorAdded ${author.id}`, author)
|
||||
this.resetEntities()
|
||||
},
|
||||
authorUpdated(author) {
|
||||
if (this.entityName !== 'authors') return
|
||||
console.log(`[LazyBookshelf] authorUpdated ${author.id}`, author)
|
||||
const indexOf = this.entities.findIndex((ent) => ent && ent.id === author.id)
|
||||
if (indexOf >= 0) {
|
||||
this.entities[indexOf] = author
|
||||
if (this.entityComponentRefs[indexOf]) {
|
||||
this.entityComponentRefs[indexOf].setEntity(author)
|
||||
}
|
||||
}
|
||||
},
|
||||
authorRemoved(author) {
|
||||
if (this.entityName !== 'authors') return
|
||||
console.log(`[LazyBookshelf] authorRemoved ${author.id}`, author)
|
||||
const indexOf = this.entities.findIndex((ent) => ent && ent.id === author.id)
|
||||
if (indexOf >= 0) {
|
||||
this.entities = this.entities.filter((ent) => ent.id !== author.id)
|
||||
this.totalEntities--
|
||||
this.$eventBus.$emit('bookshelf-total-entities', this.totalEntities)
|
||||
this.executeRebuild()
|
||||
}
|
||||
},
|
||||
|
||||
shareOpen(mediaItemShare) {
|
||||
if (this.entityName === 'items' || this.entityName === 'series-books') {
|
||||
var indexOf = this.entities.findIndex((ent) => ent?.media?.id === mediaItemShare.mediaItemId)
|
||||
@@ -727,6 +767,9 @@ export default {
|
||||
this.$root.socket.on('playlist_added', this.playlistAdded)
|
||||
this.$root.socket.on('playlist_updated', this.playlistUpdated)
|
||||
this.$root.socket.on('playlist_removed', this.playlistRemoved)
|
||||
this.$root.socket.on('author_added', this.authorAdded)
|
||||
this.$root.socket.on('author_updated', this.authorUpdated)
|
||||
this.$root.socket.on('author_removed', this.authorRemoved)
|
||||
this.$root.socket.on('share_open', this.shareOpen)
|
||||
this.$root.socket.on('share_closed', this.shareClosed)
|
||||
} else {
|
||||
@@ -756,6 +799,9 @@ export default {
|
||||
this.$root.socket.off('playlist_added', this.playlistAdded)
|
||||
this.$root.socket.off('playlist_updated', this.playlistUpdated)
|
||||
this.$root.socket.off('playlist_removed', this.playlistRemoved)
|
||||
this.$root.socket.off('author_added', this.authorAdded)
|
||||
this.$root.socket.off('author_updated', this.authorUpdated)
|
||||
this.$root.socket.off('author_removed', this.authorRemoved)
|
||||
this.$root.socket.off('share_open', this.shareOpen)
|
||||
this.$root.socket.off('share_closed', this.shareClosed)
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<div v-if="streamLibraryItem" id="mediaPlayerContainer" class="w-full fixed bottom-0 left-0 right-0 h-48 lg:h-40 z-50 bg-primary px-2 lg:px-4 pb-1 lg:pb-4 pt-2">
|
||||
<div id="videoDock" />
|
||||
<div class="absolute left-2 top-2 lg:left-4 cursor-pointer">
|
||||
<covers-book-cover expand-on-click :library-item="streamLibraryItem" :width="bookCoverWidth" :book-cover-aspect-ratio="coverAspectRatio" />
|
||||
</div>
|
||||
<div class="flex items-start mb-6 lg:mb-0" :class="playerHandler.isVideo ? 'ml-4 pl-96' : isSquareCover ? 'pl-18 sm:pl-24' : 'pl-12 sm:pl-16'">
|
||||
<div class="flex items-start mb-6 lg:mb-0" :class="isSquareCover ? 'pl-18 sm:pl-24' : 'pl-12 sm:pl-16'">
|
||||
<div class="min-w-0 w-full">
|
||||
<div class="flex items-center">
|
||||
<nuxt-link :to="`/item/${streamLibraryItem.id}`" class="hover:underline cursor-pointer text-sm sm:text-lg block truncate">
|
||||
@@ -12,10 +11,9 @@
|
||||
</nuxt-link>
|
||||
<widgets-explicit-indicator v-if="isExplicit" />
|
||||
</div>
|
||||
<div v-if="!playerHandler.isVideo" class="text-gray-400 flex items-center w-1/2 sm:w-4/5 lg:w-2/5">
|
||||
<div class="text-gray-400 flex items-center w-1/2 sm:w-4/5 lg:w-2/5">
|
||||
<span class="material-symbols text-sm">person</span>
|
||||
<div v-if="podcastAuthor" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ podcastAuthor }}</div>
|
||||
<div v-else-if="musicArtists" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ musicArtists }}</div>
|
||||
<div v-else-if="authors.length" class="pl-1 sm:pl-1.5 text-xs sm:text-base truncate">
|
||||
<nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link>
|
||||
</div>
|
||||
@@ -140,9 +138,6 @@ export default {
|
||||
isPodcast() {
|
||||
return this.streamLibraryItem?.mediaType === 'podcast'
|
||||
},
|
||||
isMusic() {
|
||||
return this.streamLibraryItem?.mediaType === 'music'
|
||||
},
|
||||
isExplicit() {
|
||||
return !!this.mediaMetadata.explicit
|
||||
},
|
||||
@@ -172,11 +167,7 @@ export default {
|
||||
},
|
||||
podcastAuthor() {
|
||||
if (!this.isPodcast) return null
|
||||
return this.mediaMetadata.author || 'Unknown'
|
||||
},
|
||||
musicArtists() {
|
||||
if (!this.isMusic) return null
|
||||
return this.mediaMetadata.artists.join(', ')
|
||||
return this.mediaMetadata.author || this.$strings.LabelUnknown
|
||||
},
|
||||
hasNextItemInQueue() {
|
||||
return this.currentPlayerQueueIndex < this.playerQueueItems.length - 1
|
||||
@@ -260,7 +251,7 @@ export default {
|
||||
sleepTimerEnd() {
|
||||
this.clearSleepTimer()
|
||||
this.playerHandler.pause()
|
||||
this.$toast.info('Sleep Timer Done.. zZzzZz')
|
||||
this.$toast.info(this.$strings.ToastSleepTimerDone)
|
||||
},
|
||||
cancelSleepTimer() {
|
||||
this.showSleepTimerModal = false
|
||||
@@ -534,7 +525,7 @@ export default {
|
||||
},
|
||||
showFailedProgressSyncs() {
|
||||
if (!isNaN(this.syncFailedToast)) this.$toast.dismiss(this.syncFailedToast)
|
||||
this.syncFailedToast = this.$toast('Progress is not being synced. Restart playback', { timeout: false, type: 'error' })
|
||||
this.syncFailedToast = this.$toast(this.$strings.ToastProgressIsNotBeingSynced, { timeout: false, type: 'error' })
|
||||
},
|
||||
sessionClosedEvent(sessionId) {
|
||||
if (this.playerHandler.currentSessionId === sessionId) {
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<div v-show="isPlaylistsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/authors`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<svg class="w-6 h-6" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
@@ -95,14 +95,6 @@
|
||||
<div v-show="isPodcastSearchPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link v-if="isMusicLibrary" :to="`/library/${currentLibraryId}/bookshelf/albums`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isMusicAlbumsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<span class="material-symbols text-xl">album</span>
|
||||
|
||||
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">Albums</p>
|
||||
|
||||
<div v-show="isMusicAlbumsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/download-queue`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPodcastDownloadQueuePage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<span class="material-symbols text-2xl"></span>
|
||||
|
||||
@@ -172,9 +164,6 @@ export default {
|
||||
isPodcastLibrary() {
|
||||
return this.currentLibraryMediaType === 'podcast'
|
||||
},
|
||||
isMusicLibrary() {
|
||||
return this.currentLibraryMediaType === 'music'
|
||||
},
|
||||
isPodcastDownloadQueuePage() {
|
||||
return this.$route.name === 'library-library-podcast-download-queue'
|
||||
},
|
||||
@@ -184,9 +173,6 @@ export default {
|
||||
isPodcastLatestPage() {
|
||||
return this.$route.name === 'library-library-podcast-latest'
|
||||
},
|
||||
isMusicAlbumsPage() {
|
||||
return this.paramId === 'albums'
|
||||
},
|
||||
homePage() {
|
||||
return this.$route.name === 'library-library'
|
||||
},
|
||||
@@ -194,7 +180,7 @@ export default {
|
||||
return this.$route.name === 'library-library-series-id' || this.paramId === 'series'
|
||||
},
|
||||
isAuthorsPage() {
|
||||
return this.$route.name === 'library-library-authors'
|
||||
return this.libraryBookshelfPage && this.paramId === 'authors'
|
||||
},
|
||||
isNarratorsPage() {
|
||||
return this.$route.name === 'library-library-narrators'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div :style="{ minWidth: cardWidth + 'px', maxWidth: cardWidth + 'px' }">
|
||||
<nuxt-link :to="`/author/${author.id}`">
|
||||
<div class="pb-3e" :style="{ minWidth: cardWidth + 'px', maxWidth: cardWidth + 'px' }">
|
||||
<nuxt-link :to="`/author/${author?.id}`">
|
||||
<div cy-id="card" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||
<div cy-id="imageArea" :style="{ height: cardHeight + 'px' }" class="bg-primary box-shadow-book rounded-md relative overflow-hidden">
|
||||
<!-- Image or placeholder -->
|
||||
@@ -40,7 +40,7 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
author: {
|
||||
authorMount: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
@@ -57,7 +57,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
searching: false,
|
||||
isHovering: false
|
||||
isHovering: false,
|
||||
author: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -68,34 +69,37 @@ export default {
|
||||
return this.height * this.sizeMultiplier
|
||||
},
|
||||
userToken() {
|
||||
return this.$store.getters['user/getToken']
|
||||
return this.store.getters['user/getToken']
|
||||
},
|
||||
_author() {
|
||||
return this.author || {}
|
||||
},
|
||||
authorId() {
|
||||
return this._author.id
|
||||
return this._author?.id || ''
|
||||
},
|
||||
name() {
|
||||
return this._author.name || ''
|
||||
return this._author?.name || ''
|
||||
},
|
||||
asin() {
|
||||
return this._author.asin || ''
|
||||
return this._author?.asin || ''
|
||||
},
|
||||
numBooks() {
|
||||
return this._author.numBooks || 0
|
||||
return this._author?.numBooks || 0
|
||||
},
|
||||
store() {
|
||||
return this.$store || this.$nuxt.$store
|
||||
},
|
||||
userCanUpdate() {
|
||||
return this.$store.getters['user/getUserCanUpdate']
|
||||
return this.store.getters['user/getUserCanUpdate']
|
||||
},
|
||||
currentLibraryId() {
|
||||
return this.$store.state.libraries.currentLibraryId
|
||||
return this.store.state.libraries.currentLibraryId
|
||||
},
|
||||
libraryProvider() {
|
||||
return this.$store.getters['libraries/getLibraryProvider'](this.currentLibraryId) || 'google'
|
||||
return this.store.getters['libraries/getLibraryProvider'](this.currentLibraryId) || 'google'
|
||||
},
|
||||
sizeMultiplier() {
|
||||
return this.$store.getters['user/getSizeMultiplier']
|
||||
return this.store.getters['user/getSizeMultiplier']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -121,24 +125,54 @@ export default {
|
||||
return null
|
||||
})
|
||||
if (!response) {
|
||||
this.$toast.error(`Author ${this.name} not found`)
|
||||
this.$toast.error(this.$getString('ToastAuthorNotFound', [this.name]))
|
||||
} else if (response.updated) {
|
||||
if (response.author.imagePath) this.$toast.success(`Author ${response.author.name} was updated`)
|
||||
else this.$toast.success(`Author ${response.author.name} was updated (no image found)`)
|
||||
if (response.author.imagePath) {
|
||||
this.$toast.success(this.$strings.ToastAuthorUpdateSuccess)
|
||||
} else {
|
||||
this.$toast.success(this.$strings.ToastAuthorUpdateSuccessNoImageFound)
|
||||
}
|
||||
} else {
|
||||
this.$toast.info(`No updates were made for Author ${response.author.name}`)
|
||||
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
|
||||
}
|
||||
this.searching = false
|
||||
},
|
||||
setSearching(isSearching) {
|
||||
this.searching = isSearching
|
||||
}
|
||||
},
|
||||
setEntity(author) {
|
||||
this.removeListeners()
|
||||
this.author = author
|
||||
this.addListeners()
|
||||
},
|
||||
addListeners() {
|
||||
if (this.author) {
|
||||
this.$eventBus.$on(`searching-author-${this.authorId}`, this.setSearching)
|
||||
}
|
||||
},
|
||||
removeListeners() {
|
||||
if (this.author) {
|
||||
this.$eventBus.$off(`searching-author-${this.authorId}`, this.setSearching)
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
// destroy the vue listeners, etc
|
||||
this.$destroy()
|
||||
|
||||
// remove the element from the DOM
|
||||
if (this.$el && this.$el.parentNode) {
|
||||
this.$el.parentNode.removeChild(this.$el)
|
||||
} else if (this.$el && this.$el.remove) {
|
||||
this.$el.remove()
|
||||
}
|
||||
},
|
||||
setSelectionMode(val) {}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on(`searching-author-${this.authorId}`, this.setSearching)
|
||||
if (this.authorMount) this.setEntity(this.authorMount)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventBus.$off(`searching-author-${this.authorId}`, this.setSearching)
|
||||
this.removeListeners()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<p class="truncate text-sm">{{ title }}</p>
|
||||
|
||||
<p class="truncate text-xs text-gray-300">{{ description }}</p>
|
||||
<p v-if="specialMessage" class="truncate text-xs text-gray-300">{{ specialMessage }}</p>
|
||||
|
||||
<p v-if="isFailed && failedMessage" class="text-xs truncate text-red-500">{{ failedMessage }}</p>
|
||||
<p v-else-if="!isFinished && cancelingScan" class="text-xs truncate">Canceling...</p>
|
||||
@@ -26,7 +27,16 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cancelingScan: false
|
||||
cancelingScan: false,
|
||||
specialMessage: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
task: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.initTask()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -34,14 +44,17 @@ export default {
|
||||
return this.$store.getters['user/getIsAdminOrUp']
|
||||
},
|
||||
title() {
|
||||
if (this.task.titleKey && this.$strings[this.task.titleKey]) {
|
||||
return this.$getString(this.task.titleKey, this.task.titleSubs)
|
||||
}
|
||||
return this.task.title || 'No Title'
|
||||
},
|
||||
description() {
|
||||
if (this.task.descriptionKey && this.$strings[this.task.descriptionKey]) {
|
||||
return this.$getString(this.task.descriptionKey, this.task.descriptionSubs)
|
||||
}
|
||||
return this.task.description || ''
|
||||
},
|
||||
details() {
|
||||
return this.task.details || 'Unknown'
|
||||
},
|
||||
isFinished() {
|
||||
return !!this.task.isFinished
|
||||
},
|
||||
@@ -52,6 +65,9 @@ export default {
|
||||
return this.isFinished && !this.isFailed
|
||||
},
|
||||
failedMessage() {
|
||||
if (this.task.errorKey && this.$strings[this.task.errorKey]) {
|
||||
return this.$getString(this.task.errorKey, this.task.errorSubs)
|
||||
}
|
||||
return this.task.error || ''
|
||||
},
|
||||
action() {
|
||||
@@ -87,6 +103,21 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initTask() {
|
||||
// special message for library scan tasks
|
||||
if (this.task?.data?.scanResults) {
|
||||
const scanResults = this.task.data.scanResults
|
||||
const strs = []
|
||||
if (scanResults.added) strs.push(this.$getString('MessageTaskScanItemsAdded', [scanResults.added]))
|
||||
if (scanResults.updated) strs.push(this.$getString('MessageTaskScanItemsUpdated', [scanResults.updated]))
|
||||
if (scanResults.missing) strs.push(this.$getString('MessageTaskScanItemsMissing', [scanResults.missing]))
|
||||
const changesDetected = strs.length > 0 ? strs.join(', ') : this.$strings.MessageTaskScanNoChangesNeeded
|
||||
const timeElapsed = scanResults.elapsed ? ` (${this.$elapsedPretty(scanResults.elapsed / 1000, false, true)})` : ''
|
||||
this.specialMessage = `${changesDetected}${timeElapsed}`
|
||||
} else {
|
||||
this.specialMessage = ''
|
||||
}
|
||||
},
|
||||
cancelScan() {
|
||||
const libraryId = this.task?.data?.libraryId
|
||||
if (!libraryId) {
|
||||
|
||||
@@ -226,9 +226,6 @@ export default {
|
||||
isPodcast() {
|
||||
return this.mediaType === 'podcast' || this.store.getters['libraries/getCurrentLibraryMediaType'] === 'podcast'
|
||||
},
|
||||
isMusic() {
|
||||
return this.mediaType === 'music'
|
||||
},
|
||||
isExplicit() {
|
||||
return this.mediaMetadata.explicit || false
|
||||
},
|
||||
@@ -328,7 +325,7 @@ export default {
|
||||
},
|
||||
displaySubtitle() {
|
||||
if (!this.libraryItem) return '\u00A0'
|
||||
if (this.collapsedSeries) return this.collapsedSeries.numBooks === 1 ? '1 book' : `${this.collapsedSeries.numBooks} books`
|
||||
if (this.collapsedSeries) return `${this.collapsedSeries.numBooks} ${this.$strings.LabelBooks}`
|
||||
if (this.mediaMetadata.subtitle) return this.mediaMetadata.subtitle
|
||||
if (this.mediaMetadata.seriesName) return this.mediaMetadata.seriesName
|
||||
return ''
|
||||
@@ -336,7 +333,6 @@ export default {
|
||||
displayLineTwo() {
|
||||
if (this.recentEpisode) return this.title
|
||||
if (this.isPodcast) return this.author
|
||||
if (this.isMusic) return this.artist
|
||||
if (this.collapsedSeries) return ''
|
||||
if (this.isAuthorBookshelfView) {
|
||||
return this.mediaMetadata.publishedYear || ''
|
||||
@@ -364,7 +360,6 @@ export default {
|
||||
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId, this.recentEpisode.id)
|
||||
},
|
||||
userProgress() {
|
||||
if (this.isMusic) return null
|
||||
if (this.episodeProgress) return this.episodeProgress
|
||||
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId)
|
||||
},
|
||||
@@ -420,7 +415,7 @@ export default {
|
||||
return !this.isSelectionMode && !this.showPlayButton && this.ebookFormat
|
||||
},
|
||||
showPlayButton() {
|
||||
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && !this.isStreaming && (this.numTracks || this.recentEpisode || this.isMusic)
|
||||
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && !this.isStreaming && (this.numTracks || this.recentEpisode)
|
||||
},
|
||||
showSmallEBookIcon() {
|
||||
return !this.isSelectionMode && this.ebookFormat
|
||||
@@ -464,8 +459,6 @@ export default {
|
||||
return this.store.getters['user/getIsAdminOrUp']
|
||||
},
|
||||
moreMenuItems() {
|
||||
if (this.isMusic) return []
|
||||
|
||||
if (this.recentEpisode) {
|
||||
const items = [
|
||||
{
|
||||
@@ -823,7 +816,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to remove series from home', error)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdateUser)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
@@ -841,7 +834,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to hide item from home', error)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdateUser)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update notification', error)
|
||||
this.$toast.error(this.$strings.ToastNotificationUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.enabling = false
|
||||
|
||||
@@ -27,38 +27,6 @@
|
||||
<nuxt-link :to="`/library/${libraryId}/bookshelf?filter=publishers.${$encode(publisher)}`" class="hover:underline">{{ publisher }}</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="musicAlbum" class="flex py-0.5">
|
||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||
<span class="text-white text-opacity-60 uppercase text-sm">Album</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ musicAlbum }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="musicAlbumArtist" class="flex py-0.5">
|
||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||
<span class="text-white text-opacity-60 uppercase text-sm">Album Artist</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ musicAlbumArtist }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="musicTrackPretty" class="flex py-0.5">
|
||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||
<span class="text-white text-opacity-60 uppercase text-sm">Track</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ musicTrackPretty }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="musicDiscPretty" class="flex py-0.5">
|
||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||
<span class="text-white text-opacity-60 uppercase text-sm">Disc</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ musicDiscPretty }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="podcastType" class="flex py-0.5">
|
||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span>
|
||||
@@ -97,7 +65,7 @@
|
||||
<nuxt-link :to="`/library/${libraryId}/bookshelf?filter=languages.${$encode(language)}`" class="hover:underline">{{ language }}</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tracks.length || audioFile || (isPodcast && totalPodcastDuration)" class="flex py-0.5">
|
||||
<div v-if="tracks.length || (isPodcast && totalPodcastDuration)" class="flex py-0.5">
|
||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
||||
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
|
||||
</div>
|
||||
@@ -134,10 +102,6 @@ export default {
|
||||
isPodcast() {
|
||||
return this.libraryItem.mediaType === 'podcast'
|
||||
},
|
||||
audioFile() {
|
||||
// Music track
|
||||
return this.media.audioFile
|
||||
},
|
||||
media() {
|
||||
return this.libraryItem.media || {}
|
||||
},
|
||||
@@ -168,25 +132,6 @@ export default {
|
||||
publisher() {
|
||||
return this.mediaMetadata.publisher || ''
|
||||
},
|
||||
musicArtists() {
|
||||
return this.mediaMetadata.artists || []
|
||||
},
|
||||
musicAlbum() {
|
||||
return this.mediaMetadata.album || ''
|
||||
},
|
||||
musicAlbumArtist() {
|
||||
return this.mediaMetadata.albumArtist || ''
|
||||
},
|
||||
musicTrackPretty() {
|
||||
if (!this.mediaMetadata.trackNumber) return null
|
||||
if (!this.mediaMetadata.trackTotal) return this.mediaMetadata.trackNumber
|
||||
return `${this.mediaMetadata.trackNumber} / ${this.mediaMetadata.trackTotal}`
|
||||
},
|
||||
musicDiscPretty() {
|
||||
if (!this.mediaMetadata.discNumber) return null
|
||||
if (!this.mediaMetadata.discTotal) return this.mediaMetadata.discNumber
|
||||
return `${this.mediaMetadata.discNumber} / ${this.mediaMetadata.discTotal}`
|
||||
},
|
||||
narrators() {
|
||||
return this.mediaMetadata.narrators || []
|
||||
},
|
||||
@@ -220,4 +165,4 @@ export default {
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -98,9 +98,6 @@ export default {
|
||||
isPodcast() {
|
||||
return this.libraryMediaType === 'podcast'
|
||||
},
|
||||
isMusic() {
|
||||
return this.libraryMediaType === 'music'
|
||||
},
|
||||
seriesItems() {
|
||||
return [
|
||||
{
|
||||
@@ -192,6 +189,12 @@ export default {
|
||||
value: 'publishers',
|
||||
sublist: true
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelPublishedDecade,
|
||||
textPlural: this.$strings.LabelPublishedDecades,
|
||||
value: 'publishedDecades',
|
||||
sublist: true
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelLanguage,
|
||||
textPlural: this.$strings.LabelLanguages,
|
||||
@@ -274,35 +277,9 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
musicItems() {
|
||||
return [
|
||||
{
|
||||
text: this.$strings.LabelAll,
|
||||
value: 'all'
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelGenre,
|
||||
textPlural: this.$strings.LabelGenres,
|
||||
value: 'genres',
|
||||
sublist: true
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelTag,
|
||||
textPlural: this.$strings.LabelTags,
|
||||
value: 'tags',
|
||||
sublist: true
|
||||
},
|
||||
{
|
||||
text: this.$strings.ButtonIssues,
|
||||
value: 'issues',
|
||||
sublist: false
|
||||
}
|
||||
]
|
||||
},
|
||||
selectItems() {
|
||||
if (this.isSeries) return this.seriesItems
|
||||
if (this.isPodcast) return this.podcastItems
|
||||
if (this.isMusic) return this.musicItems
|
||||
return this.bookItems
|
||||
},
|
||||
selectedItemSublist() {
|
||||
@@ -367,6 +344,9 @@ export default {
|
||||
publishers() {
|
||||
return this.filterData.publishers || []
|
||||
},
|
||||
publishedDecades() {
|
||||
return this.filterData.publishedDecades || []
|
||||
},
|
||||
progress() {
|
||||
return [
|
||||
{
|
||||
@@ -433,21 +413,17 @@ export default {
|
||||
id: 'isbn',
|
||||
name: 'ISBN'
|
||||
},
|
||||
{
|
||||
id: 'subtitle',
|
||||
name: this.$strings.LabelSubtitle
|
||||
},
|
||||
{
|
||||
id: 'authors',
|
||||
name: this.$strings.LabelAuthor
|
||||
},
|
||||
{
|
||||
id: 'publishedYear',
|
||||
name: this.$strings.LabelPublishYear
|
||||
id: 'chapters',
|
||||
name: this.$strings.LabelChapters
|
||||
},
|
||||
{
|
||||
id: 'series',
|
||||
name: this.$strings.LabelSeries
|
||||
id: 'cover',
|
||||
name: this.$strings.LabelCover
|
||||
},
|
||||
{
|
||||
id: 'description',
|
||||
@@ -458,24 +434,32 @@ export default {
|
||||
name: this.$strings.LabelGenres
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
name: this.$strings.LabelTags
|
||||
id: 'language',
|
||||
name: this.$strings.LabelLanguage
|
||||
},
|
||||
{
|
||||
id: 'narrators',
|
||||
name: this.$strings.LabelNarrator
|
||||
},
|
||||
{
|
||||
id: 'publishedYear',
|
||||
name: this.$strings.LabelPublishYear
|
||||
},
|
||||
{
|
||||
id: 'publisher',
|
||||
name: this.$strings.LabelPublisher
|
||||
},
|
||||
{
|
||||
id: 'language',
|
||||
name: this.$strings.LabelLanguage
|
||||
id: 'series',
|
||||
name: this.$strings.LabelSeries
|
||||
},
|
||||
{
|
||||
id: 'cover',
|
||||
name: this.$strings.LabelCover
|
||||
id: 'subtitle',
|
||||
name: this.$strings.LabelSubtitle
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
name: this.$strings.LabelTags
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -56,9 +56,6 @@ export default {
|
||||
isPodcast() {
|
||||
return this.libraryMediaType === 'podcast'
|
||||
},
|
||||
isMusic() {
|
||||
return this.libraryMediaType === 'music'
|
||||
},
|
||||
podcastItems() {
|
||||
return [
|
||||
{
|
||||
@@ -148,40 +145,10 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
musicItems() {
|
||||
return [
|
||||
{
|
||||
text: this.$strings.LabelTitle,
|
||||
value: 'media.metadata.title'
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelAddedAt,
|
||||
value: 'addedAt'
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelSize,
|
||||
value: 'size'
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelDuration,
|
||||
value: 'media.duration'
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelFileBirthtime,
|
||||
value: 'birthtimeMs'
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelFileModified,
|
||||
value: 'mtimeMs'
|
||||
}
|
||||
]
|
||||
},
|
||||
selectItems() {
|
||||
let items = null
|
||||
if (this.isPodcast) {
|
||||
items = this.podcastItems
|
||||
} else if (this.isMusic) {
|
||||
items = this.musicItems
|
||||
} else if (this.$store.getters['user/getUserSetting']('filterBy').startsWith('series.')) {
|
||||
items = this.seriesItems
|
||||
} else {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div ref="wrapper" class="relative ml-4 sm:ml-8" v-click-outside="clickOutside">
|
||||
<div class="flex items-center justify-center text-gray-300 cursor-pointer h-full" @mousedown.prevent @mouseup.prevent @click="setShowMenu(true)">
|
||||
<span class="font-mono uppercase text-gray-200 text-sm sm:text-base">{{ playbackRate.toFixed(1) }}<span class="text-base">x</span></span>
|
||||
<span class="text-gray-200 text-sm sm:text-base">{{ playbackRate.toFixed(1) }}<span class="text-base">x</span></span>
|
||||
</div>
|
||||
<div v-show="showMenu" class="absolute -top-20 z-20 bg-bg border-black-200 border shadow-xl rounded-lg" :style="{ left: menuLeft + 'px' }">
|
||||
<div v-show="showMenu" class="absolute -top-[5.5rem] z-20 bg-bg border-black-200 border shadow-xl rounded-lg" :style="{ left: menuLeft + 'px' }">
|
||||
<div class="absolute -bottom-1.5 right-0 w-full flex justify-center" :style="{ left: arrowLeft + 'px' }">
|
||||
<div class="arrow-down" />
|
||||
</div>
|
||||
@@ -11,12 +11,12 @@
|
||||
<template v-for="rate in rates">
|
||||
<div :key="rate" class="h-full border-black-300 w-11 cursor-pointer border rounded-sm" :class="value === rate ? 'bg-black-100' : 'hover:bg-black hover:bg-opacity-10'" style="min-width: 44px; max-width: 44px" @click="set(rate)">
|
||||
<div class="w-full h-full flex justify-center items-center">
|
||||
<p class="text-xs text-center font-mono">{{ rate }}<span class="text-sm">x</span></p>
|
||||
<p class="text-xs text-center">{{ rate }}<span class="text-sm">x</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="w-full py-1 px-4">
|
||||
<div class="w-full py-1 px-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<ui-icon-btn :disabled="!canDecrement" icon="remove" @click="decrement" />
|
||||
<p class="px-2 text-2xl sm:text-3xl">{{ playbackRate }}<span class="text-2xl">x</span></p>
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
currentPlaybackRate: 0,
|
||||
MIN_SPEED: 0.5,
|
||||
MAX_SPEED: 10,
|
||||
menuLeft: -92,
|
||||
menuLeft: -96,
|
||||
arrowLeft: 0
|
||||
}
|
||||
},
|
||||
@@ -89,9 +89,9 @@ export default {
|
||||
if (boundingBox.left + 110 > window.innerWidth - 10) {
|
||||
this.menuLeft = window.innerWidth - 230 - boundingBox.left
|
||||
|
||||
this.arrowLeft = Math.abs(this.menuLeft) - 92
|
||||
this.arrowLeft = Math.abs(this.menuLeft) - 96
|
||||
} else {
|
||||
this.menuLeft = -92
|
||||
this.menuLeft = -96
|
||||
this.arrowLeft = 0
|
||||
}
|
||||
},
|
||||
@@ -109,4 +109,4 @@ export default {
|
||||
this.currentPlaybackRate = this.playbackRate
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<span class="material-symbols text-2xl sm:text-3xl">{{ volumeIcon }}</span>
|
||||
</button>
|
||||
<transition name="menux">
|
||||
<div v-show="isOpen" class="volumeMenu h-6 absolute bottom-2 w-28 px-2 bg-bg shadow-sm rounded-lg" style="left: -116px">
|
||||
<div ref="volumeTrack" class="h-1 w-full bg-gray-500 my-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
|
||||
<div class="bg-gray-100 h-full absolute left-0 top-0 pointer-events-none rounded-full" :style="{ width: volume * trackWidth + 'px' }" />
|
||||
<div class="w-2.5 h-2.5 bg-white shadow-sm rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ left: cursorLeft + 'px', top: '-3px' }" />
|
||||
<div v-show="isOpen" class="volumeMenu h-28 absolute bottom-2 w-6 py-2 bg-bg shadow-sm rounded-lg" style="top: -116px">
|
||||
<div ref="volumeTrack" class="w-1 h-full bg-gray-500 mx-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
|
||||
<div class="bg-gray-100 w-full absolute left-0 bottom-0 pointer-events-none rounded-full" :style="{ height: volume * trackHeight + 'px' }" />
|
||||
<div class="w-2.5 h-2.5 bg-white shadow-sm rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ bottom: cursorBottom + 'px', left: '-3px' }" />
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
@@ -24,10 +24,10 @@ export default {
|
||||
isOpen: false,
|
||||
isDragging: false,
|
||||
isHovering: false,
|
||||
posX: 0,
|
||||
posY: 0,
|
||||
lastValue: 0.5,
|
||||
isMute: false,
|
||||
trackWidth: 112 - 20,
|
||||
trackHeight: 112 - 20,
|
||||
openTimeout: null
|
||||
}
|
||||
},
|
||||
@@ -45,9 +45,9 @@ export default {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
},
|
||||
cursorLeft() {
|
||||
var left = this.trackWidth * this.volume
|
||||
return left - 3
|
||||
cursorBottom() {
|
||||
var bottom = this.trackHeight * this.volume
|
||||
return bottom - 3
|
||||
},
|
||||
volumeIcon() {
|
||||
if (this.volume <= 0) return 'volume_mute'
|
||||
@@ -89,17 +89,10 @@ export default {
|
||||
}, 600)
|
||||
},
|
||||
mousemove(e) {
|
||||
var diff = this.posX - e.x
|
||||
this.posX = e.x
|
||||
var volShift = 0
|
||||
if (diff < 0) {
|
||||
// Volume up
|
||||
volShift = diff / this.trackWidth
|
||||
} else {
|
||||
// volume down
|
||||
volShift = diff / this.trackWidth
|
||||
}
|
||||
var newVol = this.volume - volShift
|
||||
var diff = this.posY - e.y
|
||||
this.posY = e.y
|
||||
var volShift = diff / this.trackHeight
|
||||
var newVol = this.volume + volShift
|
||||
newVol = Math.min(Math.max(0, newVol), 1)
|
||||
this.volume = newVol
|
||||
e.preventDefault()
|
||||
@@ -113,8 +106,8 @@ export default {
|
||||
},
|
||||
mousedownTrack(e) {
|
||||
this.isDragging = true
|
||||
this.posX = e.x
|
||||
var vol = e.offsetX / this.trackWidth
|
||||
this.posY = e.y
|
||||
var vol = 1 - e.offsetY / this.trackHeight
|
||||
vol = Math.min(Math.max(vol, 0), 1)
|
||||
this.volume = vol
|
||||
document.body.addEventListener('mousemove', this.mousemove)
|
||||
@@ -137,7 +130,7 @@ export default {
|
||||
this.clickVolumeIcon()
|
||||
},
|
||||
clickVolumeTrack(e) {
|
||||
var vol = e.offsetX / this.trackWidth
|
||||
var vol = 1 - e.offsetY / this.trackHeight
|
||||
vol = Math.min(Math.max(vol, 0), 1)
|
||||
this.volume = vol
|
||||
}
|
||||
@@ -147,7 +140,7 @@ export default {
|
||||
this.isMute = true
|
||||
}
|
||||
const storageVolume = localStorage.getItem('volume')
|
||||
if (storageVolume) {
|
||||
if (storageVolume && !isNaN(storageVolume)) {
|
||||
this.volume = parseFloat(storageVolume)
|
||||
}
|
||||
},
|
||||
@@ -157,4 +150,4 @@ export default {
|
||||
document.body.removeEventListener('mouseup', this.mouseup)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -56,24 +56,15 @@ export default {
|
||||
},
|
||||
imgSrc() {
|
||||
if (!this.imagePath) return null
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// Testing
|
||||
return `http://localhost:3333${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
|
||||
}
|
||||
return `/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
|
||||
return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imageLoaded() {
|
||||
var aspectRatio = 1.25
|
||||
if (this.$refs.wrapper) {
|
||||
aspectRatio = this.$refs.wrapper.clientHeight / this.$refs.wrapper.clientWidth
|
||||
}
|
||||
if (this.$refs.img) {
|
||||
var { naturalWidth, naturalHeight } = this.$refs.img
|
||||
var imgAr = naturalHeight / naturalWidth
|
||||
var arDiff = Math.abs(imgAr - aspectRatio)
|
||||
if (arDiff > 0.15) {
|
||||
if (imgAr < 0.5 || imgAr > 2) {
|
||||
this.showCoverBg = true
|
||||
} else {
|
||||
this.showCoverBg = false
|
||||
|
||||
@@ -296,7 +296,7 @@ export default {
|
||||
.then((data) => {
|
||||
this.processing = false
|
||||
if (data.error) {
|
||||
this.$toast.error(`${this.$strings.ToastAccountUpdateFailed}: ${data.error}`)
|
||||
this.$toast.error(`${this.$strings.ToastFailedToUpdate}: ${data.error}`)
|
||||
} else {
|
||||
console.log('Account updated', data.user)
|
||||
|
||||
@@ -313,7 +313,7 @@ export default {
|
||||
this.processing = false
|
||||
console.error('Failed to update account', error)
|
||||
var errMsg = error.response ? error.response.data || '' : ''
|
||||
this.$toast.error(errMsg || this.$strings.ToastFailedToUpdateAccount)
|
||||
this.$toast.error(errMsg || this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
},
|
||||
submitCreateAccount() {
|
||||
@@ -351,7 +351,7 @@ export default {
|
||||
update: type === 'admin',
|
||||
delete: type === 'admin',
|
||||
upload: type === 'admin',
|
||||
accessExplicitContent: true,
|
||||
accessExplicitContent: type === 'admin',
|
||||
accessAllLibraries: true,
|
||||
accessAllTags: true,
|
||||
selectedTagsNotAccessible: false
|
||||
@@ -386,7 +386,7 @@ export default {
|
||||
upload: false,
|
||||
accessAllLibraries: true,
|
||||
accessAllTags: true,
|
||||
accessExplicitContent: true,
|
||||
accessExplicitContent: false,
|
||||
selectedTagsNotAccessible: false
|
||||
},
|
||||
librariesAccessible: [],
|
||||
|
||||
@@ -116,10 +116,10 @@ export default {
|
||||
libraryItemIds: this.selectedBookIds
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.info('Batch quick match of ' + this.selectedBookIds.length + ' books started!')
|
||||
this.$toast.info(this.$getString('ToastBatchQuickMatchStarted', [this.selectedBookIds.length]))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error('Batch quick match failed')
|
||||
this.$toast.error(this.$strings.ToastBatchQuickMatchFailed)
|
||||
console.error('Failed to batch quick match', error)
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
this.$toast.success(this.$strings.ToastBookmarkUpdateSuccess)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(this.$strings.ToastBookmarkUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
console.error(error)
|
||||
})
|
||||
this.show = false
|
||||
|
||||
@@ -112,11 +112,11 @@ export default {
|
||||
return this.$store.state.user.user
|
||||
},
|
||||
demoShareUrl() {
|
||||
return `${window.origin}/share/${this.newShareSlug}`
|
||||
return `${window.origin}${this.$config.routerBasePath}/share/${this.newShareSlug}`
|
||||
},
|
||||
currentShareUrl() {
|
||||
if (!this.currentShare) return ''
|
||||
return `${window.origin}/share/${this.currentShare.slug}`
|
||||
return `${window.origin}${this.$config.routerBasePath}/share/${this.currentShare.slug}`
|
||||
},
|
||||
currentShareTimeRemaining() {
|
||||
if (!this.currentShare) return 'Error'
|
||||
|
||||
@@ -148,7 +148,7 @@ export default {
|
||||
var result = await this.$axios.$patch(`/api/authors/${this.authorId}`, updatePayload).catch((error) => {
|
||||
console.error('Failed', error)
|
||||
const errorMsg = error.response ? error.response.data : null
|
||||
this.$toast.error(errorMsg || this.$strings.ToastAuthorUpdateFailed)
|
||||
this.$toast.error(errorMsg || this.$strings.ToastFailedToUpdate)
|
||||
return null
|
||||
})
|
||||
if (result) {
|
||||
|
||||
@@ -135,7 +135,7 @@ export default {
|
||||
.catch((error) => {
|
||||
console.error('Failed to update collection', error)
|
||||
this.processing = false
|
||||
this.$toast.error(this.$strings.ToastCollectionUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -178,7 +178,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update device', error)
|
||||
this.$toast.error(this.$strings.ToastDeviceUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
|
||||
@@ -108,9 +108,9 @@ export default {
|
||||
if (res.warning) {
|
||||
this.$toast.warning(res.warning)
|
||||
} else if (res.updated) {
|
||||
this.$toast.success(this.$strings.ToastNoUpdatesNecessary)
|
||||
this.$toast.success(this.$strings.ToastItemDetailsUpdateSuccess)
|
||||
} else {
|
||||
this.$toast.info(this.$strings.ToastItemDetailsUpdateUnneeded)
|
||||
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -170,7 +170,7 @@ export default {
|
||||
this.isProcessing = false
|
||||
if (updateResult) {
|
||||
if (updateResult.updated) {
|
||||
this.$toast.success(this.$strings.MessageItemDetailsUpdated)
|
||||
this.$toast.success(this.$strings.ToastItemDetailsUpdateSuccess)
|
||||
return true
|
||||
} else {
|
||||
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<ui-text-input-with-label ref="maxEpisodesInput" v-model="maxEpisodesToDownload" :disabled="checkingNewEpisodes" type="number" :label="$strings.LabelLimit" class="w-16 mr-2" input-class="h-10">
|
||||
<div class="flex -mb-0.5">
|
||||
<p class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': checkingNewEpisodes }">{{ $strings.LabelLimit }}</p>
|
||||
<ui-tooltip direction="top" text="Max # of episodes to download. Use 0 for unlimited.">
|
||||
<ui-tooltip direction="top" :text="$strings.LabelMaxEpisodesToDownload">
|
||||
<span class="material-symbols text-base">info</span>
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
@@ -99,7 +99,7 @@ export default {
|
||||
|
||||
if (this.maxEpisodesToDownload < 0) {
|
||||
this.maxEpisodesToDownload = 3
|
||||
this.$toast.error('Invalid max episodes to download')
|
||||
this.$toast.error(this.$strings.ToastInvalidMaxEpisodesToDownload)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ export default {
|
||||
.then((response) => {
|
||||
if (response.episodes && response.episodes.length) {
|
||||
console.log('New episodes', response.episodes.length)
|
||||
this.$toast.success(`${response.episodes.length} new episodes found!`)
|
||||
this.$toast.success(this.$getString('ToastNewEpisodesFound', [response.episodes.length]))
|
||||
} else {
|
||||
this.$toast.info('No new episodes found')
|
||||
this.$toast.info(this.$strings.ToastNoNewEpisodesFound)
|
||||
}
|
||||
this.checkingNewEpisodes = false
|
||||
})
|
||||
@@ -141,4 +141,4 @@ export default {
|
||||
this.setLastEpisodeCheckInput()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.title" :disabled="!selectedMatchUsage.title" :label="$strings.LabelTitle" />
|
||||
<p v-if="mediaMetadata.title" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('title', mediaMetadata.title)">{{ mediaMetadata.title || '' }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('title', mediaMetadata.title)">{{ mediaMetadata.title || '' }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,7 +69,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.subtitle" :disabled="!selectedMatchUsage.subtitle" :label="$strings.LabelSubtitle" />
|
||||
<p v-if="mediaMetadata.subtitle" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('subtitle', mediaMetadata.subtitle)">{{ mediaMetadata.subtitle }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('subtitle', mediaMetadata.subtitle)">{{ mediaMetadata.subtitle }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,7 +78,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.author" :disabled="!selectedMatchUsage.author" :label="$strings.LabelAuthor" />
|
||||
<p v-if="mediaMetadata.authorName" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', mediaMetadata.authorName)">{{ mediaMetadata.authorName }}</a>
|
||||
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', mediaMetadata.authorName)">{{ mediaMetadata.authorName }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,7 +87,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-multi-select v-model="selectedMatch.narrator" :items="narrators" :disabled="!selectedMatchUsage.narrator" :label="$strings.LabelNarrators" />
|
||||
<p v-if="mediaMetadata.narratorName" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('narrator', mediaMetadata.narrators)">{{ mediaMetadata.narratorName }}</a>
|
||||
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('narrator', mediaMetadata.narrators)">{{ mediaMetadata.narratorName }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,7 +96,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-textarea-with-label v-model="selectedMatch.description" :rows="3" :disabled="!selectedMatchUsage.description" :label="$strings.LabelDescription" />
|
||||
<p v-if="mediaMetadata.description" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.description.substr(0, 100) + (mediaMetadata.description.length > 100 ? '...' : '') }}</a>
|
||||
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.description.substr(0, 100) + (mediaMetadata.description.length > 100 ? '...' : '') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,7 +105,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.publisher" :disabled="!selectedMatchUsage.publisher" :label="$strings.LabelPublisher" />
|
||||
<p v-if="mediaMetadata.publisher" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publisher', mediaMetadata.publisher)">{{ mediaMetadata.publisher }}</a>
|
||||
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publisher', mediaMetadata.publisher)">{{ mediaMetadata.publisher }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,7 +114,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.publishedYear" :disabled="!selectedMatchUsage.publishedYear" :label="$strings.LabelPublishYear" />
|
||||
<p v-if="mediaMetadata.publishedYear" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
|
||||
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,7 +124,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<widgets-series-input-widget v-model="selectedMatch.series" :disabled="!selectedMatchUsage.series" />
|
||||
<p v-if="mediaMetadata.seriesName" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('series', mediaMetadata.series)">{{ mediaMetadata.seriesName }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('series', mediaMetadata.series)">{{ mediaMetadata.seriesName }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,7 +133,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-multi-select v-model="selectedMatch.genres" :items="genres" :disabled="!selectedMatchUsage.genres" :label="$strings.LabelGenres" />
|
||||
<p v-if="mediaMetadata.genres?.length" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('genres', mediaMetadata.genres)">{{ mediaMetadata.genres.join(', ') }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('genres', mediaMetadata.genres)">{{ mediaMetadata.genres.join(', ') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,7 +142,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-multi-select v-model="selectedMatch.tags" :items="tags" :disabled="!selectedMatchUsage.tags" :label="$strings.LabelTags" />
|
||||
<p v-if="media.tags?.length" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('tags', media.tags)">{{ media.tags.join(', ') }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('tags', media.tags)">{{ media.tags.join(', ') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,7 +151,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.language" :disabled="!selectedMatchUsage.language" :label="$strings.LabelLanguage" />
|
||||
<p v-if="mediaMetadata.language" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('language', mediaMetadata.language)">{{ mediaMetadata.language }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('language', mediaMetadata.language)">{{ mediaMetadata.language }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,7 +160,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.isbn" :disabled="!selectedMatchUsage.isbn" label="ISBN" />
|
||||
<p v-if="mediaMetadata.isbn" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('isbn', mediaMetadata.isbn)">{{ mediaMetadata.isbn }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('isbn', mediaMetadata.isbn)">{{ mediaMetadata.isbn }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,7 +169,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.asin" :disabled="!selectedMatchUsage.asin" label="ASIN" />
|
||||
<p v-if="mediaMetadata.asin" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('asin', mediaMetadata.asin)">{{ mediaMetadata.asin }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('asin', mediaMetadata.asin)">{{ mediaMetadata.asin }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -179,7 +179,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.itunesId" type="number" :disabled="!selectedMatchUsage.itunesId" label="iTunes ID" />
|
||||
<p v-if="mediaMetadata.itunesId" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesId', mediaMetadata.itunesId)">{{ mediaMetadata.itunesId }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesId', mediaMetadata.itunesId)">{{ mediaMetadata.itunesId }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,7 +188,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.feedUrl" :disabled="!selectedMatchUsage.feedUrl" label="RSS Feed URL" />
|
||||
<p v-if="mediaMetadata.feedUrl" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('feedUrl', mediaMetadata.feedUrl)">{{ mediaMetadata.feedUrl }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('feedUrl', mediaMetadata.feedUrl)">{{ mediaMetadata.feedUrl }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,7 +197,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.itunesPageUrl" :disabled="!selectedMatchUsage.itunesPageUrl" label="iTunes Page URL" />
|
||||
<p v-if="mediaMetadata.itunesPageUrl" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesPageUrl', mediaMetadata.itunesPageUrl)">{{ mediaMetadata.itunesPageUrl }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesPageUrl', mediaMetadata.itunesPageUrl)">{{ mediaMetadata.itunesPageUrl }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -206,7 +206,7 @@
|
||||
<div class="flex-grow ml-4">
|
||||
<ui-text-input-with-label v-model="selectedMatch.releaseDate" :disabled="!selectedMatchUsage.releaseDate" :label="$strings.LabelReleaseDate" />
|
||||
<p v-if="mediaMetadata.releaseDate" class="text-xs ml-1 text-white text-opacity-60">
|
||||
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('releaseDate', mediaMetadata.releaseDate)">{{ mediaMetadata.releaseDate }}</a>
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('releaseDate', mediaMetadata.releaseDate)">{{ mediaMetadata.releaseDate }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -623,7 +623,7 @@ export default {
|
||||
this.clearSelectedMatch()
|
||||
this.$emit('selectTab', 'details')
|
||||
} else {
|
||||
this.$toast.error(this.$strings.ToastItemDetailsUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
}
|
||||
} else {
|
||||
this.clearSelectedMatch()
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
<div class="w-full h-full relative">
|
||||
<div id="scheduleWrapper" class="w-full overflow-y-auto px-2 py-4 md:px-6 md:py-6">
|
||||
<template v-if="!feedUrl">
|
||||
<widgets-alert type="warning" class="text-base mb-4">No RSS feed URL is set for this podcast</widgets-alert>
|
||||
<widgets-alert type="warning" class="text-base mb-4">{{ $strings.ToastPodcastNoRssFeed }}</widgets-alert>
|
||||
</template>
|
||||
<template v-if="feedUrl || autoDownloadEpisodes">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<p class="text-base md:text-xl font-semibold">Schedule Automatic Episode Downloads</p>
|
||||
<ui-checkbox v-model="enableAutoDownloadEpisodes" label="Enable" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" />
|
||||
<p class="text-base md:text-xl font-semibold">{{ $strings.HeaderScheduleEpisodeDownloads }}</p>
|
||||
<ui-checkbox v-model="enableAutoDownloadEpisodes" :label="$strings.LabelEnable" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" />
|
||||
</div>
|
||||
|
||||
<div v-if="enableAutoDownloadEpisodes" class="flex items-center py-2">
|
||||
<ui-text-input ref="maxEpisodesInput" type="number" v-model="newMaxEpisodesToKeep" no-spinner :padding-x="1" text-center class="w-10 text-base" @change="updatedMaxEpisodesToKeep" />
|
||||
<ui-tooltip text="Value of 0 sets no max limit. After a new episode is auto-downloaded this will delete the oldest episode if you have more than X episodes. <br>This will only delete 1 episode per new download.">
|
||||
<ui-tooltip :text="$strings.LabelMaxEpisodesToKeepHelp">
|
||||
<p class="pl-4 text-base">
|
||||
Max episodes to keep
|
||||
{{ $strings.LabelMaxEpisodesToKeep }}
|
||||
<span class="material-symbols icon-text">info</span>
|
||||
</p>
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
<div v-if="enableAutoDownloadEpisodes" class="flex items-center py-2">
|
||||
<ui-text-input ref="maxEpisodesToDownloadInput" type="number" v-model="newMaxNewEpisodesToDownload" no-spinner :padding-x="1" text-center class="w-10 text-base" @change="updateMaxNewEpisodesToDownload" />
|
||||
<ui-tooltip text="Value of 0 sets no max limit. When checking for new episodes this is the max number of episodes that will be downloaded.">
|
||||
<ui-tooltip :text="$strings.LabelUseZeroForUnlimited">
|
||||
<p class="pl-4 text-base">
|
||||
Max new episodes to download per check
|
||||
{{ $strings.LabelMaxEpisodesToDownloadPerCheck }}
|
||||
<span class="material-symbols icon-text">info</span>
|
||||
</p>
|
||||
</ui-tooltip>
|
||||
@@ -36,7 +36,7 @@
|
||||
<div v-if="feedUrl || autoDownloadEpisodes" class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg border-t border-white border-opacity-5">
|
||||
<div class="flex items-center px-2 md:px-4">
|
||||
<div class="flex-grow" />
|
||||
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'success' : 'primary'" class="mx-2">{{ isUpdated ? 'Save' : 'No update necessary' }}</ui-btn>
|
||||
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'success' : 'primary'" class="mx-2">{{ isUpdated ? $strings.ButtonSave : $strings.MessageNoUpdatesWereNecessary }}</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,18 +33,18 @@
|
||||
<span class="material-symbols text-lg ml-2">launch</span>
|
||||
</ui-btn>
|
||||
|
||||
<ui-btn v-if="!isMetadataEmbedQueued && !isEmbedTaskRunning" class="w-full mt-4" small @click.stop="quickEmbed">Quick Embed</ui-btn>
|
||||
<ui-btn v-if="!isMetadataEmbedQueued && !isEmbedTaskRunning" class="w-full mt-4" small @click.stop="quickEmbed">{{ $strings.ButtonQuickEmbed }}</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- queued alert -->
|
||||
<widgets-alert v-if="isMetadataEmbedQueued" type="warning" class="mt-4">
|
||||
<p class="text-lg">Queued for metadata embed ({{ queuedEmbedLIds.length }} in queue)</p>
|
||||
<p class="text-lg">{{ $getString('MessageQuickEmbedQueue', [queuedEmbedLIds.length]) }}</p>
|
||||
</widgets-alert>
|
||||
|
||||
<!-- processing alert -->
|
||||
<widgets-alert v-if="isEmbedTaskRunning" type="warning" class="mt-4">
|
||||
<p class="text-lg">Currently embedding metadata</p>
|
||||
<p class="text-lg">{{ $strings.MessageQuickEmbedInProgress }}</p>
|
||||
</widgets-alert>
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,7 @@ export default {
|
||||
methods: {
|
||||
quickEmbed() {
|
||||
const payload = {
|
||||
message: 'Warning! Quick embed will not backup your audio files. Make sure that you have a backup of your audio files. <br><br>Would you like to continue?',
|
||||
message: this.$strings.MessageConfirmQuickEmbed,
|
||||
callback: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this.$axios
|
||||
|
||||
@@ -160,7 +160,7 @@ export default {
|
||||
return false
|
||||
}
|
||||
if (!this.libraryCopy.folders.length) {
|
||||
this.$toast.error('Library must have at least 1 path')
|
||||
this.$toast.error(this.$strings.ToastMustHaveAtLeastOnePath)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ export default {
|
||||
if (error.response && error.response.data) {
|
||||
this.$toast.error(error.response.data)
|
||||
} else {
|
||||
this.$toast.error(this.$strings.ToastLibraryUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
}
|
||||
this.processing = false
|
||||
})
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<div class="w-full border border-black-200 p-4 my-8">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div>
|
||||
<p class="text-lg">Remove metadata files in library item folders</p>
|
||||
<p class="max-w-sm text-sm pt-2 text-gray-300">Remove all metadata.json or metadata.abs files in your {{ mediaType }} folders</p>
|
||||
<p class="text-lg">{{ $strings.LabelRemoveMetadataFile }}</p>
|
||||
<p class="max-w-sm text-sm pt-2 text-gray-300">{{ $getString('LabelRemoveMetadataFileHelp', [mediaType]) }}</p>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
<div>
|
||||
<ui-btn class="mb-4 block" @click.stop="removeAllMetadataClick('json')">Remove all metadata.json</ui-btn>
|
||||
<ui-btn @click.stop="removeAllMetadataClick('abs')">Remove all metadata.abs</ui-btn>
|
||||
<ui-btn class="mb-4 block" @click.stop="removeAllMetadataClick('json')">{{ $strings.LabelRemoveAllMetadataJson }}</ui-btn>
|
||||
<ui-btn @click.stop="removeAllMetadataClick('abs')">{{ $strings.LabelRemoveAllMetadataAbs }}</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
methods: {
|
||||
removeAllMetadataClick(ext) {
|
||||
const payload = {
|
||||
message: `Are you sure you want to remove all metadata.${ext} files in your library item folders?`,
|
||||
message: this.$getString('MessageConfirmRemoveMetadataFiles', [ext]),
|
||||
persistent: true,
|
||||
callback: (confirmed) => {
|
||||
if (confirmed) {
|
||||
@@ -60,16 +60,16 @@ export default {
|
||||
.$post(`/api/libraries/${this.libraryId}/remove-metadata?ext=${ext}`)
|
||||
.then((data) => {
|
||||
if (!data.found) {
|
||||
this.$toast.info(`No metadata.${ext} files were found in library`)
|
||||
this.$toast.info(this.$getString('ToastMetadataFilesRemovedNoneFound', [ext]))
|
||||
} else if (!data.removed) {
|
||||
this.$toast.success(`No metadata.${ext} files removed`)
|
||||
this.$toast.success(this.$getString('ToastMetadataFilesRemovedNoneRemoved', [ext]))
|
||||
} else {
|
||||
this.$toast.success(`Successfully removed ${data.removed} metadata.${ext} files`)
|
||||
this.$toast.success(this.$getString('ToastMetadataFilesRemovedSuccess', [data.removed, ext]))
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to remove metadata files', error)
|
||||
this.$toast.error('Failed to remove metadata files')
|
||||
this.$toast.error(this.$getString('ToastMetadataFilesRemovedError', [ext]))
|
||||
})
|
||||
.finally(() => {
|
||||
this.$emit('update:processing', false)
|
||||
|
||||
@@ -77,7 +77,13 @@ export default {
|
||||
return this.notificationData.events || []
|
||||
},
|
||||
eventOptions() {
|
||||
return this.notificationEvents.map((e) => ({ value: e.name, text: e.name, subtext: e.description }))
|
||||
return this.notificationEvents.map((e) => {
|
||||
return {
|
||||
value: e.name,
|
||||
text: e.name,
|
||||
subtext: this.$strings[e.descriptionKey] || e.description
|
||||
}
|
||||
})
|
||||
},
|
||||
selectedEventData() {
|
||||
return this.notificationEvents.find((e) => e.name === this.newNotification.eventName)
|
||||
@@ -132,7 +138,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update notification', error)
|
||||
this.$toast.error(this.$strings.ToastNotificationUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
|
||||
@@ -135,7 +135,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to remove items from playlist', error)
|
||||
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
this.processing = false
|
||||
})
|
||||
},
|
||||
@@ -153,7 +153,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to add items to playlist', error)
|
||||
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
this.processing = false
|
||||
})
|
||||
},
|
||||
|
||||
@@ -115,7 +115,7 @@ export default {
|
||||
.catch((error) => {
|
||||
console.error('Failed to update playlist', error)
|
||||
this.processing = false
|
||||
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -156,7 +156,12 @@ export default {
|
||||
return this.selectedFolder.fullPath
|
||||
},
|
||||
podcastTypes() {
|
||||
return this.$store.state.globals.podcastTypes || []
|
||||
return this.$store.state.globals.podcastTypes.map((e) => {
|
||||
return {
|
||||
text: this.$strings[e.descriptionKey] || e.text,
|
||||
value: e.value
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
</div>
|
||||
<div v-if="enclosureUrl" class="pb-4 pt-6">
|
||||
<ui-text-input-with-label :value="enclosureUrl" readonly class="text-xs">
|
||||
<label class="px-1 text-xs text-gray-200 font-semibold">Episode URL from RSS feed</label>
|
||||
<label class="px-1 text-xs text-gray-200 font-semibold">{{ $strings.LabelEpisodeUrlFromRssFeed }}</label>
|
||||
</ui-text-input-with-label>
|
||||
</div>
|
||||
<div v-else class="py-4">
|
||||
<p class="text-xs text-gray-300 font-semibold">Episode not linked to RSS feed episode</p>
|
||||
<p class="text-xs text-gray-300 font-semibold">{{ $strings.LabelEpisodeNotLinkedToRssFeed }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -97,7 +97,12 @@ export default {
|
||||
return this.enclosure.url
|
||||
},
|
||||
episodeTypes() {
|
||||
return this.$store.state.globals.episodeTypes || []
|
||||
return this.$store.state.globals.episodeTypes.map((e) => {
|
||||
return {
|
||||
text: this.$strings[e.descriptionKey] || e.text,
|
||||
value: e.value
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -152,14 +157,14 @@ export default {
|
||||
const updateResult = await this.$axios.$patch(`/api/podcasts/${this.libraryItem.id}/episode/${this.episodeId}`, updatedDetails).catch((error) => {
|
||||
console.error('Failed update episode', error)
|
||||
this.isProcessing = false
|
||||
this.$toast.error(error?.response?.data || 'Failed to update episode')
|
||||
this.$toast.error(error?.response?.data || this.$strings.ToastFailedToUpdate)
|
||||
return false
|
||||
})
|
||||
|
||||
this.isProcessing = false
|
||||
if (updateResult) {
|
||||
if (updateResult) {
|
||||
this.$toast.success('Podcast episode updated')
|
||||
this.$toast.success(this.$strings.ToastItemUpdateSuccess)
|
||||
return true
|
||||
} else {
|
||||
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
|
||||
|
||||
@@ -139,7 +139,7 @@ export default {
|
||||
slug: this.newFeedSlug,
|
||||
metadataDetails: this.metadataDetails
|
||||
}
|
||||
if (this.$isDev) payload.serverAddress = `http://localhost:3333${this.$config.routerBasePath}`
|
||||
if (this.$isDev) payload.serverAddress = process.env.serverUrl
|
||||
|
||||
console.log('Payload', payload)
|
||||
this.$axios
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
<template>
|
||||
<div class="flex items-center pt-4 pb-2 lg:pt-0 lg:pb-2">
|
||||
<div class="flex-grow" />
|
||||
<template v-if="!loading">
|
||||
<ui-tooltip direction="top" :text="$strings.ButtonPreviousChapter" class="mr-4 lg:mr-8">
|
||||
<button :aria-label="$strings.ButtonPreviousChapter" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">first_page</span>
|
||||
<div class="flex justify-center pt-4 pb-2 lg:pt-0 lg:pb-2">
|
||||
<div class="flex items-center justify-center flex-grow">
|
||||
<template v-if="!loading">
|
||||
<ui-tooltip direction="top" :text="$strings.ButtonPreviousChapter" class="mr-4 lg:mr-8">
|
||||
<button :aria-label="$strings.ButtonPreviousChapter" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">first_page</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<ui-tooltip direction="top" :text="jumpBackwardText">
|
||||
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">replay</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
|
||||
<span class="material-symbols fill text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<ui-tooltip direction="top" :text="jumpBackwardText">
|
||||
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">replay</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
|
||||
<span class="material-symbols fill text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
|
||||
</button>
|
||||
<ui-tooltip direction="top" :text="jumpForwardText">
|
||||
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">forward_media</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<ui-tooltip direction="top" :text="hasNextLabel" class="ml-4 lg:ml-8">
|
||||
<button :aria-label="hasNextLabel" :disabled="!hasNext" class="text-gray-300 disabled:text-gray-500" @mousedown.prevent @mouseup.prevent @click.stop="next">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">last_page</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<controls-playback-speed-control v-model="playbackRateInput" @input="playbackRateUpdated" @change="playbackRateChanged" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="cursor-pointer p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin">
|
||||
<span class="material-symbols text-2xl">autorenew</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex-grow" />
|
||||
<ui-tooltip direction="top" :text="jumpForwardText">
|
||||
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">forward_media</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
<ui-tooltip direction="top" :text="hasNextLabel" class="ml-4 lg:ml-8">
|
||||
<button :aria-label="hasNextLabel" :disabled="!hasNext" class="text-gray-300 disabled:text-gray-500" @mousedown.prevent @mouseup.prevent @click.stop="next">
|
||||
<span class="material-symbols text-2xl sm:text-3xl">last_page</span>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="cursor-pointer p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin">
|
||||
<span class="material-symbols text-2xl">autorenew</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -41,7 +40,6 @@ export default {
|
||||
props: {
|
||||
loading: Boolean,
|
||||
seekLoading: Boolean,
|
||||
playbackRate: Number,
|
||||
paused: Boolean,
|
||||
hasNextChapter: Boolean,
|
||||
hasNextItemInQueue: Boolean
|
||||
@@ -50,14 +48,6 @@ export default {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
playbackRateInput: {
|
||||
get() {
|
||||
return this.playbackRate
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:playbackRate', val)
|
||||
}
|
||||
},
|
||||
jumpForwardText() {
|
||||
return this.getJumpText('jumpForwardAmount', this.$strings.ButtonJumpForward)
|
||||
},
|
||||
@@ -89,15 +79,6 @@ export default {
|
||||
jumpForward() {
|
||||
this.$emit('jumpForward')
|
||||
},
|
||||
playbackRateUpdated(playbackRate) {
|
||||
this.$emit('setPlaybackRate', playbackRate)
|
||||
},
|
||||
playbackRateChanged(playbackRate) {
|
||||
this.$emit('setPlaybackRate', playbackRate)
|
||||
this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => {
|
||||
console.error('Failed to update settings', err)
|
||||
})
|
||||
},
|
||||
getJumpText(setting, prefix) {
|
||||
const amount = this.$store.getters['user/getUserSetting'](setting)
|
||||
if (!amount) return prefix
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div class="w-full -mt-6">
|
||||
<div class="w-full relative mb-1">
|
||||
<div class="absolute -top-10 lg:top-0 right-0 lg:right-2 flex items-center h-full">
|
||||
<!-- <span class="material-symbols text-2xl cursor-pointer" @click="toggleFullscreen(true)">expand_less</span> -->
|
||||
<controls-playback-speed-control v-model="playbackRate" @input="setPlaybackRate" @change="playbackRateChanged" class="mx-2 block" />
|
||||
|
||||
<ui-tooltip direction="top" :text="$strings.LabelVolume">
|
||||
<ui-tooltip direction="left" :text="$strings.LabelVolume">
|
||||
<controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden sm:block" />
|
||||
</ui-tooltip>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<span v-if="!sleepTimerSet" class="material-symbols text-2xl">snooze</span>
|
||||
<div v-else class="flex items-center">
|
||||
<span class="material-symbols text-lg text-warning">snooze</span>
|
||||
<p class="text-sm sm:text-lg text-warning font-mono font-semibold text-center px-0.5 sm:pb-0.5 sm:min-w-8">{{ sleepTimerRemainingString }}</p>
|
||||
<p class="text-sm sm:text-lg text-warning font-semibold text-center px-0.5 sm:pb-0.5 sm:min-w-8">{{ sleepTimerRemainingString }}</p>
|
||||
</div>
|
||||
</button>
|
||||
</ui-tooltip>
|
||||
@@ -48,15 +48,19 @@
|
||||
|
||||
<player-track-bar ref="trackbar" :loading="loading" :chapters="chapters" :duration="duration" :current-chapter="currentChapter" :playback-rate="playbackRate" @seek="seek" />
|
||||
|
||||
<div class="flex">
|
||||
<p ref="currentTimestamp" class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">00:00:00</p>
|
||||
<p class="font-mono text-sm hidden sm:block text-gray-100 pointer-events-auto"> / {{ progressPercent }}%</p>
|
||||
<div class="flex-grow" />
|
||||
<p class="text-xs sm:text-sm text-gray-300 pt-0.5 px-2 truncate">
|
||||
{{ currentChapterName }} <span v-if="useChapterTrack" class="text-xs text-gray-400"> ({{ $getString('LabelPlayerChapterNumberMarker', [currentChapterIndex + 1, chapters.length]) }})</span>
|
||||
</p>
|
||||
<div class="flex-grow" />
|
||||
<p class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">{{ timeRemainingPretty }}</p>
|
||||
<div class="relative flex items-center justify-between">
|
||||
<div class="flex-grow flex items-center">
|
||||
<p ref="currentTimestamp" class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">00:00:00</p>
|
||||
<p class="font-mono text-sm hidden sm:block text-gray-100 pointer-events-auto"> / {{ progressPercent }}%</p>
|
||||
</div>
|
||||
<div class="absolute left-1/2 transform -translate-x-1/2">
|
||||
<p class="text-xs sm:text-sm text-gray-300 pt-0.5 px-2 truncate">
|
||||
{{ currentChapterName }} <span v-if="useChapterTrack" class="text-xs text-gray-400"> ({{ $getString('LabelPlayerChapterNumberMarker', [currentChapterIndex + 1, chapters.length]) }})</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-grow flex items-center justify-end">
|
||||
<p class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">{{ timeRemainingPretty }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<modals-chapters-modal v-model="showChaptersModal" :current-chapter="currentChapter" :playback-rate="playbackRate" :chapters="chapters" @select="selectChapter" />
|
||||
@@ -178,22 +182,6 @@ export default {
|
||||
methods: {
|
||||
toggleFullscreen(isFullscreen) {
|
||||
this.$store.commit('setPlayerIsFullscreen', isFullscreen)
|
||||
|
||||
var videoPlayerEl = document.getElementById('video-player')
|
||||
if (videoPlayerEl) {
|
||||
if (isFullscreen) {
|
||||
videoPlayerEl.style.width = '100vw'
|
||||
videoPlayerEl.style.height = '100vh'
|
||||
videoPlayerEl.style.top = '0px'
|
||||
videoPlayerEl.style.left = '0px'
|
||||
} else {
|
||||
videoPlayerEl.style.width = '384px'
|
||||
videoPlayerEl.style.height = '216px'
|
||||
videoPlayerEl.style.top = 'unset'
|
||||
videoPlayerEl.style.bottom = '80px'
|
||||
videoPlayerEl.style.left = '16px'
|
||||
}
|
||||
}
|
||||
},
|
||||
setDuration(duration) {
|
||||
this.duration = duration
|
||||
@@ -240,6 +228,12 @@ export default {
|
||||
this.playbackRate = Number((this.playbackRate - 0.1).toFixed(1))
|
||||
this.setPlaybackRate(this.playbackRate)
|
||||
},
|
||||
playbackRateChanged(playbackRate) {
|
||||
this.setPlaybackRate(playbackRate)
|
||||
this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => {
|
||||
console.error('Failed to update settings', err)
|
||||
})
|
||||
},
|
||||
setPlaybackRate(playbackRate) {
|
||||
this.$emit('setPlaybackRate', playbackRate)
|
||||
},
|
||||
|
||||
@@ -35,22 +35,22 @@
|
||||
<div class="flex justify-between pt-12">
|
||||
<div>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsWeekListening }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ totalMinutesListeningThisWeek }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(totalMinutesListeningThisWeek) }}</p>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsMinutes }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsDailyAverage }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ averageMinutesPerDay }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(averageMinutesPerDay) }}</p>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsMinutes }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsBestDay }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ mostListenedDay }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(mostListenedDay) }}</p>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsMinutes }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsDays }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ daysInARow }}</p>
|
||||
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(daysInARow) }}</p>
|
||||
<p class="text-sm text-center">{{ $strings.LabelStatsInARow }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update collection', error)
|
||||
this.$toast.error(this.$strings.ToastCollectionUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
},
|
||||
editBook(book) {
|
||||
|
||||
@@ -92,7 +92,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update playlist', error)
|
||||
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
},
|
||||
init() {
|
||||
|
||||
@@ -223,7 +223,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to remove item from playlist', error)
|
||||
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processingRemove = false
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
</div>
|
||||
<div class="h-8 flex items-center">
|
||||
<div class="w-full inline-flex justify-between max-w-xl">
|
||||
<p v-if="episode?.season" class="text-sm text-gray-300">Season #{{ episode.season }}</p>
|
||||
<p v-if="episode?.episode" class="text-sm text-gray-300">Episode #{{ episode.episode }}</p>
|
||||
<p v-if="episode?.chapters?.length" class="text-sm text-gray-300">{{ episode.chapters.length }} Chapters</p>
|
||||
<p v-if="publishedAt" class="text-sm text-gray-300">Published {{ $formatDate(publishedAt, dateFormat) }}</p>
|
||||
<p v-if="episode?.season" class="text-sm text-gray-300">{{ $getString('LabelSeasonNumber', [episode.season]) }}</p>
|
||||
<p v-if="episode?.episode" class="text-sm text-gray-300">{{ $getString('LabelEpisodeNumber', [episode.episode]) }}</p>
|
||||
<p v-if="episode?.chapters?.length" class="text-sm text-gray-300">{{ $getString('LabelChapterCount', [episode.chapters.length]) }}</p>
|
||||
<p v-if="publishedAt" class="text-sm text-gray-300">{{ $getString('LabelPublishedDate', [$formatDate(publishedAt, dateFormat)]) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -132,13 +132,13 @@ export default {
|
||||
return this.store.state.streamIsPlaying && this.isStreaming
|
||||
},
|
||||
timeRemaining() {
|
||||
if (this.streamIsPlaying) return 'Playing'
|
||||
if (this.streamIsPlaying) return this.$strings.ButtonPlaying
|
||||
if (!this.itemProgress) return this.$elapsedPretty(this.episode?.duration || 0)
|
||||
if (this.userIsFinished) return 'Finished'
|
||||
if (this.userIsFinished) return this.$strings.LabelFinished
|
||||
|
||||
const duration = this.itemProgress.duration || this.episode?.duration || 0
|
||||
const remaining = Math.floor(duration - this.itemProgress.currentTime)
|
||||
return `${this.$elapsedPretty(remaining)} left`
|
||||
return this.$getString('LabelTimeLeft', [this.$elapsedPretty(remaining)])
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -182,7 +182,7 @@ export default {
|
||||
toggleFinished(confirmed = false) {
|
||||
if (!this.userIsFinished && this.itemProgressPercent > 0 && !confirmed) {
|
||||
const payload = {
|
||||
message: `Are you sure you want to mark "${this.episodeTitle}" as finished?`,
|
||||
message: this.$getString('MessageConfirmMarkItemFinished', [this.episodeTitle]),
|
||||
callback: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this.toggleFinished(true)
|
||||
|
||||
@@ -93,17 +93,18 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
contextMenuItems() {
|
||||
if (!this.userIsAdminOrUp) return []
|
||||
return [
|
||||
{
|
||||
text: 'Quick match all episodes',
|
||||
const menuItems = []
|
||||
if (this.userIsAdminOrUp) {
|
||||
menuItems.push({
|
||||
text: this.$strings.MessageQuickMatchAllEpisodes,
|
||||
action: 'quick-match-episodes'
|
||||
},
|
||||
{
|
||||
text: this.allEpisodesFinished ? this.$strings.MessageMarkAllEpisodesNotFinished : this.$strings.MessageMarkAllEpisodesFinished,
|
||||
action: 'batch-mark-as-finished'
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
menuItems.push({
|
||||
text: this.allEpisodesFinished ? this.$strings.MessageMarkAllEpisodesNotFinished : this.$strings.MessageMarkAllEpisodesFinished,
|
||||
action: 'batch-mark-as-finished'
|
||||
})
|
||||
return menuItems
|
||||
},
|
||||
sortItems() {
|
||||
return [
|
||||
@@ -261,21 +262,21 @@ export default {
|
||||
this.processing = true
|
||||
|
||||
const payload = {
|
||||
message: 'Quick matching episodes will overwrite details if a match is found. Only unmatched episodes will be updated. Are you sure?',
|
||||
message: this.$strings.MessageConfirmQuickMatchEpisodes,
|
||||
callback: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this.$axios
|
||||
.$post(`/api/podcasts/${this.libraryItem.id}/match-episodes?override=1`)
|
||||
.then((data) => {
|
||||
if (data.numEpisodesUpdated) {
|
||||
this.$toast.success(`${data.numEpisodesUpdated} episodes updated`)
|
||||
this.$toast.success(this.$getString('ToastEpisodeUpdateSuccess', [data.numEpisodesUpdated]))
|
||||
} else {
|
||||
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to request match episodes', error)
|
||||
this.$toast.error('Failed to match episodes')
|
||||
this.$toast.error(this.$strings.ToastFailedToMatch)
|
||||
})
|
||||
}
|
||||
this.processing = false
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<template>
|
||||
<ui-tooltip v-if="alreadyInLibrary" :text="$strings.LabelAlreadyInYourLibrary" direction="top">
|
||||
<span class="material-symbols ml-1 text-success" style="font-size: 0.8rem">check_circle</span>
|
||||
<ui-tooltip :text="$strings.LabelAlreadyInYourLibrary" direction="top" class="inline-flex">
|
||||
<span class="material-symbols ml-1 text-sm text-success">check_circle</span>
|
||||
</ui-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
alreadyInLibrary: Boolean
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
@@ -3,67 +3,67 @@
|
||||
<form class="w-full h-full px-2 md:px-4 py-6" @submit.prevent="submitForm">
|
||||
<div class="flex flex-wrap -mx-1">
|
||||
<div class="w-full md:w-1/2 px-1">
|
||||
<ui-text-input-with-label ref="titleInput" v-model="details.title" :label="$strings.LabelTitle" />
|
||||
<ui-text-input-with-label ref="titleInput" v-model="details.title" :label="$strings.LabelTitle" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1 mt-2 md:mt-0">
|
||||
<ui-text-input-with-label ref="subtitleInput" v-model="details.subtitle" :label="$strings.LabelSubtitle" />
|
||||
<ui-text-input-with-label ref="subtitleInput" v-model="details.subtitle" :label="$strings.LabelSubtitle" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap mt-2 -mx-1">
|
||||
<div class="w-full md:w-3/4 px-1">
|
||||
<!-- Authors filter only contains authors in this library, uses filter data -->
|
||||
<ui-multi-select-query-input ref="authorsSelect" v-model="details.authors" :label="$strings.LabelAuthors" filter-key="authors" />
|
||||
<ui-multi-select-query-input ref="authorsSelect" v-model="details.authors" :label="$strings.LabelAuthors" filter-key="authors" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1 mt-2 md:mt-0">
|
||||
<ui-text-input-with-label ref="publishYearInput" v-model="details.publishedYear" type="number" :label="$strings.LabelPublishYear" />
|
||||
<ui-text-input-with-label ref="publishYearInput" v-model="details.publishedYear" type="number" :label="$strings.LabelPublishYear" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-2 -mx-1">
|
||||
<div class="flex-grow px-1">
|
||||
<widgets-series-input-widget v-model="details.series" />
|
||||
<widgets-series-input-widget v-model="details.series" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ui-textarea-with-label ref="descriptionInput" v-model="details.description" :rows="3" :label="$strings.LabelDescription" class="mt-2" />
|
||||
<ui-textarea-with-label ref="descriptionInput" v-model="details.description" :rows="3" :label="$strings.LabelDescription" class="mt-2" @input="handleInputChange" />
|
||||
|
||||
<div class="flex flex-wrap mt-2 -mx-1">
|
||||
<div class="w-full md:w-1/2 px-1">
|
||||
<ui-multi-select ref="genresSelect" v-model="details.genres" :label="$strings.LabelGenres" :items="genres" />
|
||||
<ui-multi-select ref="genresSelect" v-model="details.genres" :label="$strings.LabelGenres" :items="genres" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1 mt-2 md:mt-0">
|
||||
<ui-multi-select ref="tagsSelect" v-model="newTags" :label="$strings.LabelTags" :items="tags" />
|
||||
<ui-multi-select ref="tagsSelect" v-model="newTags" :label="$strings.LabelTags" :items="tags" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap mt-2 -mx-1">
|
||||
<div class="w-full md:w-1/2 px-1">
|
||||
<ui-multi-select ref="narratorsSelect" v-model="details.narrators" :label="$strings.LabelNarrators" :items="narrators" />
|
||||
<ui-multi-select ref="narratorsSelect" v-model="details.narrators" :label="$strings.LabelNarrators" :items="narrators" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4 px-1 mt-2 md:mt-0">
|
||||
<ui-text-input-with-label ref="isbnInput" v-model="details.isbn" label="ISBN" />
|
||||
<ui-text-input-with-label ref="isbnInput" v-model="details.isbn" label="ISBN" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4 px-1 mt-2 md:mt-0">
|
||||
<ui-text-input-with-label ref="asinInput" v-model="details.asin" label="ASIN" />
|
||||
<ui-text-input-with-label ref="asinInput" v-model="details.asin" label="ASIN" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap mt-2 -mx-1">
|
||||
<div class="w-full md:w-1/4 px-1">
|
||||
<ui-text-input-with-label ref="publisherInput" v-model="details.publisher" :label="$strings.LabelPublisher" />
|
||||
<ui-text-input-with-label ref="publisherInput" v-model="details.publisher" :label="$strings.LabelPublisher" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4 px-1 mt-2 md:mt-0">
|
||||
<ui-text-input-with-label ref="languageInput" v-model="details.language" :label="$strings.LabelLanguage" />
|
||||
<ui-text-input-with-label ref="languageInput" v-model="details.language" :label="$strings.LabelLanguage" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1 pt-6 mt-2 md:mt-0">
|
||||
<div class="flex justify-center">
|
||||
<ui-checkbox v-model="details.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
|
||||
<ui-checkbox v-model="details.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow px-1 pt-6 mt-2 md:mt-0">
|
||||
<div class="flex justify-center">
|
||||
<ui-checkbox v-model="details.abridged" :label="$strings.LabelAbridged" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
|
||||
<ui-checkbox v-model="details.abridged" :label="$strings.LabelAbridged" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,6 +132,12 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInputChange() {
|
||||
this.$emit('change', {
|
||||
libraryItemId: this.libraryItem.id,
|
||||
hasChanges: this.checkForChanges().hasChanges
|
||||
})
|
||||
},
|
||||
getDetails() {
|
||||
this.forceBlur()
|
||||
return this.checkForChanges()
|
||||
@@ -172,6 +178,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.handleInputChange()
|
||||
},
|
||||
forceBlur() {
|
||||
if (this.$refs.titleInput) this.$refs.titleInput.blur()
|
||||
@@ -286,4 +293,4 @@ export default {
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default {
|
||||
},
|
||||
authors: {
|
||||
component: 'cards-author-card',
|
||||
itemPropName: 'author',
|
||||
itemPropName: 'author-mount',
|
||||
itemIdFunc: (item) => item.id
|
||||
},
|
||||
narrators: {
|
||||
|
||||
@@ -3,45 +3,45 @@
|
||||
<form class="w-full h-full px-4 py-6" @submit.prevent="submitForm">
|
||||
<div class="flex -mx-1">
|
||||
<div class="w-1/2 px-1">
|
||||
<ui-text-input-with-label ref="titleInput" v-model="details.title" :label="$strings.LabelTitle" />
|
||||
<ui-text-input-with-label ref="titleInput" v-model="details.title" :label="$strings.LabelTitle" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1">
|
||||
<ui-text-input-with-label ref="authorInput" v-model="details.author" :label="$strings.LabelAuthor" />
|
||||
<ui-text-input-with-label ref="authorInput" v-model="details.author" :label="$strings.LabelAuthor" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ui-text-input-with-label ref="feedUrlInput" v-model="details.feedUrl" :label="$strings.LabelRSSFeedURL" class="mt-2" />
|
||||
<ui-text-input-with-label ref="feedUrlInput" v-model="details.feedUrl" :label="$strings.LabelRSSFeedURL" class="mt-2" @input="handleInputChange" />
|
||||
|
||||
<ui-textarea-with-label ref="descriptionInput" v-model="details.description" :rows="3" :label="$strings.LabelDescription" class="mt-2" />
|
||||
<ui-textarea-with-label ref="descriptionInput" v-model="details.description" :rows="3" :label="$strings.LabelDescription" class="mt-2" @input="handleInputChange" />
|
||||
|
||||
<div class="flex mt-2 -mx-1">
|
||||
<div class="w-1/2 px-1">
|
||||
<ui-multi-select ref="genresSelect" v-model="details.genres" :label="$strings.LabelGenres" :items="genres" />
|
||||
<ui-multi-select ref="genresSelect" v-model="details.genres" :label="$strings.LabelGenres" :items="genres" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1">
|
||||
<ui-multi-select ref="tagsSelect" v-model="newTags" :label="$strings.LabelTags" :items="tags" />
|
||||
<ui-multi-select ref="tagsSelect" v-model="newTags" :label="$strings.LabelTags" :items="tags" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-2 -mx-1">
|
||||
<div class="w-1/4 px-1">
|
||||
<ui-text-input-with-label ref="releaseDateInput" v-model="details.releaseDate" :label="$strings.LabelReleaseDate" />
|
||||
<ui-text-input-with-label ref="releaseDateInput" v-model="details.releaseDate" :label="$strings.LabelReleaseDate" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="w-1/4 px-1">
|
||||
<ui-text-input-with-label ref="itunesIdInput" v-model="details.itunesId" label="iTunes ID" />
|
||||
<ui-text-input-with-label ref="itunesIdInput" v-model="details.itunesId" label="iTunes ID" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="w-1/4 px-1">
|
||||
<ui-text-input-with-label ref="languageInput" v-model="details.language" :label="$strings.LabelLanguage" />
|
||||
<ui-text-input-with-label ref="languageInput" v-model="details.language" :label="$strings.LabelLanguage" @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="flex-grow px-1 pt-6">
|
||||
<div class="flex justify-center">
|
||||
<ui-checkbox v-model="details.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
|
||||
<ui-checkbox v-model="details.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-2 -mx-1">
|
||||
<div class="w-1/4 px-1">
|
||||
<ui-dropdown :label="$strings.LabelPodcastType" v-model="details.type" :items="podcastTypes" small class="max-w-52" />
|
||||
<ui-dropdown :label="$strings.LabelPodcastType" v-model="details.type" :items="podcastTypes" small class="max-w-52" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -101,10 +101,21 @@ export default {
|
||||
return this.$store.state.libraries.filterData || {}
|
||||
},
|
||||
podcastTypes() {
|
||||
return this.$store.state.globals.podcastTypes || []
|
||||
return this.$store.state.globals.podcastTypes.map((e) => {
|
||||
return {
|
||||
text: this.$strings[e.descriptionKey] || e.text,
|
||||
value: e.value
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInputChange() {
|
||||
this.$emit('change', {
|
||||
libraryItemId: this.libraryItem.id,
|
||||
hasChanges: this.checkForChanges().hasChanges
|
||||
})
|
||||
},
|
||||
getDetails() {
|
||||
this.forceBlur()
|
||||
return this.checkForChanges()
|
||||
@@ -136,6 +147,8 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.handleInputChange()
|
||||
},
|
||||
forceBlur() {
|
||||
if (this.$refs.titleInput) this.$refs.titleInput.blur()
|
||||
|
||||
@@ -5,14 +5,14 @@ import Tooltip from '@/components/ui/Tooltip.vue'
|
||||
import LoadingSpinner from '@/components/widgets/LoadingSpinner.vue'
|
||||
|
||||
describe('AuthorCard', () => {
|
||||
const author = {
|
||||
const authorMount = {
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
numBooks: 5
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
author,
|
||||
authorMount,
|
||||
nameBelow: false
|
||||
}
|
||||
|
||||
|
||||
@@ -357,7 +357,8 @@ export default {
|
||||
teardown: false,
|
||||
transports: ['websocket'],
|
||||
upgrade: false,
|
||||
reconnection: true
|
||||
reconnection: true,
|
||||
path: `${this.$config.routerBasePath}/socket.io`
|
||||
})
|
||||
this.$root.socket = this.socket
|
||||
console.log('Socket initialized')
|
||||
|
||||
@@ -4,6 +4,7 @@ import LazySeriesCard from '@/components/cards/LazySeriesCard'
|
||||
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
|
||||
import LazyPlaylistCard from '@/components/cards/LazyPlaylistCard'
|
||||
import LazyAlbumCard from '@/components/cards/LazyAlbumCard'
|
||||
import AuthorCard from '@/components/cards/AuthorCard'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -20,6 +21,7 @@ export default {
|
||||
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
|
||||
if (this.entityName === 'playlists') return Vue.extend(LazyPlaylistCard)
|
||||
if (this.entityName === 'albums') return Vue.extend(LazyAlbumCard)
|
||||
if (this.entityName === 'authors') return Vue.extend(AuthorCard)
|
||||
return Vue.extend(LazyBookCard)
|
||||
},
|
||||
getComponentName() {
|
||||
@@ -27,6 +29,7 @@ export default {
|
||||
if (this.entityName === 'collections') return 'cards-lazy-collection-card'
|
||||
if (this.entityName === 'playlists') return 'cards-lazy-playlist-card'
|
||||
if (this.entityName === 'albums') return 'cards-lazy-album-card'
|
||||
if (this.entityName === 'authors') return 'cards-author-card'
|
||||
return 'cards-lazy-book-card'
|
||||
},
|
||||
async setCardSize() {
|
||||
@@ -46,13 +49,14 @@ export default {
|
||||
props.orderBy = this.seriesSortBy
|
||||
}
|
||||
const instance = new ComponentClass({
|
||||
propsData: props
|
||||
propsData: props,
|
||||
parent: this
|
||||
})
|
||||
instance.$mount()
|
||||
this.resizeObserver = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
this.cardWidth = entry.contentRect.width
|
||||
this.cardHeight = entry.contentRect.height
|
||||
this.cardWidth = entry.borderBoxSize[0].inlineSize
|
||||
this.cardHeight = entry.borderBoxSize[0].blockSize
|
||||
this.resizeObserver.disconnect()
|
||||
this.$refs.bookshelf.removeChild(instance.$el)
|
||||
}
|
||||
@@ -72,7 +76,7 @@ export default {
|
||||
})
|
||||
const timeAfter = performance.now()
|
||||
},
|
||||
async mountEntityCard(index) {
|
||||
mountEntityCard(index) {
|
||||
var shelf = Math.floor(index / this.entitiesPerShelf)
|
||||
var shelfEl = document.getElementById(`shelf-${shelf}`)
|
||||
if (!shelfEl) {
|
||||
@@ -114,6 +118,7 @@ export default {
|
||||
const _this = this
|
||||
const instance = new ComponentClass({
|
||||
propsData: props,
|
||||
parent: this,
|
||||
created() {
|
||||
this.$on('edit', (entity) => {
|
||||
if (_this.editEntity) _this.editEntity(entity)
|
||||
|
||||
+39
-52
@@ -1,19 +1,24 @@
|
||||
const pkg = require('./package.json')
|
||||
|
||||
const routerBasePath = process.env.ROUTER_BASE_PATH || ''
|
||||
const serverHostUrl = process.env.NODE_ENV === 'production' ? '' : 'http://localhost:3333'
|
||||
const serverPaths = ['api/', 'public/', 'hls/', 'auth/', 'feed/', 'status', 'login', 'logout', 'init']
|
||||
const proxy = Object.fromEntries(serverPaths.map((path) => [`${routerBasePath}/${path}`, { target: process.env.NODE_ENV !== 'production' ? serverHostUrl : '/' }]))
|
||||
|
||||
module.exports = {
|
||||
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
|
||||
ssr: false,
|
||||
target: 'static',
|
||||
dev: process.env.NODE_ENV !== 'production',
|
||||
env: {
|
||||
serverUrl: process.env.NODE_ENV === 'production' ? process.env.ROUTER_BASE_PATH || '' : 'http://localhost:3333',
|
||||
serverUrl: serverHostUrl + routerBasePath,
|
||||
chromecastReceiver: 'FD1F76C5'
|
||||
},
|
||||
telemetry: false,
|
||||
|
||||
publicRuntimeConfig: {
|
||||
version: pkg.version,
|
||||
routerBasePath: process.env.ROUTER_BASE_PATH || ''
|
||||
routerBasePath
|
||||
},
|
||||
|
||||
// Global page headers: https://go.nuxtjs.dev/config-head
|
||||
@@ -22,38 +27,23 @@ module.exports = {
|
||||
htmlAttrs: {
|
||||
lang: 'en'
|
||||
},
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ hid: 'description', name: 'description', content: '' },
|
||||
{ hid: 'robots', name: 'robots', content: 'noindex' }
|
||||
],
|
||||
meta: [{ charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: '' }, { hid: 'robots', name: 'robots', content: 'noindex' }],
|
||||
script: [],
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/x-icon', href: (process.env.ROUTER_BASE_PATH || '') + '/favicon.ico' },
|
||||
{ rel: 'apple-touch-icon', href: (process.env.ROUTER_BASE_PATH || '') + '/ios_icon.png' }
|
||||
{ rel: 'icon', type: 'image/x-icon', href: routerBasePath + '/favicon.ico' },
|
||||
{ rel: 'apple-touch-icon', href: routerBasePath + '/ios_icon.png' }
|
||||
]
|
||||
},
|
||||
|
||||
router: {
|
||||
base: process.env.ROUTER_BASE_PATH || ''
|
||||
base: routerBasePath
|
||||
},
|
||||
|
||||
// Global CSS: https://go.nuxtjs.dev/config-css
|
||||
css: [
|
||||
'@/assets/tailwind.css',
|
||||
'@/assets/app.css'
|
||||
],
|
||||
css: ['@/assets/tailwind.css', '@/assets/app.css'],
|
||||
|
||||
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
|
||||
plugins: [
|
||||
'@/plugins/constants.js',
|
||||
'@/plugins/init.client.js',
|
||||
'@/plugins/axios.js',
|
||||
'@/plugins/toast.js',
|
||||
'@/plugins/utils.js',
|
||||
'@/plugins/i18n.js'
|
||||
],
|
||||
plugins: ['@/plugins/constants.js', '@/plugins/init.client.js', '@/plugins/axios.js', '@/plugins/toast.js', '@/plugins/utils.js', '@/plugins/i18n.js'],
|
||||
|
||||
// Auto import components: https://go.nuxtjs.dev/config-components
|
||||
components: true,
|
||||
@@ -65,30 +55,25 @@ module.exports = {
|
||||
],
|
||||
|
||||
// Modules: https://go.nuxtjs.dev/config-modules
|
||||
modules: [
|
||||
'nuxt-socket-io',
|
||||
'@nuxtjs/axios',
|
||||
'@nuxtjs/proxy'
|
||||
],
|
||||
modules: ['nuxt-socket-io', '@nuxtjs/axios', '@nuxtjs/proxy'],
|
||||
|
||||
proxy: {
|
||||
'/api/': { target: process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '/' },
|
||||
'/dev/': { target: 'http://localhost:3333', pathRewrite: { '^/dev/': '' } }
|
||||
},
|
||||
proxy,
|
||||
|
||||
io: {
|
||||
sockets: [{
|
||||
name: 'dev',
|
||||
url: 'http://localhost:3333'
|
||||
},
|
||||
{
|
||||
name: 'prod'
|
||||
}]
|
||||
sockets: [
|
||||
{
|
||||
name: 'dev',
|
||||
url: serverHostUrl
|
||||
},
|
||||
{
|
||||
name: 'prod'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Axios module configuration: https://go.nuxtjs.dev/config-axios
|
||||
axios: {
|
||||
baseURL: process.env.ROUTER_BASE_PATH || ''
|
||||
baseURL: routerBasePath
|
||||
},
|
||||
|
||||
// nuxt/pwa https://pwa.nuxtjs.org
|
||||
@@ -108,11 +93,11 @@ module.exports = {
|
||||
background_color: '#232323',
|
||||
icons: [
|
||||
{
|
||||
src: (process.env.ROUTER_BASE_PATH || '') + '/icon.svg',
|
||||
src: routerBasePath + '/icon.svg',
|
||||
sizes: 'any'
|
||||
},
|
||||
{
|
||||
src: (process.env.ROUTER_BASE_PATH || '') + '/icon192.png',
|
||||
src: routerBasePath + '/icon192.png',
|
||||
type: 'image/png',
|
||||
sizes: 'any'
|
||||
}
|
||||
@@ -129,10 +114,12 @@ module.exports = {
|
||||
// Build Configuration: https://go.nuxtjs.dev/config-build
|
||||
build: {
|
||||
postcss: {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
postcssOptions: {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watchers: {
|
||||
@@ -147,12 +134,12 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Temporary workaround for @nuxt-community/tailwindcss-module.
|
||||
*
|
||||
* Reported: 2022-05-23
|
||||
* See: [Issue tracker](https://github.com/nuxt-community/tailwindcss-module/issues/480)
|
||||
*/
|
||||
* Temporary workaround for @nuxt-community/tailwindcss-module.
|
||||
*
|
||||
* Reported: 2022-05-23
|
||||
* See: [Issue tracker](https://github.com/nuxt-community/tailwindcss-module/issues/480)
|
||||
*/
|
||||
devServerHandlers: [],
|
||||
|
||||
ignore: ["**/*.test.*", "**/*.cy.*"]
|
||||
ignore: ['**/*.test.*', '**/*.cy.*']
|
||||
}
|
||||
|
||||
Generated
+3106
-2020
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "2.13.0",
|
||||
"version": "2.15.1",
|
||||
"buildNumber": 1,
|
||||
"description": "Self-hosted audiobook and podcast client",
|
||||
"main": "index.js",
|
||||
@@ -27,7 +27,7 @@
|
||||
"fast-average-color": "^9.4.0",
|
||||
"hls.js": "^1.5.7",
|
||||
"libarchive.js": "^1.3.0",
|
||||
"nuxt": "^2.17.3",
|
||||
"nuxt": "^2.18.1",
|
||||
"nuxt-socket-io": "^1.1.18",
|
||||
"trix": "^1.3.1",
|
||||
"v-click-outside": "^3.1.2",
|
||||
|
||||
@@ -415,7 +415,7 @@ export default {
|
||||
const audioEl = this.audioEl || document.createElement('audio')
|
||||
var src = audioTrack.contentUrl + `?token=${this.userToken}`
|
||||
if (this.$isDev) {
|
||||
src = `http://localhost:3333${this.$config.routerBasePath}${src}`
|
||||
src = `${process.env.serverUrl}${src}`
|
||||
}
|
||||
|
||||
audioEl.src = src
|
||||
@@ -486,7 +486,7 @@ export default {
|
||||
.then((data) => {
|
||||
this.saving = false
|
||||
if (data.updated) {
|
||||
this.$toast.success('Chapters updated')
|
||||
this.$toast.success(this.$strings.ToastChaptersUpdated)
|
||||
if (this.previousRoute) {
|
||||
this.$router.push(this.previousRoute)
|
||||
} else {
|
||||
@@ -499,7 +499,7 @@ export default {
|
||||
.catch((error) => {
|
||||
this.saving = false
|
||||
console.error('Failed to update chapters', error)
|
||||
this.$toast.error('Failed to update chapters')
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
},
|
||||
applyChapterNamesOnly() {
|
||||
@@ -533,7 +533,7 @@ export default {
|
||||
},
|
||||
findChapters() {
|
||||
if (!this.asinInput) {
|
||||
this.$toast.error('Must input an ASIN')
|
||||
this.$toast.error(this.$strings.ToastAsinRequired)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -628,15 +628,27 @@ export default {
|
||||
.finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
},
|
||||
libraryItemUpdated(libraryItem) {
|
||||
if (libraryItem.id === this.libraryItem.id) {
|
||||
if (!!libraryItem.media.metadata.asin && this.mediaMetadata.asin !== libraryItem.media.metadata.asin) {
|
||||
this.asinInput = libraryItem.media.metadata.asin
|
||||
}
|
||||
this.libraryItem = libraryItem
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.regionInput = localStorage.getItem('audibleRegion') || 'US'
|
||||
this.asinInput = this.mediaMetadata.asin || null
|
||||
this.initChapters()
|
||||
|
||||
this.$eventBus.$on(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroyAudioEl()
|
||||
|
||||
this.$eventBus.$off(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -63,11 +63,11 @@
|
||||
<div class="w-full max-w-4xl mx-auto">
|
||||
<!-- queued alert -->
|
||||
<widgets-alert v-if="isMetadataEmbedQueued" type="warning" class="mb-4">
|
||||
<p class="text-lg">Audiobook is queued for metadata embed ({{ queuedEmbedLIds.length }} in queue)</p>
|
||||
<p class="text-lg">{{ $getString('MessageEmbedQueue', [queuedEmbedLIds.length]) }}</p>
|
||||
</widgets-alert>
|
||||
<!-- metadata embed action buttons -->
|
||||
<div v-else-if="isEmbedTool" class="w-full flex justify-end items-center mb-4">
|
||||
<ui-checkbox v-if="!isTaskFinished" v-model="shouldBackupAudioFiles" :disabled="processing" label="Backup audio files" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" @input="toggleBackupAudioFiles" />
|
||||
<ui-checkbox v-if="!isTaskFinished" v-model="shouldBackupAudioFiles" :disabled="processing" :label="$strings.LabelBackupAudioFiles" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" @input="toggleBackupAudioFiles" />
|
||||
|
||||
<div class="flex-grow" />
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<!-- m4b embed action buttons -->
|
||||
<div v-else class="w-full flex items-center mb-4">
|
||||
<button :disabled="processing" class="text-sm uppercase text-gray-200 flex items-center pt-px pl-1 pr-2 hover:bg-white/5 rounded-md" @click="showEncodeOptions = !showEncodeOptions">
|
||||
<span class="material-symbols text-xl">{{ showEncodeOptions || usingCustomEncodeOptions ? 'check_box' : 'check_box_outline_blank' }}</span> <span class="pl-1">Use Advanced Options</span>
|
||||
<span class="material-symbols text-xl">{{ showEncodeOptions || usingCustomEncodeOptions ? 'check_box' : 'check_box_outline_blank' }}</span> <span class="pl-1">{{ $strings.LabelUseAdvancedOptions }}</span>
|
||||
</button>
|
||||
|
||||
<div class="flex-grow" />
|
||||
@@ -94,11 +94,11 @@
|
||||
<transition name="slide">
|
||||
<div v-if="showEncodeOptions || usingCustomEncodeOptions" class="mb-4 pb-4 border-b border-white/10">
|
||||
<div class="flex flex-wrap -mx-2">
|
||||
<ui-text-input-with-label ref="bitrateInput" v-model="encodingOptions.bitrate" :disabled="processing || isTaskFinished" :label="'Audio Bitrate (e.g. 128k)'" class="m-2 max-w-40" @input="bitrateChanged" />
|
||||
<ui-text-input-with-label ref="channelsInput" v-model="encodingOptions.channels" :disabled="processing || isTaskFinished" :label="'Audio Channels (1 or 2)'" class="m-2 max-w-40" @input="channelsChanged" />
|
||||
<ui-text-input-with-label ref="codecInput" v-model="encodingOptions.codec" :disabled="processing || isTaskFinished" :label="'Audio Codec'" class="m-2 max-w-40" @input="codecChanged" />
|
||||
<ui-text-input-with-label ref="bitrateInput" v-model="encodingOptions.bitrate" :disabled="processing || isTaskFinished" :label="$strings.LabelAudioBitrate" class="m-2 max-w-40" @input="bitrateChanged" />
|
||||
<ui-text-input-with-label ref="channelsInput" v-model="encodingOptions.channels" :disabled="processing || isTaskFinished" :label="$strings.LabelAudioChannels" class="m-2 max-w-40" @input="channelsChanged" />
|
||||
<ui-text-input-with-label ref="codecInput" v-model="encodingOptions.codec" :disabled="processing || isTaskFinished" :label="$strings.LabelAudioCodec" class="m-2 max-w-40" @input="codecChanged" />
|
||||
</div>
|
||||
<p class="text-sm text-warning">Warning: Do not update these settings unless you are familiar with ffmpeg encoding options.</p>
|
||||
<p class="text-sm text-warning">{{ $strings.LabelEncodingWarningAdvancedSettings }}</p>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -106,36 +106,36 @@
|
||||
<div class="mb-4">
|
||||
<div v-if="isEmbedTool" class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">Metadata will be embedded in the audio tracks inside your audiobook folder.</p>
|
||||
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingInfoEmbedded }}</p>
|
||||
</div>
|
||||
<div v-else class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">
|
||||
Finished M4B will be put into your audiobook folder at <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">.../{{ libraryItemRelPath }}/</span>.
|
||||
{{ $strings.LabelEncodingFinishedM4B }} <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">.../{{ libraryItemRelPath }}/</span>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="shouldBackupAudioFiles || isM4BTool" class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">
|
||||
A backup of your original audio files will be stored in <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">/metadata/cache/items/{{ libraryItemId }}/</span>. Make sure to periodically purge items cache.
|
||||
{{ $strings.LabelEncodingBackupLocation }} <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">/metadata/cache/items/{{ libraryItemId }}/</span>. {{ $strings.LabelEncodingClearItemCache }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="isEmbedTool && audioFiles.length > 1" class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">Chapters are not embedded in multi-track audiobooks.</p>
|
||||
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingChaptersNotEmbedded }}</p>
|
||||
</div>
|
||||
<div v-if="isM4BTool" class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">Encoding can take up to 30 minutes.</p>
|
||||
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingTimeWarning }}</p>
|
||||
</div>
|
||||
<div v-if="isM4BTool" class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">If you have the watcher disabled you will need to re-scan this audiobook afterwards.</p>
|
||||
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingWatcherDisabled }}</p>
|
||||
</div>
|
||||
<div class="flex items-start mb-2">
|
||||
<span class="material-symbols text-base text-warning pt-1">star</span>
|
||||
<p class="text-gray-200 ml-2">Once the task is started you can navigate away from this page.</p>
|
||||
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingStartedNavigation }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -269,11 +269,11 @@ export default {
|
||||
},
|
||||
availableTools() {
|
||||
if (this.isSingleM4b) {
|
||||
return [{ value: 'embed', text: 'Embed Metadata' }]
|
||||
return [{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata }]
|
||||
} else {
|
||||
return [
|
||||
{ value: 'embed', text: 'Embed Metadata' },
|
||||
{ value: 'm4b', text: 'M4B Encoder' }
|
||||
{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata },
|
||||
{ value: 'm4b', text: this.$strings.LabelToolsM4bEncoder }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -370,7 +370,7 @@ export default {
|
||||
},
|
||||
embedClick() {
|
||||
const payload = {
|
||||
message: `Are you sure you want to embed metadata in ${this.audioFiles.length} audio files?`,
|
||||
message: this.$getString('MessageConfirmEmbedMetadataInAudioFiles', [this.audioFiles.length]),
|
||||
callback: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this.updateAudioFileMetadata()
|
||||
|
||||
@@ -53,7 +53,7 @@ export default {
|
||||
})
|
||||
|
||||
if (!author) {
|
||||
return redirect(`/library/${store.state.libraries.currentLibraryId}/authors`)
|
||||
return redirect(`/library/${store.state.libraries.currentLibraryId}/bookshelf/authors`)
|
||||
}
|
||||
|
||||
if (store.state.libraries.currentLibraryId !== author.libraryId || !store.state.libraries.filterData) {
|
||||
@@ -109,7 +109,7 @@ export default {
|
||||
authorRemoved(author) {
|
||||
if (author.id === this.author.id) {
|
||||
console.warn('Author was removed')
|
||||
this.$router.replace(`/library/${this.currentLibraryId}/authors`)
|
||||
this.$router.replace(`/library/${this.currentLibraryId}/bookshelf/authors`)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -97,8 +97,8 @@
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<template v-for="libraryItem in libraryItemCopies">
|
||||
<div :key="libraryItem.id" class="w-full max-w-3xl border border-black-300 p-6 -ml-px -mt-px">
|
||||
<widgets-book-details-edit v-if="libraryItem.mediaType === 'book'" :ref="`itemForm-${libraryItem.id}`" :library-item="libraryItem" />
|
||||
<widgets-podcast-details-edit v-else :ref="`itemForm-${libraryItem.id}`" :library-item="libraryItem" />
|
||||
<widgets-book-details-edit v-if="libraryItem.mediaType === 'book'" :ref="`itemForm-${libraryItem.id}`" :library-item="libraryItem" @change="handleItemChange" />
|
||||
<widgets-podcast-details-edit v-else :ref="`itemForm-${libraryItem.id}`" :library-item="libraryItem" @change="handleItemChange" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
<div :class="isScrollable ? 'fixed left-0 box-shadow-lg-up bg-primary' : ''" class="w-full h-20 px-4 flex items-center border-t border-bg z-40" :style="{ bottom: streamLibraryItem ? '165px' : '0px' }">
|
||||
<div class="flex-grow" />
|
||||
<ui-btn color="success" :padding-x="8" class="text-lg" :loading="isProcessing" @click.prevent="saveClick">{{ $strings.ButtonSave }}</ui-btn>
|
||||
<ui-btn color="success" :padding-x="8" class="text-lg" :loading="isProcessing" :disabled="!hasChanges" @click.prevent="saveClick">{{ $strings.ButtonSave }}</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -170,7 +170,8 @@ export default {
|
||||
abridged: false
|
||||
},
|
||||
appendableKeys: ['authors', 'genres', 'tags', 'narrators', 'series'],
|
||||
openMapOptions: false
|
||||
openMapOptions: false,
|
||||
itemsWithChanges: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -221,9 +222,19 @@ export default {
|
||||
},
|
||||
hasSelectedBatchUsage() {
|
||||
return Object.values(this.selectedBatchUsage).some((b) => !!b)
|
||||
},
|
||||
hasChanges() {
|
||||
return this.itemsWithChanges.length > 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleItemChange(itemChange) {
|
||||
if (!itemChange.hasChanges) {
|
||||
this.itemsWithChanges = this.itemsWithChanges.filter((id) => id !== itemChange.libraryItemId)
|
||||
} else if (!this.itemsWithChanges.includes(itemChange.libraryItemId)) {
|
||||
this.itemsWithChanges.push(itemChange.libraryItemId)
|
||||
}
|
||||
},
|
||||
blurBatchForm() {
|
||||
if (this.$refs.seriesSelect && this.$refs.seriesSelect.isFocused) {
|
||||
this.$refs.seriesSelect.forceBlur()
|
||||
@@ -283,38 +294,10 @@ export default {
|
||||
removedSeriesItem(item) {},
|
||||
newNarratorItem(item) {},
|
||||
removedNarratorItem(item) {},
|
||||
newTagItem(item) {
|
||||
// if (item && !this.newTagItems.includes(item)) {
|
||||
// this.newTagItems.push(item)
|
||||
// }
|
||||
},
|
||||
removedTagItem(item) {
|
||||
// If newly added, remove if not used on any other items
|
||||
// if (item && this.newTagItems.includes(item)) {
|
||||
// var usedByOtherAb = this.libraryItemCopies.find((ab) => {
|
||||
// return ab.tags && ab.tags.includes(item)
|
||||
// })
|
||||
// if (!usedByOtherAb) {
|
||||
// this.newTagItems = this.newTagItems.filter((t) => t !== item)
|
||||
// }
|
||||
// }
|
||||
},
|
||||
newGenreItem(item) {
|
||||
// if (item && !this.newGenreItems.includes(item)) {
|
||||
// this.newGenreItems.push(item)
|
||||
// }
|
||||
},
|
||||
removedGenreItem(item) {
|
||||
// If newly added, remove if not used on any other items
|
||||
// if (item && this.newGenreItems.includes(item)) {
|
||||
// var usedByOtherAb = this.libraryItemCopies.find((ab) => {
|
||||
// return ab.book.genres && ab.book.genres.includes(item)
|
||||
// })
|
||||
// if (!usedByOtherAb) {
|
||||
// this.newGenreItems = this.newGenreItems.filter((t) => t !== item)
|
||||
// }
|
||||
// }
|
||||
},
|
||||
newTagItem(item) {},
|
||||
removedTagItem(item) {},
|
||||
newGenreItem(item) {},
|
||||
removedGenreItem(item) {},
|
||||
init() {
|
||||
// TODO: Better deep cloning of library items
|
||||
this.libraryItemCopies = this.libraryItems.map((li) => {
|
||||
@@ -376,6 +359,7 @@ export default {
|
||||
.then((data) => {
|
||||
this.isProcessing = false
|
||||
if (data.updates) {
|
||||
this.itemsWithChanges = []
|
||||
this.$toast.success(`Successfully updated ${data.updates} items`)
|
||||
this.$router.replace(`/library/${this.currentLibraryId}/bookshelf`)
|
||||
} else {
|
||||
@@ -387,10 +371,28 @@ export default {
|
||||
this.$toast.error('Failed to batch update')
|
||||
this.isProcessing = false
|
||||
})
|
||||
},
|
||||
beforeUnload(e) {
|
||||
if (!e || !this.hasChanges) return
|
||||
e.preventDefault()
|
||||
e.returnValue = ''
|
||||
}
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (this.hasChanges) {
|
||||
next(false)
|
||||
window.location = to.path
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
|
||||
window.addEventListener('beforeunload', this.beforeUnload)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('beforeunload', this.beforeUnload)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<ui-btn v-if="showPlayButton" :disabled="streaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="clickPlay">
|
||||
<span v-show="!streaming" class="material-symbols fill text-2xl -ml-2 pr-1 text-white">play_arrow</span>
|
||||
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }}
|
||||
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlayAll }}
|
||||
</ui-btn>
|
||||
|
||||
<!-- RSS feed -->
|
||||
|
||||
@@ -317,7 +317,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update server settings', error)
|
||||
this.$toast.error(this.$strings.ToastServerSettingsUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.savingSettings = false
|
||||
|
||||
@@ -162,7 +162,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to save backup path', error)
|
||||
const errorMsg = error.response?.data || this.$strings.ToastBackupPathUpdateFailed
|
||||
const errorMsg = error.response?.data || this.$strings.ToastFailedToUpdate
|
||||
this.$toast.error(errorMsg)
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -292,7 +292,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update email settings', error)
|
||||
this.$toast.error(this.$strings.ToastEmailSettingsUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.savingSettings = false
|
||||
|
||||
@@ -290,7 +290,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update prefixes', error)
|
||||
this.$toast.error(this.$strings.ToastSortingPrefixesUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.savingPrefixes = false
|
||||
@@ -328,7 +328,6 @@ export default {
|
||||
.dispatch('updateServerSettings', payload)
|
||||
.then(() => {
|
||||
this.updatingServerSettings = false
|
||||
this.$toast.success(this.$strings.ToastServerSettingsUpdateSuccess)
|
||||
|
||||
if (payload.language) {
|
||||
// Updating language after save allows for re-rendering
|
||||
@@ -338,7 +337,7 @@ export default {
|
||||
.catch((error) => {
|
||||
console.error('Failed to update server settings', error)
|
||||
this.updatingServerSettings = false
|
||||
this.$toast.error(this.$strings.ToastServerSettingsUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
},
|
||||
initServerSettings() {
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
</template>
|
||||
|
||||
<div class="flex justify-between mb-2 place-items-end">
|
||||
<ui-text-input ref="input" v-model="search" placeholder="Search filter.." @input="inputUpdate" clearable class="w-full sm:w-40 h-8 text-sm sm:mb-0" />
|
||||
<ui-text-input ref="input" v-model="search" :placeholder="$strings.PlaceholderSearch" @input="inputUpdate" clearable class="w-full sm:w-40 h-8 text-sm sm:mb-0" />
|
||||
|
||||
<ui-dropdown v-model="newServerSettings.logLevel" label="Server Log Level" :items="logLevelItems" @input="logLevelUpdated" class="w-full sm:w-44" />
|
||||
<ui-dropdown v-model="newServerSettings.logLevel" :label="$strings.LabelServerLogLevel" :items="logLevelItems" @input="logLevelUpdated" class="w-full sm:w-44" />
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
|
||||
@@ -132,7 +132,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update notification settings', error)
|
||||
this.$toast.error(this.$strings.ToastNotificationSettingsUpdateFailed)
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
})
|
||||
.finally(() => {
|
||||
this.savingSettings = false
|
||||
|
||||
@@ -39,16 +39,11 @@
|
||||
><span :key="index" v-if="index < seriesList.length - 1">, </span>
|
||||
</template>
|
||||
|
||||
<template v-if="!isVideo">
|
||||
<p v-if="isPodcast" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl">{{ $getString('LabelByAuthor', [podcastAuthor]) }}</p>
|
||||
<p v-else-if="musicArtists.length" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl max-w-[calc(100vw-2rem)] overflow-hidden overflow-ellipsis">
|
||||
<nuxt-link v-for="(artist, index) in musicArtists" :key="index" :to="`/artist/${$encode(artist)}`" class="hover:underline">{{ artist }}<span v-if="index < musicArtists.length - 1">, </span></nuxt-link>
|
||||
</p>
|
||||
<p v-else-if="authors.length" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl max-w-[calc(100vw-2rem)] overflow-hidden overflow-ellipsis">
|
||||
by <nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link>
|
||||
</p>
|
||||
<p v-else class="mb-2 mt-0.5 text-gray-200 text-xl">by Unknown</p>
|
||||
</template>
|
||||
<p v-if="isPodcast" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl">{{ $getString('LabelByAuthor', [podcastAuthor]) }}</p>
|
||||
<p v-else-if="authors.length" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl max-w-[calc(100vw-2rem)] overflow-hidden overflow-ellipsis">
|
||||
by <nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link>
|
||||
</p>
|
||||
<p v-else class="mb-2 mt-0.5 text-gray-200 text-xl">by Unknown</p>
|
||||
|
||||
<content-library-item-details :library-item="libraryItem" />
|
||||
</div>
|
||||
@@ -109,7 +104,7 @@
|
||||
<ui-icon-btn icon="" outlined class="mx-0.5" @click="editClick" />
|
||||
</ui-tooltip>
|
||||
|
||||
<ui-tooltip v-if="!isPodcast && !isMusic" :text="userIsFinished ? $strings.MessageMarkAsNotFinished : $strings.MessageMarkAsFinished" direction="top">
|
||||
<ui-tooltip v-if="!isPodcast" :text="userIsFinished ? $strings.MessageMarkAsNotFinished : $strings.MessageMarkAsFinished" direction="top">
|
||||
<ui-read-icon-btn :disabled="isProcessingReadUpdate" :is-read="userIsFinished" class="mx-0.5" @click="toggleFinished" />
|
||||
</ui-tooltip>
|
||||
|
||||
@@ -220,12 +215,6 @@ export default {
|
||||
isPodcast() {
|
||||
return this.libraryItem.mediaType === 'podcast'
|
||||
},
|
||||
isVideo() {
|
||||
return this.libraryItem.mediaType === 'video'
|
||||
},
|
||||
isMusic() {
|
||||
return this.libraryItem.mediaType === 'music'
|
||||
},
|
||||
isMissing() {
|
||||
return this.libraryItem.isMissing
|
||||
},
|
||||
@@ -240,8 +229,6 @@ export default {
|
||||
},
|
||||
showPlayButton() {
|
||||
if (this.isMissing || this.isInvalid) return false
|
||||
if (this.isMusic) return !!this.audioFile
|
||||
if (this.isVideo) return !!this.videoFile
|
||||
if (this.isPodcast) return this.podcastEpisodes.length
|
||||
return this.tracks.length
|
||||
},
|
||||
@@ -292,9 +279,6 @@ export default {
|
||||
authors() {
|
||||
return this.mediaMetadata.authors || []
|
||||
},
|
||||
musicArtists() {
|
||||
return this.mediaMetadata.artists || []
|
||||
},
|
||||
series() {
|
||||
return this.mediaMetadata.series || []
|
||||
},
|
||||
@@ -309,7 +293,7 @@ export default {
|
||||
})
|
||||
},
|
||||
duration() {
|
||||
if (!this.tracks.length && !this.audioFile) return 0
|
||||
if (!this.tracks.length) return 0
|
||||
return this.media.duration
|
||||
},
|
||||
libraryFiles() {
|
||||
@@ -321,18 +305,10 @@ export default {
|
||||
ebookFile() {
|
||||
return this.media.ebookFile
|
||||
},
|
||||
videoFile() {
|
||||
return this.media.videoFile
|
||||
},
|
||||
audioFile() {
|
||||
// Music track
|
||||
return this.media.audioFile
|
||||
},
|
||||
description() {
|
||||
return this.mediaMetadata.description || ''
|
||||
},
|
||||
userMediaProgress() {
|
||||
if (this.isMusic) return null
|
||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId)
|
||||
},
|
||||
userIsFinished() {
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
<template>
|
||||
<div class="page" :class="streamLibraryItem ? 'streaming' : ''">
|
||||
<app-book-shelf-toolbar page="authors" is-home :authors="authors" />
|
||||
<div id="bookshelf" class="w-full h-full p-8e overflow-y-auto" :style="{ fontSize: sizeMultiplier + 'rem' }">
|
||||
<!-- Cover size widget -->
|
||||
<widgets-cover-size-widget class="fixed right-4 z-50" :style="{ bottom: streamLibraryItem ? '181px' : '16px' }" />
|
||||
<div class="flex flex-wrap justify-center">
|
||||
<template v-for="author in authorsSorted">
|
||||
<cards-author-card :key="author.id" :author="author" class="p-3e" @edit="editAuthor" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
async asyncData({ store, params, redirect, query, app }) {
|
||||
var libraryId = params.library
|
||||
var libraryData = await store.dispatch('libraries/fetch', libraryId)
|
||||
if (!libraryData) {
|
||||
return redirect('/oops?message=Library not found')
|
||||
}
|
||||
|
||||
const library = libraryData.library
|
||||
if (library.mediaType === 'podcast') {
|
||||
return redirect(`/library/${libraryId}`)
|
||||
}
|
||||
|
||||
return {
|
||||
libraryId
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
authors: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sizeMultiplier() {
|
||||
return this.$store.getters['user/getSizeMultiplier']
|
||||
},
|
||||
streamLibraryItem() {
|
||||
return this.$store.state.streamLibraryItem
|
||||
},
|
||||
currentLibraryId() {
|
||||
return this.$store.state.libraries.currentLibraryId
|
||||
},
|
||||
selectedAuthor() {
|
||||
return this.$store.state.globals.selectedAuthor
|
||||
},
|
||||
authorSortBy() {
|
||||
return this.$store.getters['user/getUserSetting']('authorSortBy') || 'name'
|
||||
},
|
||||
authorSortDesc() {
|
||||
return !!this.$store.getters['user/getUserSetting']('authorSortDesc')
|
||||
},
|
||||
authorsSorted() {
|
||||
const sortProp = this.authorSortBy
|
||||
const bDesc = this.authorSortDesc ? -1 : 1
|
||||
return this.authors.sort((a, b) => {
|
||||
if (typeof a[sortProp] === 'number' && typeof b[sortProp] === 'number') {
|
||||
// Fallback to name sort if equal
|
||||
if (a[sortProp] === b[sortProp]) return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) * bDesc
|
||||
return a[sortProp] > b[sortProp] ? bDesc : -bDesc
|
||||
}
|
||||
return a[sortProp]?.localeCompare(b[sortProp], undefined, { sensitivity: 'base' }) * bDesc
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
this.authors = await this.$axios
|
||||
.$get(`/api/libraries/${this.currentLibraryId}/authors`)
|
||||
.then((response) => response.authors)
|
||||
.catch((error) => {
|
||||
console.error('Failed to load authors', error)
|
||||
return []
|
||||
})
|
||||
this.loading = false
|
||||
},
|
||||
authorAdded(author) {
|
||||
if (!this.authors.some((au) => au.id === author.id)) {
|
||||
this.authors.push(author)
|
||||
}
|
||||
},
|
||||
authorUpdated(author) {
|
||||
this.authors = this.authors.map((au) => {
|
||||
if (au.id === author.id) {
|
||||
return author
|
||||
}
|
||||
return au
|
||||
})
|
||||
},
|
||||
authorRemoved(author) {
|
||||
this.authors = this.authors.filter((au) => au.id !== author.id)
|
||||
},
|
||||
editAuthor(author) {
|
||||
this.$store.commit('globals/showEditAuthorModal', author)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
this.$root.socket.on('author_added', this.authorAdded)
|
||||
this.$root.socket.on('author_updated', this.authorUpdated)
|
||||
this.$root.socket.on('author_removed', this.authorRemoved)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$root.socket.off('author_added', this.authorAdded)
|
||||
this.$root.socket.off('author_updated', this.authorUpdated)
|
||||
this.$root.socket.off('author_removed', this.authorRemoved)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -27,7 +27,7 @@ export default {
|
||||
|
||||
// Redirect podcast libraries
|
||||
const library = libraryData.library
|
||||
if (library.mediaType === 'podcast' && (params.id === 'collections' || params.id === 'series')) {
|
||||
if (library.mediaType === 'podcast' && (params.id === 'collections' || params.id === 'series' || params.id === 'authors')) {
|
||||
return redirect(`/library/${libraryId}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to updated narrator', error)
|
||||
this.$toast.error('Failed to update narrator')
|
||||
this.$toast.error(this.$strings.ToastFailedToUpdate)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div id="bookshelf" class="w-full overflow-y-auto px-2 py-6 sm:px-4 md:p-12 relative">
|
||||
<div class="w-full max-w-4xl mx-auto flex">
|
||||
<form @submit.prevent="submit" class="flex flex-grow">
|
||||
<ui-text-input v-model="searchInput" type="search" :disabled="processing" placeholder="Enter search term or RSS feed URL" class="flex-grow mr-2 text-sm md:text-base" />
|
||||
<ui-text-input v-model="searchInput" type="search" :disabled="processing" :placeholder="$strings.MessagePodcastSearchField" class="flex-grow mr-2 text-sm md:text-base" />
|
||||
<ui-btn type="submit" :disabled="processing" class="hidden md:block">{{ $strings.ButtonSubmit }}</ui-btn>
|
||||
<ui-btn type="submit" :disabled="processing" class="block md:hidden" small>{{ $strings.ButtonSubmit }}</ui-btn>
|
||||
</form>
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="flex items-center">
|
||||
<a :href="podcast.pageUrl" class="text-base md:text-lg text-gray-200 hover:underline" target="_blank" @click.stop>{{ podcast.title }}</a>
|
||||
<widgets-explicit-indicator v-if="podcast.explicit" />
|
||||
<widgets-already-in-library-indicator :already-in-library="podcast.alreadyInLibrary" />
|
||||
<widgets-already-in-library-indicator v-if="podcast.alreadyInLibrary" />
|
||||
</div>
|
||||
<p class="text-sm md:text-base text-gray-300 whitespace-nowrap truncate">{{ $getString('LabelByAuthor', [podcast.artistName]) }}</p>
|
||||
<p class="text-xs text-gray-400 leading-5">{{ podcast.genres.join(', ') }}</p>
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
|
||||
if (!txt || !txt.includes('<opml') || !txt.includes('<outline ')) {
|
||||
// Quick lazy check for valid OPML
|
||||
this.$toast.error('Invalid OPML file <opml> tag not found OR an <outline> tag was not found')
|
||||
this.$toast.error(this.$strings.MessageTaskOpmlParseFastFail)
|
||||
this.processing = false
|
||||
return
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export default {
|
||||
.$post(`/api/podcasts/opml/parse`, { opmlText: txt })
|
||||
.then((data) => {
|
||||
if (!data.feeds?.length) {
|
||||
this.$toast.error('No feeds found in OPML file')
|
||||
this.$toast.error(this.$strings.MessageTaskOpmlParseNoneFound)
|
||||
} else {
|
||||
this.opmlFeeds = data.feeds || []
|
||||
this.showOPMLFeedsModal = true
|
||||
@@ -125,7 +125,7 @@ export default {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
this.$toast.error('Failed to parse OPML file')
|
||||
this.$toast.error(this.$strings.MessageTaskOpmlParseFailed)
|
||||
})
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
@@ -191,7 +191,7 @@ export default {
|
||||
return
|
||||
}
|
||||
if (!podcast.feedUrl) {
|
||||
this.$toast.error('Invalid podcast - no feed')
|
||||
this.$toast.error(this.$strings.MessageNoPodcastFeed)
|
||||
return
|
||||
}
|
||||
this.processing = true
|
||||
@@ -211,15 +211,15 @@ export default {
|
||||
async fetchExistentPodcastsInYourLibrary() {
|
||||
this.processing = true
|
||||
|
||||
const podcasts = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/items?page=0&minified=1`).catch((error) => {
|
||||
const podcastsResponse = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/podcast-titles`).catch((error) => {
|
||||
console.error('Failed to fetch podcasts', error)
|
||||
return []
|
||||
})
|
||||
this.existentPodcasts = podcasts.results.map((p) => {
|
||||
this.existentPodcasts = podcastsResponse.podcasts.map((p) => {
|
||||
return {
|
||||
title: p.media.metadata.title.toLowerCase(),
|
||||
itunesId: p.media.metadata.itunesId,
|
||||
id: p.id
|
||||
title: p.title.toLowerCase(),
|
||||
itunesId: p.itunesId,
|
||||
id: p.libraryItemId
|
||||
}
|
||||
})
|
||||
this.processing = false
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<ui-btn v-if="showPlayButton" :disabled="streaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="clickPlay">
|
||||
<span v-show="!streaming" class="material-symbols fill text-2xl -ml-2 pr-1 text-white">play_arrow</span>
|
||||
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }}
|
||||
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlayAll }}
|
||||
</ui-btn>
|
||||
|
||||
<ui-icon-btn icon="edit" class="mx-0.5" @click="editClick" />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<p v-if="mediaItemShare.playbackSession.displayAuthor" class="text-lg lg:text-xl text-slate-400 font-semibold text-center mb-1 truncate">{{ mediaItemShare.playbackSession.displayAuthor }}</p>
|
||||
|
||||
<div class="w-full pt-16">
|
||||
<player-ui ref="audioPlayer" :chapters="chapters" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
|
||||
<player-ui ref="audioPlayer" :chapters="chapters" :current-chapter="currentChapter" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,7 +51,8 @@ export default {
|
||||
windowHeight: 0,
|
||||
listeningTimeSinceSync: 0,
|
||||
coverRgb: null,
|
||||
coverBgIsLight: false
|
||||
coverBgIsLight: false,
|
||||
currentTime: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -60,16 +61,10 @@ export default {
|
||||
},
|
||||
coverUrl() {
|
||||
if (!this.playbackSession.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg`
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return `http://localhost:3333/public/share/${this.mediaItemShare.slug}/cover`
|
||||
}
|
||||
return `/public/share/${this.mediaItemShare.slug}/cover`
|
||||
return `${this.$config.routerBasePath}/public/share/${this.mediaItemShare.slug}/cover`
|
||||
},
|
||||
audioTracks() {
|
||||
return (this.playbackSession.audioTracks || []).map((track) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
track.contentUrl = `${process.env.serverUrl}${track.contentUrl}`
|
||||
}
|
||||
track.relativeContentUrl = track.contentUrl
|
||||
return track
|
||||
})
|
||||
@@ -83,6 +78,9 @@ export default {
|
||||
chapters() {
|
||||
return this.playbackSession.chapters || []
|
||||
},
|
||||
currentChapter() {
|
||||
return this.chapters.find((chapter) => chapter.start <= this.currentTime && this.currentTime < chapter.end)
|
||||
},
|
||||
coverAspectRatio() {
|
||||
const coverAspectRatio = this.playbackSession.coverAspectRatio
|
||||
return coverAspectRatio === this.$constants.BookCoverAspectRatio.STANDARD ? 1.6 : 1
|
||||
@@ -154,6 +152,7 @@ export default {
|
||||
|
||||
// Update UI
|
||||
this.$refs.audioPlayer.setCurrentTime(time)
|
||||
this.currentTime = time
|
||||
},
|
||||
setDuration() {
|
||||
if (!this.localAudioPlayer) return
|
||||
|
||||
@@ -384,12 +384,6 @@ export default {
|
||||
else itemsFailed++
|
||||
this.updateItemCardStatus(item.index, result ? 'success' : 'failed')
|
||||
}
|
||||
if (itemsUploaded) {
|
||||
this.$toast.success(`Successfully uploaded ${itemsUploaded} item${itemsUploaded > 1 ? 's' : ''}`)
|
||||
}
|
||||
if (itemsFailed) {
|
||||
this.$toast.success(`Failed to upload ${itemsFailed} item${itemsFailed > 1 ? 's' : ''}`)
|
||||
}
|
||||
this.processing = false
|
||||
this.uploadFinished = true
|
||||
}
|
||||
|
||||
@@ -23,10 +23,6 @@ export default class AudioTrack {
|
||||
get relativeContentUrl() {
|
||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return `${process.env.serverUrl}${this.contentUrl}?token=${this.userToken}`
|
||||
}
|
||||
|
||||
return this.contentUrl + `?token=${this.userToken}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
import Hls from 'hls.js'
|
||||
import EventEmitter from 'events'
|
||||
|
||||
export default class LocalVideoPlayer extends EventEmitter {
|
||||
constructor(ctx) {
|
||||
super()
|
||||
|
||||
this.ctx = ctx
|
||||
this.player = null
|
||||
|
||||
this.libraryItem = null
|
||||
this.videoTrack = null
|
||||
this.isHlsTranscode = null
|
||||
this.hlsInstance = null
|
||||
this.usingNativeplayer = false
|
||||
this.startTime = 0
|
||||
this.playWhenReady = false
|
||||
this.defaultPlaybackRate = 1
|
||||
|
||||
this.playableMimeTypes = []
|
||||
|
||||
this.initialize()
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (document.getElementById('video-player')) {
|
||||
document.getElementById('video-player').remove()
|
||||
}
|
||||
var videoEl = document.createElement('video')
|
||||
videoEl.id = 'video-player'
|
||||
// videoEl.style.display = 'none'
|
||||
videoEl.className = 'absolute bg-black z-50'
|
||||
videoEl.style.height = '216px'
|
||||
videoEl.style.width = '384px'
|
||||
videoEl.style.bottom = '80px'
|
||||
videoEl.style.left = '16px'
|
||||
document.body.appendChild(videoEl)
|
||||
this.player = videoEl
|
||||
|
||||
this.player.addEventListener('play', this.evtPlay.bind(this))
|
||||
this.player.addEventListener('pause', this.evtPause.bind(this))
|
||||
this.player.addEventListener('progress', this.evtProgress.bind(this))
|
||||
this.player.addEventListener('ended', this.evtEnded.bind(this))
|
||||
this.player.addEventListener('error', this.evtError.bind(this))
|
||||
this.player.addEventListener('loadedmetadata', this.evtLoadedMetadata.bind(this))
|
||||
this.player.addEventListener('timeupdate', this.evtTimeupdate.bind(this))
|
||||
|
||||
var mimeTypes = ['video/mp4']
|
||||
var mimeTypeCanPlayMap = {}
|
||||
mimeTypes.forEach((mt) => {
|
||||
var canPlay = this.player.canPlayType(mt)
|
||||
mimeTypeCanPlayMap[mt] = canPlay
|
||||
if (canPlay) this.playableMimeTypes.push(mt)
|
||||
})
|
||||
console.log(`[LocalVideoPlayer] Supported mime types`, mimeTypeCanPlayMap, this.playableMimeTypes)
|
||||
}
|
||||
|
||||
evtPlay() {
|
||||
this.emit('stateChange', 'PLAYING')
|
||||
}
|
||||
evtPause() {
|
||||
this.emit('stateChange', 'PAUSED')
|
||||
}
|
||||
evtProgress() {
|
||||
var lastBufferTime = this.getLastBufferedTime()
|
||||
this.emit('buffertimeUpdate', lastBufferTime)
|
||||
}
|
||||
evtEnded() {
|
||||
console.log(`[LocalVideoPlayer] Ended`)
|
||||
this.emit('finished')
|
||||
}
|
||||
evtError(error) {
|
||||
console.error('Player error', error)
|
||||
this.emit('error', error)
|
||||
}
|
||||
evtLoadedMetadata(data) {
|
||||
if (!this.isHlsTranscode) {
|
||||
this.player.currentTime = this.startTime
|
||||
}
|
||||
|
||||
this.emit('stateChange', 'LOADED')
|
||||
if (this.playWhenReady) {
|
||||
this.playWhenReady = false
|
||||
this.play()
|
||||
}
|
||||
}
|
||||
evtTimeupdate() {
|
||||
if (this.player.paused) {
|
||||
this.emit('timeupdate', this.getCurrentTime())
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.destroyHlsInstance()
|
||||
if (this.player) {
|
||||
this.player.remove()
|
||||
}
|
||||
}
|
||||
|
||||
set(libraryItem, videoTrack, isHlsTranscode, startTime, playWhenReady = false) {
|
||||
this.libraryItem = libraryItem
|
||||
this.videoTrack = videoTrack
|
||||
this.isHlsTranscode = isHlsTranscode
|
||||
this.playWhenReady = playWhenReady
|
||||
this.startTime = startTime
|
||||
|
||||
if (this.hlsInstance) {
|
||||
this.destroyHlsInstance()
|
||||
}
|
||||
|
||||
if (this.isHlsTranscode) {
|
||||
this.setHlsStream()
|
||||
} else {
|
||||
this.setDirectPlay()
|
||||
}
|
||||
}
|
||||
|
||||
setHlsStream() {
|
||||
// iOS does not support Media Elements but allows for HLS in the native video player
|
||||
if (!Hls.isSupported()) {
|
||||
console.warn('HLS is not supported - fallback to using video element')
|
||||
this.usingNativeplayer = true
|
||||
this.player.src = this.videoTrack.relativeContentUrl
|
||||
this.player.currentTime = this.startTime
|
||||
return
|
||||
}
|
||||
|
||||
var hlsOptions = {
|
||||
startPosition: this.startTime || -1
|
||||
// No longer needed because token is put in a query string
|
||||
// xhrSetup: (xhr) => {
|
||||
// xhr.setRequestHeader('Authorization', `Bearer ${this.token}`)
|
||||
// }
|
||||
}
|
||||
this.hlsInstance = new Hls(hlsOptions)
|
||||
|
||||
this.hlsInstance.attachMedia(this.player)
|
||||
this.hlsInstance.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||
this.hlsInstance.loadSource(this.videoTrack.relativeContentUrl)
|
||||
|
||||
this.hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
console.log('[HLS] Manifest Parsed')
|
||||
})
|
||||
|
||||
this.hlsInstance.on(Hls.Events.ERROR, (e, data) => {
|
||||
console.error('[HLS] Error', data.type, data.details, data)
|
||||
if (data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
|
||||
console.error('[HLS] BUFFER STALLED ERROR')
|
||||
}
|
||||
})
|
||||
this.hlsInstance.on(Hls.Events.DESTROYING, () => {
|
||||
console.log('[HLS] Destroying HLS Instance')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
setDirectPlay() {
|
||||
this.player.src = this.videoTrack.relativeContentUrl
|
||||
console.log(`[LocalVideoPlayer] Loading track src ${this.videoTrack.relativeContentUrl}`)
|
||||
this.player.load()
|
||||
}
|
||||
|
||||
destroyHlsInstance() {
|
||||
if (!this.hlsInstance) return
|
||||
if (this.hlsInstance.destroy) {
|
||||
var temp = this.hlsInstance
|
||||
temp.destroy()
|
||||
}
|
||||
this.hlsInstance = null
|
||||
}
|
||||
|
||||
async resetStream(startTime) {
|
||||
this.destroyHlsInstance()
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
this.set(this.libraryItem, this.videoTrack, this.isHlsTranscode, startTime, true)
|
||||
}
|
||||
|
||||
playPause() {
|
||||
if (!this.player) return
|
||||
if (this.player.paused) this.play()
|
||||
else this.pause()
|
||||
}
|
||||
|
||||
play() {
|
||||
if (this.player) this.player.play()
|
||||
}
|
||||
|
||||
pause() {
|
||||
if (this.player) this.player.pause()
|
||||
}
|
||||
|
||||
getCurrentTime() {
|
||||
return this.player ? this.player.currentTime : 0
|
||||
}
|
||||
|
||||
getDuration() {
|
||||
return this.videoTrack.duration
|
||||
}
|
||||
|
||||
setPlaybackRate(playbackRate) {
|
||||
if (!this.player) return
|
||||
this.defaultPlaybackRate = playbackRate
|
||||
this.player.playbackRate = playbackRate
|
||||
}
|
||||
|
||||
seek(time) {
|
||||
if (!this.player) return
|
||||
this.player.currentTime = Math.max(0, time)
|
||||
}
|
||||
|
||||
setVolume(volume) {
|
||||
if (!this.player) return
|
||||
this.player.volume = volume
|
||||
}
|
||||
|
||||
// Utils
|
||||
isValidDuration(duration) {
|
||||
if (duration && !isNaN(duration) && duration !== Number.POSITIVE_INFINITY && duration !== Number.NEGATIVE_INFINITY) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
getBufferedRanges() {
|
||||
if (!this.player) return []
|
||||
const ranges = []
|
||||
const seekable = this.player.buffered || []
|
||||
|
||||
let offset = 0
|
||||
|
||||
for (let i = 0, length = seekable.length; i < length; i++) {
|
||||
let start = seekable.start(i)
|
||||
let end = seekable.end(i)
|
||||
if (!this.isValidDuration(start)) {
|
||||
start = 0
|
||||
}
|
||||
if (!this.isValidDuration(end)) {
|
||||
end = 0
|
||||
continue
|
||||
}
|
||||
|
||||
ranges.push({
|
||||
start: start + offset,
|
||||
end: end + offset
|
||||
})
|
||||
}
|
||||
return ranges
|
||||
}
|
||||
|
||||
getLastBufferedTime() {
|
||||
var bufferedRanges = this.getBufferedRanges()
|
||||
if (!bufferedRanges.length) return 0
|
||||
|
||||
var buff = bufferedRanges.find((buff) => buff.start < this.player.currentTime && buff.end > this.player.currentTime)
|
||||
if (buff) return buff.end
|
||||
|
||||
var last = bufferedRanges[bufferedRanges.length - 1]
|
||||
return last.end
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import LocalAudioPlayer from './LocalAudioPlayer'
|
||||
import LocalVideoPlayer from './LocalVideoPlayer'
|
||||
import CastPlayer from './CastPlayer'
|
||||
import AudioTrack from './AudioTrack'
|
||||
import VideoTrack from './VideoTrack'
|
||||
|
||||
export default class PlayerHandler {
|
||||
constructor(ctx) {
|
||||
@@ -16,8 +14,6 @@ export default class PlayerHandler {
|
||||
this.player = null
|
||||
this.playerState = 'IDLE'
|
||||
this.isHlsTranscode = false
|
||||
this.isVideo = false
|
||||
this.isMusic = false
|
||||
this.currentSessionId = null
|
||||
this.startTimeOverride = undefined // Used for starting playback at a specific time (i.e. clicking bookmark from library item page)
|
||||
this.startTime = 0
|
||||
@@ -65,12 +61,10 @@ export default class PlayerHandler {
|
||||
|
||||
load(libraryItem, episodeId, playWhenReady, playbackRate, startTimeOverride = undefined) {
|
||||
this.libraryItem = libraryItem
|
||||
this.isVideo = libraryItem.mediaType === 'video'
|
||||
this.isMusic = libraryItem.mediaType === 'music'
|
||||
|
||||
this.episodeId = episodeId
|
||||
this.playWhenReady = playWhenReady
|
||||
this.initialPlaybackRate = this.isMusic ? 1 : playbackRate
|
||||
this.initialPlaybackRate = playbackRate
|
||||
|
||||
this.startTimeOverride = startTimeOverride == null || isNaN(startTimeOverride) ? undefined : Number(startTimeOverride)
|
||||
|
||||
@@ -97,7 +91,7 @@ export default class PlayerHandler {
|
||||
this.playWhenReady = playWhenReady
|
||||
this.prepare()
|
||||
}
|
||||
} else if (!this.isCasting && !(this.player instanceof LocalAudioPlayer) && !(this.player instanceof LocalVideoPlayer)) {
|
||||
} else if (!this.isCasting && !(this.player instanceof LocalAudioPlayer)) {
|
||||
console.log('[PlayerHandler] Switching to local player')
|
||||
|
||||
this.stopPlayInterval()
|
||||
@@ -107,11 +101,7 @@ export default class PlayerHandler {
|
||||
this.player.destroy()
|
||||
}
|
||||
|
||||
if (this.isVideo) {
|
||||
this.player = new LocalVideoPlayer(this.ctx)
|
||||
} else {
|
||||
this.player = new LocalAudioPlayer(this.ctx)
|
||||
}
|
||||
this.player = new LocalAudioPlayer(this.ctx)
|
||||
|
||||
this.setPlayerListeners()
|
||||
|
||||
@@ -203,7 +193,7 @@ export default class PlayerHandler {
|
||||
supportedMimeTypes: this.player.playableMimeTypes,
|
||||
mediaPlayer: this.isCasting ? 'chromecast' : 'html5',
|
||||
forceTranscode,
|
||||
forceDirectPlay: this.isCasting || this.isVideo // TODO: add transcode support for chromecast
|
||||
forceDirectPlay: this.isCasting // TODO: add transcode support for chromecast
|
||||
}
|
||||
|
||||
const path = this.episodeId ? `/api/items/${this.libraryItem.id}/play/${this.episodeId}` : `/api/items/${this.libraryItem.id}/play`
|
||||
@@ -218,7 +208,6 @@ export default class PlayerHandler {
|
||||
if (!this.player) this.switchPlayer() // Must set player first for open sessions
|
||||
|
||||
this.libraryItem = session.libraryItem
|
||||
this.isVideo = session.libraryItem.mediaType === 'video'
|
||||
this.playWhenReady = false
|
||||
this.initialPlaybackRate = playbackRate
|
||||
this.startTimeOverride = undefined
|
||||
@@ -237,28 +226,16 @@ export default class PlayerHandler {
|
||||
|
||||
console.log('[PlayerHandler] Preparing Session', session)
|
||||
|
||||
if (session.videoTrack) {
|
||||
var videoTrack = new VideoTrack(session.videoTrack, this.userToken)
|
||||
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, this.userToken))
|
||||
|
||||
this.ctx.playerLoading = true
|
||||
this.isHlsTranscode = true
|
||||
if (session.playMethod === this.ctx.$constants.PlayMethod.DIRECTPLAY) {
|
||||
this.isHlsTranscode = false
|
||||
}
|
||||
|
||||
this.player.set(this.libraryItem, videoTrack, this.isHlsTranscode, this.startTime, this.playWhenReady)
|
||||
} else {
|
||||
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, this.userToken))
|
||||
|
||||
this.ctx.playerLoading = true
|
||||
this.isHlsTranscode = true
|
||||
if (session.playMethod === this.ctx.$constants.PlayMethod.DIRECTPLAY) {
|
||||
this.isHlsTranscode = false
|
||||
}
|
||||
|
||||
this.player.set(this.libraryItem, audioTracks, this.isHlsTranscode, this.startTime, this.playWhenReady)
|
||||
this.ctx.playerLoading = true
|
||||
this.isHlsTranscode = true
|
||||
if (session.playMethod === this.ctx.$constants.PlayMethod.DIRECTPLAY) {
|
||||
this.isHlsTranscode = false
|
||||
}
|
||||
|
||||
this.player.set(this.libraryItem, audioTracks, this.isHlsTranscode, this.startTime, this.playWhenReady)
|
||||
|
||||
// browser media session api
|
||||
this.ctx.setMediaSession()
|
||||
}
|
||||
@@ -333,8 +310,6 @@ export default class PlayerHandler {
|
||||
}
|
||||
|
||||
sendProgressSync(currentTime) {
|
||||
if (this.isMusic) return
|
||||
|
||||
const diffSinceLastSync = Math.abs(this.lastSyncTime - currentTime)
|
||||
if (diffSinceLastSync < 1) return
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
export default class VideoTrack {
|
||||
constructor(track, userToken) {
|
||||
this.index = track.index || 0
|
||||
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
||||
this.duration = track.duration || 0
|
||||
this.title = track.title || ''
|
||||
this.contentUrl = track.contentUrl || null
|
||||
this.mimeType = track.mimeType
|
||||
this.metadata = track.metadata || {}
|
||||
|
||||
this.userToken = userToken
|
||||
}
|
||||
|
||||
get fullContentUrl() {
|
||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return `${process.env.serverUrl}${this.contentUrl}?token=${this.userToken}`
|
||||
}
|
||||
return `${window.location.origin}${this.contentUrl}?token=${this.userToken}`
|
||||
}
|
||||
|
||||
get relativeContentUrl() {
|
||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return `${process.env.serverUrl}${this.contentUrl}?token=${this.userToken}`
|
||||
}
|
||||
|
||||
return this.contentUrl + `?token=${this.userToken}`
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
export default function ({ $axios, store, $config }) {
|
||||
$axios.onRequest(config => {
|
||||
$axios.onRequest((config) => {
|
||||
if (!config.url) {
|
||||
console.error('Axios request invalid config', config)
|
||||
return
|
||||
@@ -13,14 +13,13 @@ export default function ({ $axios, store, $config }) {
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
config.url = `/dev${config.url}`
|
||||
console.log('Making request to ' + config.url)
|
||||
}
|
||||
})
|
||||
|
||||
$axios.onError(error => {
|
||||
$axios.onError((error) => {
|
||||
const code = parseInt(error.response && error.response.status)
|
||||
const message = error.response ? error.response.data || 'Unknown Error' : 'Unknown Error'
|
||||
console.error('Axios error', code, message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ const languageCodeMap = {
|
||||
pl: { label: 'Polski', dateFnsLocale: 'pl' },
|
||||
'pt-br': { label: 'Português (Brasil)', dateFnsLocale: 'ptBR' },
|
||||
ru: { label: 'Русский', dateFnsLocale: 'ru' },
|
||||
sl: { label: 'Slovenščina', dateFnsLocale: 'sl' },
|
||||
sv: { label: 'Svenska', dateFnsLocale: 'sv' },
|
||||
uk: { label: 'Українська', dateFnsLocale: 'uk' },
|
||||
'vi-vn': { label: 'Tiếng Việt', dateFnsLocale: 'vi' },
|
||||
@@ -88,10 +89,10 @@ Vue.prototype.$strings = { ...enUsStrings }
|
||||
* Get string and substitute
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string[]} subs
|
||||
* @param {string[]} [subs=[]]
|
||||
* @returns {string}
|
||||
*/
|
||||
Vue.prototype.$getString = (key, subs) => {
|
||||
Vue.prototype.$getString = (key, subs = []) => {
|
||||
if (!Vue.prototype.$strings[key]) return ''
|
||||
if (subs?.length && Array.isArray(subs)) {
|
||||
return supplant(Vue.prototype.$strings[key], subs)
|
||||
|
||||
@@ -11,14 +11,17 @@ Vue.prototype.$bytesPretty = (bytes, decimals = 2) => {
|
||||
if (isNaN(bytes) || bytes == 0) {
|
||||
return '0 Bytes'
|
||||
}
|
||||
const k = 1024
|
||||
const k = 1000
|
||||
const dm = decimals < 0 ? 0 : decimals
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
Vue.prototype.$elapsedPretty = (seconds, useFullNames = false) => {
|
||||
Vue.prototype.$elapsedPretty = (seconds, useFullNames = false, useMilliseconds = false) => {
|
||||
if (useMilliseconds && seconds > 0 && seconds < 1) {
|
||||
return `${Math.floor(seconds * 1000)} ms`
|
||||
}
|
||||
if (seconds < 60) {
|
||||
return `${Math.floor(seconds)} sec${useFullNames ? 'onds' : ''}`
|
||||
}
|
||||
|
||||
+5
-15
@@ -72,13 +72,13 @@ export const state = () => ({
|
||||
}
|
||||
],
|
||||
podcastTypes: [
|
||||
{ text: 'Episodic', value: 'episodic' },
|
||||
{ text: 'Serial', value: 'serial' }
|
||||
{ text: 'Episodic', value: 'episodic', descriptionKey: 'LabelEpisodic' },
|
||||
{ text: 'Serial', value: 'serial', descriptionKey: 'LabelSerial' }
|
||||
],
|
||||
episodeTypes: [
|
||||
{ text: 'Full', value: 'full' },
|
||||
{ text: 'Trailer', value: 'trailer' },
|
||||
{ text: 'Bonus', value: 'bonus' }
|
||||
{ text: 'Full', value: 'full', descriptionKey: 'LabelFull' },
|
||||
{ text: 'Trailer', value: 'trailer', descriptionKey: 'LabelTrailer' },
|
||||
{ text: 'Bonus', value: 'bonus', descriptionKey: 'LabelBonus' }
|
||||
],
|
||||
libraryIcons: ['database', 'audiobookshelf', 'books-1', 'books-2', 'book-1', 'microphone-1', 'microphone-3', 'radio', 'podcast', 'rss', 'headphones', 'music', 'file-picture', 'rocket', 'power', 'star', 'heart']
|
||||
})
|
||||
@@ -98,12 +98,6 @@ export const getters = {
|
||||
const userToken = rootGetters['user/getToken']
|
||||
const lastUpdate = libraryItem.updatedAt || Date.now()
|
||||
const libraryItemId = libraryItem.libraryItemId || libraryItem.id // Workaround for /users/:id page showing media progress covers
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// Testing
|
||||
return `http://localhost:3333${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}&ts=${lastUpdate}${raw ? '&raw=1' : ''}`
|
||||
}
|
||||
|
||||
return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}&ts=${lastUpdate}${raw ? '&raw=1' : ''}`
|
||||
},
|
||||
getLibraryItemCoverSrcById:
|
||||
@@ -112,10 +106,6 @@ export const getters = {
|
||||
const placeholder = `${rootState.routerBasePath}/book_placeholder.jpg`
|
||||
if (!libraryItemId) return placeholder
|
||||
const userToken = rootGetters['user/getToken']
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// Testing
|
||||
return `http://localhost:3333${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}${raw ? '&raw=1' : ''}${timestamp ? `&ts=${timestamp}` : ''}`
|
||||
}
|
||||
return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}${raw ? '&raw=1' : ''}${timestamp ? `&ts=${timestamp}` : ''}`
|
||||
},
|
||||
getIsBatchSelectingMediaItems: (state) => {
|
||||
|
||||
@@ -240,7 +240,8 @@ export const mutations = {
|
||||
series: [],
|
||||
narrators: [],
|
||||
languages: [],
|
||||
publishers: []
|
||||
publishers: [],
|
||||
publishedDecades: []
|
||||
}
|
||||
*/
|
||||
const mediaMetadata = libraryItem.media.metadata
|
||||
@@ -307,6 +308,16 @@ export const mutations = {
|
||||
state.filterData.publishers.sort((a, b) => a.localeCompare(b))
|
||||
}
|
||||
|
||||
// Add publishedDecades
|
||||
if (mediaMetadata.publishedYear && !isNaN(mediaMetadata.publishedYear)) {
|
||||
const publishedYear = parseInt(mediaMetadata.publishedYear, 10)
|
||||
const decade = (Math.floor(publishedYear / 10) * 10).toString()
|
||||
if (!state.filterData.publishedDecades.includes(decade)) {
|
||||
state.filterData.publishedDecades.push(decade)
|
||||
state.filterData.publishedDecades.sort((a, b) => a - b)
|
||||
}
|
||||
}
|
||||
|
||||
// Add language
|
||||
if (mediaMetadata.language && !state.filterData.languages.includes(mediaMetadata.language)) {
|
||||
state.filterData.languages.push(mediaMetadata.language)
|
||||
|
||||
@@ -90,7 +90,7 @@ export const actions = {
|
||||
if (state.settings.orderBy == 'media.metadata.publishedYear') {
|
||||
settingsUpdate.orderBy = 'media.metadata.title'
|
||||
}
|
||||
const invalidFilters = ['series', 'authors', 'narrators', 'publishers', 'languages', 'progress', 'issues', 'ebooks', 'abridged']
|
||||
const invalidFilters = ['series', 'authors', 'narrators', 'publishers', 'publishedDecades', 'languages', 'progress', 'issues', 'ebooks', 'abridged']
|
||||
const filterByFirstPart = (state.settings.filterBy || '').split('.').shift()
|
||||
if (invalidFilters.includes(filterByFirstPart)) {
|
||||
settingsUpdate.filterBy = 'all'
|
||||
|
||||
@@ -711,10 +711,8 @@
|
||||
"PlaceholderNewPlaylist": "Ново име на плейлиста",
|
||||
"PlaceholderSearch": "Търсене...",
|
||||
"PlaceholderSearchEpisode": "Търсене на Епизоди...",
|
||||
"ToastAccountUpdateFailed": "Неуспешно обновяване на акаунта",
|
||||
"ToastAccountUpdateSuccess": "Успешно обновяване на акаунта",
|
||||
"ToastAuthorImageRemoveSuccess": "Авторската снимка е премахната",
|
||||
"ToastAuthorUpdateFailed": "Неуспешно обновяване на автора",
|
||||
"ToastAuthorUpdateMerged": "Обновяване на автора сливано",
|
||||
"ToastAuthorUpdateSuccess": "Автора обновен",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Автор обновен (не е намерена снимка)",
|
||||
@@ -728,17 +726,13 @@
|
||||
"ToastBookmarkCreateFailed": "Неуспешно създаване на отметка",
|
||||
"ToastBookmarkCreateSuccess": "Отметката е създадена",
|
||||
"ToastBookmarkRemoveSuccess": "Отметката е премахната",
|
||||
"ToastBookmarkUpdateFailed": "Неуспешно обновяване на отметка",
|
||||
"ToastBookmarkUpdateSuccess": "Отметката е обновена",
|
||||
"ToastChaptersHaveErrors": "Главите имат грешки",
|
||||
"ToastChaptersMustHaveTitles": "Главите трябва да имат заглавия",
|
||||
"ToastCollectionItemsRemoveSuccess": "Елемент(и) премахнати от колекция",
|
||||
"ToastCollectionRemoveSuccess": "Колекцията е премахната",
|
||||
"ToastCollectionUpdateFailed": "Неуспешно обновяване на колекция",
|
||||
"ToastCollectionUpdateSuccess": "Колекцията е обновена",
|
||||
"ToastItemCoverUpdateFailed": "Неуспешно обновяване на корица на елемент",
|
||||
"ToastItemCoverUpdateSuccess": "Корицата на елемента е обновена",
|
||||
"ToastItemDetailsUpdateFailed": "Неуспешно обновяване на детайли на елемент",
|
||||
"ToastItemDetailsUpdateSuccess": "Детайлите на елемента са обновени",
|
||||
"ToastItemMarkedAsFinishedFailed": "Неуспешно маркиране като завършено",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Елементът е маркиран като завършен",
|
||||
@@ -750,12 +744,10 @@
|
||||
"ToastLibraryDeleteSuccess": "Библиотеката е изтрита",
|
||||
"ToastLibraryScanFailedToStart": "Неуспешно стартиране на сканиране",
|
||||
"ToastLibraryScanStarted": "Сканирането на библиотеката е стартирано",
|
||||
"ToastLibraryUpdateFailed": "Неуспешно обновяване на библиотека",
|
||||
"ToastLibraryUpdateSuccess": "Библиотеката \"{0}\" е обновена",
|
||||
"ToastPlaylistCreateFailed": "Неуспешно създаване на плейлист",
|
||||
"ToastPlaylistCreateSuccess": "Плейлистът е създаден",
|
||||
"ToastPlaylistRemoveSuccess": "Плейлистът е премахнат",
|
||||
"ToastPlaylistUpdateFailed": "Неуспешно обновяване на плейлист",
|
||||
"ToastPlaylistUpdateSuccess": "Плейлистът е обновен",
|
||||
"ToastPodcastCreateFailed": "Неуспешно създаване на подкаст",
|
||||
"ToastPodcastCreateSuccess": "Подкастът е създаден",
|
||||
|
||||
+237
-22
@@ -8,7 +8,8 @@
|
||||
"ButtonAddYourFirstLibrary": "আপনার প্রথম লাইব্রেরি যোগ করুন",
|
||||
"ButtonApply": "প্রয়োগ করুন",
|
||||
"ButtonApplyChapters": "অধ্যায় প্রয়োগ করুন",
|
||||
"ButtonAuthors": "লেখক",
|
||||
"ButtonAuthors": "লেখকগণ",
|
||||
"ButtonBack": "পেছনে যান",
|
||||
"ButtonBrowseForFolder": "ফোল্ডারের জন্য ব্রাউজ করুন",
|
||||
"ButtonCancel": "বাতিল করুন",
|
||||
"ButtonCancelEncode": "এনকোড বাতিল করুন",
|
||||
@@ -18,6 +19,7 @@
|
||||
"ButtonChooseFiles": "ফাইল চয়ন করুন",
|
||||
"ButtonClearFilter": "ফিল্টার পরিষ্কার করুন",
|
||||
"ButtonCloseFeed": "ফিড বন্ধ করুন",
|
||||
"ButtonCloseSession": "খোলা সেশন বন্ধ করুন",
|
||||
"ButtonCollections": "সংগ্রহ",
|
||||
"ButtonConfigureScanner": "স্ক্যানার কনফিগার করুন",
|
||||
"ButtonCreate": "তৈরি করুন",
|
||||
@@ -27,6 +29,9 @@
|
||||
"ButtonEdit": "সম্পাদনা করুন",
|
||||
"ButtonEditChapters": "অধ্যায় সম্পাদনা করুন",
|
||||
"ButtonEditPodcast": "পডকাস্ট সম্পাদনা করুন",
|
||||
"ButtonEnable": "সক্রিয় করুন",
|
||||
"ButtonFireAndFail": "সক্রিয় এবং ব্যর্থ",
|
||||
"ButtonFireOnTest": "পরীক্ষামূলক ইভেন্টে সক্রিয় করুন",
|
||||
"ButtonForceReScan": "জোরপূর্বক পুনরায় স্ক্যান করুন",
|
||||
"ButtonFullPath": "সম্পূর্ণ পথ",
|
||||
"ButtonHide": "লুকান",
|
||||
@@ -45,22 +50,28 @@
|
||||
"ButtonNevermind": "কিছু মনে করবেন না",
|
||||
"ButtonNext": "পরবর্তী",
|
||||
"ButtonNextChapter": "পরবর্তী অধ্যায়",
|
||||
"ButtonNextItemInQueue": "সারিতে পরের আইটেম",
|
||||
"ButtonOk": "ঠিক আছে",
|
||||
"ButtonOpenFeed": "ফিড খুলুন",
|
||||
"ButtonOpenManager": "ম্যানেজার খুলুন",
|
||||
"ButtonPause": "বিরতি",
|
||||
"ButtonPlay": "বাজান",
|
||||
"ButtonPlayAll": "সব চালান",
|
||||
"ButtonPlaying": "বাজছে",
|
||||
"ButtonPlaylists": "প্লেলিস্ট",
|
||||
"ButtonPrevious": "পূর্ববর্তী",
|
||||
"ButtonPreviousChapter": "আগের অধ্যায়",
|
||||
"ButtonProbeAudioFile": "প্রোব অডিও ফাইল",
|
||||
"ButtonPurgeAllCache": "সমস্ত ক্যাশে পরিষ্কার করুন",
|
||||
"ButtonPurgeItemsCache": "আইটেম ক্যাশে পরিষ্কার করুন",
|
||||
"ButtonQueueAddItem": "সারিতে যোগ করুন",
|
||||
"ButtonQueueRemoveItem": "সারি থেকে মুছে ফেলুন",
|
||||
"ButtonQuickEmbedMetadata": "মেটাডেটা দ্রুত এম্বেড করুন",
|
||||
"ButtonQuickMatch": "দ্রুত ম্যাচ",
|
||||
"ButtonReScan": "পুনরায় স্ক্যান",
|
||||
"ButtonRead": "পড়ুন",
|
||||
"ButtonReadLess": "সংক্ষিপ্ত",
|
||||
"ButtonReadMore": "বিস্তারিত পড়ুন",
|
||||
"ButtonRefresh": "রিফ্রেশ",
|
||||
"ButtonRemove": "মুছে ফেলুন",
|
||||
"ButtonRemoveAll": "সব মুছে ফেলুন",
|
||||
@@ -85,8 +96,10 @@
|
||||
"ButtonShow": "দেখান",
|
||||
"ButtonStartM4BEncode": "M4B এনকোড শুরু করুন",
|
||||
"ButtonStartMetadataEmbed": "মেটাডেটা এম্বেড শুরু করুন",
|
||||
"ButtonStats": "পরিসংখ্যান",
|
||||
"ButtonSubmit": "জমা দিন",
|
||||
"ButtonTest": "পরীক্ষা",
|
||||
"ButtonUnlinkOpenId": "ওপেন আইডি লিঙ্কমুক্ত করুন",
|
||||
"ButtonUpload": "আপলোড",
|
||||
"ButtonUploadBackup": "আপলোড ব্যাকআপ",
|
||||
"ButtonUploadCover": "কভার আপলোড করুন",
|
||||
@@ -99,9 +112,10 @@
|
||||
"ErrorUploadFetchMetadataNoResults": "মেটাডেটা আনা যায়নি - শিরোনাম এবং/অথবা লেখক আপডেট করার চেষ্টা করুন",
|
||||
"ErrorUploadLacksTitle": "একটি শিরোনাম থাকতে হবে",
|
||||
"HeaderAccount": "অ্যাকাউন্ট",
|
||||
"HeaderAddCustomMetadataProvider": "কাস্টম মেটাডেটা সরবরাহকারী যোগ করুন",
|
||||
"HeaderAdvanced": "অ্যাডভান্সড",
|
||||
"HeaderAppriseNotificationSettings": "বিজ্ঞপ্তি সেটিংস অবহিত করুন",
|
||||
"HeaderAudioTracks": "অডিও ট্র্যাকস",
|
||||
"HeaderAudioTracks": "অডিও ট্র্যাকসগুলো",
|
||||
"HeaderAudiobookTools": "অডিওবই ফাইল ম্যানেজমেন্ট টুলস",
|
||||
"HeaderAuthentication": "প্রমাণীকরণ",
|
||||
"HeaderBackups": "ব্যাকআপ",
|
||||
@@ -112,6 +126,7 @@
|
||||
"HeaderCollectionItems": "সংগ্রহ আইটেম",
|
||||
"HeaderCover": "কভার",
|
||||
"HeaderCurrentDownloads": "বর্তমান ডাউনলোডগুলি",
|
||||
"HeaderCustomMessageOnLogin": "লগইন এ কাস্টম বার্তা",
|
||||
"HeaderCustomMetadataProviders": "কাস্টম মেটাডেটা প্রদানকারী",
|
||||
"HeaderDetails": "বিস্তারিত",
|
||||
"HeaderDownloadQueue": "ডাউনলোড সারি",
|
||||
@@ -143,6 +158,8 @@
|
||||
"HeaderMetadataToEmbed": "এম্বেড করার জন্য মেটাডেটা",
|
||||
"HeaderNewAccount": "নতুন অ্যাকাউন্ট",
|
||||
"HeaderNewLibrary": "নতুন লাইব্রেরি",
|
||||
"HeaderNotificationCreate": "বিজ্ঞপ্তি তৈরি করুন",
|
||||
"HeaderNotificationUpdate": "বিজ্ঞপ্তি আপডেট করুন",
|
||||
"HeaderNotifications": "বিজ্ঞপ্তি",
|
||||
"HeaderOpenIDConnectAuthentication": "ওপেনআইডি সংযোগ প্রমাণীকরণ",
|
||||
"HeaderOpenRSSFeed": "আরএসএস ফিড খুলুন",
|
||||
@@ -150,6 +167,7 @@
|
||||
"HeaderPasswordAuthentication": "পাসওয়ার্ড প্রমাণীকরণ",
|
||||
"HeaderPermissions": "অনুমতি",
|
||||
"HeaderPlayerQueue": "প্লেয়ার সারি",
|
||||
"HeaderPlayerSettings": "প্লেয়ার সেটিংস",
|
||||
"HeaderPlaylist": "প্লেলিস্ট",
|
||||
"HeaderPlaylistItems": "প্লেলিস্ট আইটেম",
|
||||
"HeaderPodcastsToAdd": "যোগ করার জন্য পডকাস্ট",
|
||||
@@ -186,6 +204,9 @@
|
||||
"HeaderYearReview": "বাৎসরিক পর্যালোচনা {0}",
|
||||
"HeaderYourStats": "আপনার পরিসংখ্যান",
|
||||
"LabelAbridged": "সংক্ষিপ্ত",
|
||||
"LabelAbridgedChecked": "সংক্ষিপ্ত (চেক)",
|
||||
"LabelAbridgedUnchecked": "অসংক্ষেপিত (চেক করা হয়নি)",
|
||||
"LabelAccessibleBy": "দ্বারা প্রবেশযোগ্য",
|
||||
"LabelAccountType": "অ্যাকাউন্টের প্রকার",
|
||||
"LabelAccountTypeAdmin": "প্রশাসন",
|
||||
"LabelAccountTypeGuest": "অতিথি",
|
||||
@@ -196,6 +217,7 @@
|
||||
"LabelAddToPlaylist": "প্লেলিস্টে যোগ করুন",
|
||||
"LabelAddToPlaylistBatch": "প্লেলিস্টে {0}টি আইটেম যোগ করুন",
|
||||
"LabelAddedAt": "এতে যোগ করা হয়েছে",
|
||||
"LabelAddedDate": "যোগ করা হয়েছে {0}",
|
||||
"LabelAdminUsersOnly": "শুধু অ্যাডমিন ব্যবহারকারী",
|
||||
"LabelAll": "সব",
|
||||
"LabelAllUsers": "সমস্ত ব্যবহারকারী",
|
||||
@@ -218,13 +240,14 @@
|
||||
"LabelBackupLocation": "ব্যাকআপ অবস্থান",
|
||||
"LabelBackupsEnableAutomaticBackups": "স্বয়ংক্রিয় ব্যাকআপ সক্ষম করুন",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "ব্যাকআপগুলি /মেটাডাটা/ব্যাকআপে সংরক্ষিত",
|
||||
"LabelBackupsMaxBackupSize": "সর্বোচ্চ ব্যাকআপ আকার (GB-তে)",
|
||||
"LabelBackupsMaxBackupSize": "সর্বোচ্চ ব্যাকআপ আকার (GB-তে) (অসীমের জন্য 0)",
|
||||
"LabelBackupsMaxBackupSizeHelp": "ভুল কনফিগারেশনের বিরুদ্ধে সুরক্ষা হিসেবে ব্যাকআপগুলি ব্যর্থ হবে যদি তারা কনফিগার করা আকার অতিক্রম করে।",
|
||||
"LabelBackupsNumberToKeep": "ব্যাকআপের সংখ্যা রাখুন",
|
||||
"LabelBackupsNumberToKeepHelp": "এক সময়ে শুধুমাত্র ১ টি ব্যাকআপ সরানো হবে তাই যদি আপনার কাছে ইতিমধ্যে এর চেয়ে বেশি ব্যাকআপ থাকে তাহলে আপনাকে ম্যানুয়ালি সেগুলি সরিয়ে ফেলতে হবে।",
|
||||
"LabelBitrate": "বিটরেট",
|
||||
"LabelBooks": "বইগুলো",
|
||||
"LabelButtonText": "ঘর পাঠ্য",
|
||||
"LabelByAuthor": "দ্বারা {0}",
|
||||
"LabelChangePassword": "পাসওয়ার্ড পরিবর্তন করুন",
|
||||
"LabelChannels": "চ্যানেল",
|
||||
"LabelChapterTitle": "অধ্যায়ের শিরোনাম",
|
||||
@@ -234,6 +257,7 @@
|
||||
"LabelClosePlayer": "প্লেয়ার বন্ধ করুন",
|
||||
"LabelCodec": "কোডেক",
|
||||
"LabelCollapseSeries": "সিরিজ সঙ্কুচিত করুন",
|
||||
"LabelCollapseSubSeries": "উপ-সিরিজ সঙ্কুচিত করুন",
|
||||
"LabelCollection": "সংগ্রহ",
|
||||
"LabelCollections": "সংগ্রহ",
|
||||
"LabelComplete": "সম্পূর্ণ",
|
||||
@@ -249,6 +273,7 @@
|
||||
"LabelCurrently": "বর্তমানে:",
|
||||
"LabelCustomCronExpression": "কাস্টম Cron এক্সপ্রেশন:",
|
||||
"LabelDatetime": "তারিখ সময়",
|
||||
"LabelDays": "দিনগুলো",
|
||||
"LabelDeleteFromFileSystemCheckbox": "ফাইল সিস্টেম থেকে মুছে ফেলুন (শুধু ডাটাবেস থেকে সরাতে টিক চিহ্ন মুক্ত করুন)",
|
||||
"LabelDescription": "বিবরণ",
|
||||
"LabelDeselectAll": "সমস্ত অনির্বাচিত করুন",
|
||||
@@ -262,29 +287,42 @@
|
||||
"LabelDownload": "ডাউনলোড করুন",
|
||||
"LabelDownloadNEpisodes": "{0}টি পর্ব ডাউনলোড করুন",
|
||||
"LabelDuration": "সময়কাল",
|
||||
"LabelDurationComparisonExactMatch": "(সঠিক মিল)",
|
||||
"LabelDurationComparisonLonger": "({0} দীর্ঘ)",
|
||||
"LabelDurationComparisonShorter": "({0} ছোট)",
|
||||
"LabelDurationFound": "সময়কাল পাওয়া গেছে:",
|
||||
"LabelEbook": "ই-বই",
|
||||
"LabelEbooks": "ই-বইগুলো",
|
||||
"LabelEdit": "সম্পাদনা করুন",
|
||||
"LabelEmail": "ইমেইল",
|
||||
"LabelEmailSettingsFromAddress": "ঠিকানা থেকে",
|
||||
"LabelEmailSettingsRejectUnauthorizedHelp": "Disabling SSL certificate validation may expose your connection to security risks, such as man-in-the-middle attacks. Only disable this option if you understand the implications and trust the mail server you are connecting to।",
|
||||
"LabelEmailSettingsRejectUnauthorized": "অননুমোদিত সার্টিফিকেট প্রত্যাখ্যান করুন",
|
||||
"LabelEmailSettingsRejectUnauthorizedHelp": "SSL প্রমাণপত্রের বৈধতা নিষ্ক্রিয় করা আপনার সংযোগকে নিরাপত্তা ঝুঁকিতে ফেলতে পারে, যেমন ম্যান-ইন-দ্য-মিডল আক্রমণ। শুধুমাত্র এই বিকল্পটি নিষ্ক্রিয় করুন যদি আপনি এর প্রভাবগুলি বুঝতে পারেন এবং আপনি যে মেইল সার্ভারের সাথে সংযোগ করছেন তাকে বিশ্বাস করেন।",
|
||||
"LabelEmailSettingsSecure": "নিরাপদ",
|
||||
"LabelEmailSettingsSecureHelp": "যদি সত্য হয় সার্ভারের সাথে সংযোগ করার সময় সংযোগটি TLS ব্যবহার করবে। মিথ্যা হলে TLS ব্যবহার করা হবে যদি সার্ভার STARTTLS এক্সটেনশন সমর্থন করে। বেশিরভাগ ক্ষেত্রে এই মানটিকে সত্য হিসাবে সেট করুন যদি আপনি পোর্ট 465-এর সাথে সংযোগ করছেন। পোর্ট 587 বা পোর্টের জন্য 25 এটি মিথ্যা রাখুন। (nodemailer.com/smtp/#authentication থেকে)",
|
||||
"LabelEmailSettingsTestAddress": "পরীক্ষার ঠিকানা",
|
||||
"LabelEmbeddedCover": "এম্বেডেড কভার",
|
||||
"LabelEnable": "সক্ষম করুন",
|
||||
"LabelEnd": "সমাপ্ত",
|
||||
"LabelEndOfChapter": "অধ্যায়ের সমাপ্তি",
|
||||
"LabelEpisode": "পর্ব",
|
||||
"LabelEpisodeTitle": "পর্বের শিরোনাম",
|
||||
"LabelEpisodeType": "পর্বের ধরন",
|
||||
"LabelEpisodes": "পর্বগুলো",
|
||||
"LabelExample": "উদাহরণ",
|
||||
"LabelExpandSeries": "সিরিজ প্রসারিত করুন",
|
||||
"LabelExpandSubSeries": "সাব সিরিজ প্রসারিত করুন",
|
||||
"LabelExplicit": "বিশদ",
|
||||
"LabelExplicitChecked": "সুস্পষ্ট (পরীক্ষিত)",
|
||||
"LabelExplicitUnchecked": "অস্পষ্ট (অপরিক্ষীত)",
|
||||
"LabelExportOPML": "OPML এক্সপোর্ট করুন",
|
||||
"LabelFeedURL": "ফিড ইউআরএল",
|
||||
"LabelFetchingMetadata": "মেটাডেটা আনা হচ্ছে",
|
||||
"LabelFile": "ফাইল",
|
||||
"LabelFileBirthtime": "ফাইল জন্মের সময়",
|
||||
"LabelFileBornDate": "জন্ম {0}",
|
||||
"LabelFileModified": "ফাইল পরিবর্তিত",
|
||||
"LabelFileModifiedDate": "পরিবর্তিত {0}",
|
||||
"LabelFilename": "ফাইলের নাম",
|
||||
"LabelFilterByUser": "ব্যবহারকারী দ্বারা ফিল্টারকৃত",
|
||||
"LabelFindEpisodes": "পর্বগুলো খুঁজুন",
|
||||
@@ -292,7 +330,8 @@
|
||||
"LabelFolder": "ফোল্ডার",
|
||||
"LabelFolders": "ফোল্ডারগুলো",
|
||||
"LabelFontBold": "বোল্ড",
|
||||
"LabelFontFamily": "ফন্ট পরিবার",
|
||||
"LabelFontBoldness": "হরফ বোল্ডনেস",
|
||||
"LabelFontFamily": "হরফ পরিবার",
|
||||
"LabelFontItalic": "ইটালিক",
|
||||
"LabelFontScale": "ফন্ট স্কেল",
|
||||
"LabelFontStrikethrough": "অবচ্ছেদন রেখা",
|
||||
@@ -302,9 +341,11 @@
|
||||
"LabelHardDeleteFile": "জোরপূর্বক ফাইল মুছে ফেলুন",
|
||||
"LabelHasEbook": "ই-বই আছে",
|
||||
"LabelHasSupplementaryEbook": "পরিপূরক ই-বই আছে",
|
||||
"LabelHideSubtitles": "সাবটাইটেল লুকান",
|
||||
"LabelHighestPriority": "সর্বোচ্চ অগ্রাধিকার",
|
||||
"LabelHost": "নিমন্ত্রণকর্তা",
|
||||
"LabelHour": "ঘন্টা",
|
||||
"LabelHours": "ঘন্টা",
|
||||
"LabelIcon": "আইকন",
|
||||
"LabelImageURLFromTheWeb": "ওয়েব থেকে ছবির ইউআরএল",
|
||||
"LabelInProgress": "প্রগতিতে আছে",
|
||||
@@ -321,8 +362,11 @@
|
||||
"LabelIntervalEveryHour": "প্রতি ঘন্টা",
|
||||
"LabelInvert": "উল্টানো",
|
||||
"LabelItem": "আইটেম",
|
||||
"LabelJumpBackwardAmount": "পিছন দিকে ঝাঁপের পরিমাণ",
|
||||
"LabelJumpForwardAmount": "সামনের দিকে ঝাঁপের পরিমাণ",
|
||||
"LabelLanguage": "ভাষা",
|
||||
"LabelLanguageDefaultServer": "সার্ভারের ডিফল্ট ভাষা",
|
||||
"LabelLanguages": "ভাষাসমূহ",
|
||||
"LabelLastBookAdded": "শেষ বই যোগ করা হয়েছে",
|
||||
"LabelLastBookUpdated": "শেষ বই আপডেট করা হয়েছে",
|
||||
"LabelLastSeen": "শেষ দেখা",
|
||||
@@ -334,6 +378,7 @@
|
||||
"LabelLess": "কম",
|
||||
"LabelLibrariesAccessibleToUser": "ব্যবহারকারীর কাছে অ্যাক্সেসযোগ্য লাইব্রেরি",
|
||||
"LabelLibrary": "লাইব্রেরি",
|
||||
"LabelLibraryFilterSublistEmpty": "না {0}",
|
||||
"LabelLibraryItem": "লাইব্রেরি আইটেম",
|
||||
"LabelLibraryName": "লাইব্রেরির নাম",
|
||||
"LabelLimit": "সীমা",
|
||||
@@ -353,6 +398,7 @@
|
||||
"LabelMetadataOrderOfPrecedenceDescription": "উচ্চ অগ্রাধিকারের মেটাডেটার উৎসগুলো নিম্ন অগ্রাধিকারের মেটাডেটা উৎসগুলোকে ওভাররাইড করবে",
|
||||
"LabelMetadataProvider": "মেটাডেটা প্রদানকারী",
|
||||
"LabelMinute": "মিনিট",
|
||||
"LabelMinutes": "মিনিটস",
|
||||
"LabelMissing": "নিখোঁজ",
|
||||
"LabelMissingEbook": "কোনও ই-বই নেই",
|
||||
"LabelMissingSupplementaryEbook": "কোনও সম্পূরক ই-বই নেই",
|
||||
@@ -369,6 +415,7 @@
|
||||
"LabelNewestEpisodes": "নতুনতম পর্ব",
|
||||
"LabelNextBackupDate": "পরবর্তী ব্যাকআপ তারিখ",
|
||||
"LabelNextScheduledRun": "পরবর্তী নির্ধারিত দৌড়",
|
||||
"LabelNoCustomMetadataProviders": "কোনো কাস্টম মেটাডেটা প্রদানকারী নেই",
|
||||
"LabelNoEpisodesSelected": "কোন পর্ব নির্বাচন করা হয়নি",
|
||||
"LabelNotFinished": "সমাপ্ত হয়নি",
|
||||
"LabelNotStarted": "শুরু হয়নি",
|
||||
@@ -391,6 +438,7 @@
|
||||
"LabelOverwrite": "পুনঃলিখিত",
|
||||
"LabelPassword": "পাসওয়ার্ড",
|
||||
"LabelPath": "পথ",
|
||||
"LabelPermanent": "স্থায়ী",
|
||||
"LabelPermissionsAccessAllLibraries": "সমস্ত লাইব্রেরি অ্যাক্সেস করতে পারবে",
|
||||
"LabelPermissionsAccessAllTags": "সমস্ত ট্যাগ অ্যাক্সেস করতে পারবে",
|
||||
"LabelPermissionsAccessExplicitContent": "স্পষ্ট বিষয়বস্তু অ্যাক্সেস করতে পারে",
|
||||
@@ -401,6 +449,7 @@
|
||||
"LabelPersonalYearReview": "আপনার বছরের পর্যালোচনা ({0})",
|
||||
"LabelPhotoPathURL": "ছবি পথ/ইউআরএল",
|
||||
"LabelPlayMethod": "প্লে পদ্ধতি",
|
||||
"LabelPlayerChapterNumberMarker": "{1} এর মধ্যে {0}",
|
||||
"LabelPlaylists": "প্লেলিস্ট",
|
||||
"LabelPodcast": "পডকাস্ট",
|
||||
"LabelPodcastSearchRegion": "পডকাস্ট অনুসন্ধান অঞ্চল",
|
||||
@@ -412,15 +461,20 @@
|
||||
"LabelPrimaryEbook": "প্রাথমিক ই-বই",
|
||||
"LabelProgress": "প্রগতি",
|
||||
"LabelProvider": "প্রদানকারী",
|
||||
"LabelProviderAuthorizationValue": "অনুমোদন শিরোনামের মান",
|
||||
"LabelPubDate": "প্রকাশের তারিখ",
|
||||
"LabelPublishYear": "প্রকাশের বছর",
|
||||
"LabelPublishedDate": "প্রকাশিত {0}",
|
||||
"LabelPublisher": "প্রকাশক",
|
||||
"LabelPublishers": "প্রকাশকরা",
|
||||
"LabelRSSFeedCustomOwnerEmail": "কাস্টম মালিকের ইমেইল",
|
||||
"LabelRSSFeedCustomOwnerName": "কাস্টম মালিকের নাম",
|
||||
"LabelRSSFeedOpen": "আরএসএস ফিড খুলুন",
|
||||
"LabelRSSFeedPreventIndexing": "সূচীকরণ প্রতিরোধ করুন",
|
||||
"LabelRSSFeedSlug": "আরএসএস ফিড স্লাগ",
|
||||
"LabelRSSFeedURL": "আরএসএস ফিড ইউআরএল",
|
||||
"LabelRandomly": "এলোমেলোভাবে",
|
||||
"LabelReAddSeriesToContinueListening": "শোনা চালিয়ে যেতে সিরিজ পুনরায় যোগ করুন",
|
||||
"LabelRead": "পড়ুন",
|
||||
"LabelReadAgain": "আবার পড়ুন",
|
||||
"LabelReadEbookWithoutProgress": "প্রগতি না রেখে ই-বই পড়ুন",
|
||||
@@ -436,6 +490,7 @@
|
||||
"LabelSearchTitle": "অনুসন্ধান শিরোনাম",
|
||||
"LabelSearchTitleOrASIN": "অনুসন্ধান শিরোনাম বা ASIN",
|
||||
"LabelSeason": "সেশন",
|
||||
"LabelSelectAll": "সব নির্বাচন করুন",
|
||||
"LabelSelectAllEpisodes": "সমস্ত পর্ব নির্বাচন করুন",
|
||||
"LabelSelectEpisodesShowing": "দেখানো {0}টি পর্ব নির্বাচন করুন",
|
||||
"LabelSelectUsers": "ব্যবহারকারী নির্বাচন করুন",
|
||||
@@ -458,7 +513,8 @@
|
||||
"LabelSettingsEnableWatcher": "প্রহরী সক্ষম করুন",
|
||||
"LabelSettingsEnableWatcherForLibrary": "লাইব্রেরির জন্য ফোল্ডার প্রহরী সক্ষম করুন",
|
||||
"LabelSettingsEnableWatcherHelp": "ফাইলের পরিবর্তন শনাক্ত হলে আইটেমগুলির স্বয়ংক্রিয় যোগ/আপডেট সক্ষম করবে। *সার্ভার পুনরায় চালু করতে হবে",
|
||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Allow epub files to execute scripts. It is recommended to keep this setting disabled unless you trust the source of the epub files।",
|
||||
"LabelSettingsEpubsAllowScriptedContent": "ইপাবে স্ক্রিপ্ট করা বিষয়বস্তুর অনুমতি দিন",
|
||||
"LabelSettingsEpubsAllowScriptedContentHelp": "ইপাব ফাইলগুলিকে স্ক্রিপ্ট চালানোর অনুমতি দিন। আপনি ইপাব ফাইলগুলির উৎসকে বিশ্বাস না করলে এই সেটিংটি নিষ্ক্রিয় রাখার সুপারিশ করা হলো।",
|
||||
"LabelSettingsExperimentalFeatures": "পরীক্ষামূলক বৈশিষ্ট্য",
|
||||
"LabelSettingsExperimentalFeaturesHelp": "ফিচারের বৈশিষ্ট্য যা আপনার প্রতিক্রিয়া ব্যবহার করতে পারে এবং পরীক্ষায় সহায়তা করতে পারে। গিটহাব আলোচনা খুলতে ক্লিক করুন।",
|
||||
"LabelSettingsFindCovers": "কভার খুঁজুন",
|
||||
@@ -468,7 +524,7 @@
|
||||
"LabelSettingsHomePageBookshelfView": "নীড় পেজে বুকশেলফ ভিউ ব্যবহার করুন",
|
||||
"LabelSettingsLibraryBookshelfView": "লাইব্রেরি বুকশেলফ ভিউ ব্যবহার করুন",
|
||||
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "কন্টিনিউ সিরিজে আগের বইগুলো এড়িয়ে যান",
|
||||
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "কন্টিনিউ সিরিজের হোম পেজ শেল্ফ প্রথম বইটি দেখায় যেটি সিরিজে শুরু হয়নি যেটিতে অন্তত একটি বই শেষ হয়েছে এবং কোনো বই চলছে না। এই সেটিংটি সক্ষম করা হলে তা শুরু না হওয়া প্রথম বইটির পরিবর্তে সবচেয়ে দূরের সম্পূর্ণ বই থেকে সিরিজ চালিয়ে যাবে।",
|
||||
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "কন্টিনিউ সিরিজের নীড় পেজ শেল্ফ দেখায় যে সিরিজে শুরু হয়নি এমন প্রথম বই যার অন্তত একটি বই শেষ হয়েছে এবং কোনো বই চলছে না। এই সেটিংটি সক্ষম করলে শুরু না হওয়া প্রথম বইটির পরিবর্তে সবচেয়ে দূরের সম্পূর্ণ বই থেকে সিরিজ চলতে থাকবে।",
|
||||
"LabelSettingsParseSubtitles": "সাবটাইটেল পার্স করুন",
|
||||
"LabelSettingsParseSubtitlesHelp": "অডিওবুক ফোল্ডারের নাম থেকে সাবটাইটেল বের করুন৷<br>সাবটাইটেল অবশ্যই \" - \"<br>অর্থাৎ \"বুকের শিরোনাম - এখানে একটি সাবটাইটেল\" এর সাবটাইটেল আছে \"এখানে একটি সাবটাইটেল\"",
|
||||
"LabelSettingsPreferMatchedMetadata": "মিলিত মেটাডেটা পছন্দ করুন",
|
||||
@@ -484,12 +540,17 @@
|
||||
"LabelSettingsStoreMetadataWithItem": "আইটেমের সাথে মেটাডেটা সংরক্ষণ করুন",
|
||||
"LabelSettingsStoreMetadataWithItemHelp": "ডিফল্টরূপে মেটাডেটা ফাইলগুলি /মেটাডাটা/আইটেমগুলি -এ সংরক্ষণ করা হয়, এই সেটিংটি সক্ষম করলে মেটাডেটা ফাইলগুলি আপনার লাইব্রেরি আইটেম ফোল্ডারে সংরক্ষণ করা হবে",
|
||||
"LabelSettingsTimeFormat": "সময় বিন্যাস",
|
||||
"LabelShare": "শেয়ার করুন",
|
||||
"LabelShareOpen": "শেয়ার খোলা",
|
||||
"LabelShareURL": "শেয়ার ইউআরএল",
|
||||
"LabelShowAll": "সব দেখান",
|
||||
"LabelShowSeconds": "সেকেন্ড দেখান",
|
||||
"LabelShowSubtitles": "সহ-শিরোনাম দেখান",
|
||||
"LabelSize": "আকার",
|
||||
"LabelSleepTimer": "স্লিপ টাইমার",
|
||||
"LabelSlug": "স্লাগ",
|
||||
"LabelStart": "শুরু",
|
||||
"LabelStartTime": "শুরু করার সময়",
|
||||
"LabelStartTime": "শুরুর সময়",
|
||||
"LabelStarted": "শুরু হয়েছে",
|
||||
"LabelStartedAt": "এতে শুরু হয়েছে",
|
||||
"LabelStatsAudioTracks": "অডিও ট্র্যাক",
|
||||
@@ -522,6 +583,10 @@
|
||||
"LabelThemeDark": "অন্ধকার",
|
||||
"LabelThemeLight": "আলো",
|
||||
"LabelTimeBase": "সময় বেস",
|
||||
"LabelTimeDurationXHours": "{0} ঘণ্টা",
|
||||
"LabelTimeDurationXMinutes": "{0} মিনিট",
|
||||
"LabelTimeDurationXSeconds": "{0} সেকেন্ড",
|
||||
"LabelTimeInMinutes": "মিনিটে সময়",
|
||||
"LabelTimeListened": "সময় শোনা হয়েছে",
|
||||
"LabelTimeListenedToday": "আজ শোনার সময়",
|
||||
"LabelTimeRemaining": "{0}টি অবশিষ্ট",
|
||||
@@ -545,6 +610,7 @@
|
||||
"LabelUnabridged": "অসংলগ্ন",
|
||||
"LabelUndo": "পূর্বাবস্থা",
|
||||
"LabelUnknown": "অজানা",
|
||||
"LabelUnknownPublishDate": "প্রকাশের তারিখ অজানা",
|
||||
"LabelUpdateCover": "কভার আপডেট করুন",
|
||||
"LabelUpdateCoverHelp": "একটি মিল থাকা অবস্থায় নির্বাচিত বইগুলির বিদ্যমান কভারগুলি ওভাররাইট করার অনুমতি দিন",
|
||||
"LabelUpdateDetails": "বিশদ আপডেট করুন",
|
||||
@@ -561,9 +627,12 @@
|
||||
"LabelVersion": "সংস্করণ",
|
||||
"LabelViewBookmarks": "বুকমার্ক দেখুন",
|
||||
"LabelViewChapters": "অধ্যায় দেখুন",
|
||||
"LabelViewPlayerSettings": "প্লেয়ার সেটিংস দেখুন",
|
||||
"LabelViewQueue": "প্লেয়ার সারি দেখুন",
|
||||
"LabelVolume": "ভলিউম",
|
||||
"LabelWeekdaysToRun": "চলতে হবে সপ্তাহের দিন",
|
||||
"LabelXBooks": "{0}টি বই",
|
||||
"LabelXItems": "{0}টি আইটেম",
|
||||
"LabelYearReviewHide": "পর্যালোচনার বছর লুকান",
|
||||
"LabelYearReviewShow": "পর্যালোচনার বছর দেখুন",
|
||||
"LabelYourAudiobookDuration": "আপনার অডিওবুকের সময়কাল",
|
||||
@@ -571,12 +640,16 @@
|
||||
"LabelYourPlaylists": "আপনার প্লেলিস্ট",
|
||||
"LabelYourProgress": "আপনার অগ্রগতি",
|
||||
"MessageAddToPlayerQueue": "প্লেয়ার সারিতে যোগ করুন",
|
||||
"MessageAppriseDescription": "এই বৈশিষ্ট্যটি ব্যবহার করার জন্য আপনাকে <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</-এর একটি উদাহরণ থাকতে হবে a> চলমান বা একটি এপিআই যা সেই একই অনুরোধগুলি পরিচালনা করবে৷ <br /> বিজ্ঞপ্তি পাঠানোর জন্য Apprise API Url সম্পূর্ণ URL পাথ হওয়া উচিত, যেমন, যদি আপনার API উদাহরণ <code>http://192.168 এ পরিবেশিত হয়৷ 1.1:8337</code> তারপর আপনি <code>http://192.168.1.1:8337/notify</code> লিখবেন।",
|
||||
"MessageAppriseDescription": "এই বৈশিষ্ট্যটি ব্যবহার করার জন্য আপনাকে <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> চালানোর একটি উদাহরণ বা একটি এপিআই পরিচালনা করতে হবে যে একই অনুরোধ পরিচালনা করবে। <br />অ্যাপ্রাইজ এপিআই ইউআরএলটি বিজ্ঞপ্তি পাঠানোর জন্য সম্পূর্ণ ইউআরএল পথ হওয়া উচিত, যেমন, যদি আপনার API ইনস্ট্যান্স <code>http://192.168.1.1:8337</code> এ পরিবেশিত হয় তাহলে আপনি <code> রাখবেন >http://192.168.1.1:8337/notify</code>।",
|
||||
"MessageBackupsDescription": "ব্যাকআপের মধ্যে রয়েছে ব্যবহারকারী, ব্যবহারকারীর অগ্রগতি, লাইব্রেরি আইটেমের বিবরণ, সার্ভার সেটিংস এবং <code>/metadata/items</code> & <code>/metadata/authors</code>-এ সংরক্ষিত ছবি। ব্যাকআপগুলি <strong> আপনার লাইব্রেরি ফোল্ডারে সঞ্চিত কোনো ফাইল >অন্তর্ভুক্ত করবেন না</strong>।",
|
||||
"MessageBackupsLocationEditNote": "দ্রষ্টব্য: ব্যাকআপ অবস্থান আপডেট করলে বিদ্যমান ব্যাকআপগুলি সরানো বা সংশোধন করা হবে না",
|
||||
"MessageBackupsLocationNoEditNote": "দ্রষ্টব্য: ব্যাকআপ অবস্থান একটি পরিবেশ পরিবর্তনশীল মাধ্যমে স্থির করা হয়েছে এবং এখানে পরিবর্তন করা যাবে না।",
|
||||
"MessageBackupsLocationPathEmpty": "ব্যাকআপ অবস্থানের পথ খালি থাকতে পারবে না",
|
||||
"MessageBatchQuickMatchDescription": "কুইক ম্যাচ নির্বাচিত আইটেমগুলির জন্য অনুপস্থিত কভার এবং মেটাডেটা যোগ করার চেষ্টা করবে। বিদ্যমান কভার এবং/অথবা মেটাডেটা ওভাররাইট করার জন্য দ্রুত ম্যাচকে অনুমতি দিতে নীচের বিকল্পগুলি সক্ষম করুন।",
|
||||
"MessageBookshelfNoCollections": "আপনি এখনও কোনো সংগ্রহ করেননি",
|
||||
"MessageBookshelfNoRSSFeeds": "কোনও RSS ফিড খোলা নেই",
|
||||
"MessageBookshelfNoResultsForFilter": "ফিল্টার \"{0}: {1}\" এর জন্য কোন ফলাফল নেই",
|
||||
"MessageBookshelfNoResultsForQuery": "প্রশ্নের জন্য কোন ফলাফল নেই",
|
||||
"MessageBookshelfNoSeries": "আপনার কোনো সিরিজ নেই",
|
||||
"MessageChapterEndIsAfter": "অধ্যায়ের সমাপ্তি আপনার অডিওবুকের শেষে",
|
||||
"MessageChapterErrorFirstNotZero": "প্রথম অধ্যায় 0 এ শুরু হতে হবে",
|
||||
@@ -586,16 +659,24 @@
|
||||
"MessageCheckingCron": "ক্রন পরীক্ষা করা হচ্ছে...",
|
||||
"MessageConfirmCloseFeed": "আপনি কি নিশ্চিত যে আপনি এই ফিডটি বন্ধ করতে চান?",
|
||||
"MessageConfirmDeleteBackup": "আপনি কি নিশ্চিত যে আপনি {0} এর ব্যাকআপ মুছে ফেলতে চান?",
|
||||
"MessageConfirmDeleteDevice": "আপনি কি নিশ্চিতভাবে ই-রিডার ডিভাইস \"{0}\" মুছতে চান?",
|
||||
"MessageConfirmDeleteFile": "এটি আপনার ফাইল সিস্টেম থেকে ফাইলটি মুছে দেবে। আপনি কি নিশ্চিত?",
|
||||
"MessageConfirmDeleteLibrary": "আপনি কি নিশ্চিত যে আপনি স্থায়ীভাবে লাইব্রেরি \"{0}\" মুছে ফেলতে চান?",
|
||||
"MessageConfirmDeleteLibraryItem": "এটি ডাটাবেস এবং আপনার ফাইল সিস্টেম থেকে লাইব্রেরি আইটেমটি মুছে ফেলবে। আপনি কি নিশ্চিত?",
|
||||
"MessageConfirmDeleteLibraryItems": "এটি ডাটাবেস এবং আপনার ফাইল সিস্টেম থেকে {0}টি লাইব্রেরি আইটেম মুছে ফেলবে। আপনি কি নিশ্চিত?",
|
||||
"MessageConfirmDeleteMetadataProvider": "আপনি কি নিশ্চিতভাবে কাস্টম মেটাডেটা প্রদানকারী \"{0}\" মুছতে চান?",
|
||||
"MessageConfirmDeleteNotification": "আপনি কি নিশ্চিতভাবে এই বিজ্ঞপ্তিটি মুছতে চান?",
|
||||
"MessageConfirmDeleteSession": "আপনি কি নিশ্চিত আপনি এই অধিবেশন মুছে দিতে চান?",
|
||||
"MessageConfirmForceReScan": "আপনি কি নিশ্চিত যে আপনি জোর করে পুনরায় স্ক্যান করতে চান?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "আপনি কি নিশ্চিত যে আপনি সমস্ত পর্ব সমাপ্ত হিসাবে চিহ্নিত করতে চান?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "আপনি কি নিশ্চিত যে আপনি সমস্ত পর্বকে শেষ হয়নি বলে চিহ্নিত করতে চান?",
|
||||
"MessageConfirmMarkItemFinished": "আপনি কি \"{0}\" কে সমাপ্ত হিসাবে চিহ্নিত করার বিষয়ে নিশ্চিত?",
|
||||
"MessageConfirmMarkItemNotFinished": "আপনি কি \"{0}\" শেষ হয়নি বলে চিহ্নিত করার বিষয়ে নিশ্চিত?",
|
||||
"MessageConfirmMarkSeriesFinished": "আপনি কি নিশ্চিত যে আপনি এই সিরিজের সমস্ত বইকে সমাপ্ত হিসাবে চিহ্নিত করতে চান?",
|
||||
"MessageConfirmMarkSeriesNotFinished": "আপনি কি নিশ্চিত যে আপনি এই সিরিজের সমস্ত বইকে শেষ হয়নি বলে চিহ্নিত করতে চান?",
|
||||
"MessageConfirmNotificationTestTrigger": "পরীক্ষার তথ্য দিয়ে এই বিজ্ঞপ্তিটি ট্রিগার করবেন?",
|
||||
"MessageConfirmPurgeCache": "ক্যাশে পরিষ্কারক <code>/metadata/cache</code>-এ সম্পূর্ণ ডিরেক্টরি মুছে ফেলবে। <br /><br />আপনি কি নিশ্চিত আপনি ক্যাশে ডিরেক্টরি সরাতে চান?",
|
||||
"MessageConfirmPurgeItemsCache": "আইটেম ক্যাশে পরিষ্কারক <code>/metadata/cache/items</code>-এ সম্পূর্ণ ডিরেক্টরি মুছে ফেলবে।<br />আপনি কি নিশ্চিত?",
|
||||
"MessageConfirmQuickEmbed": "সতর্কতা! দ্রুত এম্বেড আপনার অডিও ফাইলের ব্যাকআপ করবে না। নিশ্চিত করুন যে আপনার অডিও ফাইলগুলির একটি ব্যাকআপ আছে। <br><br>আপনি কি চালিয়ে যেতে চান?",
|
||||
"MessageConfirmReScanLibraryItems": "আপনি কি নিশ্চিত যে আপনি {0}টি আইটেম পুনরায় স্ক্যান করতে চান?",
|
||||
"MessageConfirmRemoveAllChapters": "আপনি কি নিশ্চিত যে আপনি সমস্ত অধ্যায় সরাতে চান?",
|
||||
@@ -612,14 +693,17 @@
|
||||
"MessageConfirmRenameTag": "আপনি কি সব আইটেমের জন্য \"{0}\" ট্যাগের নাম পরিবর্তন করে \"{1}\" করার বিষয়ে নিশ্চিত?",
|
||||
"MessageConfirmRenameTagMergeNote": "দ্রষ্টব্য: এই ট্যাগটি আগে থেকেই বিদ্যমান তাই সেগুলিকে একত্র করা হবে।",
|
||||
"MessageConfirmRenameTagWarning": "সতর্কতা! একটি ভিন্ন কেসিং সহ একটি অনুরূপ ট্যাগ ইতিমধ্যেই বিদ্যমান \"{0}\"।",
|
||||
"MessageConfirmResetProgress": "আপনি কি আপনার অগ্রগতি রিসেট করার বিষয়ে নিশ্চিত?",
|
||||
"MessageConfirmSendEbookToDevice": "আপনি কি নিশ্চিত যে আপনি \"{2}\" ডিভাইসে {0} ইবুক \"{1}\" পাঠাতে চান?",
|
||||
"MessageConfirmUnlinkOpenId": "আপনি কি এই ব্যবহারকারীকে ওপেনআইডি থেকে লিঙ্কমুক্ত করার বিষয়ে নিশ্চিত?",
|
||||
"MessageDownloadingEpisode": "ডাউনলোডিং পর্ব",
|
||||
"MessageDragFilesIntoTrackOrder": "সঠিক ট্র্যাক অর্ডারে ফাইল টেনে আনুন",
|
||||
"MessageEmbedFailed": "এম্বেড ব্যর্থ হয়েছে!",
|
||||
"MessageEmbedFinished": "এম্বেড করা শেষ!",
|
||||
"MessageEpisodesQueuedForDownload": "{0} পর্ব(গুলি) ডাউনলোডের জন্য সারিবদ্ধ",
|
||||
"MessageEreaderDevices": "To ensure delivery of ebooks, you may need to add the above email address as a valid sender for each device listed below।",
|
||||
"MessageEreaderDevices": "ই-বুক সরবরাহ নিশ্চিত করতে, আপনাকে নীচে তালিকাভুক্ত প্রতিটি ডিভাইসের জন্য একটি বৈধ প্রেরক হিসাবে উপরের ইমেল ঠিকানাটি যুক্ত করতে হতে পারে।",
|
||||
"MessageFeedURLWillBe": "ফিড URL হবে {0}",
|
||||
"MessageFetching": "আনয় হচ্ছে...",
|
||||
"MessageFetching": "আনয় হচ্ছে.।",
|
||||
"MessageForceReScanDescription": "সকল ফাইল আবার নতুন স্ক্যানের মত স্ক্যান করবে। অডিও ফাইল ID3 ট্যাগ, OPF ফাইল, এবং টেক্সট ফাইলগুলি নতুন হিসাবে স্ক্যান করা হবে।",
|
||||
"MessageImportantNotice": "গুরুত্বপূর্ণ বিজ্ঞপ্তি!",
|
||||
"MessageInsertChapterBelow": "নীচে অধ্যায় ঢোকান",
|
||||
@@ -627,9 +711,9 @@
|
||||
"MessageItemsUpdated": "{0}টি আইটেম আপডেট করা হয়েছে",
|
||||
"MessageJoinUsOn": "আমাদের সাথে যোগ দিন",
|
||||
"MessageListeningSessionsInTheLastYear": "গত বছরে {0}টি শোনার সেশন",
|
||||
"MessageLoading": "লোড হচ্ছে...",
|
||||
"MessageLoading": "লোড হচ্ছে.।",
|
||||
"MessageLoadingFolders": "ফোল্ডার লোড হচ্ছে...",
|
||||
"MessageLogsDescription": "Logs are stored in <code>/metadata/logs</code> as JSON files. Crash logs are stored in <code>/metadata/logs/crash_logs.txt</code>।",
|
||||
"MessageLogsDescription": "লগগুলি JSON ফাইল হিসাবে <code>/metadata/logs</code>-এ সংরক্ষণ করা হয়। ক্র্যাশ লগগুলি <code>/metadata/logs/crash_logs.txt</code>-এ সংরক্ষণ করা হয়।",
|
||||
"MessageM4BFailed": "M4B ব্যর্থ!",
|
||||
"MessageM4BFinished": "M4B সমাপ্ত!",
|
||||
"MessageMapChapterTitles": "টাইমস্ট্যাম্প সামঞ্জস্য না করে আপনার বিদ্যমান অডিওবুক অধ্যায়গুলিতে অধ্যায়ের শিরোনাম ম্যাপ করুন",
|
||||
@@ -646,6 +730,7 @@
|
||||
"MessageNoCollections": "কোন সংগ্রহ নেই",
|
||||
"MessageNoCoversFound": "কোন কভার পাওয়া যায়নি",
|
||||
"MessageNoDescription": "কোন বর্ণনা নেই",
|
||||
"MessageNoDevices": "কোনো ডিভাইস নেই",
|
||||
"MessageNoDownloadsInProgress": "বর্তমানে কোনো ডাউনলোড চলছে না",
|
||||
"MessageNoDownloadsQueued": "কোনও ডাউনলোড সারি নেই",
|
||||
"MessageNoEpisodeMatchesFound": "কোন পর্বের মিল পাওয়া যায়নি",
|
||||
@@ -668,10 +753,12 @@
|
||||
"MessageNoUpdatesWereNecessary": "কোন আপডেটের প্রয়োজন ছিল না",
|
||||
"MessageNoUserPlaylists": "আপনার কোনো প্লেলিস্ট নেই",
|
||||
"MessageNotYetImplemented": "এখনও বাস্তবায়িত হয়নি",
|
||||
"MessageOpmlPreviewNote": "দ্রষ্টব্য: এটি পার্স করা OPML ফাইলের একটি পূর্বরূপ। প্রকৃত পডকাস্ট শিরোনাম RSS ফিড থেকে নেওয়া হবে।",
|
||||
"MessageOr": "বা",
|
||||
"MessagePauseChapter": "পজ অধ্যায় প্লেব্যাক",
|
||||
"MessagePlayChapter": "অধ্যায়ের শুরুতে শুনুন",
|
||||
"MessagePlaylistCreateFromCollection": "সংগ্রহ থেকে প্লেলিস্ট তৈরি করুন",
|
||||
"MessagePleaseWait": "অনুগ্রহ করে অপেক্ষা করুন..।",
|
||||
"MessagePodcastHasNoRSSFeedForMatching": "পডকাস্টের সাথে মিলের জন্য ব্যবহার করার জন্য কোন RSS ফিড ইউআরএল নেই",
|
||||
"MessageQuickMatchDescription": "খালি আইটেমের বিশদ বিবরণ এবং '{0}' থেকে প্রথম ম্যাচের ফলাফলের সাথে কভার করুন। সার্ভার সেটিং সক্ষম না থাকলে বিশদ ওভাররাইট করে না।",
|
||||
"MessageRemoveChapter": "অধ্যায় সরান",
|
||||
@@ -686,7 +773,42 @@
|
||||
"MessageSelected": "{0}টি নির্বাচিত",
|
||||
"MessageServerCouldNotBeReached": "সার্ভারে পৌঁছানো যায়নি",
|
||||
"MessageSetChaptersFromTracksDescription": "প্রতিটি অডিও ফাইলকে অধ্যায় হিসেবে ব্যবহার করে অধ্যায় সেট করুন এবং অডিও ফাইলের নাম হিসেবে অধ্যায়ের শিরোনাম করুন",
|
||||
"MessageShareExpirationWillBe": "মেয়াদ শেষ হবে <strong>{0}</strong>",
|
||||
"MessageShareExpiresIn": "মেয়াদ শেষ হবে {0}",
|
||||
"MessageShareURLWillBe": "শেয়ার করা ইউআরএল হবে <strong>{0}</strong>",
|
||||
"MessageStartPlaybackAtTime": "\"{0}\" এর জন্য {1} এ প্লেব্যাক শুরু করবেন?",
|
||||
"MessageTaskAudioFileNotWritable": "অডিও ফাইল \"{0}\" লেখার যোগ্য নয়",
|
||||
"MessageTaskCanceledByUser": "ব্যবহারকারী দ্বারা টাস্ক বাতিল করা হয়েছে",
|
||||
"MessageTaskDownloadingEpisodeDescription": "\"{0}\" পর্ব ডাউনলোড করা হচ্ছে",
|
||||
"MessageTaskEmbeddingMetadata": "মেটাডেটা এম্বেড করা হচ্ছে",
|
||||
"MessageTaskEmbeddingMetadataDescription": "অডিওবুক \"{0}\" এ মেটাডেটা এম্বেড করা হচ্ছে",
|
||||
"MessageTaskEncodingM4b": "এনকোডিং M4B",
|
||||
"MessageTaskEncodingM4bDescription": "একটি একক m4b ফাইলে অডিওবুক \"{0}\" এনকোড করা হচ্ছে",
|
||||
"MessageTaskFailed": "ব্যর্থ হয়েছে",
|
||||
"MessageTaskFailedToBackupAudioFile": "অডিও ফাইল \"{0}\" ব্যাকআপ করতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskFailedToCreateCacheDirectory": "ক্যাশে ডিরেক্টরি তৈরি করতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskFailedToEmbedMetadataInFile": "\"{0}\" ফাইলে মেটাডেটা এম্বেড করতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskFailedToMergeAudioFiles": "অডিও ফাইল মার্জ করতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskFailedToMoveM4bFile": "m4b ফাইল সরাতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskFailedToWriteMetadataFile": "মেটাডেটা ফাইল লিখতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskMatchingBooksInLibrary": "লাইব্রেরি \"{0}\"-এ বই মিলানো হচ্ছে",
|
||||
"MessageTaskNoFilesToScan": "স্ক্যান করার জন্য কোন ফাইল নেই",
|
||||
"MessageTaskOpmlImport": "OPML আমদানি",
|
||||
"MessageTaskOpmlImportDescription": "{0} RSS ফিড থেকে পডকাস্ট তৈরি করা হচ্ছে",
|
||||
"MessageTaskOpmlImportFeed": "OPML ফিড আমদানি",
|
||||
"MessageTaskOpmlImportFeedDescription": "RSS ফিড \"{0}\" আমদানি করা হচ্ছে",
|
||||
"MessageTaskOpmlImportFeedFailed": "পডকাস্ট ফিড পেতে ব্যর্থ হয়েছে",
|
||||
"MessageTaskOpmlImportFeedPodcastDescription": "পডকাস্ট তৈরি করা হচ্ছে \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedPodcastExists": "পডকাস্ট আগে থেকেই পাথে বিদ্যমান",
|
||||
"MessageTaskOpmlImportFeedPodcastFailed": "পডকাস্ট তৈরি করতে ব্যর্থ",
|
||||
"MessageTaskOpmlImportFinished": "{0}টি পডকাস্ট যোগ করা হয়েছে",
|
||||
"MessageTaskScanItemsAdded": "{0}টি করা হয়েছে",
|
||||
"MessageTaskScanItemsMissing": "{0}টি অনুপস্থিত",
|
||||
"MessageTaskScanItemsUpdated": "{0} টি আপডেট করা হয়েছে",
|
||||
"MessageTaskScanNoChangesNeeded": "কোন পরিবর্তন প্রয়োজন নেই",
|
||||
"MessageTaskScanningFileChanges": "\"{0}\" এ ফাইলের পরিবর্তন স্ক্যান করা হচ্ছে",
|
||||
"MessageTaskScanningLibrary": "\"{0}\" লাইব্রেরি স্ক্যান করা হচ্ছে",
|
||||
"MessageTaskTargetDirectoryNotWritable": "টার্গেট ডিরেক্টরি লেখার যোগ্য নয়",
|
||||
"MessageThinking": "চিন্তা করছি...",
|
||||
"MessageUploaderItemFailed": "আপলোড করতে ব্যর্থ",
|
||||
"MessageUploaderItemSuccess": "সফলভাবে আপলোড হয়েছে!",
|
||||
@@ -709,69 +831,162 @@
|
||||
"PlaceholderNewPlaylist": "নতুন প্লেলিস্টের নাম",
|
||||
"PlaceholderSearch": "অনুসন্ধান..",
|
||||
"PlaceholderSearchEpisode": "অনুসন্ধান পর্ব..",
|
||||
"ToastAccountUpdateFailed": "অ্যাকাউন্ট আপডেট করতে ব্যর্থ",
|
||||
"StatsAuthorsAdded": "লেখক যোগ করা হয়েছে",
|
||||
"StatsBooksAdded": "বই যোগ করা হয়েছে",
|
||||
"StatsBooksAdditional": "কিছু সংযোজনের মধ্যে রয়েছে…",
|
||||
"StatsBooksFinished": "বই সমাপ্ত",
|
||||
"StatsBooksFinishedThisYear": "এ বছর শেষ হওয়া কিছু বই …",
|
||||
"StatsBooksListenedTo": "বই শোনা হয়েছে",
|
||||
"StatsCollectionGrewTo": "আপনার বইয়ের সংগ্রহ বেড়েছে…",
|
||||
"StatsSessions": "অধিবেশনসমূহ",
|
||||
"StatsSpentListening": "শুনে কাটিয়েছেন",
|
||||
"StatsTopAuthor": "শীর্ষস্থানীয় লেখক",
|
||||
"StatsTopAuthors": "শীর্ষস্থানীয় লেখকগণ",
|
||||
"StatsTopGenre": "শীর্ষ ঘরানা",
|
||||
"StatsTopGenres": "শীর্ষ ঘরানাগুলো",
|
||||
"StatsTopMonth": "সেরা মাস",
|
||||
"StatsTopNarrator": "শীর্ষ কথক",
|
||||
"StatsTopNarrators": "শীর্ষ কথকগণ",
|
||||
"StatsTotalDuration": "মোট সময়কাল…",
|
||||
"StatsYearInReview": "বাৎসরিক পর্যালোচনা",
|
||||
"ToastAccountUpdateSuccess": "অ্যাকাউন্ট আপডেট করা হয়েছে",
|
||||
"ToastAppriseUrlRequired": "একটি Apprise ইউআরএল লিখতে হবে",
|
||||
"ToastAuthorImageRemoveSuccess": "লেখকের ছবি সরানো হয়েছে",
|
||||
"ToastAuthorUpdateFailed": "লেখক আপডেট করতে ব্যর্থ",
|
||||
"ToastAuthorNotFound": "লেখক \"{0}\" খুঁজে পাওয়া যায়নি",
|
||||
"ToastAuthorRemoveSuccess": "লেখক সরানো হয়েছে",
|
||||
"ToastAuthorSearchNotFound": "লেখক পাওয়া যায়নি",
|
||||
"ToastAuthorUpdateMerged": "লেখক একত্রিত হয়েছে",
|
||||
"ToastAuthorUpdateSuccess": "লেখক আপডেট করেছেন",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "লেখক আপডেট করেছেন (কোন ছবি পাওয়া যায়নি)",
|
||||
"ToastBackupAppliedSuccess": "ব্যাকআপ প্রয়োগ করা হয়েছে",
|
||||
"ToastBackupCreateFailed": "ব্যাকআপ তৈরি করতে ব্যর্থ",
|
||||
"ToastBackupCreateSuccess": "ব্যাকআপ তৈরি করা হয়েছে",
|
||||
"ToastBackupDeleteFailed": "ব্যাকআপ মুছে ফেলতে ব্যর্থ",
|
||||
"ToastBackupDeleteSuccess": "ব্যাকআপ মুছে ফেলা হয়েছে",
|
||||
"ToastBackupInvalidMaxKeep": "রাখার জন্য অকার্যকর ব্যাকআপের সংখ্যা",
|
||||
"ToastBackupInvalidMaxSize": "অকার্যকর সর্বোচ্চ ব্যাকআপ আকার",
|
||||
"ToastBackupRestoreFailed": "ব্যাকআপ পুনরুদ্ধার করতে ব্যর্থ",
|
||||
"ToastBackupUploadFailed": "ব্যাকআপ আপলোড করতে ব্যর্থ",
|
||||
"ToastBackupUploadSuccess": "ব্যাকআপ আপলোড হয়েছে",
|
||||
"ToastBatchDeleteFailed": "ব্যাচ মুছে ফেলতে ব্যর্থ হয়েছে",
|
||||
"ToastBatchDeleteSuccess": "ব্যাচ মুছে ফেলা সফল হয়েছে",
|
||||
"ToastBatchUpdateFailed": "ব্যাচ আপডেট ব্যর্থ হয়েছে",
|
||||
"ToastBatchUpdateSuccess": "ব্যাচ আপডেট সাফল্য",
|
||||
"ToastBookmarkCreateFailed": "বুকমার্ক তৈরি করতে ব্যর্থ",
|
||||
"ToastBookmarkCreateSuccess": "বুকমার্ক যোগ করা হয়েছে",
|
||||
"ToastBookmarkRemoveSuccess": "বুকমার্ক সরানো হয়েছে",
|
||||
"ToastBookmarkUpdateFailed": "বুকমার্ক আপডেট করতে ব্যর্থ",
|
||||
"ToastBookmarkUpdateSuccess": "বুকমার্ক আপডেট করা হয়েছে",
|
||||
"ToastCachePurgeFailed": "ক্যাশে পরিষ্কার করতে ব্যর্থ হয়েছে",
|
||||
"ToastCachePurgeSuccess": "ক্যাশে সফলভাবে পরিষ্কার করা হয়েছে",
|
||||
"ToastChaptersHaveErrors": "অধ্যায়ে ত্রুটি আছে",
|
||||
"ToastChaptersMustHaveTitles": "অধ্যায়ের শিরোনাম থাকতে হবে",
|
||||
"ToastChaptersRemoved": "অধ্যায়গুলো মুছে ফেলা হয়েছে",
|
||||
"ToastCollectionItemsAddFailed": "আইটেম(গুলি) সংগ্রহে যোগ করা ব্যর্থ হয়েছে",
|
||||
"ToastCollectionItemsAddSuccess": "আইটেম(গুলি) সংগ্রহে যোগ করা সফল হয়েছে",
|
||||
"ToastCollectionItemsRemoveSuccess": "আইটেম(গুলি) সংগ্রহ থেকে সরানো হয়েছে",
|
||||
"ToastCollectionRemoveSuccess": "সংগ্রহ সরানো হয়েছে",
|
||||
"ToastCollectionUpdateFailed": "সংগ্রহ আপডেট করতে ব্যর্থ",
|
||||
"ToastCollectionUpdateSuccess": "সংগ্রহ আপডেট করা হয়েছে",
|
||||
"ToastItemCoverUpdateFailed": "আইটেম কভার আপডেট করতে ব্যর্থ হয়েছে",
|
||||
"ToastCoverUpdateFailed": "কভার আপডেট ব্যর্থ হয়েছে",
|
||||
"ToastDeleteFileFailed": "ফাইল মুছে ফেলতে ব্যর্থ হয়েছে",
|
||||
"ToastDeleteFileSuccess": "ফাইল মুছে ফেলা হয়েছে",
|
||||
"ToastDeviceAddFailed": "ডিভাইস যোগ করতে ব্যর্থ হয়েছে",
|
||||
"ToastDeviceNameAlreadyExists": "এই নামের ইরিডার ডিভাইস ইতিমধ্যেই বিদ্যমান",
|
||||
"ToastDeviceTestEmailFailed": "পরীক্ষামূলক ইমেল পাঠাতে ব্যর্থ হয়েছে",
|
||||
"ToastDeviceTestEmailSuccess": "পরীক্ষামূলক ইমেল পাঠানো হয়েছে",
|
||||
"ToastEmailSettingsUpdateSuccess": "ইমেল সেটিংস আপডেট করা হয়েছে",
|
||||
"ToastEncodeCancelFailed": "এনকোড বাতিল করতে ব্যর্থ হয়েছে",
|
||||
"ToastEncodeCancelSucces": "এনকোড বাতিল করা হয়েছে",
|
||||
"ToastEpisodeDownloadQueueClearFailed": "সারি সাফ করতে ব্যর্থ হয়েছে",
|
||||
"ToastEpisodeDownloadQueueClearSuccess": "পর্ব ডাউনলোড সারি পরিষ্কার করা হয়েছে",
|
||||
"ToastErrorCannotShare": "এই ডিভাইসে স্থানীয়ভাবে শেয়ার করা যাবে না",
|
||||
"ToastFailedToLoadData": "ডেটা লোড করা যায়নি",
|
||||
"ToastFailedToShare": "শেয়ার করতে ব্যর্থ",
|
||||
"ToastFailedToUpdate": "আপডেট করতে ব্যর্থ হয়েছে",
|
||||
"ToastInvalidImageUrl": "অকার্যকর ছবির ইউআরএল",
|
||||
"ToastInvalidUrl": "অকার্যকর ইউআরএল",
|
||||
"ToastItemCoverUpdateSuccess": "আইটেম কভার আপডেট করা হয়েছে",
|
||||
"ToastItemDetailsUpdateFailed": "আইটেমের বিবরণ আপডেট করতে ব্যর্থ",
|
||||
"ToastItemDeletedFailed": "আইটেম মুছে ফেলতে ব্যর্থ",
|
||||
"ToastItemDeletedSuccess": "মুছে ফেলা আইটেম",
|
||||
"ToastItemDetailsUpdateSuccess": "আইটেমের বিবরণ আপডেট করা হয়েছে",
|
||||
"ToastItemMarkedAsFinishedFailed": "সমাপ্ত হিসাবে চিহ্নিত করতে ব্যর্থ",
|
||||
"ToastItemMarkedAsFinishedSuccess": "আইটেম সমাপ্ত হিসাবে চিহ্নিত",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "সমাপ্ত হয়নি হিসাবে চিহ্নিত করতে ব্যর্থ",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "আইটেম সমাপ্ত হয়নি বলে চিহ্নিত",
|
||||
"ToastItemUpdateSuccess": "আইটেম আপডেট করা হয়েছে",
|
||||
"ToastLibraryCreateFailed": "লাইব্রেরি তৈরি করতে ব্যর্থ",
|
||||
"ToastLibraryCreateSuccess": "লাইব্রেরি \"{0}\" তৈরি করা হয়েছে",
|
||||
"ToastLibraryDeleteFailed": "লাইব্রেরি মুছে ফেলতে ব্যর্থ",
|
||||
"ToastLibraryDeleteSuccess": "লাইব্রেরি মুছে ফেলা হয়েছে",
|
||||
"ToastLibraryScanFailedToStart": "স্ক্যান শুরু করতে ব্যর্থ",
|
||||
"ToastLibraryScanStarted": "লাইব্রেরি স্ক্যান শুরু হয়েছে",
|
||||
"ToastLibraryUpdateFailed": "লাইব্রেরি আপডেট করতে ব্যর্থ",
|
||||
"ToastLibraryUpdateSuccess": "লাইব্রেরি \"{0}\" আপডেট করা হয়েছে",
|
||||
"ToastNameEmailRequired": "নাম এবং ইমেইল আবশ্যক",
|
||||
"ToastNameRequired": "নাম আবশ্যক",
|
||||
"ToastNewUserCreatedFailed": "অ্যাকাউন্ট তৈরি করতে ব্যর্থ: \"{0}\"",
|
||||
"ToastNewUserCreatedSuccess": "নতুন একাউন্ট তৈরি হয়েছে",
|
||||
"ToastNewUserLibraryError": "অন্তত একটি লাইব্রেরি নির্বাচন করতে হবে",
|
||||
"ToastNewUserPasswordError": "অন্তত একটি পাসওয়ার্ড থাকতে হবে, শুধুমাত্র রুট ব্যবহারকারীর একটি খালি পাসওয়ার্ড থাকতে পারে",
|
||||
"ToastNewUserTagError": "অন্তত একটি ট্যাগ নির্বাচন করতে হবে",
|
||||
"ToastNewUserUsernameError": "একটি ব্যবহারকারীর নাম লিখুন",
|
||||
"ToastNoUpdatesNecessary": "কোন আপডেটের প্রয়োজন নেই",
|
||||
"ToastNotificationCreateFailed": "বিজ্ঞপ্তি তৈরি করতে ব্যর্থ",
|
||||
"ToastNotificationDeleteFailed": "বিজ্ঞপ্তি মুছে ফেলতে ব্যর্থ",
|
||||
"ToastNotificationFailedMaximum": "সর্বাধিক ব্যর্থ প্রচেষ্টা >= 0 হতে হবে",
|
||||
"ToastNotificationQueueMaximum": "সর্বাধিক বিজ্ঞপ্তি সারি >= 0 হতে হবে",
|
||||
"ToastNotificationSettingsUpdateSuccess": "বিজ্ঞপ্তি সেটিংস আপডেট করা হয়েছে",
|
||||
"ToastNotificationTestTriggerFailed": "পরীক্ষামূলক বিজ্ঞপ্তি ট্রিগার করতে ব্যর্থ হয়েছে",
|
||||
"ToastNotificationTestTriggerSuccess": "পরীক্ষামুলক বিজ্ঞপ্তি ট্রিগার হয়েছে",
|
||||
"ToastNotificationUpdateSuccess": "বিজ্ঞপ্তি আপডেট হয়েছে",
|
||||
"ToastPlaylistCreateFailed": "প্লেলিস্ট তৈরি করতে ব্যর্থ",
|
||||
"ToastPlaylistCreateSuccess": "প্লেলিস্ট তৈরি করা হয়েছে",
|
||||
"ToastPlaylistRemoveSuccess": "প্লেলিস্ট সরানো হয়েছে",
|
||||
"ToastPlaylistUpdateFailed": "প্লেলিস্ট আপডেট করতে ব্যর্থ",
|
||||
"ToastPlaylistUpdateSuccess": "প্লেলিস্ট আপডেট করা হয়েছে",
|
||||
"ToastPodcastCreateFailed": "পডকাস্ট তৈরি করতে ব্যর্থ",
|
||||
"ToastPodcastCreateSuccess": "পডকাস্ট সফলভাবে তৈরি করা হয়েছে",
|
||||
"ToastPodcastGetFeedFailed": "পডকাস্ট ফিড পেতে ব্যর্থ হয়েছে",
|
||||
"ToastPodcastNoEpisodesInFeed": "আরএসএস ফিডে কোনো পর্ব পাওয়া যায়নি",
|
||||
"ToastPodcastNoRssFeed": "পডকাস্টের কোন আরএসএস ফিড নেই",
|
||||
"ToastProviderCreatedFailed": "প্রদানকারী যোগ করতে ব্যর্থ হয়েছে",
|
||||
"ToastProviderCreatedSuccess": "নতুন প্রদানকারী যোগ করা হয়েছে",
|
||||
"ToastProviderNameAndUrlRequired": "নাম এবং ইউআরএল আবশ্যক",
|
||||
"ToastProviderRemoveSuccess": "প্রদানকারী সরানো হয়েছে",
|
||||
"ToastRSSFeedCloseFailed": "RSS ফিড বন্ধ করতে ব্যর্থ",
|
||||
"ToastRSSFeedCloseSuccess": "RSS ফিড বন্ধ",
|
||||
"ToastRemoveFailed": "মুছে ফেলতে ব্যর্থ হয়েছে",
|
||||
"ToastRemoveItemFromCollectionFailed": "সংগ্রহ থেকে আইটেম সরাতে ব্যর্থ",
|
||||
"ToastRemoveItemFromCollectionSuccess": "সংগ্রহ থেকে আইটেম সরানো হয়েছে",
|
||||
"ToastRemoveItemsWithIssuesFailed": "সমস্যাযুক্ত লাইব্রেরি আইটেমগুলি সরাতে ব্যর্থ হয়েছে",
|
||||
"ToastRemoveItemsWithIssuesSuccess": "সমস্যাযুক্ত লাইব্রেরি আইটেম সরানো হয়েছে",
|
||||
"ToastRenameFailed": "পুনঃনামকরণ ব্যর্থ হয়েছে",
|
||||
"ToastRescanFailed": "{0} এর জন্য পুনরায় স্ক্যান করা ব্যর্থ হয়েছে",
|
||||
"ToastRescanRemoved": "পুনরায় স্ক্যান সম্পূর্ণ,আইটেম সরানো হয়েছে",
|
||||
"ToastRescanUpToDate": "পুনরায় স্ক্যান সম্পূর্ণ, আইটেম সাম্প্রতিক ছিল",
|
||||
"ToastRescanUpdated": "পুনরায় স্ক্যান সম্পূর্ণ, আইটেম আপডেট করা হয়েছে",
|
||||
"ToastScanFailed": "লাইব্রেরি আইটেম স্ক্যান করতে ব্যর্থ হয়েছে",
|
||||
"ToastSelectAtLeastOneUser": "অন্তত একজন ব্যবহারকারী নির্বাচন করুন",
|
||||
"ToastSendEbookToDeviceFailed": "ডিভাইসে ইবুক পাঠাতে ব্যর্থ",
|
||||
"ToastSendEbookToDeviceSuccess": "ইবুক \"{0}\" ডিভাইসে পাঠানো হয়েছে",
|
||||
"ToastSeriesUpdateFailed": "সিরিজ আপডেট ব্যর্থ হয়েছে",
|
||||
"ToastSeriesUpdateSuccess": "সিরিজ আপডেট সাফল্য",
|
||||
"ToastServerSettingsUpdateSuccess": "সার্ভার সেটিংস আপডেট করা হয়েছে",
|
||||
"ToastSessionCloseFailed": "অধিবেশন বন্ধ করতে ব্যর্থ হয়েছে",
|
||||
"ToastSessionDeleteFailed": "সেশন মুছে ফেলতে ব্যর্থ",
|
||||
"ToastSessionDeleteSuccess": "সেশন মুছে ফেলা হয়েছে",
|
||||
"ToastSlugMustChange": "স্লাগে অবৈধ অক্ষর রয়েছে",
|
||||
"ToastSlugRequired": "স্লাগ আবশ্যক",
|
||||
"ToastSocketConnected": "সকেট সংযুক্ত",
|
||||
"ToastSocketDisconnected": "সকেট সংযোগ বিচ্ছিন্ন",
|
||||
"ToastSocketFailedToConnect": "সকেট সংযোগ করতে ব্যর্থ হয়েছে",
|
||||
"ToastSortingPrefixesEmptyError": "কমপক্ষে ১ টি সাজানোর উপসর্গ থাকতে হবে",
|
||||
"ToastSortingPrefixesUpdateSuccess": "বাছাই করা উপসর্গ আপডেট করা হয়েছে ({0}টি আইটেম)",
|
||||
"ToastTitleRequired": "শিরোনাম আবশ্যক",
|
||||
"ToastUnknownError": "অজানা ত্রুটি",
|
||||
"ToastUnlinkOpenIdFailed": "OpenID থেকে ব্যবহারকারীকে আনলিঙ্ক করতে ব্যর্থ হয়েছে",
|
||||
"ToastUnlinkOpenIdSuccess": "OpenID থেকে ব্যবহারকারীকে লিঙ্কমুক্ত করা হয়েছে",
|
||||
"ToastUserDeleteFailed": "ব্যবহারকারী মুছতে ব্যর্থ",
|
||||
"ToastUserDeleteSuccess": "ব্যবহারকারী মুছে ফেলা হয়েছে"
|
||||
"ToastUserDeleteSuccess": "ব্যবহারকারী মুছে ফেলা হয়েছে",
|
||||
"ToastUserPasswordChangeSuccess": "পাসওয়ার্ড সফলভাবে পরিবর্তন করা হয়েছে",
|
||||
"ToastUserPasswordMismatch": "পাসওয়ার্ড মিলছে না",
|
||||
"ToastUserPasswordMustChange": "নতুন পাসওয়ার্ড পুরানো পাসওয়ার্ডের সাথে মিলতে পারবে না",
|
||||
"ToastUserRootRequireName": "একটি রুট ব্যবহারকারীর নাম লিখতে হবে"
|
||||
}
|
||||
|
||||
+102
-16
@@ -28,6 +28,7 @@
|
||||
"ButtonEdit": "Upravit",
|
||||
"ButtonEditChapters": "Upravit kapitoly",
|
||||
"ButtonEditPodcast": "Upravit podcast",
|
||||
"ButtonEnable": "Povolit",
|
||||
"ButtonForceReScan": "Vynutit opětovné prohledání",
|
||||
"ButtonFullPath": "Úplná cesta",
|
||||
"ButtonHide": "Skrýt",
|
||||
@@ -44,10 +45,15 @@
|
||||
"ButtonMatchAllAuthors": "Spárovat všechny autory",
|
||||
"ButtonMatchBooks": "Spárovat Knihy",
|
||||
"ButtonNevermind": "Nevadí",
|
||||
"ButtonNext": "Další",
|
||||
"ButtonNextChapter": "Další Kapitola",
|
||||
"ButtonNextItemInQueue": "Žádná další položka ve frontě",
|
||||
"ButtonOk": "Ok",
|
||||
"ButtonOpenFeed": "Otevřít kanál",
|
||||
"ButtonOpenManager": "Otevřít správce",
|
||||
"ButtonPause": "Pozastavit",
|
||||
"ButtonPlay": "Přehrát",
|
||||
"ButtonPlayAll": "Přehrát vše",
|
||||
"ButtonPlaying": "Hraje",
|
||||
"ButtonPlaylists": "Seznamy skladeb",
|
||||
"ButtonPrevious": "Předchozí",
|
||||
@@ -88,6 +94,8 @@
|
||||
"ButtonStartMetadataEmbed": "Spustit vkládání metadat",
|
||||
"ButtonStats": "Statistiky",
|
||||
"ButtonSubmit": "Odeslat",
|
||||
"ButtonTest": "Test",
|
||||
"ButtonUnlinkOpenId": "Odpojit OpenID",
|
||||
"ButtonUpload": "Nahrát",
|
||||
"ButtonUploadBackup": "Nahrát zálohu",
|
||||
"ButtonUploadCover": "Nahrát obálku",
|
||||
@@ -100,10 +108,12 @@
|
||||
"ErrorUploadFetchMetadataNoResults": "Nepodařilo se načíst metadata - zkuste aktualizovat název a/nebo autora",
|
||||
"ErrorUploadLacksTitle": "Musí mít titul",
|
||||
"HeaderAccount": "Účet",
|
||||
"HeaderAddCustomMetadataProvider": "Přidat vlastního poskytovatele metadat",
|
||||
"HeaderAdvanced": "Pokročilé",
|
||||
"HeaderAppriseNotificationSettings": "Nastavení oznámení Apprise",
|
||||
"HeaderAudioTracks": "Zvukové stopy",
|
||||
"HeaderAudiobookTools": "Nástroje pro správu souborů audioknih",
|
||||
"HeaderAuthentication": "Autentizace",
|
||||
"HeaderBackups": "Zálohy",
|
||||
"HeaderChangePassword": "Změnit heslo",
|
||||
"HeaderChapters": "Kapitoly",
|
||||
@@ -144,10 +154,13 @@
|
||||
"HeaderMetadataToEmbed": "Metadata k vložení",
|
||||
"HeaderNewAccount": "Nový účet",
|
||||
"HeaderNewLibrary": "Nová knihovna",
|
||||
"HeaderNotificationCreate": "Vytvořit notifikaci",
|
||||
"HeaderNotificationUpdate": "Aktualizovat notifikaci",
|
||||
"HeaderNotifications": "Oznámení",
|
||||
"HeaderOpenIDConnectAuthentication": "Ověřování pomocí OpenID Connect",
|
||||
"HeaderOpenRSSFeed": "Otevřít RSS kanál",
|
||||
"HeaderOtherFiles": "Ostatní soubory",
|
||||
"HeaderPasswordAuthentication": "Autentizace heslem",
|
||||
"HeaderPermissions": "Oprávnění",
|
||||
"HeaderPlayerQueue": "Fronta přehrávače",
|
||||
"HeaderPlayerSettings": "Nastavení přehrávače",
|
||||
@@ -200,6 +213,7 @@
|
||||
"LabelAddToPlaylist": "Přidat do seznamu přehrávání",
|
||||
"LabelAddToPlaylistBatch": "Přidat {0} položky do seznamu přehrávání",
|
||||
"LabelAddedAt": "Přidáno v",
|
||||
"LabelAddedDate": "Přidáno {0}",
|
||||
"LabelAdminUsersOnly": "Pouze administrátoři",
|
||||
"LabelAll": "Vše",
|
||||
"LabelAllUsers": "Všichni uživatelé",
|
||||
@@ -229,6 +243,7 @@
|
||||
"LabelBitrate": "Datový tok",
|
||||
"LabelBooks": "Knihy",
|
||||
"LabelButtonText": "Text tlačítka",
|
||||
"LabelByAuthor": "od {0}",
|
||||
"LabelChangePassword": "Změnit heslo",
|
||||
"LabelChannels": "Kanály",
|
||||
"LabelChapterTitle": "Název kapitoly",
|
||||
@@ -238,6 +253,7 @@
|
||||
"LabelClosePlayer": "Zavřít přehrávač",
|
||||
"LabelCodec": "Kodek",
|
||||
"LabelCollapseSeries": "Sbalit sérii",
|
||||
"LabelCollapseSubSeries": "Sbalit podsérie",
|
||||
"LabelCollection": "Kolekce",
|
||||
"LabelCollections": "Kolekce",
|
||||
"LabelComplete": "Dokončeno",
|
||||
@@ -288,16 +304,21 @@
|
||||
"LabelEpisode": "Epizoda",
|
||||
"LabelEpisodeTitle": "Název epizody",
|
||||
"LabelEpisodeType": "Typ epizody",
|
||||
"LabelEpisodes": "Epizody",
|
||||
"LabelExample": "Příklad",
|
||||
"LabelExpandSeries": "Rozbalit série",
|
||||
"LabelExpandSubSeries": "Rozbalit podsérie",
|
||||
"LabelExplicit": "Explicitní",
|
||||
"LabelExplicitChecked": "Explicitní (zaškrtnuto)",
|
||||
"LabelExplicitUnchecked": "Není explicitní (nezaškrtnuto)",
|
||||
"LabelExportOPML": "Export OPML",
|
||||
"LabelFeedURL": "URL zdroje",
|
||||
"LabelFetchingMetadata": "Získávání metadat",
|
||||
"LabelFile": "Soubor",
|
||||
"LabelFileBirthtime": "Čas vzniku souboru",
|
||||
"LabelFileBornDate": "Vytvořeno {0}",
|
||||
"LabelFileModified": "Soubor změněn",
|
||||
"LabelFileModifiedDate": "Změněno {0}",
|
||||
"LabelFilename": "Název souboru",
|
||||
"LabelFilterByUser": "Filtrovat podle uživatele",
|
||||
"LabelFindEpisodes": "Najít epizody",
|
||||
@@ -307,6 +328,7 @@
|
||||
"LabelFontBold": "Tučně",
|
||||
"LabelFontBoldness": "Výraznost písma",
|
||||
"LabelFontFamily": "Rodina písem",
|
||||
"LabelFontItalic": "Kurzíva",
|
||||
"LabelFontScale": "Měřítko písma",
|
||||
"LabelFontStrikethrough": "Přeškrtnutí",
|
||||
"LabelFormat": "Formát",
|
||||
@@ -325,6 +347,7 @@
|
||||
"LabelInProgress": "Probíhá",
|
||||
"LabelIncludeInTracklist": "Zahrnout do seznamu stop",
|
||||
"LabelIncomplete": "Neúplné",
|
||||
"LabelInterval": "Interval",
|
||||
"LabelIntervalCustomDailyWeekly": "Vlastní denně/týdně",
|
||||
"LabelIntervalEvery12Hours": "Každých 12 hodin",
|
||||
"LabelIntervalEvery15Minutes": "Každých 15 minut",
|
||||
@@ -421,17 +444,22 @@
|
||||
"LabelPersonalYearReview": "Váš přehled roku ({0})",
|
||||
"LabelPhotoPathURL": "Cesta k fotografii/URL",
|
||||
"LabelPlayMethod": "Metoda přehrávání",
|
||||
"LabelPlayerChapterNumberMarker": "{0} z {1}",
|
||||
"LabelPlaylists": "Seznamy skladeb",
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcastSearchRegion": "Oblast vyhledávání podcastu",
|
||||
"LabelPodcastType": "Typ podcastu",
|
||||
"LabelPodcasts": "Podcasty",
|
||||
"LabelPort": "Port",
|
||||
"LabelPrefixesToIgnore": "Předpony, které se mají ignorovat (nerozlišují se malá a velká písmena)",
|
||||
"LabelPreventIndexing": "Zabránit indexování vašeho kanálu v adresářích podcastů iTunes a Google",
|
||||
"LabelPrimaryEbook": "Hlavní e-kniha",
|
||||
"LabelProgress": "Průběh",
|
||||
"LabelProvider": "Poskytovatel",
|
||||
"LabelProviderAuthorizationValue": "Hodnota autorizačního headeru",
|
||||
"LabelPubDate": "Datum vydání",
|
||||
"LabelPublishYear": "Rok vydání",
|
||||
"LabelPublishedDate": "Vydáno {0}",
|
||||
"LabelPublisher": "Vydavatel",
|
||||
"LabelPublishers": "Vydavatelé",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Vlastní e-mail vlastníka",
|
||||
@@ -441,6 +469,7 @@
|
||||
"LabelRSSFeedSlug": "RSS kanál Slug",
|
||||
"LabelRSSFeedURL": "URL RSS kanálu",
|
||||
"LabelRandomly": "Náhodně",
|
||||
"LabelReAddSeriesToContinueListening": "Znovu přidat sérii k pokračování poslechu",
|
||||
"LabelRead": "Číst",
|
||||
"LabelReadAgain": "Číst znovu",
|
||||
"LabelReadEbookWithoutProgress": "Číst e-knihu bez zachování průběhu",
|
||||
@@ -448,6 +477,7 @@
|
||||
"LabelRecentlyAdded": "Nedávno přidané",
|
||||
"LabelRecommended": "Doporučeno",
|
||||
"LabelRedo": "Přepracovat",
|
||||
"LabelRegion": "Region",
|
||||
"LabelReleaseDate": "Datum vydání",
|
||||
"LabelRemoveCover": "Odstranit obálku",
|
||||
"LabelRowsPerPage": "Řádky na stránku",
|
||||
@@ -539,6 +569,7 @@
|
||||
"LabelTagsNotAccessibleToUser": "Značky nepřístupné uživateli",
|
||||
"LabelTasks": "Spuštěné Úlohy",
|
||||
"LabelTextEditorBulletedList": "Seznam s odrážkami",
|
||||
"LabelTextEditorLink": "Odkaz",
|
||||
"LabelTextEditorNumberedList": "Seznam s čísly",
|
||||
"LabelTextEditorUnlink": "Zrušit odkaz",
|
||||
"LabelTheme": "Téma",
|
||||
@@ -572,6 +603,7 @@
|
||||
"LabelUnabridged": "Nezkráceno",
|
||||
"LabelUndo": "Zpět",
|
||||
"LabelUnknown": "Neznámý",
|
||||
"LabelUnknownPublishDate": "Neznámé datum vydání",
|
||||
"LabelUpdateCover": "Aktualizovat obálku",
|
||||
"LabelUpdateCoverHelp": "Povolit přepsání existujících obálek pro vybrané knihy, pokud je nalezena shoda",
|
||||
"LabelUpdateDetails": "Aktualizovat podrobnosti",
|
||||
@@ -620,14 +652,19 @@
|
||||
"MessageCheckingCron": "Kontrola cronu...",
|
||||
"MessageConfirmCloseFeed": "Opravdu chcete zavřít tento kanál?",
|
||||
"MessageConfirmDeleteBackup": "Opravdu chcete smazat zálohu pro {0}?",
|
||||
"MessageConfirmDeleteDevice": "Opravdu chcete vymazat zařízení e-reader \"{0}\"?",
|
||||
"MessageConfirmDeleteFile": "Tento krok smaže soubor ze souborového systému. Jsi si jisti?",
|
||||
"MessageConfirmDeleteLibrary": "Opravdu chcete trvale smazat knihovnu \"{0}\"?",
|
||||
"MessageConfirmDeleteLibraryItem": "Tento krok odstraní položku knihovny z databáze a vašeho souborového systému. Jste si jisti?",
|
||||
"MessageConfirmDeleteLibraryItems": "Tímto smažete {0} položkek knihovny z databáze a vašeho souborového systému. Jsi si jisti?",
|
||||
"MessageConfirmDeleteMetadataProvider": "Opravdu chcete vymazat vlastního poskytovatele metadat \"{0}\"?",
|
||||
"MessageConfirmDeleteNotification": "Opravdu chcete vymazat tuto notifikaci?",
|
||||
"MessageConfirmDeleteSession": "Opravdu chcete smazat tuto relaci?",
|
||||
"MessageConfirmForceReScan": "Opravdu chcete vynutit opětovné prohledání?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Opravdu chcete označit všechny epizody jako dokončené?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Opravdu chcete označit všechny epizody jako nedokončené?",
|
||||
"MessageConfirmMarkItemFinished": "Opravdu chcete označit \"{0}\" jako dokončené?",
|
||||
"MessageConfirmMarkItemNotFinished": "Opravdu chcete označit \"{0}\" jako nedokončené?",
|
||||
"MessageConfirmMarkSeriesFinished": "Opravdu chcete označit všechny knihy z této série jako dokončené?",
|
||||
"MessageConfirmMarkSeriesNotFinished": "Opravdu chcete označit všechny knihy z této série jako nedokončené?",
|
||||
"MessageConfirmPurgeCache": "Vyčistit mezipaměť odstraní celý adresář na adrese <code>/metadata/cache</code>. <br /><br />Určitě chcete odstranit adresář mezipaměti?",
|
||||
@@ -648,7 +685,9 @@
|
||||
"MessageConfirmRenameTag": "Opravdu chcete přejmenovat tag \"{0}\" na \"{1}\" pro všechny položky?",
|
||||
"MessageConfirmRenameTagMergeNote": "Poznámka: Tato značka již existuje, takže budou sloučeny.",
|
||||
"MessageConfirmRenameTagWarning": "Varování! Podobná značka s jinými velkými a malými písmeny již existuje \"{0}\".",
|
||||
"MessageConfirmResetProgress": "Opravdu chcete zahodit svůj pokrok?",
|
||||
"MessageConfirmSendEbookToDevice": "Opravdu chcete odeslat e-knihu {0} {1}\" do zařízení \"{2}\"?",
|
||||
"MessageConfirmUnlinkOpenId": "Opravdu chcete odpojit tohoto uživatele z OpenID?",
|
||||
"MessageDownloadingEpisode": "Stahuji epizodu",
|
||||
"MessageDragFilesIntoTrackOrder": "Přetáhněte soubory do správného pořadí stop",
|
||||
"MessageEmbedFailed": "Vložení selhalo!",
|
||||
@@ -656,7 +695,7 @@
|
||||
"MessageEpisodesQueuedForDownload": "{0} Epizody zařazené do fronty ke stažení",
|
||||
"MessageEreaderDevices": "Aby bylo zajištěno doručení elektronických knih, může být nutné přidat výše uvedenou e-mailovou adresu jako platného odesílatele pro každé zařízení uvedené níže.",
|
||||
"MessageFeedURLWillBe": "URL zdroje bude {0}",
|
||||
"MessageFetching": "Stahování...",
|
||||
"MessageFetching": "Načítání...",
|
||||
"MessageForceReScanDescription": "znovu prohledá všechny soubory jako při novém skenování. ID3 tagy zvukových souborů OPF soubory a textové soubory budou skenovány jako nové.",
|
||||
"MessageImportantNotice": "Důležité upozornění!",
|
||||
"MessageInsertChapterBelow": "Vložit kapitolu níže",
|
||||
@@ -683,6 +722,7 @@
|
||||
"MessageNoCollections": "Žádné kolekce",
|
||||
"MessageNoCoversFound": "Nebyly nalezeny žádné obálky",
|
||||
"MessageNoDescription": "Bez popisu",
|
||||
"MessageNoDevices": "Žádná zařízení",
|
||||
"MessageNoDownloadsInProgress": "Momentálně neprobíhá žádné stahování",
|
||||
"MessageNoDownloadsQueued": "Žádné stahování ve frontě",
|
||||
"MessageNoEpisodeMatchesFound": "Nebyly nalezeny žádné odpovídající epizody",
|
||||
@@ -710,6 +750,7 @@
|
||||
"MessagePauseChapter": "Pozastavit přehrávání kapitoly",
|
||||
"MessagePlayChapter": "Poslechnout si začátek kapitoly",
|
||||
"MessagePlaylistCreateFromCollection": "Vytvořit seznam skladeb z kolekce",
|
||||
"MessagePleaseWait": "Čekejte prosím...",
|
||||
"MessagePodcastHasNoRSSFeedForMatching": "Podcast nemá žádnou adresu URL kanálu RSS, kterou by mohl použít pro porovnávání",
|
||||
"MessageQuickMatchDescription": "Vyplňte prázdné detaily položky a obálku prvním výsledkem shody z '{0}'. Nepřepisuje podrobnosti, pokud není povoleno nastavení serveru \"Preferovat párování metadata\".",
|
||||
"MessageRemoveChapter": "Odstranit kapitolu",
|
||||
@@ -728,17 +769,46 @@
|
||||
"MessageShareExpiresIn": "Expiruje za {0}",
|
||||
"MessageShareURLWillBe": "Sdílené URL bude <strong>{0}</strong>",
|
||||
"MessageStartPlaybackAtTime": "Spustit přehrávání pro \"{0}\" v {1}?",
|
||||
"MessageTaskAudioFileNotWritable": "Nelze zapisovat do audio souboru \"{0}\"",
|
||||
"MessageTaskCanceledByUser": "Task zrušen uživatelem",
|
||||
"MessageTaskDownloadingEpisodeDescription": "Stahování epizody \"{0}\"",
|
||||
"MessageTaskEmbeddingMetadata": "Vkládání metadat",
|
||||
"MessageTaskEmbeddingMetadataDescription": "Vkládání metadat do audioknihy \"{0}\"",
|
||||
"MessageTaskEncodingM4b": "Kódování M4B",
|
||||
"MessageTaskEncodingM4bDescription": "Kódování audioknihy \"{0}\" do jednoho m4b souboru",
|
||||
"MessageTaskFailed": "Selhalo",
|
||||
"MessageTaskFailedToBackupAudioFile": "Zálohování audio souboru \"{0}\" se selhalo",
|
||||
"MessageTaskFailedToCreateCacheDirectory": "Vytvoření cache adresáře selhalo",
|
||||
"MessageTaskFailedToEmbedMetadataInFile": "Vkládání metadat do souboru \"{0}\" selhalo",
|
||||
"MessageTaskFailedToMergeAudioFiles": "Spojení audio souborů selhalo",
|
||||
"MessageTaskFailedToMoveM4bFile": "Přesunutí m4b souboru selhalo",
|
||||
"MessageTaskFailedToWriteMetadataFile": "Zápis souboru metadat selhal",
|
||||
"MessageTaskNoFilesToScan": "Žádné soubory ke skenování",
|
||||
"MessageTaskOpmlImport": "Import OPML",
|
||||
"MessageTaskOpmlImportDescription": "Vytváření podcastů z {0} RSS feedů",
|
||||
"MessageTaskOpmlImportFeedDescription": "Importování RSS feedu \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedPodcastDescription": "Vytváření podcastu \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedPodcastExists": "Podcast se stejnou cestou již existuje",
|
||||
"MessageTaskOpmlImportFeedPodcastFailed": "Vytváření podcastu selhalo",
|
||||
"MessageTaskOpmlImportFinished": "Přidáno {0} podcastů",
|
||||
"MessageTaskScanItemsAdded": "{0} přidáno",
|
||||
"MessageTaskScanItemsMissing": "{0} chybí",
|
||||
"MessageTaskScanItemsUpdated": "{0} aktualizováno",
|
||||
"MessageTaskScanNoChangesNeeded": "Žádné změny nejsou nutné",
|
||||
"MessageTaskScanningFileChanges": "Skenování změn souborů v \"{0}\"",
|
||||
"MessageTaskScanningLibrary": "Skenování \"{0}\" knihovny",
|
||||
"MessageTaskTargetDirectoryNotWritable": "Do cílové složky nelze zapisovat",
|
||||
"MessageThinking": "Přemýšlení...",
|
||||
"MessageUploaderItemFailed": "Nahrávání se nezdařilo",
|
||||
"MessageUploaderItemSuccess": "Nahráno bylo úspěšně!",
|
||||
"MessageUploading": "Odesílám...",
|
||||
"MessageUploaderItemFailed": "Nahrávání selhalo",
|
||||
"MessageUploaderItemSuccess": "Úspěšně nahráno!",
|
||||
"MessageUploading": "Nahrávám...",
|
||||
"MessageValidCronExpression": "Platný výraz cronu",
|
||||
"MessageWatcherIsDisabledGlobally": "Hlídač je globálně zakázán v nastavení serveru",
|
||||
"MessageXLibraryIsEmpty": "{0} knihovna je prázdná!",
|
||||
"MessageYourAudiobookDurationIsLonger": "Doba trvání audioknihy je delší než nalezená délka",
|
||||
"MessageYourAudiobookDurationIsLonger": "Délka audioknihy je delší, než byla nalezena",
|
||||
"MessageYourAudiobookDurationIsShorter": "Délka audioknihy je kratší, než byla nalezena",
|
||||
"NoteChangeRootPassword": "Uživatel root je jediný uživatel, který může mít prázdné heslo",
|
||||
"NoteChapterEditorTimes": "Poznámka: Čas začátku první kapitoly musí zůstat v 0:00 a čas začátku poslední kapitoly nesmí překročit tuto dobu trvání audioknihy.",
|
||||
"NoteChapterEditorTimes": "Poznámka: Čas začátku první kapitoly musí zůstat na 0:00 a čas začátku poslední kapitoly nesmí překročit dobu trvání audioknihy.",
|
||||
"NoteFolderPicker": "Poznámka: složky, které jsou již namapovány, nebudou zobrazeny",
|
||||
"NoteRSSFeedPodcastAppsHttps": "Upozornění: Většina aplikací pro podcasty bude vyžadovat, aby adresa URL kanálu RSS používala protokol HTTPS",
|
||||
"NoteRSSFeedPodcastAppsPubDate": "Upozornění: 1 nebo více epizod nemá datum vydání. Některé podcastové aplikace to vyžadují.",
|
||||
@@ -752,8 +822,10 @@
|
||||
"PlaceholderSearchEpisode": "Hledat epizodu..",
|
||||
"StatsAuthorsAdded": "autoři přidáni",
|
||||
"StatsBooksAdded": "knihy přidány",
|
||||
"StatsBooksAdditional": "Některé další zahrnují…",
|
||||
"StatsBooksFinished": "dokončené knihy",
|
||||
"StatsBooksFinishedThisYear": "Některé knihy dokončené tento rok…",
|
||||
"StatsCollectionGrewTo": "Vaše kolekce knih se rozrostla na…",
|
||||
"StatsSessions": "sezení",
|
||||
"StatsSpentListening": "stráveno posloucháním",
|
||||
"StatsTopAuthor": "TOP AUTOR",
|
||||
@@ -763,59 +835,75 @@
|
||||
"StatsTopMonth": "TOP MĚSÍC",
|
||||
"StatsTotalDuration": "S celkovou dobou…",
|
||||
"StatsYearInReview": "ROK V PŘEHLEDU",
|
||||
"ToastAccountUpdateFailed": "Aktualizace účtu se nezdařila",
|
||||
"ToastAccountUpdateSuccess": "Účet aktualizován",
|
||||
"ToastAppriseUrlRequired": "Je nutné zadat Apprise URL",
|
||||
"ToastAuthorImageRemoveSuccess": "Obrázek autora odstraněn",
|
||||
"ToastAuthorUpdateFailed": "Aktualizace autora se nezdařila",
|
||||
"ToastAuthorNotFound": "Author \"{0}\" nenalezen",
|
||||
"ToastAuthorRemoveSuccess": "Autor odstraněn",
|
||||
"ToastAuthorSearchNotFound": "Autor nenalezen",
|
||||
"ToastAuthorUpdateMerged": "Autor sloučen",
|
||||
"ToastAuthorUpdateSuccess": "Autor aktualizován",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Autor aktualizován (nebyl nalezen žádný obrázek)",
|
||||
"ToastBackupAppliedSuccess": "Záloha obnovena",
|
||||
"ToastBackupCreateFailed": "Vytvoření zálohy se nezdařilo",
|
||||
"ToastBackupCreateSuccess": "Záloha vytvořena",
|
||||
"ToastBackupDeleteFailed": "Nepodařilo se smazat zálohu",
|
||||
"ToastBackupDeleteSuccess": "Záloha smazána",
|
||||
"ToastBackupInvalidMaxKeep": "Neplatný počet záloh k zachování",
|
||||
"ToastBackupInvalidMaxSize": "Neplatná maximální velikost zálohy",
|
||||
"ToastBackupRestoreFailed": "Nepodařilo se obnovit zálohu",
|
||||
"ToastBackupUploadFailed": "Nepodařilo se nahrát zálohu",
|
||||
"ToastBackupUploadSuccess": "Záloha nahrána",
|
||||
"ToastBatchDeleteFailed": "Hromadné smazání selhalo",
|
||||
"ToastBatchDeleteSuccess": "Hromadné smazání proběhlo úspěšně",
|
||||
"ToastBatchUpdateFailed": "Dávková aktualizace se nezdařila",
|
||||
"ToastBatchUpdateSuccess": "Dávková aktualizace proběhla úspěšně",
|
||||
"ToastBookmarkCreateFailed": "Vytvoření záložky se nezdařilo",
|
||||
"ToastBookmarkCreateSuccess": "Přidána záložka",
|
||||
"ToastBookmarkRemoveSuccess": "Záložka odstraněna",
|
||||
"ToastBookmarkUpdateFailed": "Aktualizace záložky se nezdařila",
|
||||
"ToastBookmarkUpdateSuccess": "Záložka aktualizována",
|
||||
"ToastCachePurgeFailed": "Nepodařilo se vyčistit mezipaměť",
|
||||
"ToastCachePurgeSuccess": "Vyrovnávací paměť úspěšně vyčištěna",
|
||||
"ToastChaptersHaveErrors": "Kapitoly obsahují chyby",
|
||||
"ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy",
|
||||
"ToastChaptersRemoved": "Kapitoly odstraněny",
|
||||
"ToastCollectionItemsRemoveSuccess": "Položky odstraněny z kolekce",
|
||||
"ToastCollectionRemoveSuccess": "Kolekce odstraněna",
|
||||
"ToastCollectionUpdateFailed": "Aktualizace kolekce se nezdařila",
|
||||
"ToastCollectionUpdateSuccess": "Kolekce aktualizována",
|
||||
"ToastCoverUpdateFailed": "Aktualizace obálky selhala",
|
||||
"ToastDeleteFileFailed": "Nepodařilo se smazat soubor",
|
||||
"ToastDeleteFileSuccess": "Soubor smazán",
|
||||
"ToastDeviceAddFailed": "Přidání zařízení selhalo",
|
||||
"ToastDeviceNameAlreadyExists": "Zařízení se stejným jménem již existuje",
|
||||
"ToastDeviceTestEmailFailed": "Odeslání testovacího emailu selhalo",
|
||||
"ToastDeviceTestEmailSuccess": "Testovací email byl odeslán",
|
||||
"ToastEmailSettingsUpdateSuccess": "Nastavení emailu aktualizována",
|
||||
"ToastEpisodeDownloadQueueClearFailed": "Vyčištění fronty selhalo",
|
||||
"ToastErrorCannotShare": "Na tomto zařízení nelze nativně sdílet",
|
||||
"ToastFailedToLoadData": "Nepodařilo se načíst data",
|
||||
"ToastItemCoverUpdateFailed": "Aktualizace obálky se nezdařila",
|
||||
"ToastFailedToShare": "Sdílení selhalo",
|
||||
"ToastFailedToUpdate": "Aktualizace selhala",
|
||||
"ToastInvalidImageUrl": "Neplatná URL obrázku",
|
||||
"ToastInvalidUrl": "Neplatná URL",
|
||||
"ToastItemCoverUpdateSuccess": "Obálka předmětu byl aktualizována",
|
||||
"ToastItemDetailsUpdateFailed": "Nepodařilo se aktualizovat podrobnosti o položce",
|
||||
"ToastItemDeletedFailed": "Smazání položky selhalo",
|
||||
"ToastItemDeletedSuccess": "Položka smazána",
|
||||
"ToastItemDetailsUpdateSuccess": "Podrobnosti o položce byly aktualizovány",
|
||||
"ToastItemMarkedAsFinishedFailed": "Nepodařilo se označit jako dokončené",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Položka označena jako dokončená",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "Nepodařilo se označit jako nedokončené",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Položka označena jako nedokončená",
|
||||
"ToastItemUpdateSuccess": "Položka aktualizována",
|
||||
"ToastLibraryCreateFailed": "Vytvoření knihovny se nezdařilo",
|
||||
"ToastLibraryCreateSuccess": "Knihovna \"{0}\" vytvořena",
|
||||
"ToastLibraryDeleteFailed": "Nepodařilo se smazat knihovnu",
|
||||
"ToastLibraryDeleteSuccess": "Knihovna smazána",
|
||||
"ToastLibraryScanFailedToStart": "Nepodařilo se spustit kontrolu",
|
||||
"ToastLibraryScanStarted": "Kontrola knihovny spuštěna",
|
||||
"ToastLibraryUpdateFailed": "Aktualizace knihovny se nezdařila",
|
||||
"ToastLibraryUpdateSuccess": "Knihovna \"{0}\" aktualizována",
|
||||
"ToastPlaylistCreateFailed": "Vytvoření seznamu přehrávání se nezdařilo",
|
||||
"ToastPlaylistCreateSuccess": "Seznam přehrávání vytvořen",
|
||||
"ToastPlaylistRemoveSuccess": "Seznam přehrávání odstraněn",
|
||||
"ToastPlaylistUpdateFailed": "Aktualizace seznamu přehrávání se nezdařila",
|
||||
"ToastPlaylistUpdateSuccess": "Seznam přehrávání aktualizován",
|
||||
"ToastPodcastCreateFailed": "Vytvoření podcastu se nezdařilo",
|
||||
"ToastPodcastCreateSuccess": "Podcast byl úspěšně vytvořen",
|
||||
@@ -827,7 +915,6 @@
|
||||
"ToastSendEbookToDeviceSuccess": "E-kniha odeslána do zařízení \"{0}\"",
|
||||
"ToastSeriesUpdateFailed": "Aktualizace série se nezdařila",
|
||||
"ToastSeriesUpdateSuccess": "Aktualizace série byla úspěšná",
|
||||
"ToastServerSettingsUpdateFailed": "Nepodařilo se aktualizovat nastavení serveru",
|
||||
"ToastServerSettingsUpdateSuccess": "Nastavení serveru aktualizováno",
|
||||
"ToastSessionDeleteFailed": "Nepodařilo se smazat relaci",
|
||||
"ToastSessionDeleteSuccess": "Relace smazána",
|
||||
@@ -835,7 +922,6 @@
|
||||
"ToastSocketDisconnected": "Socket odpojen",
|
||||
"ToastSocketFailedToConnect": "Socket se nepodařilo připojit",
|
||||
"ToastSortingPrefixesEmptyError": "Musí mít alespoň 1 třídicí předponu",
|
||||
"ToastSortingPrefixesUpdateFailed": "Nepodařilo se aktualizovat třídicí předpony",
|
||||
"ToastSortingPrefixesUpdateSuccess": "Aktualizovány předpony třídění ({0} položek)",
|
||||
"ToastUserDeleteFailed": "Nepodařilo se smazat uživatele",
|
||||
"ToastUserDeleteSuccess": "Uživatel smazán"
|
||||
|
||||
+20
-15
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"ButtonAdd": "Tilføj",
|
||||
"ButtonAddChapters": "Tilføj kapitler",
|
||||
"ButtonAddDevice": "Tilføj enhed",
|
||||
"ButtonAddLibrary": "Tilføj Bibliotek",
|
||||
"ButtonAddPodcasts": "Tilføj podcasts",
|
||||
"ButtonAddUser": "Tilføj bruger",
|
||||
"ButtonAddYourFirstLibrary": "Tilføj din første bibliotek",
|
||||
"ButtonApply": "Anvend",
|
||||
"ButtonApplyChapters": "Anvend kapitler",
|
||||
@@ -25,6 +28,7 @@
|
||||
"ButtonEdit": "Rediger",
|
||||
"ButtonEditChapters": "Rediger kapitler",
|
||||
"ButtonEditPodcast": "Rediger podcast",
|
||||
"ButtonEnable": "Aktiver",
|
||||
"ButtonForceReScan": "Tvungen genindlæsning",
|
||||
"ButtonFullPath": "Fuld sti",
|
||||
"ButtonHide": "Skjul",
|
||||
@@ -42,6 +46,7 @@
|
||||
"ButtonOk": "OK",
|
||||
"ButtonOpenFeed": "Åbn feed",
|
||||
"ButtonOpenManager": "Åbn manager",
|
||||
"ButtonPause": "Pause",
|
||||
"ButtonPlay": "Afspil",
|
||||
"ButtonPlaying": "Afspiller",
|
||||
"ButtonPlaylists": "Afspilningslister",
|
||||
@@ -66,7 +71,7 @@
|
||||
"ButtonScanLibrary": "Scan Bibliotek",
|
||||
"ButtonSearch": "Søg",
|
||||
"ButtonSelectFolderPath": "Vælg Mappen Sti",
|
||||
"ButtonSeries": "Serie",
|
||||
"ButtonSeries": "Serier",
|
||||
"ButtonSetChaptersFromTracks": "Sæt kapitler fra spor",
|
||||
"ButtonShiftTimes": "Skift Tider",
|
||||
"ButtonShow": "Vis",
|
||||
@@ -188,14 +193,14 @@
|
||||
"LabelChapters": "Kapitler",
|
||||
"LabelChaptersFound": "fundne kapitler",
|
||||
"LabelClosePlayer": "Luk afspiller",
|
||||
"LabelCollapseSeries": "Fold Serie Sammen",
|
||||
"LabelCollapseSeries": "Fold Serier Sammen",
|
||||
"LabelCollection": "Samling",
|
||||
"LabelCollections": "Samlinger",
|
||||
"LabelComplete": "Fuldfør",
|
||||
"LabelConfirmPassword": "Bekræft Adgangskode",
|
||||
"LabelContinueListening": "Fortsæt Lytning",
|
||||
"LabelContinueReading": "Fortsæt Læsning",
|
||||
"LabelContinueSeries": "Fortsæt Serie",
|
||||
"LabelContinueListening": "Fortsæt med at lytte",
|
||||
"LabelContinueReading": "Fortsæt med at læse",
|
||||
"LabelContinueSeries": "Fortsæt Serien",
|
||||
"LabelCover": "Omslag",
|
||||
"LabelCoverImageURL": "Omslagsbillede URL",
|
||||
"LabelCreatedAt": "Oprettet Kl.",
|
||||
@@ -212,6 +217,7 @@
|
||||
"LabelDiscFromFilename": "Disk fra Filnavn",
|
||||
"LabelDiscFromMetadata": "Disk fra Metadata",
|
||||
"LabelDiscover": "Opdag",
|
||||
"LabelDownload": "Download",
|
||||
"LabelDownloadNEpisodes": "Download {0} episoder",
|
||||
"LabelDuration": "Varighed",
|
||||
"LabelDurationFound": "Fundet varighed:",
|
||||
@@ -225,12 +231,15 @@
|
||||
"LabelEmbeddedCover": "Indlejret Omslag",
|
||||
"LabelEnable": "Aktivér",
|
||||
"LabelEnd": "Slut",
|
||||
"LabelEndOfChapter": "Slutningen af kapitel",
|
||||
"LabelEpisode": "Episode",
|
||||
"LabelEpisodeTitle": "Episodetitel",
|
||||
"LabelEpisodeType": "Episodetype",
|
||||
"LabelExample": "Eksempel",
|
||||
"LabelExplicit": "Eksplisit",
|
||||
"LabelFeedURL": "Feed URL",
|
||||
"LabelFile": "Fil",
|
||||
"LabelFileBirthtime": "Fødselstidspunkt for fil",
|
||||
"LabelFileBirthtime": "Oprettelsestidspunkt for fil",
|
||||
"LabelFileModified": "Fil ændret",
|
||||
"LabelFilename": "Filnavn",
|
||||
"LabelFilterByUser": "Filtrér efter bruger",
|
||||
@@ -238,8 +247,10 @@
|
||||
"LabelFinished": "Færdig",
|
||||
"LabelFolder": "Mappe",
|
||||
"LabelFolders": "Mapper",
|
||||
"LabelFontBoldness": "Skrift tykkelse",
|
||||
"LabelFontFamily": "Fontfamilie",
|
||||
"LabelFontScale": "Skriftstørrelse",
|
||||
"LabelGenre": "Genre",
|
||||
"LabelGenres": "Genrer",
|
||||
"LabelHardDeleteFile": "Permanent slet fil",
|
||||
"LabelHasEbook": "Har e-bog",
|
||||
@@ -267,6 +278,7 @@
|
||||
"LabelLastSeen": "Sidst set",
|
||||
"LabelLastTime": "Sidste gang",
|
||||
"LabelLastUpdate": "Seneste opdatering",
|
||||
"LabelLayout": "Layout",
|
||||
"LabelLayoutSinglePage": "Enkeltside",
|
||||
"LabelLayoutSplitPage": "Opdelt side",
|
||||
"LabelLess": "Mindre",
|
||||
@@ -344,10 +356,11 @@
|
||||
"LabelRSSFeedPreventIndexing": "Forhindrer indeksering",
|
||||
"LabelRSSFeedSlug": "RSS-feed-slug",
|
||||
"LabelRSSFeedURL": "RSS-feed-URL",
|
||||
"LabelRandomly": "Tilfældigt",
|
||||
"LabelRead": "Læst",
|
||||
"LabelReadAgain": "Læs igen",
|
||||
"LabelReadEbookWithoutProgress": "Læs e-bog uden at følge fremskridt",
|
||||
"LabelRecentSeries": "Seneste serie",
|
||||
"LabelRecentSeries": "Seneste serier",
|
||||
"LabelRecentlyAdded": "Senest tilføjet",
|
||||
"LabelRecommended": "Anbefalet",
|
||||
"LabelReleaseDate": "Udgivelsesdato",
|
||||
@@ -604,10 +617,8 @@
|
||||
"PlaceholderNewPlaylist": "Nyt afspilningslistnavn",
|
||||
"PlaceholderSearch": "Søg..",
|
||||
"PlaceholderSearchEpisode": "Søg efter episode..",
|
||||
"ToastAccountUpdateFailed": "Mislykkedes opdatering af konto",
|
||||
"ToastAccountUpdateSuccess": "Konto opdateret",
|
||||
"ToastAuthorImageRemoveSuccess": "Forfatterbillede fjernet",
|
||||
"ToastAuthorUpdateFailed": "Mislykkedes opdatering af forfatter",
|
||||
"ToastAuthorUpdateMerged": "Forfatter fusioneret",
|
||||
"ToastAuthorUpdateSuccess": "Forfatter opdateret",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Forfatter opdateret (ingen billede fundet)",
|
||||
@@ -623,17 +634,13 @@
|
||||
"ToastBookmarkCreateFailed": "Mislykkedes oprettelse af bogmærke",
|
||||
"ToastBookmarkCreateSuccess": "Bogmærke tilføjet",
|
||||
"ToastBookmarkRemoveSuccess": "Bogmærke fjernet",
|
||||
"ToastBookmarkUpdateFailed": "Mislykkedes opdatering af bogmærke",
|
||||
"ToastBookmarkUpdateSuccess": "Bogmærke opdateret",
|
||||
"ToastChaptersHaveErrors": "Kapitler har fejl",
|
||||
"ToastChaptersMustHaveTitles": "Kapitler skal have titler",
|
||||
"ToastCollectionItemsRemoveSuccess": "Element(er) fjernet fra samlingen",
|
||||
"ToastCollectionRemoveSuccess": "Samling fjernet",
|
||||
"ToastCollectionUpdateFailed": "Mislykkedes opdatering af samling",
|
||||
"ToastCollectionUpdateSuccess": "Samling opdateret",
|
||||
"ToastItemCoverUpdateFailed": "Mislykkedes opdatering af varens omslag",
|
||||
"ToastItemCoverUpdateSuccess": "Varens omslag opdateret",
|
||||
"ToastItemDetailsUpdateFailed": "Mislykkedes opdatering af varedetaljer",
|
||||
"ToastItemDetailsUpdateSuccess": "Varedetaljer opdateret",
|
||||
"ToastItemMarkedAsFinishedFailed": "Mislykkedes markering som afsluttet",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Vare markeret som afsluttet",
|
||||
@@ -645,12 +652,10 @@
|
||||
"ToastLibraryDeleteSuccess": "Bibliotek slettet",
|
||||
"ToastLibraryScanFailedToStart": "Mislykkedes start af skanning",
|
||||
"ToastLibraryScanStarted": "Biblioteksskanning startet",
|
||||
"ToastLibraryUpdateFailed": "Mislykkedes opdatering af bibliotek",
|
||||
"ToastLibraryUpdateSuccess": "Bibliotek \"{0}\" opdateret",
|
||||
"ToastPlaylistCreateFailed": "Mislykkedes oprettelse af afspilningsliste",
|
||||
"ToastPlaylistCreateSuccess": "Afspilningsliste oprettet",
|
||||
"ToastPlaylistRemoveSuccess": "Afspilningsliste fjernet",
|
||||
"ToastPlaylistUpdateFailed": "Mislykkedes opdatering af afspilningsliste",
|
||||
"ToastPlaylistUpdateSuccess": "Afspilningsliste opdateret",
|
||||
"ToastPodcastCreateFailed": "Mislykkedes oprettelse af podcast",
|
||||
"ToastPodcastCreateSuccess": "Podcast oprettet med succes",
|
||||
|
||||
+170
-49
@@ -19,7 +19,7 @@
|
||||
"ButtonChooseFiles": "Wähle eine Datei",
|
||||
"ButtonClearFilter": "Filter löschen",
|
||||
"ButtonCloseFeed": "Feed schließen",
|
||||
"ButtonCloseSession": "Offene Session schließen",
|
||||
"ButtonCloseSession": "Offene Sitzung schließen",
|
||||
"ButtonCollections": "Sammlungen",
|
||||
"ButtonConfigureScanner": "Scannereinstellungen",
|
||||
"ButtonCreate": "Erstellen",
|
||||
@@ -51,9 +51,12 @@
|
||||
"ButtonNext": "Vor",
|
||||
"ButtonNextChapter": "Nächstes Kapitel",
|
||||
"ButtonNextItemInQueue": "Das nächste Element in der Warteschlange",
|
||||
"ButtonOk": "OK",
|
||||
"ButtonOpenFeed": "Feed öffnen",
|
||||
"ButtonOpenManager": "Manager öffnen",
|
||||
"ButtonPause": "Pausieren",
|
||||
"ButtonPlay": "Abspielen",
|
||||
"ButtonPlayAll": "Alles abspielen",
|
||||
"ButtonPlaying": "Spielt",
|
||||
"ButtonPlaylists": "Wiedergabelisten",
|
||||
"ButtonPrevious": "Zurück",
|
||||
@@ -63,6 +66,7 @@
|
||||
"ButtonPurgeItemsCache": "Lösche Medien-Cache",
|
||||
"ButtonQueueAddItem": "Zur Warteschlange hinzufügen",
|
||||
"ButtonQueueRemoveItem": "Aus der Warteschlange entfernen",
|
||||
"ButtonQuickEmbed": "Schnelles Hinzufügen",
|
||||
"ButtonQuickEmbedMetadata": "Schnelles Hinzufügen von Metadaten",
|
||||
"ButtonQuickMatch": "Schnellabgleich",
|
||||
"ButtonReScan": "Neu scannen",
|
||||
@@ -95,7 +99,8 @@
|
||||
"ButtonStartMetadataEmbed": "Metadateneinbettung starten",
|
||||
"ButtonStats": "Statistiken",
|
||||
"ButtonSubmit": "Ok",
|
||||
"ButtonUnlinkOpedId": "OpenID trennen",
|
||||
"ButtonTest": "Test",
|
||||
"ButtonUnlinkOpenId": "OpenID trennen",
|
||||
"ButtonUpload": "Hochladen",
|
||||
"ButtonUploadBackup": "Sicherung hochladen",
|
||||
"ButtonUploadCover": "Titelbild hochladen",
|
||||
@@ -112,7 +117,7 @@
|
||||
"HeaderAdvanced": "Erweitert",
|
||||
"HeaderAppriseNotificationSettings": "Apprise Benachrichtigungseinstellungen",
|
||||
"HeaderAudioTracks": "Audiodateien",
|
||||
"HeaderAudiobookTools": "Hörbuch-Dateiverwaltungstools",
|
||||
"HeaderAudiobookTools": "Hörbuch-Dateiverwaltungswerkzeuge",
|
||||
"HeaderAuthentication": "Authentifizierung",
|
||||
"HeaderBackups": "Sicherungen",
|
||||
"HeaderChangePassword": "Passwort ändern",
|
||||
@@ -122,11 +127,13 @@
|
||||
"HeaderCollectionItems": "Sammlungseinträge",
|
||||
"HeaderCover": "Titelbild",
|
||||
"HeaderCurrentDownloads": "Aktuelle Downloads",
|
||||
"HeaderCustomMessageOnLogin": "Benutzerdefinierte Nachricht für den Login",
|
||||
"HeaderCustomMetadataProviders": "Benutzerdefinierte Metadata Anbieter",
|
||||
"HeaderCustomMessageOnLogin": "Benutzerdefinierte Nachricht für die Anmeldung",
|
||||
"HeaderCustomMetadataProviders": "Benutzerdefinierte Metadatenanbieter",
|
||||
"HeaderDetails": "Details",
|
||||
"HeaderDownloadQueue": "Download Warteschlange",
|
||||
"HeaderEbookFiles": "E-Buch-Dateien",
|
||||
"HeaderEmailSettings": "Email Einstellungen",
|
||||
"HeaderEmail": "E-Mail",
|
||||
"HeaderEmailSettings": "E-Mail-Einstellungen",
|
||||
"HeaderEpisodes": "Episoden",
|
||||
"HeaderEreaderDevices": "E-Reader Geräte",
|
||||
"HeaderEreaderSettings": "Einstellungen zum Lesen",
|
||||
@@ -153,12 +160,12 @@
|
||||
"HeaderNewAccount": "Neues Konto",
|
||||
"HeaderNewLibrary": "Neue Bibliothek",
|
||||
"HeaderNotificationCreate": "Benachrichtigung erstellen",
|
||||
"HeaderNotificationUpdate": "Benachrichtigung updaten",
|
||||
"HeaderNotificationUpdate": "Benachrichtigung bearbeiten",
|
||||
"HeaderNotifications": "Benachrichtigungen",
|
||||
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentifizierung",
|
||||
"HeaderOpenRSSFeed": "RSS-Feed öffnen",
|
||||
"HeaderOtherFiles": "Sonstige Dateien",
|
||||
"HeaderPasswordAuthentication": "Passwort Authentifizierung",
|
||||
"HeaderPasswordAuthentication": "Passwortauthentifizierung",
|
||||
"HeaderPermissions": "Berechtigungen",
|
||||
"HeaderPlayerQueue": "Player Warteschlange",
|
||||
"HeaderPlayerSettings": "Player Einstellungen",
|
||||
@@ -166,6 +173,7 @@
|
||||
"HeaderPlaylistItems": "Einträge in der Wiedergabeliste",
|
||||
"HeaderPodcastsToAdd": "Podcasts zum Hinzufügen",
|
||||
"HeaderPreviewCover": "Vorschau Titelbild",
|
||||
"HeaderRSSFeedGeneral": "RSS Details",
|
||||
"HeaderRSSFeedIsOpen": "RSS-Feed ist geöffnet",
|
||||
"HeaderRSSFeeds": "RSS-Feeds",
|
||||
"HeaderRemoveEpisode": "Episode entfernen",
|
||||
@@ -179,6 +187,7 @@
|
||||
"HeaderSettingsDisplay": "Anzeige",
|
||||
"HeaderSettingsExperimental": "Experimentelle Funktionen",
|
||||
"HeaderSettingsGeneral": "Allgemein",
|
||||
"HeaderSettingsScanner": "Scanner",
|
||||
"HeaderSleepTimer": "Sleep-Timer",
|
||||
"HeaderStatsLargestItems": "Größte Medien",
|
||||
"HeaderStatsLongestItems": "Längste Medien (h)",
|
||||
@@ -200,6 +209,7 @@
|
||||
"LabelAbridgedUnchecked": "Ungekürzt (nicht angehakt)",
|
||||
"LabelAccessibleBy": "Zugänglich für",
|
||||
"LabelAccountType": "Kontoart",
|
||||
"LabelAccountTypeAdmin": "Admin",
|
||||
"LabelAccountTypeGuest": "Gast",
|
||||
"LabelAccountTypeUser": "Benutzer",
|
||||
"LabelActivity": "Aktivitäten",
|
||||
@@ -216,6 +226,9 @@
|
||||
"LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste",
|
||||
"LabelAlreadyInYourLibrary": "Bereits in der Bibliothek",
|
||||
"LabelAppend": "Anhängen",
|
||||
"LabelAudioBitrate": "Audiobitrate (z. B. 128 kbit/s)",
|
||||
"LabelAudioChannels": "Audiokanäle (1 oder 2)",
|
||||
"LabelAudioCodec": "Audiocodec",
|
||||
"LabelAuthor": "Autor",
|
||||
"LabelAuthorFirstLast": "Autor (Vorname Nachname)",
|
||||
"LabelAuthorLastFirst": "Autor (Nachname, Vorname)",
|
||||
@@ -228,6 +241,7 @@
|
||||
"LabelAutoRegister": "Automatische Registrierung",
|
||||
"LabelAutoRegisterDescription": "Automatische neue Neutzer anlegen nach dem Registrieren",
|
||||
"LabelBackToUser": "Zurück zum Benutzer",
|
||||
"LabelBackupAudioFiles": "Audio-Dateien sichern",
|
||||
"LabelBackupLocation": "Backup-Ort",
|
||||
"LabelBackupsEnableAutomaticBackups": "Automatische Sicherung aktivieren",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Backups werden in /metadata/backups gespeichert",
|
||||
@@ -235,7 +249,9 @@
|
||||
"LabelBackupsMaxBackupSizeHelp": "Zum Schutz vor Fehlkonfigurationen schlagen Sicherungen fehl, wenn sie die konfigurierte Größe überschreiten.",
|
||||
"LabelBackupsNumberToKeep": "Anzahl der aufzubewahrenden Sicherungen",
|
||||
"LabelBackupsNumberToKeepHelp": "Es wird immer nur 1 Sicherung auf einmal entfernt. Wenn du bereits mehrere Sicherungen als die definierte max. Anzahl hast, solltest du diese manuell entfernen.",
|
||||
"LabelBitrate": "Bitrate",
|
||||
"LabelBooks": "Bücher",
|
||||
"LabelButtonText": "Knopftext",
|
||||
"LabelByAuthor": "von {0}",
|
||||
"LabelChangePassword": "Passwort ändern",
|
||||
"LabelChannels": "Kanäle",
|
||||
@@ -244,6 +260,7 @@
|
||||
"LabelChaptersFound": "Gefundene Kapitel",
|
||||
"LabelClickForMoreInfo": "Klicken für mehr Informationen",
|
||||
"LabelClosePlayer": "Player schließen",
|
||||
"LabelCodec": "Codec",
|
||||
"LabelCollapseSeries": "Serien einklappen",
|
||||
"LabelCollapseSubSeries": "Unterserien einklappen",
|
||||
"LabelCollection": "Sammlung",
|
||||
@@ -282,16 +299,26 @@
|
||||
"LabelEbook": "E-Buch",
|
||||
"LabelEbooks": "E-Bücher",
|
||||
"LabelEdit": "Bearbeiten",
|
||||
"LabelEmailSettingsFromAddress": "Von Adresse",
|
||||
"LabelEmail": "E-Mail",
|
||||
"LabelEmailSettingsFromAddress": "Sender",
|
||||
"LabelEmailSettingsRejectUnauthorized": "Nicht autorisierte Zertifikate ablehnen",
|
||||
"LabelEmailSettingsRejectUnauthorizedHelp": "Durch das Deaktivieren der SSL-Zertifikatsüberprüfung kann deine Verbindung Sicherheitsrisiken wie Man-in-the-Middle-Angriffen ausgesetzt sein. Deaktiviere diese Option nur, wenn due die Auswirkungen verstehst und dem Mailserver vertraust, mit dem eine Verbindung hergestellt wird.",
|
||||
"LabelEmailSettingsRejectUnauthorizedHelp": "Durch das Deaktivieren der SSL-Zertifikatsüberprüfung kann deine Verbindung Sicherheitsrisiken wie Man-in-the-Middle-Angriffen ausgesetzt sein. Deaktiviere diese Option nur, wenn due die Auswirkungen verstehst und dem E-Mail-Server vertraust, mit dem eine Verbindung hergestellt wird.",
|
||||
"LabelEmailSettingsSecure": "Sicher",
|
||||
"LabelEmailSettingsSecureHelp": "Wenn \"an\", verwendet die Verbindung TLS, wenn du eine Verbindung zum Server herstellst. Bei \"aus\" wird TLS verwendet, wenn der Server die STARTTLS-Erweiterung unterstützt. In den meisten Fällen solltest du diesen Wert auf \"an\" schalten, wenn du eine Verbindung zu Port 465 herstellst. Für Port 587 oder 25 behalte den Wert \"aus\" bei. (von nodemailer.com/smtp/#authentication)",
|
||||
"LabelEmailSettingsTestAddress": "Test Adresse",
|
||||
"LabelEmailSettingsSecureHelp": "Wenn an, verwendet die Verbindung TLS, wenn du eine Verbindung zum Server herstellst. Bei „aus“ wird TLS verwendet, wenn der Server die STARTTLS-Erweiterung unterstützt. In den meisten Fällen solltest du diesen Wert auf „an“ schalten, wenn du eine Verbindung zu Port 465 herstellst. Für Port 587 oder 25 behalte den Wert „aus“ bei. (von nodemailer.com/smtp/#authentication)",
|
||||
"LabelEmailSettingsTestAddress": "Test-Adresse",
|
||||
"LabelEmbeddedCover": "Eingebettetes Cover",
|
||||
"LabelEnable": "Aktivieren",
|
||||
"LabelEncodingBackupLocation": "Eine Sicherungskopie der originalen Audiodateien wird gespeichert in:",
|
||||
"LabelEncodingChaptersNotEmbedded": "Kapitel sind in mehrspurigen Hörbüchern nicht eingebettet.",
|
||||
"LabelEncodingClearItemCache": "Stelle sicher, dass der Cache regelmäßig geleert wird.",
|
||||
"LabelEncodingFinishedM4B": "Die fertige M4B-Datei wird im Hörbuch-Ordner unter folgendem Pfad abgelegt:",
|
||||
"LabelEncodingInfoEmbedded": "Metadaten werden in die Audiodateien innerhalb des Audiobook Ordners eingebunden.",
|
||||
"LabelEncodingStartedNavigation": "Sobald die Aufgabe gestartet ist, kann die Seite verlassen werden.",
|
||||
"LabelEncodingTimeWarning": "Kodierung kann bis zu 30 Minuten dauern.",
|
||||
"LabelEncodingWarningAdvancedSettings": "Achtung: Ändere diese Einstellungen nur, wenn du dich mit ffmpeg Kodierung auskennst.",
|
||||
"LabelEnd": "Ende",
|
||||
"LabelEndOfChapter": "Ende des Kapitels",
|
||||
"LabelEpisode": "Episode",
|
||||
"LabelEpisodeTitle": "Episodentitel",
|
||||
"LabelEpisodeType": "Episodentyp",
|
||||
"LabelEpisodes": "Episoden",
|
||||
@@ -302,6 +329,7 @@
|
||||
"LabelExplicitChecked": "Explicit (Altersbeschränkung) (angehakt)",
|
||||
"LabelExplicitUnchecked": "Not Explicit (Altersbeschränkung) (nicht angehakt)",
|
||||
"LabelExportOPML": "OPML exportieren",
|
||||
"LabelFeedURL": "Feed-URL",
|
||||
"LabelFetchingMetadata": "Abholen der Metadaten",
|
||||
"LabelFile": "Datei",
|
||||
"LabelFileBirthtime": "Datei erstellt",
|
||||
@@ -320,13 +348,15 @@
|
||||
"LabelFontItalic": "Kursiv",
|
||||
"LabelFontScale": "Schriftgröße",
|
||||
"LabelFontStrikethrough": "Durchgestrichen",
|
||||
"LabelFormat": "Format",
|
||||
"LabelGenre": "Kategorie",
|
||||
"LabelGenres": "Kategorien",
|
||||
"LabelHardDeleteFile": "Datei dauerhaft löschen",
|
||||
"LabelHasEbook": "E-Book verfügbar",
|
||||
"LabelHasSupplementaryEbook": "Ergänzendes E-Book verfügbar",
|
||||
"LabelHasEbook": "E-Buch verfügbar",
|
||||
"LabelHasSupplementaryEbook": "Ergänzendes E-Buch verfügbar",
|
||||
"LabelHideSubtitles": "Untertitel ausblenden",
|
||||
"LabelHighestPriority": "Höchste Priorität",
|
||||
"LabelHost": "Anbieter",
|
||||
"LabelHour": "Stunde",
|
||||
"LabelHours": "Stunden",
|
||||
"LabelIcon": "Symbol",
|
||||
@@ -355,12 +385,13 @@
|
||||
"LabelLastSeen": "Zuletzt gesehen",
|
||||
"LabelLastTime": "Letztes Mal",
|
||||
"LabelLastUpdate": "Letzte Aktualisierung",
|
||||
"LabelLayout": "Ansicht",
|
||||
"LabelLayoutSinglePage": "Eine Seite",
|
||||
"LabelLayoutSplitPage": "Geteilte Seite",
|
||||
"LabelLess": "Weniger",
|
||||
"LabelLibrariesAccessibleToUser": "Für Benutzer zugängliche Bibliotheken",
|
||||
"LabelLibrary": "Bibliothek",
|
||||
"LabelLibraryFilterSublistEmpty": "Nr. {0}",
|
||||
"LabelLibraryFilterSublistEmpty": "Keine {0}",
|
||||
"LabelLibraryItem": "Bibliothekseintrag",
|
||||
"LabelLibraryName": "Bibliotheksname",
|
||||
"LabelLimit": "Begrenzung",
|
||||
@@ -376,16 +407,19 @@
|
||||
"LabelMediaPlayer": "Mediaplayer",
|
||||
"LabelMediaType": "Medientyp",
|
||||
"LabelMetaTag": "Meta Schlagwort",
|
||||
"LabelMetaTags": "Meta Tags",
|
||||
"LabelMetadataOrderOfPrecedenceDescription": "Höher priorisierte Quellen für Metadaten überschreiben Metadaten aus Quellen mit niedrigerer Priorität",
|
||||
"LabelMetadataProvider": "Metadatenanbieter",
|
||||
"LabelMinute": "Minute",
|
||||
"LabelMinutes": "Minuten",
|
||||
"LabelMissing": "Fehlend",
|
||||
"LabelMissingEbook": "E-Book fehlt",
|
||||
"LabelMissingSupplementaryEbook": "Ergänzendes E-Book fehlt",
|
||||
"LabelMissingEbook": "E-Buch fehlt",
|
||||
"LabelMissingSupplementaryEbook": "Ergänzendes E-Buch fehlt",
|
||||
"LabelMobileRedirectURIs": "Erlaubte Weiterleitungs-URIs für die mobile App",
|
||||
"LabelMobileRedirectURIsDescription": "Dies ist eine Whitelist gültiger Umleitungs-URIs für mobile Apps. Der Standardwert ist <code>audiobookshelf://oauth</code>, den du entfernen oder durch zusätzliche URIs für die Integration von Drittanbieter-Apps ergänzen kannst. Die Verwendung eines Sternchens (<code>*</code>) als alleiniger Eintrag erlaubt jede URI.",
|
||||
"LabelMobileRedirectURIsDescription": "Dies ist eine weiße Liste gültiger Umleitungs-URIs für mobile Apps. Der Standardwert ist <code>audiobookshelf://oauth</code>, den du entfernen oder durch zusätzliche URIs für die Integration von Drittanbieter-Apps ergänzen kannst. Die Verwendung eines Sternchens (<code>*</code>) als alleiniger Eintrag erlaubt jede URI.",
|
||||
"LabelMore": "Mehr",
|
||||
"LabelMoreInfo": "Mehr Infos",
|
||||
"LabelName": "Name",
|
||||
"LabelNarrator": "Erzähler",
|
||||
"LabelNarrators": "Erzähler",
|
||||
"LabelNew": "Neu",
|
||||
@@ -399,6 +433,7 @@
|
||||
"LabelNotFinished": "Nicht beendet",
|
||||
"LabelNotStarted": "Nicht begonnen",
|
||||
"LabelNotes": "Notizen",
|
||||
"LabelNotificationAppriseURL": "Apprise-URL(s)",
|
||||
"LabelNotificationAvailableVariables": "Verfügbare Variablen",
|
||||
"LabelNotificationBodyTemplate": "Textvorlage",
|
||||
"LabelNotificationEvent": "Benachrichtigungs Event",
|
||||
@@ -429,33 +464,40 @@
|
||||
"LabelPlayMethod": "Abspielmethode",
|
||||
"LabelPlayerChapterNumberMarker": "{0} von {1}",
|
||||
"LabelPlaylists": "Wiedergabelisten",
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcastSearchRegion": "Podcast-Suchregion",
|
||||
"LabelPodcastType": "Podcast Typ",
|
||||
"LabelPodcasts": "Podcasts",
|
||||
"LabelPort": "Port",
|
||||
"LabelPrefixesToIgnore": "Zu ignorierende(s) Vorwort(e) (Groß- und Kleinschreibung wird nicht berücksichtigt)",
|
||||
"LabelPreventIndexing": "Verhindere, dass dein Feed von iTunes- und Google-Podcast-Verzeichnissen indiziert wird",
|
||||
"LabelPrimaryEbook": "Primäres E-Book",
|
||||
"LabelPrimaryEbook": "Primäres E-Buch",
|
||||
"LabelProgress": "Fortschritt",
|
||||
"LabelProvider": "Anbieter",
|
||||
"LabelProviderAuthorizationValue": "Autorisierungsheader-Wert",
|
||||
"LabelPubDate": "Veröffentlichungsdatum",
|
||||
"LabelPublishYear": "Jahr",
|
||||
"LabelPublishedDate": "Veröffentlicht {0}",
|
||||
"LabelPublishedDecade": "Jahrzehnt",
|
||||
"LabelPublishedDecades": "Jahrzehnte",
|
||||
"LabelPublisher": "Herausgeber",
|
||||
"LabelPublishers": "Herausgeber",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Benutzerdefinierte Eigentümer-E-Mail",
|
||||
"LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers",
|
||||
"LabelRSSFeedOpen": "RSS Feed Offen",
|
||||
"LabelRSSFeedOpen": "RSS Feed offen",
|
||||
"LabelRSSFeedPreventIndexing": "Indizierung verhindern",
|
||||
"LabelRSSFeedSlug": "RSS-Feed-Schlagwort",
|
||||
"LabelRSSFeedURL": "RSS-Feed-URL",
|
||||
"LabelRandomly": "Zufällig",
|
||||
"LabelReAddSeriesToContinueListening": "Serien erneut zur Fortsetzungsliste hinzufügen",
|
||||
"LabelRead": "Lesen",
|
||||
"LabelReadAgain": "Noch einmal Lesen",
|
||||
"LabelReadEbookWithoutProgress": "E-Book lesen und Fortschritt verwerfen",
|
||||
"LabelReadEbookWithoutProgress": "E-Buch lesen und Fortschritt verwerfen",
|
||||
"LabelRecentSeries": "Aktuelle Serien",
|
||||
"LabelRecentlyAdded": "Kürzlich hinzugefügt",
|
||||
"LabelRecommended": "Empfohlen",
|
||||
"LabelRedo": "Wiederholen",
|
||||
"LabelRegion": "Region",
|
||||
"LabelReleaseDate": "Veröffentlichungsdatum",
|
||||
"LabelRemoveCover": "Entferne Titelbild",
|
||||
"LabelRowsPerPage": "Zeilen pro Seite",
|
||||
@@ -467,16 +509,17 @@
|
||||
"LabelSelectAllEpisodes": "Alle Episoden auswählen",
|
||||
"LabelSelectEpisodesShowing": "{0} ausgewählte Episoden werden angezeigt",
|
||||
"LabelSelectUsers": "Benutzer auswählen",
|
||||
"LabelSendEbookToDevice": "E-Book senden an...",
|
||||
"LabelSendEbookToDevice": "E-Buch senden an …",
|
||||
"LabelSequence": "Reihenfolge",
|
||||
"LabelSeries": "Serien",
|
||||
"LabelSeriesName": "Serienname",
|
||||
"LabelSeriesProgress": "Serienfortschritt",
|
||||
"LabelServerLogLevel": "Server Log Level",
|
||||
"LabelServerYearReview": "Server Jahr in Übersicht ({0})",
|
||||
"LabelSetEbookAsPrimary": "Als Hauptbuch setzen",
|
||||
"LabelSetEbookAsSupplementary": "Als Ergänzung setzen",
|
||||
"LabelSettingsAudiobooksOnly": "Nur Hörbücher",
|
||||
"LabelSettingsAudiobooksOnlyHelp": "Wenn du diese Einstellung aktivierst, werden E-Book-Dateien ignoriert, es sei denn, sie befinden sich in einem Hörbuchordner. In diesem Fall werden sie als zusätzliche E-Books festgelegt",
|
||||
"LabelSettingsAudiobooksOnlyHelp": "Wenn du diese Einstellung aktivierst, werden E-Buch-Dateien ignoriert, es sei denn, sie befinden sich in einem Hörbuchordner. In diesem Fall werden sie als zusätzliche E-Bücher festgelegt",
|
||||
"LabelSettingsBookshelfViewHelp": "Skeumorphes Design mit Holzeinlegeböden",
|
||||
"LabelSettingsChromecastSupport": "Chromecastunterstützung",
|
||||
"LabelSettingsDateFormat": "Datumsformat",
|
||||
@@ -522,6 +565,7 @@
|
||||
"LabelSize": "Größe",
|
||||
"LabelSleepTimer": "Schlummerfunktion",
|
||||
"LabelSlug": "URL Teil",
|
||||
"LabelStart": "Start",
|
||||
"LabelStartTime": "Startzeit",
|
||||
"LabelStarted": "Gestartet",
|
||||
"LabelStartedAt": "Gestartet am",
|
||||
@@ -539,7 +583,7 @@
|
||||
"LabelStatsMinutesListening": "Gehörte Minuten",
|
||||
"LabelStatsOverallDays": "Gesamte Tage",
|
||||
"LabelStatsOverallHours": "Gesamte Stunden",
|
||||
"LabelStatsWeekListening": "Gehörte Wochen",
|
||||
"LabelStatsWeekListening": "Wochenhördauer",
|
||||
"LabelSubtitle": "Untertitel",
|
||||
"LabelSupportedFileTypes": "Unterstützte Dateitypen",
|
||||
"LabelTag": "Schlagwort",
|
||||
@@ -548,6 +592,7 @@
|
||||
"LabelTagsNotAccessibleToUser": "Für Benutzer nicht zugängliche Schlagwörter",
|
||||
"LabelTasks": "Laufende Aufgaben",
|
||||
"LabelTextEditorBulletedList": "Aufzählungsliste",
|
||||
"LabelTextEditorLink": "Link",
|
||||
"LabelTextEditorNumberedList": "nummerierte Liste",
|
||||
"LabelTextEditorUnlink": "entkoppeln",
|
||||
"LabelTheme": "Farbschema",
|
||||
@@ -565,9 +610,10 @@
|
||||
"LabelTitle": "Titel",
|
||||
"LabelToolsEmbedMetadata": "Metadaten einbetten",
|
||||
"LabelToolsEmbedMetadataDescription": "Bettet die Metadaten einschließlich des Titelbildes und der Kapitel in die Audiodatein ein.",
|
||||
"LabelToolsM4bEncoder": "M4B Kodierer",
|
||||
"LabelToolsMakeM4b": "M4B-Datei erstellen",
|
||||
"LabelToolsMakeM4bDescription": "Erstellt eine M4B-Datei (Endung \".m4b\") welche mehrere mp3-Dateien in einer einzigen Datei inkl. derer Metadaten (Beschreibung, Titelbild, Kapitel, ...) zusammenfasst. M4B-Datei können darüber hinaus Lesezeichen speichern und mit einem Abspielschutz (Passwort) versehen werden.",
|
||||
"LabelToolsSplitM4b": "M4B in MP3's aufteilen",
|
||||
"LabelToolsSplitM4b": "M4B in MP3s aufteilen",
|
||||
"LabelToolsSplitM4bDescription": "Erstellt aus einer mit Metadaten und nach Kapiteln aufgeteilten M4B-Datei seperate MP3's mit eingebetteten Metadaten, Coverbild und Kapiteln.",
|
||||
"LabelTotalDuration": "Gesamtdauer",
|
||||
"LabelTotalTimeListened": "Gehörte Gesamtzeit",
|
||||
@@ -590,11 +636,13 @@
|
||||
"LabelUploaderDragAndDrop": "Ziehen und Ablegen von Dateien oder Ordnern",
|
||||
"LabelUploaderDropFiles": "Dateien löschen",
|
||||
"LabelUploaderItemFetchMetadataHelp": "Automatisches Aktualisieren von Titel, Autor und Serie",
|
||||
"LabelUseAdvancedOptions": "Nutze Erweiterte Optionen",
|
||||
"LabelUseChapterTrack": "Kapiteldatei verwenden",
|
||||
"LabelUseFullTrack": "Gesamte Datei verwenden",
|
||||
"LabelUser": "Benutzer",
|
||||
"LabelUsername": "Benutzername",
|
||||
"LabelValue": "Wert",
|
||||
"LabelVersion": "Version",
|
||||
"LabelViewBookmarks": "Lesezeichen anzeigen",
|
||||
"LabelViewChapters": "Kapitel anzeigen",
|
||||
"LabelViewPlayerSettings": "Zeige player Einstellungen",
|
||||
@@ -629,7 +677,7 @@
|
||||
"MessageCheckingCron": "Überprüfe Cron...",
|
||||
"MessageConfirmCloseFeed": "Feed wird geschlossen! Bist du dir sicher?",
|
||||
"MessageConfirmDeleteBackup": "Sicherung für {0} wird gelöscht! Bist du dir sicher?",
|
||||
"MessageConfirmDeleteDevice": "Möchtest Du das E-Reader-Gerät „{0}“ wirklich löschen?",
|
||||
"MessageConfirmDeleteDevice": "Möchtest du das Lesegerät „{0}“ wirklich löschen?",
|
||||
"MessageConfirmDeleteFile": "Datei wird vom System gelöscht! Bist du dir sicher?",
|
||||
"MessageConfirmDeleteLibrary": "Bibliothek \"{0}\" wird dauerhaft gelöscht! Bist du dir sicher?",
|
||||
"MessageConfirmDeleteLibraryItem": "Bibliothekselement wird aus der Datenbank + Festplatte gelöscht? Bist du dir sicher?",
|
||||
@@ -637,6 +685,7 @@
|
||||
"MessageConfirmDeleteMetadataProvider": "Möchtest du den benutzerdefinierten Metadatenanbieter \"{0}\" wirklich löschen?",
|
||||
"MessageConfirmDeleteNotification": "Möchtest du diese Benachrichtigung wirklich löschen?",
|
||||
"MessageConfirmDeleteSession": "Sitzung wird gelöscht! Bist du dir sicher?",
|
||||
"MessageConfirmEmbedMetadataInAudioFiles": "Bist du dir sicher, dass die Metadaten in {0} Audiodateien eingebettet werden sollen?",
|
||||
"MessageConfirmForceReScan": "Scanvorgang erzwingen! Bist du dir sicher?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Alle Episoden werden als abgeschlossen markiert! Bist du dir sicher?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Alle Episoden werden als nicht abgeschlossen markiert! Bist du dir sicher?",
|
||||
@@ -664,14 +713,14 @@
|
||||
"MessageConfirmRenameTagMergeNote": "Hinweis: Tag existiert bereits -> Tags werden zusammengelegt.",
|
||||
"MessageConfirmRenameTagWarning": "Warnung! Ein ähnlicher Tag mit einem anderen Wortlaut existiert bereits: \"{0}\".",
|
||||
"MessageConfirmResetProgress": "Möchtest du Ihren Fortschritt wirklich zurücksetzen?",
|
||||
"MessageConfirmSendEbookToDevice": "{0} E-Book \"{1}\" wird auf das Gerät \"{2}\" gesendet! Bist du dir sicher?",
|
||||
"MessageConfirmSendEbookToDevice": "{0} E-Buch „{1}“ wird auf das Gerät „{2}“ gesendet! Bist du dir sicher?",
|
||||
"MessageConfirmUnlinkOpenId": "Möchtest du die Verknüpfung dieses Benutzers mit OpenID wirklich löschen?",
|
||||
"MessageDownloadingEpisode": "Episode wird heruntergeladen",
|
||||
"MessageDragFilesIntoTrackOrder": "Verschiebe die Dateien in die richtige Reihenfolge",
|
||||
"MessageEmbedFailed": "Einbetten fehlgeschlagen!",
|
||||
"MessageEmbedFinished": "Einbettung abgeschlossen!",
|
||||
"MessageEpisodesQueuedForDownload": "{0} Episode(n) in der Warteschlange zum Herunterladen",
|
||||
"MessageEreaderDevices": "Um die Zustellung von E-Books sicherzustellen, musst du eventuell die oben genannte E-Mail-Adresse als gültigen Absender für jedes unten aufgeführte Gerät hinzufügen.",
|
||||
"MessageEreaderDevices": "Um die Zustellung von E-Büchern sicherzustellen, musst du eventuell die oben genannte E-Mail-Adresse als gültigen Absender für jedes unten aufgeführte Gerät hinzufügen.",
|
||||
"MessageFeedURLWillBe": "Feed-URL wird {0} sein",
|
||||
"MessageFetching": "Wird abgerufen …",
|
||||
"MessageForceReScanDescription": "Durchsucht alle Dateien erneut, wie bei einem frischen Scan. ID3-Tags von Audiodateien, OPF-Dateien und Textdateien werden neu durchsucht.",
|
||||
@@ -714,6 +763,7 @@
|
||||
"MessageNoLogs": "Keine Protokolle",
|
||||
"MessageNoMediaProgress": "Kein Medienfortschritt",
|
||||
"MessageNoNotifications": "Keine Benachrichtigungen",
|
||||
"MessageNoPodcastFeed": "Ungültiger Podcast: Kein Feed",
|
||||
"MessageNoPodcastsFound": "Keine Podcasts gefunden",
|
||||
"MessageNoResults": "Keine Ergebnisse",
|
||||
"MessageNoSearchResultsFor": "Keine Suchergebnisse für \"{0}\"",
|
||||
@@ -747,6 +797,38 @@
|
||||
"MessageShareExpiresIn": "Läuft in {0} ab",
|
||||
"MessageShareURLWillBe": "Der Freigabe Link wird <strong>{0}</strong> sein.",
|
||||
"MessageStartPlaybackAtTime": "Start der Wiedergabe für \"{0}\" bei {1}?",
|
||||
"MessageTaskAudioFileNotWritable": "Die Audiodatei \"{0}\" ist schreibgeschützt",
|
||||
"MessageTaskCanceledByUser": "Aufgabe vom Benutzer abgebrochen",
|
||||
"MessageTaskDownloadingEpisodeDescription": "Folge \"{0}\" wird heruntergeladen",
|
||||
"MessageTaskEmbeddingMetadata": "Metadaten werden eingebettet",
|
||||
"MessageTaskEmbeddingMetadataDescription": "Metadaten werden in Hörbuch \"{0}\" eingebettet",
|
||||
"MessageTaskEncodingM4b": "M4B wird encodiert",
|
||||
"MessageTaskEncodingM4bDescription": "Hörbuch \"{0}\" wird in eine einzelne m4b Datei encodiert",
|
||||
"MessageTaskFailed": "Fehlgeschlagen",
|
||||
"MessageTaskFailedToBackupAudioFile": "Sicherung der Audiodatei \"{0}\" fehlgeschlagen",
|
||||
"MessageTaskFailedToCreateCacheDirectory": "Fehler beim erstellen des Cache-Verzeichnisses",
|
||||
"MessageTaskFailedToEmbedMetadataInFile": "Einbetten der Metadaten in die Datei \"{0}\" fehlgeschlagen",
|
||||
"MessageTaskFailedToMergeAudioFiles": "Fehler beim zusammenführen der Audiodateien",
|
||||
"MessageTaskFailedToMoveM4bFile": "Fehler beim verschieben der m4b Datei",
|
||||
"MessageTaskFailedToWriteMetadataFile": "Fehler beim schreiben der Metadaten-Datei",
|
||||
"MessageTaskMatchingBooksInLibrary": "Vergleiche Bücher in Bibliothek \"{0}\"",
|
||||
"MessageTaskNoFilesToScan": "Keine Dateien zum scannen",
|
||||
"MessageTaskOpmlImport": "OPML-Import",
|
||||
"MessageTaskOpmlImportDescription": "Podcasts von {0} RSS-Feeds werden ersrtellt",
|
||||
"MessageTaskOpmlImportFeed": "OPML-Feed importieren",
|
||||
"MessageTaskOpmlImportFeedDescription": "RSS-Feed \"{0}\" wird importiert",
|
||||
"MessageTaskOpmlImportFeedFailed": "Podcast Feed konnte nicht geladen werden",
|
||||
"MessageTaskOpmlImportFeedPodcastDescription": "Podcast \"{0}\" wird erstellt",
|
||||
"MessageTaskOpmlImportFeedPodcastExists": "Der Podcast ist bereits im Pfad vorhanden",
|
||||
"MessageTaskOpmlImportFeedPodcastFailed": "Erstellen des Podcasts fehlgeschlagen",
|
||||
"MessageTaskOpmlImportFinished": "{0} Podcasts hinzugefügt",
|
||||
"MessageTaskScanItemsAdded": "{0} hinzugefügt",
|
||||
"MessageTaskScanItemsMissing": "{0} fehlend",
|
||||
"MessageTaskScanItemsUpdated": "{0} aktualisiert",
|
||||
"MessageTaskScanNoChangesNeeded": "Keine Änderungen nötig",
|
||||
"MessageTaskScanningFileChanges": "Überprüfe \"{0}\" nach geänderten Dateien",
|
||||
"MessageTaskScanningLibrary": "Bibliothek \"{0}\" wird durchsucht",
|
||||
"MessageTaskTargetDirectoryNotWritable": "Das Zielverzeichnis ist schreibgeschützt",
|
||||
"MessageThinking": "Nachdenken...",
|
||||
"MessageUploaderItemFailed": "Hochladen fehlgeschlagen",
|
||||
"MessageUploaderItemSuccess": "Erfolgreich hochgeladen!",
|
||||
@@ -780,19 +862,19 @@
|
||||
"StatsSpentListening": "zugehört",
|
||||
"StatsTopAuthor": "TOP AUTOR",
|
||||
"StatsTopAuthors": "TOP AUTOREN",
|
||||
"StatsTopGenre": "TOP GENRE",
|
||||
"StatsTopGenres": "TOP GENRES",
|
||||
"StatsTopMonth": "TOP MONAT",
|
||||
"StatsTopNarrator": "TOP SPRECHER",
|
||||
"StatsTopNarrators": "TOP SPRECHER",
|
||||
"StatsTotalDuration": "Mit einer totalen Dauer von…",
|
||||
"StatsYearInReview": "DAS JAHR IM RÜCKBLICK",
|
||||
"ToastAccountUpdateFailed": "Aktualisierung des Kontos fehlgeschlagen",
|
||||
"ToastAccountUpdateSuccess": "Konto aktualisiert",
|
||||
"ToastAppriseUrlRequired": "Eine Apprise-URL ist notwendig",
|
||||
"ToastAuthorImageRemoveSuccess": "Autorenbild entfernt",
|
||||
"ToastAuthorNotFound": "Autor \"{0}\" nicht gefunden",
|
||||
"ToastAuthorRemoveSuccess": "Autor entfernt",
|
||||
"ToastAuthorSearchNotFound": "Autor nicht gefunden",
|
||||
"ToastAuthorUpdateFailed": "Aktualisierung des Autors fehlgeschlagen",
|
||||
"ToastAuthorUpdateMerged": "Autor zusammengeführt",
|
||||
"ToastAuthorUpdateSuccess": "Autor aktualisiert",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Autor aktualisiert (kein Bild gefunden)",
|
||||
@@ -803,7 +885,6 @@
|
||||
"ToastBackupDeleteSuccess": "Sicherung gelöscht",
|
||||
"ToastBackupInvalidMaxKeep": "Ungültige Anzahl aufzubewahrender Backups",
|
||||
"ToastBackupInvalidMaxSize": "Ungültige maximale Backupgröße",
|
||||
"ToastBackupPathUpdateFailed": "Der Backuppfad konnte nicht aktualisiert werden",
|
||||
"ToastBackupRestoreFailed": "Sicherung konnte nicht wiederhergestellt werden",
|
||||
"ToastBackupUploadFailed": "Sicherung konnte nicht hochgeladen werden",
|
||||
"ToastBackupUploadSuccess": "Sicherung hochgeladen",
|
||||
@@ -814,7 +895,6 @@
|
||||
"ToastBookmarkCreateFailed": "Lesezeichen konnte nicht erstellt werden",
|
||||
"ToastBookmarkCreateSuccess": "Lesezeichen hinzugefügt",
|
||||
"ToastBookmarkRemoveSuccess": "Lesezeichen entfernt",
|
||||
"ToastBookmarkUpdateFailed": "Lesezeichenaktualisierung fehlgeschlagen",
|
||||
"ToastBookmarkUpdateSuccess": "Lesezeichen aktualisiert",
|
||||
"ToastCachePurgeFailed": "Cache leeren fehlgeschlagen",
|
||||
"ToastCachePurgeSuccess": "Cache geleert",
|
||||
@@ -825,7 +905,6 @@
|
||||
"ToastCollectionItemsAddSuccess": "Element(e) erfolgreich zur Sammlung hinzugefügt",
|
||||
"ToastCollectionItemsRemoveSuccess": "Medien aus der Sammlung entfernt",
|
||||
"ToastCollectionRemoveSuccess": "Sammlung entfernt",
|
||||
"ToastCollectionUpdateFailed": "Sammlung konnte nicht aktualisiert werden",
|
||||
"ToastCollectionUpdateSuccess": "Sammlung aktualisiert",
|
||||
"ToastCoverUpdateFailed": "Cover-Update fehlgeschlagen",
|
||||
"ToastDeleteFileFailed": "Die Datei konnte nicht gelöscht werden",
|
||||
@@ -833,9 +912,7 @@
|
||||
"ToastDeviceAddFailed": "Gerät konnte nicht hinzugefügt werden",
|
||||
"ToastDeviceNameAlreadyExists": "E-Reader-Gerät mit diesem Namen existiert bereits",
|
||||
"ToastDeviceTestEmailFailed": "Senden der Test-E-Mail fehlgeschlagen",
|
||||
"ToastDeviceTestEmailSuccess": "Test-E-Mail versand",
|
||||
"ToastDeviceUpdateFailed": "Das Gerät konnte nicht aktualisiert werden",
|
||||
"ToastEmailSettingsUpdateFailed": "E-Mail-Einstellungen konnten nicht aktualisiert werden",
|
||||
"ToastDeviceTestEmailSuccess": "Test-E-Mail gesendet",
|
||||
"ToastEmailSettingsUpdateSuccess": "E-Mail-Einstellungen aktualisiert",
|
||||
"ToastEncodeCancelFailed": "Das Encoding konnte nicht abgebrochen werden",
|
||||
"ToastEncodeCancelSucces": "Encoding abgebrochen",
|
||||
@@ -843,50 +920,94 @@
|
||||
"ToastEpisodeDownloadQueueClearSuccess": "Warteschlange für Episoden-Downloads gelöscht",
|
||||
"ToastErrorCannotShare": "Das kann nicht nativ auf diesem Gerät freigegeben werden",
|
||||
"ToastFailedToLoadData": "Daten laden fehlgeschlagen",
|
||||
"ToastItemCoverUpdateFailed": "Fehler bei der Aktualisierung des Titelbildes",
|
||||
"ToastFailedToShare": "Fehler beim Teilen",
|
||||
"ToastFailedToUpdate": "Aktualisierung ist fehlgeschlagen",
|
||||
"ToastInvalidImageUrl": "Ungültiger Bild URL",
|
||||
"ToastInvalidUrl": "Ungültiger URL",
|
||||
"ToastItemCoverUpdateSuccess": "Titelbild aktualisiert",
|
||||
"ToastItemDetailsUpdateFailed": "Fehler bei der Aktualisierung der Artikeldetails",
|
||||
"ToastItemDeletedFailed": "Fehler beim löschen des Artikels",
|
||||
"ToastItemDeletedSuccess": "Artikel gelöscht",
|
||||
"ToastItemDetailsUpdateSuccess": "Artikeldetails aktualisiert",
|
||||
"ToastItemMarkedAsFinishedFailed": "Fehler bei der Markierung des Mediums als \"Beendet\"",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Medium als \"Beendet\" markiert",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "Fehler bei der Markierung des Mediums als \"Nicht Beendet\"",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Medium als \"Nicht Beendet\" markiert",
|
||||
"ToastItemMarkedAsFinishedFailed": "Fehler bei der Markierung des Artikels als \"Beendet\"",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Artikel als \"Beendet\" markiert",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "Fehler bei der Markierung des Artikels als \"Nicht Beendet\"",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Artikel als \"Nicht Beendet\" markiert",
|
||||
"ToastItemUpdateSuccess": "Artikel wurde verändert",
|
||||
"ToastLibraryCreateFailed": "Bibliothek konnte nicht erstellt werden",
|
||||
"ToastLibraryCreateSuccess": "Bibliothek \"{0}\" erstellt",
|
||||
"ToastLibraryDeleteFailed": "Bibliothek konnte nicht gelöscht werden",
|
||||
"ToastLibraryDeleteSuccess": "Bibliothek gelöscht",
|
||||
"ToastLibraryScanFailedToStart": "Scan konnte nicht gestartet werden",
|
||||
"ToastLibraryScanStarted": "Bibliotheksscan gestartet",
|
||||
"ToastLibraryUpdateFailed": "Aktualisierung der Bibliothek fehlgeschlagen",
|
||||
"ToastLibraryUpdateSuccess": "Bibliothek \"{0}\" aktualisiert",
|
||||
"ToastMatchAllAuthorsFailed": "Nicht alle Autoren konnten zugeordnet werden",
|
||||
"ToastNameEmailRequired": "Name und E-Mail sind erforderlich",
|
||||
"ToastNameRequired": "Name ist erforderlich",
|
||||
"ToastNewUserCreatedFailed": "Fehler beim erstellen des Accounts: \"{ 0}\"",
|
||||
"ToastNewUserCreatedSuccess": "Neuer Account erstellt",
|
||||
"ToastNewUserLibraryError": "Mindestens eine Bibliothek muss ausgewählt werden",
|
||||
"ToastNewUserPasswordError": "Passwort erforderlich, nur der root Benutzer darf ein leeres Passwort haben",
|
||||
"ToastNewUserTagError": "Mindestens ein Tag muss ausgewählt sein",
|
||||
"ToastNewUserUsernameError": "Nutzername eingeben",
|
||||
"ToastNoUpdatesNecessary": "Keine Änderungen nötig",
|
||||
"ToastNotificationCreateFailed": "Fehler beim erstellen der Benachrichtig",
|
||||
"ToastNotificationDeleteFailed": "Fehler beim löschen der Benachrichtigung",
|
||||
"ToastNotificationFailedMaximum": "Maximale Fehlversuche muss >= 0 sein",
|
||||
"ToastNotificationQueueMaximum": "Maximale Benachrichtigungswarteschlange muss >= 0 sein",
|
||||
"ToastNotificationSettingsUpdateSuccess": "Benachrichtigungseinstellungen geändert",
|
||||
"ToastNotificationTestTriggerFailed": "Fehler beim Auslösen der Testbenachrichtigung",
|
||||
"ToastNotificationTestTriggerSuccess": "Testbenachrichtigung ausgelöst",
|
||||
"ToastNotificationUpdateSuccess": "Benachrichtigung geändert",
|
||||
"ToastPlaylistCreateFailed": "Erstellen der Wiedergabeliste fehlgeschlagen",
|
||||
"ToastPlaylistCreateSuccess": "Wiedergabeliste erstellt",
|
||||
"ToastPlaylistRemoveSuccess": "Wiedergabeliste gelöscht",
|
||||
"ToastPlaylistUpdateFailed": "Aktualisieren der Wiedergabeliste fehlgeschlagen",
|
||||
"ToastPlaylistUpdateSuccess": "Wiedergabeliste aktualisiert",
|
||||
"ToastPodcastCreateFailed": "Podcast konnte nicht erstellt werden",
|
||||
"ToastPodcastCreateSuccess": "Podcast erstellt",
|
||||
"ToastPodcastGetFeedFailed": "Fehler beim abrufen des Podcast Feeds",
|
||||
"ToastPodcastNoEpisodesInFeed": "Keine Episoden in RSS Feed gefunden",
|
||||
"ToastPodcastNoRssFeed": "Podcast enthält keinen RSS Feed",
|
||||
"ToastProviderCreatedFailed": "Fehler beim hinzufügen des Anbieters",
|
||||
"ToastProviderCreatedSuccess": "Neuer Anbieter hinzugefügt",
|
||||
"ToastProviderNameAndUrlRequired": "Name und URL notwendig",
|
||||
"ToastProviderRemoveSuccess": "Anbieter entfernt",
|
||||
"ToastRSSFeedCloseFailed": "RSS-Feed konnte nicht geschlossen werden",
|
||||
"ToastRSSFeedCloseSuccess": "RSS-Feed geschlossen",
|
||||
"ToastRemoveFailed": "Fehler beim entfernen",
|
||||
"ToastRemoveItemFromCollectionFailed": "Löschen des Mediums aus der Sammlung fehlgeschlagen",
|
||||
"ToastRemoveItemFromCollectionSuccess": "Medium aus der Sammlung gelöscht",
|
||||
"ToastSendEbookToDeviceFailed": "E-Book konnte nicht auf Gerät übertragen werden",
|
||||
"ToastSendEbookToDeviceSuccess": "E-Book an Gerät \"{0}\" gesendet",
|
||||
"ToastRemoveItemsWithIssuesFailed": "Entfernen von fehlerhaften Bibliotheksartikeln fehlgeschlagenen",
|
||||
"ToastRemoveItemsWithIssuesSuccess": "Fehlerhafte Bibliotheksartikel entfernt",
|
||||
"ToastRenameFailed": "Umbenennen fehlgeschlagen",
|
||||
"ToastRescanFailed": "Erneut scannen fehlgeschlagen für {0}",
|
||||
"ToastRescanRemoved": "Erneut scannen erledigt, Artikel wurde entfernt",
|
||||
"ToastRescanUpToDate": "Erneut scannen erledigt, Artikel wahr auf dem neusten Stand",
|
||||
"ToastRescanUpdated": "Erneut scannen erledigt, Artikel wurde verändert",
|
||||
"ToastScanFailed": "Fehler beim scannen des Artikels der Bibliothek",
|
||||
"ToastSelectAtLeastOneUser": "Wähle mindestens einen Benutzer aus",
|
||||
"ToastSendEbookToDeviceFailed": "E-Buch konnte nicht auf Gerät übertragen werden",
|
||||
"ToastSendEbookToDeviceSuccess": "E-Buch an Gerät „{0}“ gesendet",
|
||||
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
|
||||
"ToastSeriesUpdateSuccess": "Serien aktualisiert",
|
||||
"ToastServerSettingsUpdateFailed": "Die Server-Einstellungen wurden nicht gespeichert",
|
||||
"ToastServerSettingsUpdateSuccess": "Die Server-Einstellungen wurden geupdated",
|
||||
"ToastSessionCloseFailed": "Fehler beim schließen der Sitzung",
|
||||
"ToastSessionDeleteFailed": "Sitzung konnte nicht gelöscht werden",
|
||||
"ToastSessionDeleteSuccess": "Sitzung gelöscht",
|
||||
"ToastSlugMustChange": "URL-Schlüssel enthält ungültige Zeichen",
|
||||
"ToastSlugRequired": "URL-Schlüssel erforderlich",
|
||||
"ToastSocketConnected": "Verbindung zum WebSocket hergestellt",
|
||||
"ToastSocketDisconnected": "Verbindung zum WebSocket verloren",
|
||||
"ToastSocketFailedToConnect": "Verbindung zum WebSocket fehlgeschlagen",
|
||||
"ToastSortingPrefixesEmptyError": "Es muss mindestens ein Sortier-Prefix vorhanden sein",
|
||||
"ToastSortingPrefixesUpdateFailed": "Update der Sortier-Prefixe ist fehlgeschlagen",
|
||||
"ToastSortingPrefixesUpdateSuccess": "Die Sortier-Prefixe wirden geupdated ({0} Einträge)",
|
||||
"ToastTitleRequired": "Titel erforderlich",
|
||||
"ToastUnknownError": "Unbekannter Fehler",
|
||||
"ToastUnlinkOpenIdFailed": "Fehler beim entkoppeln des Benutzers von OpenID",
|
||||
"ToastUnlinkOpenIdSuccess": "Benutzer entkoppelt von OpenID",
|
||||
"ToastUserDeleteFailed": "Benutzer konnte nicht gelöscht werden",
|
||||
"ToastUserDeleteSuccess": "Benutzer gelöscht"
|
||||
"ToastUserDeleteSuccess": "Benutzer gelöscht",
|
||||
"ToastUserPasswordChangeSuccess": "Passwort erfolgreich verändert",
|
||||
"ToastUserPasswordMismatch": "Passwörter stimmen nicht überein",
|
||||
"ToastUserPasswordMustChange": "Neues Passwort muss sich von altem Passwort unterscheiden",
|
||||
"ToastUserRootRequireName": "Root Benutzername muss angegeben werden"
|
||||
}
|
||||
|
||||
+109
-19
@@ -56,6 +56,7 @@
|
||||
"ButtonOpenManager": "Open Manager",
|
||||
"ButtonPause": "Pause",
|
||||
"ButtonPlay": "Play",
|
||||
"ButtonPlayAll": "Play All",
|
||||
"ButtonPlaying": "Playing",
|
||||
"ButtonPlaylists": "Playlists",
|
||||
"ButtonPrevious": "Previous",
|
||||
@@ -65,6 +66,7 @@
|
||||
"ButtonPurgeItemsCache": "Purge Items Cache",
|
||||
"ButtonQueueAddItem": "Add to queue",
|
||||
"ButtonQueueRemoveItem": "Remove from queue",
|
||||
"ButtonQuickEmbed": "Quick Embed",
|
||||
"ButtonQuickEmbedMetadata": "Quick Embed Metadata",
|
||||
"ButtonQuickMatch": "Quick Match",
|
||||
"ButtonReScan": "Re-Scan",
|
||||
@@ -98,7 +100,7 @@
|
||||
"ButtonStats": "Stats",
|
||||
"ButtonSubmit": "Submit",
|
||||
"ButtonTest": "Test",
|
||||
"ButtonUnlinkOpedId": "Unlink OpenID",
|
||||
"ButtonUnlinkOpenId": "Unlink OpenID",
|
||||
"ButtonUpload": "Upload",
|
||||
"ButtonUploadBackup": "Upload Backup",
|
||||
"ButtonUploadCover": "Upload Cover",
|
||||
@@ -178,6 +180,7 @@
|
||||
"HeaderRemoveEpisodes": "Remove {0} Episodes",
|
||||
"HeaderSavedMediaProgress": "Saved Media Progress",
|
||||
"HeaderSchedule": "Schedule",
|
||||
"HeaderScheduleEpisodeDownloads": "Schedule Automatic Episode Downloads",
|
||||
"HeaderScheduleLibraryScans": "Schedule Automatic Library Scans",
|
||||
"HeaderSession": "Session",
|
||||
"HeaderSetBackupSchedule": "Set Backup Schedule",
|
||||
@@ -224,6 +227,9 @@
|
||||
"LabelAllUsersIncludingGuests": "All users including guests",
|
||||
"LabelAlreadyInYourLibrary": "Already in your library",
|
||||
"LabelAppend": "Append",
|
||||
"LabelAudioBitrate": "Audio Bitrate (e.g. 128k)",
|
||||
"LabelAudioChannels": "Audio Channels (1 or 2)",
|
||||
"LabelAudioCodec": "Audio Codec",
|
||||
"LabelAuthor": "Author",
|
||||
"LabelAuthorFirstLast": "Author (First Last)",
|
||||
"LabelAuthorLastFirst": "Author (Last, First)",
|
||||
@@ -236,6 +242,7 @@
|
||||
"LabelAutoRegister": "Auto Register",
|
||||
"LabelAutoRegisterDescription": "Automatically create new users after logging in",
|
||||
"LabelBackToUser": "Back to User",
|
||||
"LabelBackupAudioFiles": "Backup Audio Files",
|
||||
"LabelBackupLocation": "Backup Location",
|
||||
"LabelBackupsEnableAutomaticBackups": "Enable automatic backups",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Backups saved in /metadata/backups",
|
||||
@@ -244,15 +251,18 @@
|
||||
"LabelBackupsNumberToKeep": "Number of backups to keep",
|
||||
"LabelBackupsNumberToKeepHelp": "Only 1 backup will be removed at a time so if you already have more backups than this you should manually remove them.",
|
||||
"LabelBitrate": "Bitrate",
|
||||
"LabelBonus": "Bonus",
|
||||
"LabelBooks": "Books",
|
||||
"LabelButtonText": "Button Text",
|
||||
"LabelByAuthor": "by {0}",
|
||||
"LabelChangePassword": "Change Password",
|
||||
"LabelChannels": "Channels",
|
||||
"LabelChapterCount": "{0} Chapters",
|
||||
"LabelChapterTitle": "Chapter Title",
|
||||
"LabelChapters": "Chapters",
|
||||
"LabelChaptersFound": "chapters found",
|
||||
"LabelClickForMoreInfo": "Click for more info",
|
||||
"LabelClickToUseCurrentValue": "Click to use current value",
|
||||
"LabelClosePlayer": "Close player",
|
||||
"LabelCodec": "Codec",
|
||||
"LabelCollapseSeries": "Collapse Series",
|
||||
@@ -302,12 +312,25 @@
|
||||
"LabelEmailSettingsTestAddress": "Test Address",
|
||||
"LabelEmbeddedCover": "Embedded Cover",
|
||||
"LabelEnable": "Enable",
|
||||
"LabelEncodingBackupLocation": "A backup of your original audio files will be stored in:",
|
||||
"LabelEncodingChaptersNotEmbedded": "Chapters are not embedded in multi-track audiobooks.",
|
||||
"LabelEncodingClearItemCache": "Make sure to periodically purge items cache.",
|
||||
"LabelEncodingFinishedM4B": "Finished M4B will be put into your audiobook folder at:",
|
||||
"LabelEncodingInfoEmbedded": "Metadata will be embedded in the audio tracks inside your audiobook folder.",
|
||||
"LabelEncodingStartedNavigation": "Once the task is started you can navigate away from this page.",
|
||||
"LabelEncodingTimeWarning": "Encoding can take up to 30 minutes.",
|
||||
"LabelEncodingWarningAdvancedSettings": "Warning: Do not update these settings unless you are familiar with ffmpeg encoding options.",
|
||||
"LabelEncodingWatcherDisabled": "If you have the watcher disabled you will need to re-scan this audiobook afterwards.",
|
||||
"LabelEnd": "End",
|
||||
"LabelEndOfChapter": "End of Chapter",
|
||||
"LabelEpisode": "Episode",
|
||||
"LabelEpisodeNotLinkedToRssFeed": "Episode not linked to RSS feed",
|
||||
"LabelEpisodeNumber": "Episode #{0}",
|
||||
"LabelEpisodeTitle": "Episode Title",
|
||||
"LabelEpisodeType": "Episode Type",
|
||||
"LabelEpisodeUrlFromRssFeed": "Episode URL from RSS feed",
|
||||
"LabelEpisodes": "Episodes",
|
||||
"LabelEpisodic": "Episodic",
|
||||
"LabelExample": "Example",
|
||||
"LabelExpandSeries": "Expand Series",
|
||||
"LabelExpandSubSeries": "Expand Sub Series",
|
||||
@@ -335,6 +358,7 @@
|
||||
"LabelFontScale": "Font scale",
|
||||
"LabelFontStrikethrough": "Strikethrough",
|
||||
"LabelFormat": "Format",
|
||||
"LabelFull": "Full",
|
||||
"LabelGenre": "Genre",
|
||||
"LabelGenres": "Genres",
|
||||
"LabelHardDeleteFile": "Hard delete file",
|
||||
@@ -390,6 +414,10 @@
|
||||
"LabelLowestPriority": "Lowest Priority",
|
||||
"LabelMatchExistingUsersBy": "Match existing users by",
|
||||
"LabelMatchExistingUsersByDescription": "Used for connecting existing users. Once connected, users will be matched by a unique id from your SSO provider",
|
||||
"LabelMaxEpisodesToDownload": "Max # of episodes to download. Use 0 for unlimited.",
|
||||
"LabelMaxEpisodesToDownloadPerCheck": "Max # of new episodes to download per check",
|
||||
"LabelMaxEpisodesToKeep": "Max # of episodes to keep",
|
||||
"LabelMaxEpisodesToKeepHelp": "Value of 0 sets no max limit. After a new episode is auto-downloaded this will delete the oldest episode if you have more than X episodes. This will only delete 1 episode per new download.",
|
||||
"LabelMediaPlayer": "Media Player",
|
||||
"LabelMediaType": "Media Type",
|
||||
"LabelMetaTag": "Meta Tag",
|
||||
@@ -464,6 +492,8 @@
|
||||
"LabelPubDate": "Pub Date",
|
||||
"LabelPublishYear": "Publish Year",
|
||||
"LabelPublishedDate": "Published {0}",
|
||||
"LabelPublishedDecade": "Published Decade",
|
||||
"LabelPublishedDecades": "Published Decades",
|
||||
"LabelPublisher": "Publisher",
|
||||
"LabelPublishers": "Publishers",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
||||
@@ -483,21 +513,28 @@
|
||||
"LabelRedo": "Redo",
|
||||
"LabelRegion": "Region",
|
||||
"LabelReleaseDate": "Release Date",
|
||||
"LabelRemoveAllMetadataAbs": "Remove all metadata.abs files",
|
||||
"LabelRemoveAllMetadataJson": "Remove all metadata.json files",
|
||||
"LabelRemoveCover": "Remove cover",
|
||||
"LabelRemoveMetadataFile": "Remove metadata files in library item folders",
|
||||
"LabelRemoveMetadataFileHelp": "Remove all metadata.json and metadata.abs files in your {0} folders.",
|
||||
"LabelRowsPerPage": "Rows per page",
|
||||
"LabelSearchTerm": "Search Term",
|
||||
"LabelSearchTitle": "Search Title",
|
||||
"LabelSearchTitleOrASIN": "Search Title or ASIN",
|
||||
"LabelSeason": "Season",
|
||||
"LabelSeasonNumber": "Season #{0}",
|
||||
"LabelSelectAll": "Select all",
|
||||
"LabelSelectAllEpisodes": "Select all episodes",
|
||||
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
|
||||
"LabelSelectUsers": "Select users",
|
||||
"LabelSendEbookToDevice": "Send Ebook to...",
|
||||
"LabelSequence": "Sequence",
|
||||
"LabelSerial": "Serial",
|
||||
"LabelSeries": "Series",
|
||||
"LabelSeriesName": "Series Name",
|
||||
"LabelSeriesProgress": "Series Progress",
|
||||
"LabelServerLogLevel": "Server Log Level",
|
||||
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||
"LabelSetEbookAsPrimary": "Set as primary",
|
||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||
@@ -586,6 +623,7 @@
|
||||
"LabelTimeDurationXMinutes": "{0} minutes",
|
||||
"LabelTimeDurationXSeconds": "{0} seconds",
|
||||
"LabelTimeInMinutes": "Time in minutes",
|
||||
"LabelTimeLeft": "{0} left",
|
||||
"LabelTimeListened": "Time Listened",
|
||||
"LabelTimeListenedToday": "Time Listened Today",
|
||||
"LabelTimeRemaining": "{0} remaining",
|
||||
@@ -593,6 +631,7 @@
|
||||
"LabelTitle": "Title",
|
||||
"LabelToolsEmbedMetadata": "Embed Metadata",
|
||||
"LabelToolsEmbedMetadataDescription": "Embed metadata into audio files including cover image and chapters.",
|
||||
"LabelToolsM4bEncoder": "M4B Encoder",
|
||||
"LabelToolsMakeM4b": "Make M4B Audiobook File",
|
||||
"LabelToolsMakeM4bDescription": "Generate a .M4B audiobook file with embedded metadata, cover image, and chapters.",
|
||||
"LabelToolsSplitM4b": "Split M4B to MP3's",
|
||||
@@ -605,6 +644,7 @@
|
||||
"LabelTracksMultiTrack": "Multi-track",
|
||||
"LabelTracksNone": "No tracks",
|
||||
"LabelTracksSingleTrack": "Single-track",
|
||||
"LabelTrailer": "Trailer",
|
||||
"LabelType": "Type",
|
||||
"LabelUnabridged": "Unabridged",
|
||||
"LabelUndo": "Undo",
|
||||
@@ -618,8 +658,10 @@
|
||||
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
||||
"LabelUploaderDropFiles": "Drop files",
|
||||
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||
"LabelUseAdvancedOptions": "Use Advanced Options",
|
||||
"LabelUseChapterTrack": "Use chapter track",
|
||||
"LabelUseFullTrack": "Use full track",
|
||||
"LabelUseZeroForUnlimited": "Use 0 for unlimited",
|
||||
"LabelUser": "User",
|
||||
"LabelUsername": "Username",
|
||||
"LabelValue": "Value",
|
||||
@@ -666,6 +708,7 @@
|
||||
"MessageConfirmDeleteMetadataProvider": "Are you sure you want to delete custom metadata provider \"{0}\"?",
|
||||
"MessageConfirmDeleteNotification": "Are you sure you want to delete this notification?",
|
||||
"MessageConfirmDeleteSession": "Are you sure you want to delete this session?",
|
||||
"MessageConfirmEmbedMetadataInAudioFiles": "Are you sure you want to embed metadata in {0} audio files?",
|
||||
"MessageConfirmForceReScan": "Are you sure you want to force re-scan?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?",
|
||||
@@ -677,6 +720,7 @@
|
||||
"MessageConfirmPurgeCache": "Purge cache will delete the entire directory at <code>/metadata/cache</code>. <br /><br />Are you sure you want to remove the cache directory?",
|
||||
"MessageConfirmPurgeItemsCache": "Purge items cache will delete the entire directory at <code>/metadata/cache/items</code>.<br />Are you sure?",
|
||||
"MessageConfirmQuickEmbed": "Warning! Quick embed will not backup your audio files. Make sure that you have a backup of your audio files. <br><br>Would you like to continue?",
|
||||
"MessageConfirmQuickMatchEpisodes": "Quick matching episodes will overwrite details if a match is found. Only unmatched episodes will be updated. Are you sure?",
|
||||
"MessageConfirmReScanLibraryItems": "Are you sure you want to re-scan {0} items?",
|
||||
"MessageConfirmRemoveAllChapters": "Are you sure you want to remove all chapters?",
|
||||
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?",
|
||||
@@ -684,6 +728,7 @@
|
||||
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
||||
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
||||
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||
"MessageConfirmRemoveMetadataFiles": "Are you sure you want to remove all metadata.{0} files in your library item folders?",
|
||||
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
||||
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
||||
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
||||
@@ -699,6 +744,7 @@
|
||||
"MessageDragFilesIntoTrackOrder": "Drag files into correct track order",
|
||||
"MessageEmbedFailed": "Embed Failed!",
|
||||
"MessageEmbedFinished": "Embed Finished!",
|
||||
"MessageEmbedQueue": "Queued for metadata embed ({0} in queue)",
|
||||
"MessageEpisodesQueuedForDownload": "{0} Episode(s) queued for download",
|
||||
"MessageEreaderDevices": "To ensure delivery of ebooks, you may need to add the above email address as a valid sender for each device listed below.",
|
||||
"MessageFeedURLWillBe": "Feed URL will be {0}",
|
||||
@@ -743,6 +789,7 @@
|
||||
"MessageNoLogs": "No Logs",
|
||||
"MessageNoMediaProgress": "No Media Progress",
|
||||
"MessageNoNotifications": "No Notifications",
|
||||
"MessageNoPodcastFeed": "Invalid podcast: No Feed",
|
||||
"MessageNoPodcastsFound": "No podcasts found",
|
||||
"MessageNoResults": "No Results",
|
||||
"MessageNoSearchResultsFor": "No search results for \"{0}\"",
|
||||
@@ -759,6 +806,10 @@
|
||||
"MessagePlaylistCreateFromCollection": "Create playlist from collection",
|
||||
"MessagePleaseWait": "Please wait...",
|
||||
"MessagePodcastHasNoRSSFeedForMatching": "Podcast has no RSS feed url to use for matching",
|
||||
"MessagePodcastSearchField": "Enter search term or RSS feed URL",
|
||||
"MessageQuickEmbedInProgress": "Quick embed in progress",
|
||||
"MessageQuickEmbedQueue": "Queued for quick embed ({0} in queue)",
|
||||
"MessageQuickMatchAllEpisodes": "Quick Match All Episodes",
|
||||
"MessageQuickMatchDescription": "Populate empty item details & cover with first match result from '{0}'. Does not overwrite details unless 'Prefer matched metadata' server setting is enabled.",
|
||||
"MessageRemoveChapter": "Remove chapter",
|
||||
"MessageRemoveEpisodes": "Remove {0} episode(s)",
|
||||
@@ -776,6 +827,41 @@
|
||||
"MessageShareExpiresIn": "Expires in {0}",
|
||||
"MessageShareURLWillBe": "Share URL will be <strong>{0}</strong>",
|
||||
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
||||
"MessageTaskAudioFileNotWritable": "Audio file \"{0}\" is not writable",
|
||||
"MessageTaskCanceledByUser": "Task canceled by user",
|
||||
"MessageTaskDownloadingEpisodeDescription": "Downloading episode \"{0}\"",
|
||||
"MessageTaskEmbeddingMetadata": "Embedding metadata",
|
||||
"MessageTaskEmbeddingMetadataDescription": "Embedding metadata in audiobook \"{0}\"",
|
||||
"MessageTaskEncodingM4b": "Encoding M4B",
|
||||
"MessageTaskEncodingM4bDescription": "Encoding audiobook \"{0}\" into a single m4b file",
|
||||
"MessageTaskFailed": "Failed",
|
||||
"MessageTaskFailedToBackupAudioFile": "Failed to backup audio file \"{0}\"",
|
||||
"MessageTaskFailedToCreateCacheDirectory": "Failed to create cache directory",
|
||||
"MessageTaskFailedToEmbedMetadataInFile": "Failed to embed metadata in file \"{0}\"",
|
||||
"MessageTaskFailedToMergeAudioFiles": "Failed to merge audio files",
|
||||
"MessageTaskFailedToMoveM4bFile": "Failed to move m4b file",
|
||||
"MessageTaskFailedToWriteMetadataFile": "Failed to write metadata file",
|
||||
"MessageTaskMatchingBooksInLibrary": "Matching books in library \"{0}\"",
|
||||
"MessageTaskNoFilesToScan": "No files to scan",
|
||||
"MessageTaskOpmlImport": "OPML import",
|
||||
"MessageTaskOpmlImportDescription": "Creating podcasts from {0} RSS feeds",
|
||||
"MessageTaskOpmlImportFeed": "OPML import feed",
|
||||
"MessageTaskOpmlImportFeedDescription": "Importing RSS feed \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedFailed": "Failed to get podcast feed",
|
||||
"MessageTaskOpmlImportFeedPodcastDescription": "Creating podcast \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedPodcastExists": "Podcast already exists at path",
|
||||
"MessageTaskOpmlImportFeedPodcastFailed": "Failed to create podcast",
|
||||
"MessageTaskOpmlImportFinished": "Added {0} podcasts",
|
||||
"MessageTaskOpmlParseFailed": "Failed to parse OPML file",
|
||||
"MessageTaskOpmlParseFastFail": "Invalid OPML file <opml> tag not found OR an <outline> tag was not found",
|
||||
"MessageTaskOpmlParseNoneFound": "No feeds found in OPML file",
|
||||
"MessageTaskScanItemsAdded": "{0} added",
|
||||
"MessageTaskScanItemsMissing": "{0} missing",
|
||||
"MessageTaskScanItemsUpdated": "{0} updated",
|
||||
"MessageTaskScanNoChangesNeeded": "No changes needed",
|
||||
"MessageTaskScanningFileChanges": "Scanning file changes in \"{0}\"",
|
||||
"MessageTaskScanningLibrary": "Scanning \"{0}\" library",
|
||||
"MessageTaskTargetDirectoryNotWritable": "Target directory is not writable",
|
||||
"MessageThinking": "Thinking...",
|
||||
"MessageUploaderItemFailed": "Failed to upload",
|
||||
"MessageUploaderItemSuccess": "Successfully Uploaded!",
|
||||
@@ -793,6 +879,10 @@
|
||||
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
||||
"NoteUploaderOnlyAudioFiles": "If uploading only audio files then each audio file will be handled as a separate audiobook.",
|
||||
"NoteUploaderUnsupportedFiles": "Unsupported files are ignored. When choosing or dropping a folder, other files that are not in an item folder are ignored.",
|
||||
"NotificationOnBackupCompletedDescription": "Triggered when a backup is completed",
|
||||
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
|
||||
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
|
||||
"NotificationOnTestDescription": "Event for testing the notification system",
|
||||
"PlaceholderNewCollection": "New collection name",
|
||||
"PlaceholderNewFolderPath": "New folder path",
|
||||
"PlaceholderNewPlaylist": "New playlist name",
|
||||
@@ -816,14 +906,13 @@
|
||||
"StatsTopNarrators": "TOP NARRATORS",
|
||||
"StatsTotalDuration": "With a total duration of…",
|
||||
"StatsYearInReview": "YEAR IN REVIEW",
|
||||
"ToastAccountUpdateFailed": "Failed to update account",
|
||||
"ToastAccountUpdateSuccess": "Account updated",
|
||||
"ToastAppriseUrlRequired": "Must enter an Apprise URL",
|
||||
"ToastAsinRequired": "ASIN is required",
|
||||
"ToastAuthorImageRemoveSuccess": "Author image removed",
|
||||
"ToastAuthorNotFound": "Author \"{0}\" not found",
|
||||
"ToastAuthorRemoveSuccess": "Author removed",
|
||||
"ToastAuthorSearchNotFound": "Author not found",
|
||||
"ToastAuthorUpdateFailed": "Failed to update author",
|
||||
"ToastAuthorUpdateMerged": "Author merged",
|
||||
"ToastAuthorUpdateSuccess": "Author updated",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Author updated (no image found)",
|
||||
@@ -834,29 +923,29 @@
|
||||
"ToastBackupDeleteSuccess": "Backup deleted",
|
||||
"ToastBackupInvalidMaxKeep": "Invalid number of backups to keep",
|
||||
"ToastBackupInvalidMaxSize": "Invalid maximum backup size",
|
||||
"ToastBackupPathUpdateFailed": "Failed to update backup path",
|
||||
"ToastBackupRestoreFailed": "Failed to restore backup",
|
||||
"ToastBackupUploadFailed": "Failed to upload backup",
|
||||
"ToastBackupUploadSuccess": "Backup uploaded",
|
||||
"ToastBatchDeleteFailed": "Batch delete failed",
|
||||
"ToastBatchDeleteSuccess": "Batch delete success",
|
||||
"ToastBatchQuickMatchFailed": "Batch Quick Match failed!",
|
||||
"ToastBatchQuickMatchStarted": "Batch Quick Match of {0} books started!",
|
||||
"ToastBatchUpdateFailed": "Batch update failed",
|
||||
"ToastBatchUpdateSuccess": "Batch update success",
|
||||
"ToastBookmarkCreateFailed": "Failed to create bookmark",
|
||||
"ToastBookmarkCreateSuccess": "Bookmark added",
|
||||
"ToastBookmarkRemoveSuccess": "Bookmark removed",
|
||||
"ToastBookmarkUpdateFailed": "Failed to update bookmark",
|
||||
"ToastBookmarkUpdateSuccess": "Bookmark updated",
|
||||
"ToastCachePurgeFailed": "Failed to purge cache",
|
||||
"ToastCachePurgeSuccess": "Cache purged successfully",
|
||||
"ToastChaptersHaveErrors": "Chapters have errors",
|
||||
"ToastChaptersMustHaveTitles": "Chapters must have titles",
|
||||
"ToastChaptersRemoved": "Chapters removed",
|
||||
"ToastChaptersUpdated": "Chapters updated",
|
||||
"ToastCollectionItemsAddFailed": "Item(s) added to collection failed",
|
||||
"ToastCollectionItemsAddSuccess": "Item(s) added to collection success",
|
||||
"ToastCollectionItemsRemoveSuccess": "Item(s) removed from collection",
|
||||
"ToastCollectionRemoveSuccess": "Collection removed",
|
||||
"ToastCollectionUpdateFailed": "Failed to update collection",
|
||||
"ToastCollectionUpdateSuccess": "Collection updated",
|
||||
"ToastCoverUpdateFailed": "Cover update failed",
|
||||
"ToastDeleteFileFailed": "Failed to delete file",
|
||||
@@ -865,31 +954,28 @@
|
||||
"ToastDeviceNameAlreadyExists": "Ereader device with that name already exists",
|
||||
"ToastDeviceTestEmailFailed": "Failed to send test email",
|
||||
"ToastDeviceTestEmailSuccess": "Test email sent",
|
||||
"ToastDeviceUpdateFailed": "Failed to update device",
|
||||
"ToastEmailSettingsUpdateFailed": "Failed to update email settings",
|
||||
"ToastEmailSettingsUpdateSuccess": "Email settings updated",
|
||||
"ToastEncodeCancelFailed": "Failed to cancel encode",
|
||||
"ToastEncodeCancelSucces": "Encode canceled",
|
||||
"ToastEpisodeDownloadQueueClearFailed": "Failed to clear queue",
|
||||
"ToastEpisodeDownloadQueueClearSuccess": "Episode download queue cleared",
|
||||
"ToastEpisodeUpdateSuccess": "{0} episodes updated",
|
||||
"ToastErrorCannotShare": "Cannot share natively on this device",
|
||||
"ToastFailedToLoadData": "Failed to load data",
|
||||
"ToastFailedToMatch": "Failed to match",
|
||||
"ToastFailedToShare": "Failed to share",
|
||||
"ToastFailedToUpdateAccount": "Failed to update account",
|
||||
"ToastFailedToUpdateUser": "Failed to update user",
|
||||
"ToastFailedToUpdate": "Failed to update",
|
||||
"ToastInvalidImageUrl": "Invalid image URL",
|
||||
"ToastInvalidMaxEpisodesToDownload": "Invalid max episodes to download",
|
||||
"ToastInvalidUrl": "Invalid URL",
|
||||
"ToastItemCoverUpdateFailed": "Failed to update item cover",
|
||||
"ToastItemCoverUpdateSuccess": "Item cover updated",
|
||||
"ToastItemDeletedFailed": "Failed to delete item",
|
||||
"ToastItemDeletedSuccess": "Deleted item",
|
||||
"ToastItemDetailsUpdateFailed": "Failed to update item details",
|
||||
"ToastItemDetailsUpdateSuccess": "Item details updated",
|
||||
"ToastItemMarkedAsFinishedFailed": "Failed to mark as Finished",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Item marked as Finished",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "Failed to mark as Not Finished",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Item marked as Not Finished",
|
||||
"ToastItemUpdateFailed": "Failed to update item",
|
||||
"ToastItemUpdateSuccess": "Item updated",
|
||||
"ToastLibraryCreateFailed": "Failed to create library",
|
||||
"ToastLibraryCreateSuccess": "Library \"{0}\" created",
|
||||
@@ -897,37 +983,42 @@
|
||||
"ToastLibraryDeleteSuccess": "Library deleted",
|
||||
"ToastLibraryScanFailedToStart": "Failed to start scan",
|
||||
"ToastLibraryScanStarted": "Library scan started",
|
||||
"ToastLibraryUpdateFailed": "Failed to update library",
|
||||
"ToastLibraryUpdateSuccess": "Library \"{0}\" updated",
|
||||
"ToastMatchAllAuthorsFailed": "Failed to match all authors",
|
||||
"ToastMetadataFilesRemovedError": "Error removing metadata.{0} files",
|
||||
"ToastMetadataFilesRemovedNoneFound": "No metadata.{0} files found in library",
|
||||
"ToastMetadataFilesRemovedNoneRemoved": "No metadata.{0} files removed",
|
||||
"ToastMetadataFilesRemovedSuccess": "{0} metadata.{1} files removed",
|
||||
"ToastMustHaveAtLeastOnePath": "Must have at least one path",
|
||||
"ToastNameEmailRequired": "Name and email are required",
|
||||
"ToastNameRequired": "Name is required",
|
||||
"ToastNewEpisodesFound": "{0} new episodes found",
|
||||
"ToastNewUserCreatedFailed": "Failed to create account: \"{0}\"",
|
||||
"ToastNewUserCreatedSuccess": "New account created",
|
||||
"ToastNewUserLibraryError": "Must select at least one library",
|
||||
"ToastNewUserPasswordError": "Must have a password, only root user can have an empty password",
|
||||
"ToastNewUserTagError": "Must select at least one tag",
|
||||
"ToastNewUserUsernameError": "Enter a username",
|
||||
"ToastNoNewEpisodesFound": "No new episodes found",
|
||||
"ToastNoUpdatesNecessary": "No updates necessary",
|
||||
"ToastNotificationCreateFailed": "Failed to create notification",
|
||||
"ToastNotificationDeleteFailed": "Failed to delete notification",
|
||||
"ToastNotificationFailedMaximum": "Max failed attempts must be >= 0",
|
||||
"ToastNotificationQueueMaximum": "Max notification queue must be >= 0",
|
||||
"ToastNotificationSettingsUpdateFailed": "Failed to update notification settings",
|
||||
"ToastNotificationSettingsUpdateSuccess": "Notification settings updated",
|
||||
"ToastNotificationTestTriggerFailed": "Failed to trigger test notification",
|
||||
"ToastNotificationTestTriggerSuccess": "Triggered test notification",
|
||||
"ToastNotificationUpdateFailed": "Failed to update notification",
|
||||
"ToastNotificationUpdateSuccess": "Notification updated",
|
||||
"ToastPlaylistCreateFailed": "Failed to create playlist",
|
||||
"ToastPlaylistCreateSuccess": "Playlist created",
|
||||
"ToastPlaylistRemoveSuccess": "Playlist removed",
|
||||
"ToastPlaylistUpdateFailed": "Failed to update playlist",
|
||||
"ToastPlaylistUpdateSuccess": "Playlist updated",
|
||||
"ToastPodcastCreateFailed": "Failed to create podcast",
|
||||
"ToastPodcastCreateSuccess": "Podcast created successfully",
|
||||
"ToastPodcastGetFeedFailed": "Failed to get podcast feed",
|
||||
"ToastPodcastNoEpisodesInFeed": "No episodes found in RSS feed",
|
||||
"ToastPodcastNoRssFeed": "Podcast does not have an RSS feed",
|
||||
"ToastProgressIsNotBeingSynced": "Progress is not being synced, restart playback",
|
||||
"ToastProviderCreatedFailed": "Failed to add provider",
|
||||
"ToastProviderCreatedSuccess": "New provider added",
|
||||
"ToastProviderNameAndUrlRequired": "Name and Url required",
|
||||
@@ -950,18 +1041,17 @@
|
||||
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"",
|
||||
"ToastSeriesUpdateFailed": "Series update failed",
|
||||
"ToastSeriesUpdateSuccess": "Series update success",
|
||||
"ToastServerSettingsUpdateFailed": "Failed to update server settings",
|
||||
"ToastServerSettingsUpdateSuccess": "Server settings updated",
|
||||
"ToastSessionCloseFailed": "Failed to close session",
|
||||
"ToastSessionDeleteFailed": "Failed to delete session",
|
||||
"ToastSessionDeleteSuccess": "Session deleted",
|
||||
"ToastSleepTimerDone": "Sleep timer done... zZzzZz",
|
||||
"ToastSlugMustChange": "Slug contains invalid characters",
|
||||
"ToastSlugRequired": "Slug is required",
|
||||
"ToastSocketConnected": "Socket connected",
|
||||
"ToastSocketDisconnected": "Socket disconnected",
|
||||
"ToastSocketFailedToConnect": "Socket failed to connect",
|
||||
"ToastSortingPrefixesEmptyError": "Must have at least 1 sorting prefix",
|
||||
"ToastSortingPrefixesUpdateFailed": "Failed to update sorting prefixes",
|
||||
"ToastSortingPrefixesUpdateSuccess": "Sorting prefixes updated ({0} items)",
|
||||
"ToastTitleRequired": "Title is required",
|
||||
"ToastUnknownError": "Unknown error",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
+181
-19
@@ -30,6 +30,8 @@
|
||||
"ButtonEditChapters": "Editar Capítulo",
|
||||
"ButtonEditPodcast": "Editar Podcast",
|
||||
"ButtonEnable": "Permitir",
|
||||
"ButtonFireAndFail": "Ejecutado y fallido",
|
||||
"ButtonFireOnTest": "Activar evento de prueba",
|
||||
"ButtonForceReScan": "Forzar Re-Escaneo",
|
||||
"ButtonFullPath": "Ruta de Acceso Completa",
|
||||
"ButtonHide": "Esconder",
|
||||
@@ -46,12 +48,15 @@
|
||||
"ButtonMatchAllAuthors": "Encontrar Todos los Autores",
|
||||
"ButtonMatchBooks": "Encontrar Libros",
|
||||
"ButtonNevermind": "Olvidar",
|
||||
"ButtonNext": "Siguiente",
|
||||
"ButtonNextChapter": "Siguiente Capítulo",
|
||||
"ButtonNextItemInQueue": "El siguiente elemento en cola",
|
||||
"ButtonOk": "De acuerdo",
|
||||
"ButtonOpenFeed": "Abrir fuente",
|
||||
"ButtonOpenManager": "Abrir Editor",
|
||||
"ButtonPause": "Pausar",
|
||||
"ButtonPlay": "Reproducir",
|
||||
"ButtonPlayAll": "Reproducir todo",
|
||||
"ButtonPlaying": "Reproduciendo",
|
||||
"ButtonPlaylists": "Listas de reproducción",
|
||||
"ButtonPrevious": "Anterior",
|
||||
@@ -61,6 +66,7 @@
|
||||
"ButtonPurgeItemsCache": "Purgar Elementos de Cache",
|
||||
"ButtonQueueAddItem": "Agregar a la Fila",
|
||||
"ButtonQueueRemoveItem": "Remover de la Fila",
|
||||
"ButtonQuickEmbed": "Inserción rápida",
|
||||
"ButtonQuickEmbedMetadata": "Agregue metadatos rápidamente",
|
||||
"ButtonQuickMatch": "Encontrar Rápido",
|
||||
"ButtonReScan": "Re-Escanear",
|
||||
@@ -84,6 +90,7 @@
|
||||
"ButtonScanLibrary": "Escanear Biblioteca",
|
||||
"ButtonSearch": "Buscar",
|
||||
"ButtonSelectFolderPath": "Seleccionar Ruta de Carpeta",
|
||||
"ButtonSeries": "Series",
|
||||
"ButtonSetChaptersFromTracks": "Seleccionar Capítulos Según las Pistas",
|
||||
"ButtonShare": "Compartir",
|
||||
"ButtonShiftTimes": "Desplazar Tiempos",
|
||||
@@ -93,7 +100,7 @@
|
||||
"ButtonStats": "Estadísticas",
|
||||
"ButtonSubmit": "Enviar",
|
||||
"ButtonTest": "Prueba",
|
||||
"ButtonUnlinkOpedId": "Desvincular OpenID",
|
||||
"ButtonUnlinkOpenId": "Desvincular OpenID",
|
||||
"ButtonUpload": "Subir",
|
||||
"ButtonUploadBackup": "Subir Respaldo",
|
||||
"ButtonUploadCover": "Subir Portada",
|
||||
@@ -125,6 +132,7 @@
|
||||
"HeaderDetails": "Detalles",
|
||||
"HeaderDownloadQueue": "Lista de Descarga",
|
||||
"HeaderEbookFiles": "Archivos de libros digitales",
|
||||
"HeaderEmail": "Correo electrónico",
|
||||
"HeaderEmailSettings": "Opciones de Email",
|
||||
"HeaderEpisodes": "Episodios",
|
||||
"HeaderEreaderDevices": "Dispositivos Ereader",
|
||||
@@ -152,6 +160,7 @@
|
||||
"HeaderNewAccount": "Nueva Cuenta",
|
||||
"HeaderNewLibrary": "Nueva Biblioteca",
|
||||
"HeaderNotificationCreate": "Crear notificación",
|
||||
"HeaderNotificationUpdate": "Notificación de actualización",
|
||||
"HeaderNotifications": "Notificaciones",
|
||||
"HeaderOpenIDConnectAuthentication": "Autenticación OpenID Connect",
|
||||
"HeaderOpenRSSFeed": "Abrir fuente RSS",
|
||||
@@ -171,12 +180,14 @@
|
||||
"HeaderRemoveEpisodes": "Remover {0} Episodios",
|
||||
"HeaderSavedMediaProgress": "Guardar Progreso de Multimedia",
|
||||
"HeaderSchedule": "Horario",
|
||||
"HeaderScheduleEpisodeDownloads": "Programar descargas automáticas de episodios",
|
||||
"HeaderScheduleLibraryScans": "Programar Escaneo Automático de Biblioteca",
|
||||
"HeaderSession": "Sesión",
|
||||
"HeaderSetBackupSchedule": "Programar Respaldo",
|
||||
"HeaderSettings": "Configuraciones",
|
||||
"HeaderSettingsDisplay": "Interfaz",
|
||||
"HeaderSettingsExperimental": "Funciones Experimentales",
|
||||
"HeaderSettingsGeneral": "General",
|
||||
"HeaderSettingsScanner": "Escáner",
|
||||
"HeaderSleepTimer": "Temporizador de apagado",
|
||||
"HeaderStatsLargestItems": "Artículos mas Grandes",
|
||||
@@ -216,6 +227,9 @@
|
||||
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
|
||||
"LabelAlreadyInYourLibrary": "Ya existe en la Biblioteca",
|
||||
"LabelAppend": "Adjuntar",
|
||||
"LabelAudioBitrate": "Tasa de bits del audio (por ejemplo, 128k)",
|
||||
"LabelAudioChannels": "Canales de audio (1 o 2)",
|
||||
"LabelAudioCodec": "Códec de audio",
|
||||
"LabelAuthor": "Autor",
|
||||
"LabelAuthorFirstLast": "Autor (Nombre Apellido)",
|
||||
"LabelAuthorLastFirst": "Autor (Apellido, Nombre)",
|
||||
@@ -228,6 +242,7 @@
|
||||
"LabelAutoRegister": "Registro automático",
|
||||
"LabelAutoRegisterDescription": "Crear usuarios automáticamente tras iniciar sesión",
|
||||
"LabelBackToUser": "Regresar a Usuario",
|
||||
"LabelBackupAudioFiles": "Copia de seguridad de archivos de audio",
|
||||
"LabelBackupLocation": "Ubicación del Respaldo",
|
||||
"LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Respaldo Guardado en /metadata/backups",
|
||||
@@ -235,15 +250,20 @@
|
||||
"LabelBackupsMaxBackupSizeHelp": "Como protección contra una configuración errónea, los respaldos fallarán si se excede el tamaño configurado.",
|
||||
"LabelBackupsNumberToKeep": "Numero de respaldos para conservar",
|
||||
"LabelBackupsNumberToKeepHelp": "Solamente 1 respaldo se removerá a la vez. Si tiene mas respaldos guardados, debe removerlos manualmente.",
|
||||
"LabelBitrate": "Tasa de bits",
|
||||
"LabelBooks": "Libros",
|
||||
"LabelButtonText": "Texto del botón",
|
||||
"LabelByAuthor": "por {0}",
|
||||
"LabelChangePassword": "Cambiar Contraseña",
|
||||
"LabelChannels": "Canales",
|
||||
"LabelChapterCount": "{0} capítulos",
|
||||
"LabelChapterTitle": "Titulo del Capítulo",
|
||||
"LabelChapters": "Capítulos",
|
||||
"LabelChaptersFound": "Capítulo Encontrado",
|
||||
"LabelClickForMoreInfo": "Click para más información",
|
||||
"LabelClickToUseCurrentValue": "Haz clic para utilizar el valor actual",
|
||||
"LabelClosePlayer": "Cerrar reproductor",
|
||||
"LabelCodec": "Codec",
|
||||
"LabelCollapseSeries": "Colapsar serie",
|
||||
"LabelCollapseSubSeries": "Contraer la subserie",
|
||||
"LabelCollection": "Colección",
|
||||
@@ -282,6 +302,7 @@
|
||||
"LabelEbook": "Libro electrónico",
|
||||
"LabelEbooks": "Libros electrónicos",
|
||||
"LabelEdit": "Editar",
|
||||
"LabelEmail": "Correo electrónico",
|
||||
"LabelEmailSettingsFromAddress": "Remitente",
|
||||
"LabelEmailSettingsRejectUnauthorized": "Rechazar certificados no autorizados",
|
||||
"LabelEmailSettingsRejectUnauthorizedHelp": "Deshabilitar la validación de certificados SSL puede exponer tu conexión a riesgos de seguridad, como ataques man-in-the-middle. Desactiva esta opción sólo si conoces las implicaciones y confias en el servidor de correo al que te conectas.",
|
||||
@@ -290,11 +311,23 @@
|
||||
"LabelEmailSettingsTestAddress": "Probar Dirección",
|
||||
"LabelEmbeddedCover": "Portada Integrada",
|
||||
"LabelEnable": "Habilitar",
|
||||
"LabelEncodingBackupLocation": "Se guardará una copia de seguridad de tus archivos de audio originales en:",
|
||||
"LabelEncodingChaptersNotEmbedded": "Los capítulos no se incrustan en los audiolibros multipista.",
|
||||
"LabelEncodingClearItemCache": "Asegúrese de purgar periódicamente la caché.",
|
||||
"LabelEncodingFinishedM4B": "El M4B terminado se colocará en su carpeta de audiolibros en:",
|
||||
"LabelEncodingInfoEmbedded": "Los metadatos se integrarán en las pistas de audio dentro de la carpeta de audiolibros.",
|
||||
"LabelEncodingStartedNavigation": "Una vez iniciada la tarea, puedes salir de esta página.",
|
||||
"LabelEncodingTimeWarning": "La codificación puede tardar hasta 30 minutos.",
|
||||
"LabelEncodingWarningAdvancedSettings": "Advertencia: No actualice esta configuración a menos que esté familiarizado con las opciones de codificación de ffmpeg.",
|
||||
"LabelEncodingWatcherDisabled": "Si ha desactivado la supervisión de los archivos, deberá volver a escanear este audiolibro más adelante.",
|
||||
"LabelEnd": "Fin",
|
||||
"LabelEndOfChapter": "Fin del capítulo",
|
||||
"LabelEpisode": "Episodio",
|
||||
"LabelEpisodeNotLinkedToRssFeed": "Episodio no enlazado al feed RSS",
|
||||
"LabelEpisodeNumber": "Episodio #{0}",
|
||||
"LabelEpisodeTitle": "Titulo de Episodio",
|
||||
"LabelEpisodeType": "Tipo de Episodio",
|
||||
"LabelEpisodeUrlFromRssFeed": "URL del episodio del feed RSS",
|
||||
"LabelEpisodes": "Episodios",
|
||||
"LabelExample": "Ejemplo",
|
||||
"LabelExpandSeries": "Ampliar serie",
|
||||
@@ -323,6 +356,7 @@
|
||||
"LabelFontScale": "Tamaño de fuente",
|
||||
"LabelFontStrikethrough": "Tachado",
|
||||
"LabelFormat": "Formato",
|
||||
"LabelFull": "Completo",
|
||||
"LabelGenre": "Genero",
|
||||
"LabelGenres": "Géneros",
|
||||
"LabelHardDeleteFile": "Eliminar Definitivamente",
|
||||
@@ -330,6 +364,7 @@
|
||||
"LabelHasSupplementaryEbook": "Tiene un libro complementario",
|
||||
"LabelHideSubtitles": "Ocultar subtítulos",
|
||||
"LabelHighestPriority": "Mayor prioridad",
|
||||
"LabelHost": "Host",
|
||||
"LabelHour": "Hora",
|
||||
"LabelHours": "Horas",
|
||||
"LabelIcon": "Icono",
|
||||
@@ -359,22 +394,25 @@
|
||||
"LabelLastTime": "Última Vez",
|
||||
"LabelLastUpdate": "Última Actualización",
|
||||
"LabelLayout": "Distribución",
|
||||
"LabelLayoutSinglePage": "Una Página",
|
||||
"LabelLayoutSinglePage": "Página única",
|
||||
"LabelLayoutSplitPage": "Dos Páginas",
|
||||
"LabelLess": "Menos",
|
||||
"LabelLibrariesAccessibleToUser": "Bibliotecas Disponibles para el Usuario",
|
||||
"LabelLibrary": "Biblioteca",
|
||||
"LabelLibraryFilterSublistEmpty": "Sin {0}",
|
||||
"LabelLibraryItem": "Elemento de Biblioteca",
|
||||
"LabelLibraryName": "Nombre de Biblioteca",
|
||||
"LabelLimit": "Limites",
|
||||
"LabelLineSpacing": "Interlineado",
|
||||
"LabelListenAgain": "Volver a escuchar",
|
||||
"LabelLogLevelDebug": "Depurar",
|
||||
"LabelLogLevelInfo": "Información",
|
||||
"LabelLogLevelWarn": "Advertencia",
|
||||
"LabelLookForNewEpisodesAfterDate": "Buscar Nuevos Episodios a partir de esta Fecha",
|
||||
"LabelLowestPriority": "Menor prioridad",
|
||||
"LabelMatchExistingUsersBy": "Emparejar a los usuarios existentes por",
|
||||
"LabelMatchExistingUsersByDescription": "Se utiliza para conectar usuarios existentes. Una vez conectados, los usuarios serán emparejados por un identificador único de su proveedor de SSO",
|
||||
"LabelMaxEpisodesToDownload": "Número máximo # de episodios para descargar. Usa 0 para descargar una cantidad ilimitada.",
|
||||
"LabelMediaPlayer": "Reproductor de Medios",
|
||||
"LabelMediaType": "Tipo de multimedia",
|
||||
"LabelMetaTag": "Metaetiqueta",
|
||||
@@ -416,7 +454,7 @@
|
||||
"LabelNumberOfBooks": "Numero de Libros",
|
||||
"LabelNumberOfEpisodes": "# de Episodios",
|
||||
"LabelOpenIDAdvancedPermsClaimDescription": "Nombre de la notificación de OpenID que contiene permisos avanzados para acciones de usuario dentro de la aplicación que se aplicarán a roles que no sean de administrador (<b>si están configurados</b>). Si el reclamo no aparece en la respuesta, se denegará el acceso a ABS. Si falta una sola opción, se tratará como <code>falsa</code>. Asegúrese de que la notificación del proveedor de identidades coincida con la estructura esperada:",
|
||||
"LabelOpenIDClaims": "Deje las siguientes opciones vacías para deshabilitar la asignación avanzada de grupos y permisos, lo que asignaría de manera automática al grupo 'Usuario'",
|
||||
"LabelOpenIDClaims": "Deje las siguientes opciones vacías para deshabilitar la asignación avanzada de grupos y permisos, lo que asignaría de manera automática al grupo 'Usuario'.",
|
||||
"LabelOpenIDGroupClaimDescription": "Nombre de la declaración OpenID que contiene una lista de grupos del usuario. Comúnmente conocidos como <code>grupos</code>. <b>Si se configura</b>, la aplicación asignará automáticamente roles en función de la pertenencia a grupos del usuario, siempre que estos grupos se denominen \"admin\", \"user\" o \"guest\" en la notificación. La solicitud debe contener una lista, y si un usuario pertenece a varios grupos, la aplicación asignará el rol correspondiente al mayor nivel de acceso. Si ningún grupo coincide, se denegará el acceso.",
|
||||
"LabelOpenRSSFeed": "Abrir Fuente RSS",
|
||||
"LabelOverwrite": "Sobrescribir",
|
||||
@@ -433,18 +471,24 @@
|
||||
"LabelPersonalYearReview": "Revisión de tu año ({0})",
|
||||
"LabelPhotoPathURL": "Ruta de Acceso/URL de Foto",
|
||||
"LabelPlayMethod": "Método de Reproducción",
|
||||
"LabelPlayerChapterNumberMarker": "{0} de {1}",
|
||||
"LabelPlaylists": "Lista de Reproducción",
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcastSearchRegion": "Región de búsqueda de podcasts",
|
||||
"LabelPodcastType": "Tipo Podcast",
|
||||
"LabelPodcasts": "Podcasts",
|
||||
"LabelPort": "Puerto",
|
||||
"LabelPrefixesToIgnore": "Prefijos para Ignorar (no distingue entre mayúsculas y minúsculas.)",
|
||||
"LabelPreventIndexing": "Evite que su fuente sea indexada por los directorios de podcasts de iTunes y Google",
|
||||
"LabelPrimaryEbook": "Ebook principal",
|
||||
"LabelProgress": "Progreso",
|
||||
"LabelProvider": "Proveedor",
|
||||
"LabelProviderAuthorizationValue": "Valor del encabezado de autorización",
|
||||
"LabelPubDate": "Fecha de publicación",
|
||||
"LabelPublishYear": "Año de publicación",
|
||||
"LabelPublishedDate": "Publicado {0}",
|
||||
"LabelPublishedDecade": "Década de publicación",
|
||||
"LabelPublishedDecades": "Décadas publicadas",
|
||||
"LabelPublisher": "Editor",
|
||||
"LabelPublishers": "Editores",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Correo electrónico de dueño personalizado",
|
||||
@@ -476,8 +520,10 @@
|
||||
"LabelSelectUsers": "Seleccionar usuarios",
|
||||
"LabelSendEbookToDevice": "Enviar Ebook a...",
|
||||
"LabelSequence": "Secuencia",
|
||||
"LabelSeries": "Series",
|
||||
"LabelSeriesName": "Nombre de la Serie",
|
||||
"LabelSeriesProgress": "Progreso de la Serie",
|
||||
"LabelServerLogLevel": "Nivel de registro del servidor",
|
||||
"LabelServerYearReview": "Resumen del año del servidor ({0})",
|
||||
"LabelSetEbookAsPrimary": "Establecer como primario",
|
||||
"LabelSetEbookAsSupplementary": "Establecer como suplementario",
|
||||
@@ -527,6 +573,7 @@
|
||||
"LabelShowSubtitles": "Mostrar subtítulos",
|
||||
"LabelSize": "Tamaño",
|
||||
"LabelSleepTimer": "Temporizador de apagado",
|
||||
"LabelSlug": "Slug",
|
||||
"LabelStart": "Iniciar",
|
||||
"LabelStartTime": "Tiempo de Inicio",
|
||||
"LabelStarted": "Iniciado",
|
||||
@@ -572,6 +619,7 @@
|
||||
"LabelTitle": "Título",
|
||||
"LabelToolsEmbedMetadata": "Incrustar Metadatos",
|
||||
"LabelToolsEmbedMetadataDescription": "Incrusta metadatos en los archivos de audio, incluyendo la portada y capítulos.",
|
||||
"LabelToolsM4bEncoder": "Codificador M4B",
|
||||
"LabelToolsMakeM4b": "Hacer Archivo de Audiolibro M4B",
|
||||
"LabelToolsMakeM4bDescription": "Generar archivo de audiolibro .M4B con metadatos, imágenes de portada y capítulos incorporados.",
|
||||
"LabelToolsSplitM4b": "Dividir M4B en Archivos MP3",
|
||||
@@ -597,6 +645,7 @@
|
||||
"LabelUploaderDragAndDrop": "Arrastre y suelte archivos o carpetas",
|
||||
"LabelUploaderDropFiles": "Suelte los Archivos",
|
||||
"LabelUploaderItemFetchMetadataHelp": "Buscar título, autor y series automáticamente",
|
||||
"LabelUseAdvancedOptions": "Usar opciones avanzadas",
|
||||
"LabelUseChapterTrack": "Usar pista por capitulo",
|
||||
"LabelUseFullTrack": "Usar pista completa",
|
||||
"LabelUser": "Usuario",
|
||||
@@ -624,14 +673,14 @@
|
||||
"MessageBackupsLocationNoEditNote": "Nota: La ubicación de la copia de seguridad se establece a través de una variable de entorno y no se puede cambiar aquí.",
|
||||
"MessageBackupsLocationPathEmpty": "La ruta de la copia de seguridad no puede estar vacía",
|
||||
"MessageBatchQuickMatchDescription": "\"Encontrar Rápido\" tratará de agregar portadas y metadatos faltantes de los elementos seleccionados. Habilite la opción de abajo para que \"Encontrar Rápido\" pueda sobrescribir portadas y/o metadatos existentes.",
|
||||
"MessageBookshelfNoCollections": "No tienes ninguna colección.",
|
||||
"MessageBookshelfNoCollections": "No tienes ninguna colección",
|
||||
"MessageBookshelfNoRSSFeeds": "Ninguna Fuente RSS esta abierta",
|
||||
"MessageBookshelfNoResultsForFilter": "Ningún Resultado para el filtro \"{0}: {1}\"",
|
||||
"MessageBookshelfNoResultsForQuery": "No hay resultados para la consulta",
|
||||
"MessageBookshelfNoSeries": "No tienes 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",
|
||||
"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",
|
||||
"MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro",
|
||||
"MessageCheckingCron": "Revisando cron...",
|
||||
@@ -645,6 +694,7 @@
|
||||
"MessageConfirmDeleteMetadataProvider": "¿Estás seguro de que deseas eliminar el proveedor de metadatos personalizado \"{0}\"?",
|
||||
"MessageConfirmDeleteNotification": "¿Estás seguro de que deseas eliminar esta notificación?",
|
||||
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
|
||||
"MessageConfirmEmbedMetadataInAudioFiles": "¿Está seguro de que desea incrustar metadatos en {0} archivos de audio?",
|
||||
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
|
||||
@@ -673,10 +723,12 @@
|
||||
"MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".",
|
||||
"MessageConfirmResetProgress": "¿Estás seguro de que quieres reiniciar tu progreso?",
|
||||
"MessageConfirmSendEbookToDevice": "¿Está seguro de que enviar {0} ebook(s) \"{1}\" al dispositivo \"{2}\"?",
|
||||
"MessageConfirmUnlinkOpenId": "¿Estás seguro de que deseas desvincular este usuario de OpenID?",
|
||||
"MessageDownloadingEpisode": "Descargando Capitulo",
|
||||
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas.",
|
||||
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas",
|
||||
"MessageEmbedFailed": "¡Error al insertar!",
|
||||
"MessageEmbedFinished": "Incrustación Terminada!",
|
||||
"MessageEmbedQueue": "En cola para incrustar metadatos ({0} en cola)",
|
||||
"MessageEpisodesQueuedForDownload": "{0} Episodio(s) en cola para descargar",
|
||||
"MessageEreaderDevices": "Para garantizar la entrega de libros electrónicos, es posible que tenga que agregar la dirección de correo electrónico anterior como remitente válido para cada dispositivo enumerado a continuación.",
|
||||
"MessageFeedURLWillBe": "URL de la fuente será {0}",
|
||||
@@ -707,6 +759,7 @@
|
||||
"MessageNoCollections": "Sin Colecciones",
|
||||
"MessageNoCoversFound": "Ninguna Portada Encontrada",
|
||||
"MessageNoDescription": "Sin Descripción",
|
||||
"MessageNoDevices": "Sin dispositivos",
|
||||
"MessageNoDownloadsInProgress": "No hay descargas actualmente en curso",
|
||||
"MessageNoDownloadsQueued": "Sin Lista de Descarga",
|
||||
"MessageNoEpisodeMatchesFound": "No se encontraron episodios que coinciden",
|
||||
@@ -720,6 +773,7 @@
|
||||
"MessageNoLogs": "No hay logs",
|
||||
"MessageNoMediaProgress": "Multimedia sin Progreso",
|
||||
"MessageNoNotifications": "Ninguna Notificación",
|
||||
"MessageNoPodcastFeed": "Podcast no válido: Sin feed",
|
||||
"MessageNoPodcastsFound": "Ningún podcast encontrado",
|
||||
"MessageNoResults": "Sin Resultados",
|
||||
"MessageNoSearchResultsFor": "No hay resultados para la búsqueda \"{0}\"",
|
||||
@@ -734,7 +788,11 @@
|
||||
"MessagePauseChapter": "Pausar la reproducción del capítulo",
|
||||
"MessagePlayChapter": "Escuchar el comienzo del capítulo",
|
||||
"MessagePlaylistCreateFromCollection": "Crear una lista de reproducción a partir de una colección",
|
||||
"MessagePleaseWait": "Por favor, espera...",
|
||||
"MessagePodcastHasNoRSSFeedForMatching": "El podcast no tiene una URL de fuente RSS que pueda usar",
|
||||
"MessagePodcastSearchField": "Introduzca el término de búsqueda o la URL de la fuente RSS",
|
||||
"MessageQuickEmbedInProgress": "Integración rápida en proceso",
|
||||
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
|
||||
"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",
|
||||
"MessageRemoveEpisodes": "Remover {0} episodio(s)",
|
||||
@@ -752,6 +810,41 @@
|
||||
"MessageShareExpiresIn": "Caduduca en {0}",
|
||||
"MessageShareURLWillBe": "La URL para compartir será <strong> {0} </strong>",
|
||||
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
|
||||
"MessageTaskAudioFileNotWritable": "El archivo de audio \"{0}\" no se puede grabar",
|
||||
"MessageTaskCanceledByUser": "Tarea cancelada por el usuario",
|
||||
"MessageTaskDownloadingEpisodeDescription": "Descargando el episodio \"{0}\"",
|
||||
"MessageTaskEmbeddingMetadata": "Inserción de metadatos",
|
||||
"MessageTaskEmbeddingMetadataDescription": "Inserción de metadatos en el audiolibro \"{0}\"",
|
||||
"MessageTaskEncodingM4b": "Codificación M4B",
|
||||
"MessageTaskEncodingM4bDescription": "Codificación del audiolibro \"{0}\" en un único archivo m4b",
|
||||
"MessageTaskFailed": "Fallida",
|
||||
"MessageTaskFailedToBackupAudioFile": "Error en la copia de seguridad del archivo de audio \"{0}\"",
|
||||
"MessageTaskFailedToCreateCacheDirectory": "Error al crear el directorio de la caché",
|
||||
"MessageTaskFailedToEmbedMetadataInFile": "Error al incrustar metadatos en el archivo \"{0}\"",
|
||||
"MessageTaskFailedToMergeAudioFiles": "Error al fusionar archivos de audio",
|
||||
"MessageTaskFailedToMoveM4bFile": "Error al mover el archivo m4b",
|
||||
"MessageTaskFailedToWriteMetadataFile": "Error al escribir el archivo de metadatos",
|
||||
"MessageTaskMatchingBooksInLibrary": "Libros coincidentes en la biblioteca \"{0}\"",
|
||||
"MessageTaskNoFilesToScan": "Sin archivos para escanear",
|
||||
"MessageTaskOpmlImport": "Importar OPML",
|
||||
"MessageTaskOpmlImportDescription": "Creando podcasts a partir de {0} fuentes RSS",
|
||||
"MessageTaskOpmlImportFeed": "Feed de importación OPML",
|
||||
"MessageTaskOpmlImportFeedDescription": "Importando el feed RSS \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedFailed": "No se puede obtener el podcast",
|
||||
"MessageTaskOpmlImportFeedPodcastDescription": "Creando podcast \"{0}\"",
|
||||
"MessageTaskOpmlImportFeedPodcastExists": "Podcast ya existe en la ruta",
|
||||
"MessageTaskOpmlImportFeedPodcastFailed": "Error al crear podcast",
|
||||
"MessageTaskOpmlImportFinished": "Añadido {0} podcasts",
|
||||
"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>",
|
||||
"MessageTaskOpmlParseNoneFound": "No se encontraron fuentes en el archivo OPML",
|
||||
"MessageTaskScanItemsAdded": "{0} añadido",
|
||||
"MessageTaskScanItemsMissing": "Falta {0}",
|
||||
"MessageTaskScanItemsUpdated": "{0} actualizado",
|
||||
"MessageTaskScanNoChangesNeeded": "No se necesitan cambios",
|
||||
"MessageTaskScanningFileChanges": "Escaneando cambios en el archivo en \"{0}\"",
|
||||
"MessageTaskScanningLibrary": "Escaneando la biblioteca \"{0}\"",
|
||||
"MessageTaskTargetDirectoryNotWritable": "El directorio de destino no se puede escribir",
|
||||
"MessageThinking": "Pensando...",
|
||||
"MessageUploaderItemFailed": "Error al Subir",
|
||||
"MessageUploaderItemSuccess": "¡Éxito al Subir!",
|
||||
@@ -769,6 +862,10 @@
|
||||
"NoteUploaderFoldersWithMediaFiles": "Las carpetas con archivos multimedia se manejarán como elementos separados en la biblioteca.",
|
||||
"NoteUploaderOnlyAudioFiles": "Si sube solamente archivos de audio, cada archivo se manejará como un audiolibro por separado.",
|
||||
"NoteUploaderUnsupportedFiles": "Se ignorarán los archivos no soportados. Al elegir o arrastrar una carpeta, los archivos que no estén dentro de una subcarpeta serán ignorados.",
|
||||
"NotificationOnBackupCompletedDescription": "Se activa cuando se completa una copia de seguridad",
|
||||
"NotificationOnBackupFailedDescription": "Se activa cuando falla una copia de seguridad",
|
||||
"NotificationOnEpisodeDownloadedDescription": "Se activa cuando se descarga automáticamente un episodio de un podcast",
|
||||
"NotificationOnTestDescription": "Evento para probar el sistema de notificaciones",
|
||||
"PlaceholderNewCollection": "Nuevo nombre de la colección",
|
||||
"PlaceholderNewFolderPath": "Nueva ruta de carpeta",
|
||||
"PlaceholderNewPlaylist": "Nuevo nombre de la lista de reproducción",
|
||||
@@ -792,80 +889,145 @@
|
||||
"StatsTopNarrators": "NARRADORES DESTACADOS",
|
||||
"StatsTotalDuration": "Con una duración total de…",
|
||||
"StatsYearInReview": "RESEÑA DEL AÑO",
|
||||
"ToastAccountUpdateFailed": "Error al actualizar cuenta",
|
||||
"ToastAccountUpdateSuccess": "Cuenta actualizada",
|
||||
"ToastAppriseUrlRequired": "Debes ingresar una URL de Apprise",
|
||||
"ToastAuthorImageRemoveSuccess": "Se eliminó la imagen del autor",
|
||||
"ToastAuthorUpdateFailed": "Error al actualizar el autor",
|
||||
"ToastAuthorNotFound": "No se encontró el autor \"{0}\"",
|
||||
"ToastAuthorRemoveSuccess": "Autor eliminado",
|
||||
"ToastAuthorSearchNotFound": "No se encontró al autor",
|
||||
"ToastAuthorUpdateMerged": "Autor combinado",
|
||||
"ToastAuthorUpdateSuccess": "Autor actualizado",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Autor actualizado (Imagen no encontrada)",
|
||||
"ToastBackupAppliedSuccess": "Copia de seguridad aplicada",
|
||||
"ToastBackupCreateFailed": "Error al crear respaldo",
|
||||
"ToastBackupCreateSuccess": "Respaldo creado",
|
||||
"ToastBackupDeleteFailed": "Error al eliminar respaldo",
|
||||
"ToastBackupDeleteSuccess": "Respaldo eliminado",
|
||||
"ToastBackupInvalidMaxKeep": "Número no válido de copias de seguridad a conservar",
|
||||
"ToastBackupInvalidMaxSize": "Tamaño máximo de copia de seguridad no válido",
|
||||
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
|
||||
"ToastBackupUploadFailed": "Error al subir el respaldo",
|
||||
"ToastBackupUploadSuccess": "Respaldo cargado",
|
||||
"ToastBatchDeleteFailed": "Error al eliminar por lotes",
|
||||
"ToastBatchDeleteSuccess": "Borrado por lotes correcto",
|
||||
"ToastBatchUpdateFailed": "Subida masiva fallida",
|
||||
"ToastBatchUpdateSuccess": "Subida masiva exitosa",
|
||||
"ToastBookmarkCreateFailed": "Error al crear marcador",
|
||||
"ToastBookmarkCreateSuccess": "Marcador Agregado",
|
||||
"ToastBookmarkRemoveSuccess": "Marcador eliminado",
|
||||
"ToastBookmarkUpdateFailed": "Error al actualizar el marcador",
|
||||
"ToastBookmarkUpdateSuccess": "Marcador actualizado",
|
||||
"ToastCachePurgeFailed": "Error al purgar el caché",
|
||||
"ToastCachePurgeSuccess": "Caché purgado de manera exitosa",
|
||||
"ToastChaptersHaveErrors": "Los capítulos tienen errores",
|
||||
"ToastChaptersMustHaveTitles": "Los capítulos tienen que tener un título",
|
||||
"ToastChaptersRemoved": "Capítulos eliminados",
|
||||
"ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)",
|
||||
"ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente",
|
||||
"ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección",
|
||||
"ToastCollectionRemoveSuccess": "Colección removida",
|
||||
"ToastCollectionUpdateFailed": "Error al actualizar la colección",
|
||||
"ToastCollectionUpdateSuccess": "Colección actualizada",
|
||||
"ToastCoverUpdateFailed": "Error al actualizar la cubierta",
|
||||
"ToastDeleteFileFailed": "Error el eliminar archivo",
|
||||
"ToastDeleteFileSuccess": "Archivo eliminado",
|
||||
"ToastDeviceAddFailed": "Error al añadir dispositivo",
|
||||
"ToastDeviceNameAlreadyExists": "Un libro electrónico ya existe con ese nombre",
|
||||
"ToastDeviceTestEmailFailed": "Error al enviar correo de prueba",
|
||||
"ToastDeviceTestEmailSuccess": "Correo electrónico de prueba enviado",
|
||||
"ToastEmailSettingsUpdateSuccess": "Configuración del correo electrónico actualizada",
|
||||
"ToastEncodeCancelFailed": "No se pudo cancelar la codificación",
|
||||
"ToastEncodeCancelSucces": "Codificación cancelada",
|
||||
"ToastEpisodeDownloadQueueClearFailed": "No se pudo borrar la cola",
|
||||
"ToastEpisodeDownloadQueueClearSuccess": "Se borró la cola de descargas de los episodios",
|
||||
"ToastErrorCannotShare": "No se puede compartir de forma nativa en este dispositivo",
|
||||
"ToastFailedToLoadData": "Error al cargar data",
|
||||
"ToastItemCoverUpdateFailed": "Error al actualizar la portada del elemento",
|
||||
"ToastFailedToShare": "Error al compartir",
|
||||
"ToastFailedToUpdate": "Error al actualizar",
|
||||
"ToastInvalidImageUrl": "URL de la imagen no válida",
|
||||
"ToastInvalidUrl": "URL no válida",
|
||||
"ToastItemCoverUpdateSuccess": "Portada del elemento actualizada",
|
||||
"ToastItemDetailsUpdateFailed": "Error al actualizar los detalles del elemento",
|
||||
"ToastItemDeletedFailed": "Error al eliminar el elemento",
|
||||
"ToastItemDeletedSuccess": "Elemento borrado",
|
||||
"ToastItemDetailsUpdateSuccess": "Detalles del Elemento Actualizados",
|
||||
"ToastItemMarkedAsFinishedFailed": "Error al marcar como terminado",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Elemento marcado como terminado",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "No se ha podido marcar como no finalizado",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
|
||||
"ToastItemUpdateSuccess": "Elemento actualizado",
|
||||
"ToastLibraryCreateFailed": "Error al crear biblioteca",
|
||||
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada",
|
||||
"ToastLibraryDeleteFailed": "Error al eliminar biblioteca",
|
||||
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
|
||||
"ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
|
||||
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
|
||||
"ToastLibraryUpdateFailed": "Error al actualizar la biblioteca",
|
||||
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada",
|
||||
"ToastMatchAllAuthorsFailed": "No coincide con todos los autores",
|
||||
"ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico",
|
||||
"ToastNameRequired": "Nombre obligatorio",
|
||||
"ToastNewUserCreatedFailed": "Error al crear la cuenta: \"{0}\"",
|
||||
"ToastNewUserCreatedSuccess": "Nueva cuenta creada",
|
||||
"ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca",
|
||||
"ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña",
|
||||
"ToastNewUserTagError": "Debes seleccionar al menos una etiqueta",
|
||||
"ToastNewUserUsernameError": "Introduce un nombre de usuario",
|
||||
"ToastNoUpdatesNecessary": "No es necesario actualizar",
|
||||
"ToastNotificationCreateFailed": "Error al crear notificación",
|
||||
"ToastNotificationDeleteFailed": "Error al borrar la notificación",
|
||||
"ToastNotificationFailedMaximum": "El número máximo de intentos fallidos debe ser ≥ 0",
|
||||
"ToastNotificationQueueMaximum": "La cola de notificación máxima debe ser ≥ 0",
|
||||
"ToastNotificationSettingsUpdateSuccess": "Ajustes de la notificación actualizados",
|
||||
"ToastNotificationTestTriggerFailed": "No se ha podido activar la notificación de prueba",
|
||||
"ToastNotificationTestTriggerSuccess": "Notificación de prueba activada",
|
||||
"ToastNotificationUpdateSuccess": "Notificación actualizada",
|
||||
"ToastPlaylistCreateFailed": "Error al crear la lista de reproducción",
|
||||
"ToastPlaylistCreateSuccess": "Lista de reproducción creada",
|
||||
"ToastPlaylistRemoveSuccess": "Lista de reproducción eliminada",
|
||||
"ToastPlaylistUpdateFailed": "Error al actualizar la lista de reproducción.",
|
||||
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
|
||||
"ToastPodcastCreateFailed": "Error al crear podcast",
|
||||
"ToastPodcastCreateSuccess": "Podcast creado",
|
||||
"ToastPodcastGetFeedFailed": "No se puede obtener el podcast",
|
||||
"ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS",
|
||||
"ToastPodcastNoRssFeed": "El podcast no tiene feed RSS",
|
||||
"ToastProviderCreatedFailed": "Error al añadir el proveedor",
|
||||
"ToastProviderCreatedSuccess": "Nuevo proveedor añadido",
|
||||
"ToastProviderNameAndUrlRequired": "Nombre y Url obligatorios",
|
||||
"ToastProviderRemoveSuccess": "Proveedor eliminado",
|
||||
"ToastRSSFeedCloseFailed": "Error al cerrar fuente RSS",
|
||||
"ToastRSSFeedCloseSuccess": "Fuente RSS cerrada",
|
||||
"ToastRemoveFailed": "Error al eliminar",
|
||||
"ToastRemoveItemFromCollectionFailed": "Error al eliminar el elemento de la colección",
|
||||
"ToastRemoveItemFromCollectionSuccess": "Elemento eliminado de la colección.",
|
||||
"ToastRemoveItemFromCollectionSuccess": "Elemento eliminado de la colección",
|
||||
"ToastRemoveItemsWithIssuesFailed": "Error en la eliminación de artículos de biblioteca incorrectos",
|
||||
"ToastRemoveItemsWithIssuesSuccess": "Se eliminaron artículos de biblioteca incorrectos",
|
||||
"ToastRenameFailed": "Error al cambiar el nombre",
|
||||
"ToastRescanFailed": "Error al volver a escanear para {0}",
|
||||
"ToastRescanRemoved": "Se eliminó el elemento reescaneado",
|
||||
"ToastRescanUpToDate": "Reescaneado del artículo completo, estaba actualizado",
|
||||
"ToastRescanUpdated": "Reescaneado completado, el artículo ha sido actualizado",
|
||||
"ToastScanFailed": "No se pudo escanear el elemento de la biblioteca",
|
||||
"ToastSelectAtLeastOneUser": "Selecciona al menos un usuario",
|
||||
"ToastSendEbookToDeviceFailed": "Error al enviar el ebook al dispositivo",
|
||||
"ToastSendEbookToDeviceSuccess": "Ebook enviado al dispositivo \"{0}\"",
|
||||
"ToastSeriesUpdateFailed": "Error al actualizar la serie",
|
||||
"ToastSeriesUpdateSuccess": "Serie actualizada",
|
||||
"ToastServerSettingsUpdateFailed": "Error al actualizar configuración del servidor",
|
||||
"ToastServerSettingsUpdateSuccess": "Configuración del servidor actualizada",
|
||||
"ToastSessionCloseFailed": "Error al cerrar la sesión",
|
||||
"ToastSessionDeleteFailed": "Error al eliminar sesión",
|
||||
"ToastSessionDeleteSuccess": "Sesión eliminada",
|
||||
"ToastSlugMustChange": "El slug contiene caracteres no válidos",
|
||||
"ToastSlugRequired": "Slug obligatorio",
|
||||
"ToastSocketConnected": "Socket conectado",
|
||||
"ToastSocketDisconnected": "Socket desconectado",
|
||||
"ToastSocketFailedToConnect": "Error al conectar al Socket",
|
||||
"ToastSortingPrefixesEmptyError": "Debe tener por lo menos 1 prefijo para ordenar",
|
||||
"ToastSortingPrefixesUpdateFailed": "Error al actualizar los prefijos de ordenar",
|
||||
"ToastSortingPrefixesUpdateSuccess": "Prefijos de ordenar actualizaron ({0} items)",
|
||||
"ToastTitleRequired": "Título obligatorio",
|
||||
"ToastUnknownError": "Error desconocido",
|
||||
"ToastUnlinkOpenIdFailed": "Error al desvincular el usuario de OpenID",
|
||||
"ToastUnlinkOpenIdSuccess": "Usuario desvinculado de OpenID",
|
||||
"ToastUserDeleteFailed": "Error al eliminar el usuario",
|
||||
"ToastUserDeleteSuccess": "Usuario eliminado"
|
||||
"ToastUserDeleteSuccess": "Usuario eliminado",
|
||||
"ToastUserPasswordChangeSuccess": "Contraseña modificada correctamente",
|
||||
"ToastUserPasswordMismatch": "No coinciden las contraseñas",
|
||||
"ToastUserPasswordMustChange": "La nueva contraseña no puede ser igual que la anterior",
|
||||
"ToastUserRootRequireName": "Debes introducir un nombre de usuario root"
|
||||
}
|
||||
|
||||
+13
-10
@@ -9,6 +9,7 @@
|
||||
"ButtonApply": "Rakenda",
|
||||
"ButtonApplyChapters": "Rakenda peatükid",
|
||||
"ButtonAuthors": "Autorid",
|
||||
"ButtonBack": "Tagasi",
|
||||
"ButtonBrowseForFolder": "Sirvi kausta",
|
||||
"ButtonCancel": "Tühista",
|
||||
"ButtonCancelEncode": "Tühista kodeerimine",
|
||||
@@ -18,6 +19,7 @@
|
||||
"ButtonChooseFiles": "Vali failid",
|
||||
"ButtonClearFilter": "Tühista filter",
|
||||
"ButtonCloseFeed": "Sulge voog",
|
||||
"ButtonCloseSession": "Sulge avatud sessioon",
|
||||
"ButtonCollections": "Kogud",
|
||||
"ButtonConfigureScanner": "Konfigureeri skanner",
|
||||
"ButtonCreate": "Loo",
|
||||
@@ -27,6 +29,7 @@
|
||||
"ButtonEdit": "Muuda",
|
||||
"ButtonEditChapters": "Muuda peatükke",
|
||||
"ButtonEditPodcast": "Muuda podcasti",
|
||||
"ButtonEnable": "Aktiveeri",
|
||||
"ButtonForceReScan": "Sunnitud uuestiskaneerimine",
|
||||
"ButtonFullPath": "Täielik asukoht",
|
||||
"ButtonHide": "Peida",
|
||||
@@ -43,13 +46,18 @@
|
||||
"ButtonMatchAllAuthors": "Sobita kõik autorid",
|
||||
"ButtonMatchBooks": "Sobita raamatud",
|
||||
"ButtonNevermind": "Pole tähtis",
|
||||
"ButtonNext": "Järgmine",
|
||||
"ButtonNextChapter": "Järgmine peatükk",
|
||||
"ButtonNextItemInQueue": "Järgmine kirje järjekorras",
|
||||
"ButtonOk": "Ok",
|
||||
"ButtonOpenFeed": "Ava voog",
|
||||
"ButtonOpenManager": "Ava haldur",
|
||||
"ButtonPause": "Peata",
|
||||
"ButtonPlay": "Mängi",
|
||||
"ButtonPlayAll": "Mängi kõik",
|
||||
"ButtonPlaying": "Mängib",
|
||||
"ButtonPlaylists": "Esitusloendid",
|
||||
"ButtonPrevious": "Eelmine",
|
||||
"ButtonPreviousChapter": "Eelmine peatükk",
|
||||
"ButtonPurgeAllCache": "Tühjenda kogu vahemälu",
|
||||
"ButtonPurgeItemsCache": "Tühjenda esemete vahemälu",
|
||||
@@ -58,6 +66,9 @@
|
||||
"ButtonQuickMatch": "Kiire sobitamine",
|
||||
"ButtonReScan": "Uuestiskaneeri",
|
||||
"ButtonRead": "Loe",
|
||||
"ButtonReadLess": "Loe vähem",
|
||||
"ButtonReadMore": "Loe rohkem",
|
||||
"ButtonRefresh": "Värskenda",
|
||||
"ButtonRemove": "Eemalda",
|
||||
"ButtonRemoveAll": "Eemalda kõik",
|
||||
"ButtonRemoveAllLibraryItems": "Eemalda kõik raamatukogu esemed",
|
||||
@@ -211,7 +222,7 @@
|
||||
"LabelBackupLocation": "Varukoopia asukoht",
|
||||
"LabelBackupsEnableAutomaticBackups": "Luba automaatsed varukoopiad",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Varukoopiad salvestatakse /metadata/backups kausta",
|
||||
"LabelBackupsMaxBackupSize": "Maksimaalne varukoopia suurus (GB-des)",
|
||||
"LabelBackupsMaxBackupSize": "Maksimaalne varukoopia suurus (GB-des) (0 lõpmatu suuruse jaoks)",
|
||||
"LabelBackupsMaxBackupSizeHelp": "Kaitsena valesti seadistamise vastu ebaõnnestuvad varukoopiad, kui need ületavad seadistatud suuruse.",
|
||||
"LabelBackupsNumberToKeep": "Varukoopiate arv, mida hoida",
|
||||
"LabelBackupsNumberToKeepHelp": "Ühel ajal eemaldatakse ainult 1 varukoopia, seega kui teil on juba rohkem varukoopiaid kui siin määratud, peaksite need käsitsi eemaldama.",
|
||||
@@ -449,7 +460,7 @@
|
||||
"LabelSettingsHomePageBookshelfView": "Avaleht kasutage raamatukoguvaadet",
|
||||
"LabelSettingsLibraryBookshelfView": "Raamatukogu kasutamiseks kasutage raamatukoguvaadet",
|
||||
"LabelSettingsParseSubtitles": "Lugege subtiitreid",
|
||||
"LabelSettingsParseSubtitlesHelp": "Eraldage subtiitrid heliraamatu kaustade nimedest.<br>Subtiitrid peavad olema eraldatud \" - \".<br>Näiteks: \"Raamatu pealkiri - Siin on alapealkiri\" alapealkiri on \"Siin on alapealkiri\"",
|
||||
"LabelSettingsParseSubtitlesHelp": "Eraldage subtiitrid heliraamatu kaustade nimedest.<br>Subtiitrid peavad olema eraldatud kasutades \" - \".<br>Näiteks: \"Raamatu pealkiri - Siin on alapealkiri\" alapealkiri on \"Siin on alapealkiri\"",
|
||||
"LabelSettingsPreferMatchedMetadata": "Eelista sobitatud metaandmeid",
|
||||
"LabelSettingsPreferMatchedMetadataHelp": "Sobitatud andmed kirjutavad Kiir Sobitamise kasutamisel üle üksikasjad.",
|
||||
"LabelSettingsSkipMatchingBooksWithASIN": "Jätke ASIN-iga sobituvad raamatud vahele",
|
||||
@@ -682,10 +693,8 @@
|
||||
"PlaceholderNewPlaylist": "Uue esitusloendi nimi",
|
||||
"PlaceholderSearch": "Otsi...",
|
||||
"PlaceholderSearchEpisode": "Otsi episoodi...",
|
||||
"ToastAccountUpdateFailed": "Konto värskendamine ebaõnnestus",
|
||||
"ToastAccountUpdateSuccess": "Konto on värskendatud",
|
||||
"ToastAuthorImageRemoveSuccess": "Autori pilt on eemaldatud",
|
||||
"ToastAuthorUpdateFailed": "Autori värskendamine ebaõnnestus",
|
||||
"ToastAuthorUpdateMerged": "Autor liidetud",
|
||||
"ToastAuthorUpdateSuccess": "Autor värskendatud",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Autor värskendatud (pilti ei leitud)",
|
||||
@@ -701,17 +710,13 @@
|
||||
"ToastBookmarkCreateFailed": "Järjehoidja loomine ebaõnnestus",
|
||||
"ToastBookmarkCreateSuccess": "Järjehoidja lisatud",
|
||||
"ToastBookmarkRemoveSuccess": "Järjehoidja eemaldatud",
|
||||
"ToastBookmarkUpdateFailed": "Järjehoidja värskendamine ebaõnnestus",
|
||||
"ToastBookmarkUpdateSuccess": "Järjehoidja värskendatud",
|
||||
"ToastChaptersHaveErrors": "Peatükkidel on vigu",
|
||||
"ToastChaptersMustHaveTitles": "Peatükkidel peab olema pealkiri",
|
||||
"ToastCollectionItemsRemoveSuccess": "Üksus(ed) eemaldatud kogumist",
|
||||
"ToastCollectionRemoveSuccess": "Kogum eemaldatud",
|
||||
"ToastCollectionUpdateFailed": "Kogumi värskendamine ebaõnnestus",
|
||||
"ToastCollectionUpdateSuccess": "Kogum värskendatud",
|
||||
"ToastItemCoverUpdateFailed": "Üksuse kaane värskendamine ebaõnnestus",
|
||||
"ToastItemCoverUpdateSuccess": "Üksuse kaas värskendatud",
|
||||
"ToastItemDetailsUpdateFailed": "Üksuse üksikasjade värskendamine ebaõnnestus",
|
||||
"ToastItemDetailsUpdateSuccess": "Üksuse üksikasjad värskendatud",
|
||||
"ToastItemMarkedAsFinishedFailed": "Märgistamine kui lõpetatud ebaõnnestus",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Üksus märgitud kui lõpetatud",
|
||||
@@ -723,12 +728,10 @@
|
||||
"ToastLibraryDeleteSuccess": "Raamatukogu kustutatud",
|
||||
"ToastLibraryScanFailedToStart": "Skanneerimine ei käivitunud",
|
||||
"ToastLibraryScanStarted": "Raamatukogu skaneerimine alustatud",
|
||||
"ToastLibraryUpdateFailed": "Raamatukogu värskendamine ebaõnnestus",
|
||||
"ToastLibraryUpdateSuccess": "Raamatukogu \"{0}\" värskendatud",
|
||||
"ToastPlaylistCreateFailed": "Esitusloendi loomine ebaõnnestus",
|
||||
"ToastPlaylistCreateSuccess": "Esitusloend loodud",
|
||||
"ToastPlaylistRemoveSuccess": "Esitusloend eemaldatud",
|
||||
"ToastPlaylistUpdateFailed": "Esitusloendi värskendamine ebaõnnestus",
|
||||
"ToastPlaylistUpdateSuccess": "Esitusloend värskendatud",
|
||||
"ToastPodcastCreateFailed": "Podcasti loomine ebaõnnestus",
|
||||
"ToastPodcastCreateSuccess": "Podcast loodud edukalt",
|
||||
|
||||
+166
-2
@@ -19,6 +19,7 @@
|
||||
"ButtonChooseFiles": "Valitse tiedostot",
|
||||
"ButtonClearFilter": "Poista suodatus",
|
||||
"ButtonCloseFeed": "Sulje syöte",
|
||||
"ButtonCloseSession": "Sulje Avoin Sessio",
|
||||
"ButtonCollections": "Kokoelmat",
|
||||
"ButtonConfigureScanner": "Skannerin asetukset",
|
||||
"ButtonCreate": "Luo",
|
||||
@@ -28,6 +29,9 @@
|
||||
"ButtonEdit": "Muokkaa",
|
||||
"ButtonEditChapters": "Muokkaa lukuja",
|
||||
"ButtonEditPodcast": "Muokkaa podcastia",
|
||||
"ButtonEnable": "Aktivoi",
|
||||
"ButtonFireAndFail": "Laukaise ja epäonnistu",
|
||||
"ButtonFireOnTest": "Laukaise onTest tapahtuma",
|
||||
"ButtonForceReScan": "Pakota uudelleenskannaus",
|
||||
"ButtonFullPath": "Koko polku",
|
||||
"ButtonHide": "Piilota",
|
||||
@@ -46,10 +50,13 @@
|
||||
"ButtonNevermind": "Ei sittenkään",
|
||||
"ButtonNext": "Seuraava",
|
||||
"ButtonNextChapter": "Seuraava luku",
|
||||
"ButtonNextItemInQueue": "Seuraava jonossa",
|
||||
"ButtonOk": "Ok",
|
||||
"ButtonOpenFeed": "Avaa syöte",
|
||||
"ButtonOpenManager": "Avaa hallinta",
|
||||
"ButtonPause": "Pysäytä",
|
||||
"ButtonPlay": "Toista",
|
||||
"ButtonPlayAll": "Toista kaikki",
|
||||
"ButtonPlaying": "Toistetaan",
|
||||
"ButtonPlaylists": "Soittolistat",
|
||||
"ButtonPrevious": "Edellinen",
|
||||
@@ -90,6 +97,7 @@
|
||||
"ButtonStats": "Tilastot",
|
||||
"ButtonSubmit": "Lähetä",
|
||||
"ButtonTest": "Testi",
|
||||
"ButtonUnlinkOpenId": "Poista OpenID linkitys",
|
||||
"ButtonUpload": "Lähetä palvelimelle",
|
||||
"ButtonUploadBackup": "Lähetä varmuuskopio",
|
||||
"ButtonUploadCover": "Lähetä kansikuva",
|
||||
@@ -102,6 +110,7 @@
|
||||
"ErrorUploadFetchMetadataNoResults": "Metadatan haku epäonnistui, yritä päivittää Teoksen nimi ja/tai Tekijä",
|
||||
"ErrorUploadLacksTitle": "Pitää sisältää Nimi",
|
||||
"HeaderAccount": "Tili",
|
||||
"HeaderAddCustomMetadataProvider": "Lisää mukautettu metadata tarjoaja",
|
||||
"HeaderAdvanced": "Edistynyt",
|
||||
"HeaderAppriseNotificationSettings": "Apprise-ilmoitusasetukset",
|
||||
"HeaderAudioTracks": "Ääniraidat",
|
||||
@@ -126,38 +135,60 @@
|
||||
"HeaderEreaderDevices": "E-lukijalaitteet",
|
||||
"HeaderEreaderSettings": "E-lukijan asetukset",
|
||||
"HeaderFiles": "Tiedostot",
|
||||
"HeaderFindChapters": "Etsi kappaleet",
|
||||
"HeaderIgnoredFiles": "Ohitetut tiedostot",
|
||||
"HeaderLastListeningSession": "Edellinen kuuntelukerta",
|
||||
"HeaderLatestEpisodes": "Viimeisimmät jaksot",
|
||||
"HeaderLibraries": "Kirjastot",
|
||||
"HeaderLibraryFiles": "Kirjaston tiedostot",
|
||||
"HeaderLibraryStats": "Kirjaston tilastot",
|
||||
"HeaderListeningSessions": "Kuuntelukerrat",
|
||||
"HeaderListeningStats": "Kuuntelutilastot",
|
||||
"HeaderLogin": "Kirjaudu",
|
||||
"HeaderLogs": "Lokit",
|
||||
"HeaderManageGenres": "Hallitse lajityyppejä",
|
||||
"HeaderManageTags": "Hallitse tageja",
|
||||
"HeaderMetadataToEmbed": "Sisällytettävä metadata",
|
||||
"HeaderNewAccount": "Uusi tili",
|
||||
"HeaderNewLibrary": "Uusi kirjasto",
|
||||
"HeaderNotificationCreate": "Luo ilmoitus",
|
||||
"HeaderNotificationUpdate": "Päivitä ilmoitus",
|
||||
"HeaderNotifications": "Ilmoitukset",
|
||||
"HeaderOpenRSSFeed": "Avaa RSS-syöte",
|
||||
"HeaderOtherFiles": "Muut tiedostot",
|
||||
"HeaderPermissions": "Käyttöoikeudet",
|
||||
"HeaderPlayerQueue": "Soittimen jono",
|
||||
"HeaderPlayerSettings": "Soittimen asetukset",
|
||||
"HeaderPlaylist": "Soittolista",
|
||||
"HeaderPlaylistItems": "Soittolistan kohteet",
|
||||
"HeaderPodcastsToAdd": "Lisättävät podcastit",
|
||||
"HeaderPreviewCover": "Esikatsele kansikuvaa",
|
||||
"HeaderRSSFeedGeneral": "RSS yksityiskohdat",
|
||||
"HeaderRSSFeedIsOpen": "RSS syöte on avoinna",
|
||||
"HeaderRSSFeeds": "RSS syötteet",
|
||||
"HeaderRemoveEpisode": "Poista jakso",
|
||||
"HeaderRemoveEpisodes": "Poista {0} jaksoa",
|
||||
"HeaderSchedule": "Ajoita",
|
||||
"HeaderScheduleLibraryScans": "Ajoita automaattiset kirjastoskannaukset",
|
||||
"HeaderSession": "Istunto",
|
||||
"HeaderSetBackupSchedule": "Aseta varmuuskopiointiaikataulu",
|
||||
"HeaderSettings": "Asetukset",
|
||||
"HeaderSettingsExperimental": "Kokeelliset ominaisuudet",
|
||||
"HeaderSettingsGeneral": "Yleiset",
|
||||
"HeaderSleepTimer": "Uniajastin",
|
||||
"HeaderStatsMinutesListeningChart": "Kuunteluminuutit (viim. 7 pv)",
|
||||
"HeaderStatsRecentSessions": "Viimeaikaiset istunnot",
|
||||
"HeaderStatsTop5Genres": "Top 5 lajityypit",
|
||||
"HeaderTableOfContents": "Sisällysluettelo",
|
||||
"HeaderTools": "Työkalut",
|
||||
"HeaderUpdateAccount": "Päivitä tili",
|
||||
"HeaderUpdateAuthor": "Päivitä kirjailija",
|
||||
"HeaderUpdateLibrary": "Päivitä kirjasto",
|
||||
"HeaderUsers": "Käyttäjät",
|
||||
"HeaderYourStats": "Tilastosi",
|
||||
"LabelAbridged": "Lyhennetty",
|
||||
"LabelAccountType": "Tilin tyyppi",
|
||||
"LabelAccountTypeAdmin": "Järjestelmänvalvoja",
|
||||
"LabelAccountTypeGuest": "Vieras",
|
||||
"LabelAccountTypeUser": "Käyttäjä",
|
||||
"LabelActivity": "Toiminta",
|
||||
@@ -166,22 +197,29 @@
|
||||
"LabelAddToPlaylist": "Lisää soittolistaan",
|
||||
"LabelAddToPlaylistBatch": "Lisää {0} kohdetta soittolistaan",
|
||||
"LabelAddedAt": "Lisätty listalle",
|
||||
"LabelAddedDate": "Lisätty {0}",
|
||||
"LabelAdminUsersOnly": "Vain järjestelmänvalvojat",
|
||||
"LabelAll": "Kaikki",
|
||||
"LabelAllUsers": "Kaikki käyttäjät",
|
||||
"LabelAllUsersExcludingGuests": "Kaikki käyttäjät vieraita lukuun ottamatta",
|
||||
"LabelAllUsersIncludingGuests": "Kaikki käyttäjät mukaan lukien vieraat",
|
||||
"LabelAlreadyInYourLibrary": "Jo kirjastossasi",
|
||||
"LabelAuthor": "Tekijä",
|
||||
"LabelAuthorFirstLast": "Tekijä (Etunimi Sukunimi)",
|
||||
"LabelAuthorLastFirst": "Tekijä (Sukunimi, Etunimi)",
|
||||
"LabelAuthors": "Tekijät",
|
||||
"LabelAutoDownloadEpisodes": "Lataa jaksot automaattisesti",
|
||||
"LabelBackToUser": "Takaisin käyttäjään",
|
||||
"LabelBackupLocation": "Varmuuskopiointipaikka",
|
||||
"LabelBackupsEnableAutomaticBackups": "Ota automaattinen varmuuskopiointi käyttöön",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Varmuuskopiot tallennettu kansioon /metadata/backups",
|
||||
"LabelBackupsMaxBackupSize": "Varmuuskopion enimmäiskoko (Gt) (0 rajaton)",
|
||||
"LabelBackupsNumberToKeep": "Säilytettävien varmuuskopioiden määrä",
|
||||
"LabelBitrate": "Bittinopeus",
|
||||
"LabelBooks": "Kirjat",
|
||||
"LabelButtonText": "Painikkeen teksti",
|
||||
"LabelChangePassword": "Vaihda salasana",
|
||||
"LabelChannels": "Kanavat",
|
||||
"LabelChapters": "Luvut",
|
||||
"LabelClickForMoreInfo": "Napsauta saadaksesi lisätietoja",
|
||||
"LabelClosePlayer": "Sulje soitin",
|
||||
@@ -196,79 +234,205 @@
|
||||
"LabelContinueSeries": "Jatka sarjoja",
|
||||
"LabelCover": "Kansikuva",
|
||||
"LabelCoverImageURL": "Kansikuvan URL-osoite",
|
||||
"LabelCreatedAt": "Luotu",
|
||||
"LabelCurrent": "Nykyinen",
|
||||
"LabelDays": "Päivää",
|
||||
"LabelDescription": "Kuvaus",
|
||||
"LabelDevice": "Laite",
|
||||
"LabelDeviceInfo": "Laitteen tiedot",
|
||||
"LabelDiscover": "Löydä",
|
||||
"LabelDownload": "Lataa",
|
||||
"LabelDownloadNEpisodes": "Lataa {0} jaksoa",
|
||||
"LabelDuration": "Kesto",
|
||||
"LabelDurationComparisonLonger": "({0} pidempi)",
|
||||
"LabelDurationComparisonShorter": "({0} lyhyempi)",
|
||||
"LabelEbook": "E-kirja",
|
||||
"LabelEbooks": "E-kirjat",
|
||||
"LabelEdit": "Muokkaa",
|
||||
"LabelEmail": "Sähköposti",
|
||||
"LabelEmailSettingsTestAddress": "Testiosoite",
|
||||
"LabelEmbeddedCover": "Upotettu kansikuva",
|
||||
"LabelEnable": "Ota käyttöön",
|
||||
"LabelEnd": "Loppu",
|
||||
"LabelEndOfChapter": "Luvun loppu",
|
||||
"LabelEpisode": "Jakso",
|
||||
"LabelEpisodes": "Jaksot",
|
||||
"LabelExample": "Esimerkki",
|
||||
"LabelFeedURL": "Syötteen URL",
|
||||
"LabelFile": "Tiedosto",
|
||||
"LabelFileBirthtime": "Tiedoston syntymäaika",
|
||||
"LabelFileBornDate": "Syntynyt {0}",
|
||||
"LabelFileModified": "Muutettu tiedosto",
|
||||
"LabelFileModifiedDate": "Muokattu {0}",
|
||||
"LabelFilename": "Tiedostonimi",
|
||||
"LabelFindEpisodes": "Etsi jaksoja",
|
||||
"LabelFinished": "Valmis",
|
||||
"LabelFolder": "Kansio",
|
||||
"LabelFolders": "Kansiot",
|
||||
"LabelGenre": "Lajityyppi",
|
||||
"LabelGenres": "Lajityypit",
|
||||
"LabelHost": "Isäntä",
|
||||
"LabelHours": "Tunnit",
|
||||
"LabelInProgress": "Kesken",
|
||||
"LabelIncomplete": "Keskeneräinen",
|
||||
"LabelIntervalEvery12Hours": "12 tunnin välein",
|
||||
"LabelIntervalEvery15Minutes": "15 minuutin välein",
|
||||
"LabelIntervalEvery2Hours": "2 tunnin välein",
|
||||
"LabelIntervalEvery30Minutes": "30 minuutin välein",
|
||||
"LabelIntervalEvery6Hours": "6 tunnin välein",
|
||||
"LabelIntervalEveryDay": "Joka päivä",
|
||||
"LabelIntervalEveryHour": "Joka tunti",
|
||||
"LabelItem": "Kohde",
|
||||
"LabelLanguage": "Kieli",
|
||||
"LabelLanguageDefaultServer": "Palvelimen oletuskieli",
|
||||
"LabelLanguages": "Kielet",
|
||||
"LabelLastBookAdded": "Viimeisin lisätty kirja",
|
||||
"LabelLibrary": "Kirjasto",
|
||||
"LabelLineSpacing": "Riviväli",
|
||||
"LabelListenAgain": "Kuuntele uudelleen",
|
||||
"LabelMediaType": "Mediatyyppi",
|
||||
"LabelMinute": "Minuutti",
|
||||
"LabelMinutes": "Minuutit",
|
||||
"LabelMore": "Lisää",
|
||||
"LabelMoreInfo": "Lisätietoja",
|
||||
"LabelName": "Nimi",
|
||||
"LabelNarrator": "Lukija",
|
||||
"LabelNarrators": "Lukijat",
|
||||
"LabelNew": "Uusi",
|
||||
"LabelNewPassword": "Uusi salasana",
|
||||
"LabelNewestAuthors": "Uusimmat kirjailijat",
|
||||
"LabelNewestEpisodes": "Uusimmat jaksot",
|
||||
"LabelNotStarted": "Ei aloitettu",
|
||||
"LabelPassword": "Salasana",
|
||||
"LabelPath": "Polku",
|
||||
"LabelPermanent": "Pysyvä",
|
||||
"LabelPermissionsAccessAllLibraries": "Käyttöoikeudet kaikkiin kirjastoihin",
|
||||
"LabelPermissionsDelete": "Voi poistaa",
|
||||
"LabelPermissionsDownload": "Voi ladata",
|
||||
"LabelPermissionsUpdate": "Voi päivittää",
|
||||
"LabelPermissionsUpload": "Voi lähettää",
|
||||
"LabelPlaylists": "Soittolistat",
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcasts": "Podcastit",
|
||||
"LabelPort": "Portti",
|
||||
"LabelPublishYear": "Julkaisuvuosi",
|
||||
"LabelPublisher": "Julkaisija",
|
||||
"LabelPublishers": "Julkaisijat",
|
||||
"LabelRSSFeedPreventIndexing": "Estä indeksointi",
|
||||
"LabelRandomly": "Satunnaisesti",
|
||||
"LabelRead": "Lue",
|
||||
"LabelReadAgain": "Lue uudelleen",
|
||||
"LabelRecentSeries": "Viimeisimmät sarjat",
|
||||
"LabelRecentlyAdded": "Viimeeksi lisätyt",
|
||||
"LabelRecommended": "Suositeltu",
|
||||
"LabelRegion": "Alue",
|
||||
"LabelRemoveCover": "Poista kansikuva",
|
||||
"LabelSeason": "Kausi",
|
||||
"LabelSelectAll": "Valitse kaikki",
|
||||
"LabelSelectUsers": "Valitse käyttäjät",
|
||||
"LabelSeries": "Sarja",
|
||||
"LabelSeriesName": "Sarjan nimi",
|
||||
"LabelSetEbookAsPrimary": "Aseta ensisijaiseksi",
|
||||
"LabelSetEbookAsSupplementary": "Aseta täydentäväksi",
|
||||
"LabelSettingsAudiobooksOnly": "Vain äänikirjat",
|
||||
"LabelSettingsChromecastSupport": "Chromecast-tuki",
|
||||
"LabelSettingsExperimentalFeatures": "Kokeelliset ominaisuudet",
|
||||
"LabelSettingsFindCovers": "Etsi kansikuvia",
|
||||
"LabelShare": "Jaa",
|
||||
"LabelShowAll": "Näytä kaikki",
|
||||
"LabelShowSeconds": "Näytä sekunnit",
|
||||
"LabelSize": "Koko",
|
||||
"LabelSleepTimer": "Uniajastin",
|
||||
"LabelStart": "Aloita",
|
||||
"LabelStartTime": "Aloitusaika",
|
||||
"LabelStatsAudioTracks": "Ääniraidat",
|
||||
"LabelStatsBestDay": "Paras päivä",
|
||||
"LabelStatsDailyAverage": "Päivittäinen keskiarvo",
|
||||
"LabelStatsDays": "Päivää",
|
||||
"LabelStatsDaysListened": "Päivää kuunneltu",
|
||||
"LabelStatsHours": "Tunnit",
|
||||
"LabelStatsInARow": "peräjälkeen",
|
||||
"LabelStatsMinutes": "minuuttia",
|
||||
"LabelStatsMinutesListening": "Minuuttia kuunneltu",
|
||||
"LabelStatsWeekListening": "Viikon aikana kuunneltu",
|
||||
"LabelTag": "Tägi",
|
||||
"LabelTags": "Tägit",
|
||||
"LabelTheme": "Teema",
|
||||
"LabelThemeDark": "Tumma",
|
||||
"LabelThemeLight": "Kirkas",
|
||||
"LabelTimeRemaining": "{0} jäljellä",
|
||||
"LabelTitle": "Nimi",
|
||||
"LabelTotalDuration": "Kokonaiskesto",
|
||||
"LabelTracks": "Raidat",
|
||||
"LabelType": "Tyyppi",
|
||||
"LabelUnknown": "Tuntematon",
|
||||
"LabelUpdateCover": "Päivitä kansikuva",
|
||||
"LabelUser": "Käyttäjä",
|
||||
"LabelUsername": "Käyttäjätunnus",
|
||||
"LabelValue": "Arvo",
|
||||
"LabelVersion": "Versio",
|
||||
"LabelYourBookmarks": "Kirjanmerkkisi",
|
||||
"LabelYourProgress": "Edistymisesi",
|
||||
"MessageDownloadingEpisode": "Ladataan jaksoa",
|
||||
"MessageEpisodesQueuedForDownload": "{0} jaksoa on latausjonossa",
|
||||
"MessageFeedURLWillBe": "Syötteen URL tulee olemaan {0}",
|
||||
"MessageFetching": "Haetaan...",
|
||||
"MessageLoading": "Ladataan...",
|
||||
"MessageMarkAsFinished": "Merkitse valmiiksi",
|
||||
"MessageNoBookmarks": "Ei kirjanmerkkejä",
|
||||
"MessageNoChapters": "Ei kappaleita",
|
||||
"MessageNoCoversFound": "Kansikuvia ei löydetty",
|
||||
"MessageNoGenres": "Ei lajityyppejä",
|
||||
"MessageNoItems": "Ei kohteita",
|
||||
"MessageNoItemsFound": "Kohteita ei löytynyt",
|
||||
"MessageNoListeningSessions": "Ei kuunteluistuntoja",
|
||||
"MessageNoPodcastsFound": "Podcasteja ei löytynyt",
|
||||
"MessageNoUpdatesWereNecessary": "Päivityksiä ei tarvittu",
|
||||
"MessageNoUserPlaylists": "Sinulla ei ole soittolistoja",
|
||||
"MessageOr": "tai",
|
||||
"MessageReportBugsAndContribute": "Ilmoita virheistä, toivo ominaisuuksia ja osallistu",
|
||||
"MessageTaskFailed": "Epäonnistunut",
|
||||
"StatsSessions": "istunnot",
|
||||
"ToastAccountUpdateSuccess": "Tili päivitetty",
|
||||
"ToastBookmarkCreateFailed": "Kirjanmerkin luominen epäonnistui",
|
||||
"ToastBookmarkUpdateFailed": "Kirjanmerkin päivittäminen epäonnistui",
|
||||
"ToastCoverUpdateFailed": "Kansikuvan päivitys epäonnistui",
|
||||
"ToastItemCoverUpdateSuccess": "Kohteen kansikuva päivitetty",
|
||||
"ToastItemMarkedAsFinishedFailed": "Valmiiksi merkitseminen epäonnistui",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "Valmiiksi merkitsemisen poisto epäonnistui",
|
||||
"ToastItemUpdateSuccess": "Kohde päivitetty",
|
||||
"ToastLibraryCreateFailed": "Kirjaston luominen epäonnistui",
|
||||
"ToastLibraryCreateSuccess": "Kirjasto \"{0}\" luotu",
|
||||
"ToastLibraryDeleteFailed": "Kirjaston poistaminen epäonnistui",
|
||||
"ToastLibraryDeleteSuccess": "Kirjasto poistettu",
|
||||
"ToastLibraryUpdateSuccess": "Kirjasto \"{0}\" päivitetty",
|
||||
"ToastNewUserCreatedFailed": "Tilin \"{0}\" luominen epäonnistui",
|
||||
"ToastNewUserCreatedSuccess": "Uusi tili luotu",
|
||||
"ToastPlaylistCreateFailed": "Soittolistan luominen epäonnistui",
|
||||
"ToastPlaylistCreateSuccess": "Soittolista luotu",
|
||||
"ToastPlaylistRemoveSuccess": "Soittolista poistettu",
|
||||
"ToastPlaylistUpdateSuccess": "Soittolista päivitetty",
|
||||
"ToastPodcastCreateFailed": "Podcastin luominen epäonnistui",
|
||||
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui"
|
||||
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui",
|
||||
"ToastRSSFeedCloseFailed": "RSS syötteen sulkeminen epäonnistui",
|
||||
"ToastRSSFeedCloseSuccess": "RSS syöte suljettu",
|
||||
"ToastRemoveFailed": "Poistaminen epäonnistui",
|
||||
"ToastRemoveItemFromCollectionFailed": "Kohteen poistaminen kokoelmasta epäonnistui",
|
||||
"ToastRemoveItemFromCollectionSuccess": "Kohde poistettu kokoelmasta",
|
||||
"ToastRenameFailed": "Uudelleennimeäminen epäonnistui",
|
||||
"ToastSelectAtLeastOneUser": "Valitse ainakin yksi käyttäjä",
|
||||
"ToastServerSettingsUpdateSuccess": "Palvelimen asetukset päivitetty",
|
||||
"ToastSessionCloseFailed": "Istunnon sulkeminen epäonnistui",
|
||||
"ToastSessionDeleteFailed": "Istunnon poistaminen epäonnistui",
|
||||
"ToastSessionDeleteSuccess": "Istunto poistettu",
|
||||
"ToastSocketConnected": "Yhteys saatu",
|
||||
"ToastSocketDisconnected": "Yhteys katkaistu",
|
||||
"ToastSocketFailedToConnect": "Yhteyden muodostus epäonnistui",
|
||||
"ToastTitleRequired": "Otsikko on pakollinen",
|
||||
"ToastUnknownError": "Tuntematon virhe",
|
||||
"ToastUserDeleteFailed": "Käyttäjän poisto epäonnistui",
|
||||
"ToastUserDeleteSuccess": "Käyttäjä poistettu",
|
||||
"ToastUserPasswordChangeSuccess": "Salasana vaihdettu onnistuneesti",
|
||||
"ToastUserPasswordMismatch": "Salasanat eivät täsmää",
|
||||
"ToastUserPasswordMustChange": "Uusi salasana ei voi olla sama kuin vanha salasana",
|
||||
"ToastUserRootRequireName": "Pääkäyttäjän nimi on pakollinen"
|
||||
}
|
||||
|
||||
+207
-58
@@ -9,7 +9,7 @@
|
||||
"ButtonApply": "Appliquer",
|
||||
"ButtonApplyChapters": "Appliquer aux chapitres",
|
||||
"ButtonAuthors": "Auteurs",
|
||||
"ButtonBack": "Reculer",
|
||||
"ButtonBack": "Retour",
|
||||
"ButtonBrowseForFolder": "Naviguer vers le répertoire",
|
||||
"ButtonCancel": "Annuler",
|
||||
"ButtonCancelEncode": "Annuler l’encodage",
|
||||
@@ -20,6 +20,7 @@
|
||||
"ButtonClearFilter": "Effacer le filtre",
|
||||
"ButtonCloseFeed": "Fermer le flux",
|
||||
"ButtonCloseSession": "Fermer la session",
|
||||
"ButtonCollections": "Collections",
|
||||
"ButtonConfigureScanner": "Configurer l’analyse",
|
||||
"ButtonCreate": "Créer",
|
||||
"ButtonCreateBackup": "Créer une sauvegarde",
|
||||
@@ -49,10 +50,13 @@
|
||||
"ButtonNevermind": "Non merci",
|
||||
"ButtonNext": "Suivant",
|
||||
"ButtonNextChapter": "Chapitre suivant",
|
||||
"ButtonNextItemInQueue": "Elément suivant de la file d'attente",
|
||||
"ButtonNextItemInQueue": "Élément suivant dans la file d’attente",
|
||||
"ButtonOk": "D’accord",
|
||||
"ButtonOpenFeed": "Ouvrir le flux",
|
||||
"ButtonOpenManager": "Ouvrir le gestionnaire",
|
||||
"ButtonPause": "Pause",
|
||||
"ButtonPlay": "Lire",
|
||||
"ButtonPlayAll": "Lire tout",
|
||||
"ButtonPlaying": "En lecture",
|
||||
"ButtonPlaylists": "Listes de lecture",
|
||||
"ButtonPrevious": "Précédent",
|
||||
@@ -62,6 +66,7 @@
|
||||
"ButtonPurgeItemsCache": "Purger le cache des éléments",
|
||||
"ButtonQueueAddItem": "Ajouter à la liste de lecture",
|
||||
"ButtonQueueRemoveItem": "Supprimer de la liste de lecture",
|
||||
"ButtonQuickEmbed": "Intégration rapide",
|
||||
"ButtonQuickEmbedMetadata": "Ajouter rapidement des métadonnées",
|
||||
"ButtonQuickMatch": "Recherche rapide",
|
||||
"ButtonReScan": "Nouvelle analyse",
|
||||
@@ -94,7 +99,8 @@
|
||||
"ButtonStartMetadataEmbed": "Démarrer les Métadonnées intégrées",
|
||||
"ButtonStats": "Statistiques",
|
||||
"ButtonSubmit": "Soumettre",
|
||||
"ButtonUnlinkOpedId": "Dissocier OpenID",
|
||||
"ButtonTest": "Test",
|
||||
"ButtonUnlinkOpenId": "Dissocier OpenID",
|
||||
"ButtonUpload": "Téléverser",
|
||||
"ButtonUploadBackup": "Téléverser une sauvegarde",
|
||||
"ButtonUploadCover": "Téléverser une couverture",
|
||||
@@ -112,10 +118,12 @@
|
||||
"HeaderAppriseNotificationSettings": "Configuration des notifications Apprise",
|
||||
"HeaderAudioTracks": "Pistes audio",
|
||||
"HeaderAudiobookTools": "Outils de gestion de fichiers de livres audio",
|
||||
"HeaderAuthentication": "Authentification",
|
||||
"HeaderBackups": "Sauvegardes",
|
||||
"HeaderChangePassword": "Modifier le mot de passe",
|
||||
"HeaderChapters": "Chapitres",
|
||||
"HeaderChooseAFolder": "Choisir un dossier",
|
||||
"HeaderCollection": "Collection",
|
||||
"HeaderCollectionItems": "Entrées de la collection",
|
||||
"HeaderCover": "Couverture",
|
||||
"HeaderCurrentDownloads": "Téléchargements en cours",
|
||||
@@ -153,10 +161,12 @@
|
||||
"HeaderNewLibrary": "Nouvelle bibliothèque",
|
||||
"HeaderNotificationCreate": "Créer une notification",
|
||||
"HeaderNotificationUpdate": "Mise à jour de la notification",
|
||||
"HeaderNotifications": "Notifications",
|
||||
"HeaderOpenIDConnectAuthentication": "Authentification via OpenID Connect",
|
||||
"HeaderOpenRSSFeed": "Ouvrir le flux RSS",
|
||||
"HeaderOtherFiles": "Autres fichiers",
|
||||
"HeaderPasswordAuthentication": "Authentification par mot de passe",
|
||||
"HeaderPermissions": "Permissions",
|
||||
"HeaderPlayerQueue": "Liste d’écoute",
|
||||
"HeaderPlayerSettings": "Paramètres du lecteur",
|
||||
"HeaderPlaylist": "Liste de lecture",
|
||||
@@ -171,6 +181,7 @@
|
||||
"HeaderSavedMediaProgress": "Progression de la sauvegarde des médias",
|
||||
"HeaderSchedule": "Programmation",
|
||||
"HeaderScheduleLibraryScans": "Analyse automatique de la bibliothèque",
|
||||
"HeaderSession": "Session",
|
||||
"HeaderSetBackupSchedule": "Activer la sauvegarde automatique",
|
||||
"HeaderSettings": "Paramètres",
|
||||
"HeaderSettingsDisplay": "Affichage",
|
||||
@@ -183,6 +194,7 @@
|
||||
"HeaderStatsMinutesListeningChart": "Minutes d’écoute (7 derniers jours)",
|
||||
"HeaderStatsRecentSessions": "Sessions récentes",
|
||||
"HeaderStatsTop10Authors": "Top 10 Auteurs",
|
||||
"HeaderStatsTop5Genres": "Top 5 Genres",
|
||||
"HeaderTableOfContents": "Table des matières",
|
||||
"HeaderTools": "Outils",
|
||||
"HeaderUpdateAccount": "Mettre à jour le compte",
|
||||
@@ -197,15 +209,16 @@
|
||||
"LabelAbridgedUnchecked": "Intégral (non vérifié)",
|
||||
"LabelAccessibleBy": "Accessible par",
|
||||
"LabelAccountType": "Type de compte",
|
||||
"LabelAccountTypeAdmin": "Admin",
|
||||
"LabelAccountTypeGuest": "Invité",
|
||||
"LabelAccountTypeUser": "Utilisateur",
|
||||
"LabelActivity": "Activité",
|
||||
"LabelAddToCollection": "Ajouter à la collection",
|
||||
"LabelAddToCollectionBatch": "Ajout de {0} livres à la lollection",
|
||||
"LabelAddToCollectionBatch": "Ajout de {0} livres à la collection",
|
||||
"LabelAddToPlaylist": "Ajouter à la liste de lecture",
|
||||
"LabelAddToPlaylistBatch": "{0} éléments ajoutés à la liste de lecture",
|
||||
"LabelAddedAt": "Date d’ajout",
|
||||
"LabelAddedDate": "{0} ajoutés",
|
||||
"LabelAddedDate": "Ajouté le {0}",
|
||||
"LabelAdminUsersOnly": "Administrateurs uniquement",
|
||||
"LabelAll": "Tout",
|
||||
"LabelAllUsers": "Tous les utilisateurs",
|
||||
@@ -213,6 +226,9 @@
|
||||
"LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités",
|
||||
"LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
|
||||
"LabelAppend": "Ajouter",
|
||||
"LabelAudioBitrate": "Débit audio (par exemple 128k)",
|
||||
"LabelAudioChannels": "Canaux audio (1 ou 2)",
|
||||
"LabelAudioCodec": "Codec audio",
|
||||
"LabelAuthor": "Auteur",
|
||||
"LabelAuthorFirstLast": "Auteur (Prénom Nom)",
|
||||
"LabelAuthorLastFirst": "Auteur (Nom, Prénom)",
|
||||
@@ -225,6 +241,7 @@
|
||||
"LabelAutoRegister": "Enregistrement automatique",
|
||||
"LabelAutoRegisterDescription": "Créer automatiquement de nouveaux utilisateurs après la connexion",
|
||||
"LabelBackToUser": "Retour à l’utilisateur",
|
||||
"LabelBackupAudioFiles": "Sauvegarder les fichiers audio",
|
||||
"LabelBackupLocation": "Emplacement de la sauvegarde",
|
||||
"LabelBackupsEnableAutomaticBackups": "Activer les sauvegardes automatiques",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Sauvegardes enregistrées dans /metadata/backups",
|
||||
@@ -232,6 +249,7 @@
|
||||
"LabelBackupsMaxBackupSizeHelp": "Afin de prévenir les mauvaises configuration, la sauvegarde échouera si elle excède la taille limite.",
|
||||
"LabelBackupsNumberToKeep": "Nombre de sauvegardes à conserver",
|
||||
"LabelBackupsNumberToKeepHelp": "Seule une sauvegarde sera supprimée à la fois. Si vous avez déjà plus de sauvegardes à effacer, vous devez les supprimer manuellement.",
|
||||
"LabelBitrate": "Débit binaire",
|
||||
"LabelBooks": "Livres",
|
||||
"LabelButtonText": "Texte du bouton",
|
||||
"LabelByAuthor": "par {0}",
|
||||
@@ -242,8 +260,11 @@
|
||||
"LabelChaptersFound": "chapitres trouvés",
|
||||
"LabelClickForMoreInfo": "Cliquez ici pour plus d’informations",
|
||||
"LabelClosePlayer": "Fermer le lecteur",
|
||||
"LabelCodec": "Codec",
|
||||
"LabelCollapseSeries": "Réduire les séries",
|
||||
"LabelCollapseSubSeries": "Replier les sous-séries",
|
||||
"LabelCollection": "Collection",
|
||||
"LabelCollections": "Collections",
|
||||
"LabelComplete": "Complet",
|
||||
"LabelConfirmPassword": "Confirmer le mot de passe",
|
||||
"LabelContinueListening": "Continuer la lecture",
|
||||
@@ -259,6 +280,7 @@
|
||||
"LabelDatetime": "Date",
|
||||
"LabelDays": "Jours",
|
||||
"LabelDeleteFromFileSystemCheckbox": "Supprimer du système de fichiers (décocher pour ne supprimer que de la base de données)",
|
||||
"LabelDescription": "Description",
|
||||
"LabelDeselectAll": "Tout déselectionner",
|
||||
"LabelDevice": "Appareil",
|
||||
"LabelDeviceInfo": "Détail de l’appareil",
|
||||
@@ -286,6 +308,15 @@
|
||||
"LabelEmailSettingsTestAddress": "Adresse de test",
|
||||
"LabelEmbeddedCover": "Couverture du livre intégrée",
|
||||
"LabelEnable": "Activer",
|
||||
"LabelEncodingBackupLocation": "Une sauvegarde de vos fichiers audio originaux sera stockée dans :",
|
||||
"LabelEncodingChaptersNotEmbedded": "Les chapitres ne sont pas intégrés dans les livres audio multipistes.",
|
||||
"LabelEncodingClearItemCache": "Assurez-vous de purger périodiquement le cache des éléments.",
|
||||
"LabelEncodingFinishedM4B": "Le fichier M4B terminé sera placé dans votre dossier de livre audio à l'adresse suivante :",
|
||||
"LabelEncodingInfoEmbedded": "Les métadonnées seront intégrées dans les pistes audio de votre dossier de livre audio.",
|
||||
"LabelEncodingStartedNavigation": "Une fois la tâche démarrée, vous pouvez quitter cette page.",
|
||||
"LabelEncodingTimeWarning": "L’encodage peut prendre jusqu’à 30 minutes.",
|
||||
"LabelEncodingWarningAdvancedSettings": "Avertissement : ne mettez pas à jour ces paramètres à moins que vous ne soyez familier avec les options d'encodage « ffmpeg ».",
|
||||
"LabelEncodingWatcherDisabled": "Si l'observateur est désactivé, vous devrez ensuite réanalyser ce livre audio.",
|
||||
"LabelEnd": "Fin",
|
||||
"LabelEndOfChapter": "Fin du chapitre",
|
||||
"LabelEpisode": "Épisode",
|
||||
@@ -303,8 +334,9 @@
|
||||
"LabelFetchingMetadata": "Récupération des métadonnées",
|
||||
"LabelFile": "Fichier",
|
||||
"LabelFileBirthtime": "Création du fichier",
|
||||
"LabelFileBornDate": "Créé le {0}",
|
||||
"LabelFileModified": "Modification du fichier",
|
||||
"LabelFileModifiedDate": "{0} modifiés",
|
||||
"LabelFileModifiedDate": "Modifié le {0}",
|
||||
"LabelFilename": "Nom de fichier",
|
||||
"LabelFilterByUser": "Filtrer par utilisateur",
|
||||
"LabelFindEpisodes": "Trouver des épisodes",
|
||||
@@ -317,6 +349,9 @@
|
||||
"LabelFontItalic": "Italique",
|
||||
"LabelFontScale": "Taille de la police de caractère",
|
||||
"LabelFontStrikethrough": "Barrer",
|
||||
"LabelFormat": "Format",
|
||||
"LabelGenre": "Genre",
|
||||
"LabelGenres": "Genres",
|
||||
"LabelHardDeleteFile": "Suppression du fichier",
|
||||
"LabelHasEbook": "A un livre numérique",
|
||||
"LabelHasSupplementaryEbook": "A un livre numérique supplémentaire",
|
||||
@@ -363,6 +398,9 @@
|
||||
"LabelLimit": "Limite",
|
||||
"LabelLineSpacing": "Espacement des lignes",
|
||||
"LabelListenAgain": "Écouter à nouveau",
|
||||
"LabelLogLevelDebug": "Débogage",
|
||||
"LabelLogLevelInfo": "Info",
|
||||
"LabelLogLevelWarn": "Warn",
|
||||
"LabelLookForNewEpisodesAfterDate": "Rechercher les nouveaux épisodes après cette date",
|
||||
"LabelLowestPriority": "Priorité la plus basse",
|
||||
"LabelMatchExistingUsersBy": "Correspondance avec les utilisateurs existants",
|
||||
@@ -373,6 +411,8 @@
|
||||
"LabelMetaTags": "Balises de métadonnée",
|
||||
"LabelMetadataOrderOfPrecedenceDescription": "Les sources de métadonnées ayant une priorité plus élevée auront la priorité sur celles ayant une priorité moins élevée",
|
||||
"LabelMetadataProvider": "Fournisseur de métadonnées",
|
||||
"LabelMinute": "Minute",
|
||||
"LabelMinutes": "Minutes",
|
||||
"LabelMissing": "Manquant",
|
||||
"LabelMissingEbook": "Ne possède aucun livre numérique",
|
||||
"LabelMissingSupplementaryEbook": "Ne possède aucun livre numérique supplémentaire",
|
||||
@@ -393,12 +433,13 @@
|
||||
"LabelNoEpisodesSelected": "Aucun épisode sélectionné",
|
||||
"LabelNotFinished": "Non terminé",
|
||||
"LabelNotStarted": "Pas commencé",
|
||||
"LabelNotes": "Notes",
|
||||
"LabelNotificationAppriseURL": "URL(s) d’Apprise",
|
||||
"LabelNotificationAvailableVariables": "Variables disponibles",
|
||||
"LabelNotificationBodyTemplate": "Modèle de message",
|
||||
"LabelNotificationEvent": "Evènement de Notification",
|
||||
"LabelNotificationTitleTemplate": "Modèle de titre",
|
||||
"LabelNotificationsMaxFailedAttempts": "Nombres de tentatives d’envoi",
|
||||
"LabelNotificationsMaxFailedAttempts": "Nombre maximal de tentatives échouées atteint",
|
||||
"LabelNotificationsMaxFailedAttemptsHelp": "La notification est abandonnée une fois ce seuil atteint",
|
||||
"LabelNotificationsMaxQueueSize": "Nombres de notifications maximum à mettre en attente",
|
||||
"LabelNotificationsMaxQueueSizeHelp": "La limite de notification est de un évènement par seconde. Les notifications seront ignorées si la file d’attente est à son maximum. Cela empêche un flot trop important.",
|
||||
@@ -411,6 +452,7 @@
|
||||
"LabelOverwrite": "Écraser",
|
||||
"LabelPassword": "Mot de passe",
|
||||
"LabelPath": "Chemin",
|
||||
"LabelPermanent": "Permanent",
|
||||
"LabelPermissionsAccessAllLibraries": "Peut accéder à toutes les bibliothèque",
|
||||
"LabelPermissionsAccessAllTags": "Peut accéder à toutes les étiquettes",
|
||||
"LabelPermissionsAccessExplicitContent": "Peut accéder au contenu restreint",
|
||||
@@ -423,8 +465,11 @@
|
||||
"LabelPlayMethod": "Méthode d’écoute",
|
||||
"LabelPlayerChapterNumberMarker": "{0} sur {1}",
|
||||
"LabelPlaylists": "Listes de lecture",
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcastSearchRegion": "Région de recherche de podcasts",
|
||||
"LabelPodcastType": "Type de Podcast",
|
||||
"LabelPodcasts": "Podcasts",
|
||||
"LabelPort": "Port",
|
||||
"LabelPrefixesToIgnore": "Préfixes à Ignorer (Insensible à la Casse)",
|
||||
"LabelPreventIndexing": "Empêcher l’indexation de votre flux par les bases de données iTunes et Google podcast",
|
||||
"LabelPrimaryEbook": "Premier livre numérique",
|
||||
@@ -433,14 +478,16 @@
|
||||
"LabelProviderAuthorizationValue": "Valeur de l’en-tête d’autorisation",
|
||||
"LabelPubDate": "Date de publication",
|
||||
"LabelPublishYear": "Année de publication",
|
||||
"LabelPublishedDate": "{0} publiés",
|
||||
"LabelPublishedDate": "Publié en {0}",
|
||||
"LabelPublishedDecade": "Décennie de publication",
|
||||
"LabelPublishedDecades": "Décennies de publication",
|
||||
"LabelPublisher": "Éditeur",
|
||||
"LabelPublishers": "Éditeurs",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Courriel personnalisée du propriétaire",
|
||||
"LabelRSSFeedCustomOwnerName": "Nom propriétaire personnalisé",
|
||||
"LabelRSSFeedOpen": "Flux RSS ouvert",
|
||||
"LabelRSSFeedPreventIndexing": "Empêcher l’indexation",
|
||||
"LabelRSSFeedSlug": "Balise URL du flux RSS",
|
||||
"LabelRSSFeedSlug": "Identifiant d’URL du flux RSS",
|
||||
"LabelRSSFeedURL": "Adresse du flux RSS",
|
||||
"LabelRandomly": "Au hasard",
|
||||
"LabelReAddSeriesToContinueListening": "Ajouter à nouveau la série pour continuer à l’écouter",
|
||||
@@ -461,13 +508,14 @@
|
||||
"LabelSeason": "Saison",
|
||||
"LabelSelectAll": "Tout sélectionner",
|
||||
"LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
|
||||
"LabelSelectEpisodesShowing": "Sélectionner {0} episode(s) en cours",
|
||||
"LabelSelectEpisodesShowing": "Sélectionner {0} épisode(s) en cours",
|
||||
"LabelSelectUsers": "Sélectionner les utilisateurs",
|
||||
"LabelSendEbookToDevice": "Envoyer le livre numérique à…",
|
||||
"LabelSequence": "Séquence",
|
||||
"LabelSeries": "Séries",
|
||||
"LabelSeriesName": "Nom de la série",
|
||||
"LabelSeriesProgress": "Progression de séries",
|
||||
"LabelServerLogLevel": "Niveau de journalisation du serveur",
|
||||
"LabelServerYearReview": "Bilan de l’année du serveur ({0})",
|
||||
"LabelSetEbookAsPrimary": "Définir comme principale",
|
||||
"LabelSetEbookAsSupplementary": "Définir comme supplémentaire",
|
||||
@@ -517,7 +565,7 @@
|
||||
"LabelShowSubtitles": "Afficher les sous-titres",
|
||||
"LabelSize": "Taille",
|
||||
"LabelSleepTimer": "Minuterie de mise en veille",
|
||||
"LabelSlug": "Balise",
|
||||
"LabelSlug": "Identifiant d’URL",
|
||||
"LabelStart": "Démarrer",
|
||||
"LabelStartTime": "Heure de démarrage",
|
||||
"LabelStarted": "Démarré",
|
||||
@@ -532,6 +580,7 @@
|
||||
"LabelStatsInARow": "d’affilée(s)",
|
||||
"LabelStatsItemsFinished": "Éléments terminés",
|
||||
"LabelStatsItemsInLibrary": "Éléments dans la bibliothèque",
|
||||
"LabelStatsMinutes": "minutes",
|
||||
"LabelStatsMinutesListening": "Minutes d’écoute",
|
||||
"LabelStatsOverallDays": "Nombre total de jours",
|
||||
"LabelStatsOverallHours": "Nombre total d’heures",
|
||||
@@ -552,6 +601,7 @@
|
||||
"LabelThemeLight": "Clair",
|
||||
"LabelTimeBase": "Base de temps",
|
||||
"LabelTimeDurationXHours": "{0} heures",
|
||||
"LabelTimeDurationXMinutes": "{0} minutes",
|
||||
"LabelTimeDurationXSeconds": "{0} secondes",
|
||||
"LabelTimeInMinutes": "Temps en minutes",
|
||||
"LabelTimeListened": "Temps d’écoute",
|
||||
@@ -561,6 +611,7 @@
|
||||
"LabelTitle": "Titre",
|
||||
"LabelToolsEmbedMetadata": "Métadonnées intégrées",
|
||||
"LabelToolsEmbedMetadataDescription": "Intègre les métadonnées au fichier audio avec la couverture et les chapitres.",
|
||||
"LabelToolsM4bEncoder": "Encodeur M4B",
|
||||
"LabelToolsMakeM4b": "Créer un fichier livre audio M4B",
|
||||
"LabelToolsMakeM4bDescription": "Générer un fichier de livre audio .M4B avec des métadonnées intégrées, une image de couverture et des chapitres.",
|
||||
"LabelToolsSplitM4b": "Scinde le fichier M4B en fichiers MP3",
|
||||
@@ -573,6 +624,7 @@
|
||||
"LabelTracksMultiTrack": "Piste multiple",
|
||||
"LabelTracksNone": "Aucune piste",
|
||||
"LabelTracksSingleTrack": "Piste simple",
|
||||
"LabelType": "Type",
|
||||
"LabelUnabridged": "Version intégrale",
|
||||
"LabelUndo": "Annuler",
|
||||
"LabelUnknown": "Inconnu",
|
||||
@@ -585,15 +637,18 @@
|
||||
"LabelUploaderDragAndDrop": "Glisser et déposer des fichiers ou dossiers",
|
||||
"LabelUploaderDropFiles": "Déposer des fichiers",
|
||||
"LabelUploaderItemFetchMetadataHelp": "Récupérer automatiquement le titre, l’auteur et la série",
|
||||
"LabelUseAdvancedOptions": "Utiliser les options avancées",
|
||||
"LabelUseChapterTrack": "Utiliser la piste du chapitre",
|
||||
"LabelUseFullTrack": "Utiliser la piste complète",
|
||||
"LabelUser": "Utilisateur",
|
||||
"LabelUsername": "Nom d’utilisateur",
|
||||
"LabelValue": "Valeur",
|
||||
"LabelVersion": "Version",
|
||||
"LabelViewBookmarks": "Afficher les favoris",
|
||||
"LabelViewChapters": "Afficher les chapitres",
|
||||
"LabelViewPlayerSettings": "Afficher les paramètres du lecteur",
|
||||
"LabelViewQueue": "Afficher la liste de lecture",
|
||||
"LabelVolume": "Volume",
|
||||
"LabelWeekdaysToRun": "Jours de la semaine à exécuter",
|
||||
"LabelXBooks": "{0} livres",
|
||||
"LabelXItems": "{0} éléments",
|
||||
@@ -621,51 +676,53 @@
|
||||
"MessageChapterErrorStartLtPrev": "Horodatage invalide car il doit débuter au moins après le précédent chapitre",
|
||||
"MessageChapterStartIsAfter": "Le premier chapitre est situé au début de votre livre audio",
|
||||
"MessageCheckingCron": "Vérification du cron…",
|
||||
"MessageConfirmCloseFeed": "Êtes-vous sûr de vouloir fermer ce flux ?",
|
||||
"MessageConfirmDeleteBackup": "Êtes-vous sûr de vouloir supprimer la sauvegarde de « {0} » ?",
|
||||
"MessageConfirmDeleteDevice": "Êtes-vous sûr de vouloir supprimer la liseuse « {0} » ?",
|
||||
"MessageConfirmCloseFeed": "Êtes-vous sûr·e de vouloir fermer ce flux ?",
|
||||
"MessageConfirmDeleteBackup": "Êtes-vous sûr·e de vouloir supprimer la sauvegarde de « {0} » ?",
|
||||
"MessageConfirmDeleteDevice": "Êtes-vous sûr·e de vouloir supprimer la liseuse « {0} » ?",
|
||||
"MessageConfirmDeleteFile": "Cela supprimera le fichier de votre système de fichiers. Êtes-vous sûr ?",
|
||||
"MessageConfirmDeleteLibrary": "Êtes-vous sûr de vouloir supprimer définitivement la bibliothèque « {0} » ?",
|
||||
"MessageConfirmDeleteLibrary": "Êtes-vous sûr·e de vouloir supprimer définitivement la bibliothèque « {0} » ?",
|
||||
"MessageConfirmDeleteLibraryItem": "Cette opération supprimera l’élément de la base de données et de votre système de fichiers. Êtes-vous sûr ?",
|
||||
"MessageConfirmDeleteLibraryItems": "Cette opération supprimera {0} éléments de la base de données et de votre système de fichiers. Êtes-vous sûr ?",
|
||||
"MessageConfirmDeleteMetadataProvider": "Êtes-vous sûr de vouloir supprimer le fournisseur de métadonnées personnalisées « {0} » ?",
|
||||
"MessageConfirmDeleteNotification": "Êtes-vous sûr de vouloir supprimer cette notification ?",
|
||||
"MessageConfirmDeleteSession": "Êtes-vous sûr de vouloir supprimer cette session ?",
|
||||
"MessageConfirmForceReScan": "Êtes-vous sûr de vouloir lancer une analyse forcée ?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr de marquer tous les épisodes comme terminés ?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr de vouloir marquer tous les épisodes comme non terminés ?",
|
||||
"MessageConfirmMarkItemFinished": "Êtes-vous sûr de vouloir marquer \"{0}\" comme terminé ?",
|
||||
"MessageConfirmMarkItemNotFinished": "Êtes-vous sûr de vouloir marquer \"{0}\" comme non terminé ?",
|
||||
"MessageConfirmMarkSeriesFinished": "Êtes-vous sûr de vouloir marquer tous les livres de cette série comme terminées ?",
|
||||
"MessageConfirmMarkSeriesNotFinished": "Êtes-vous sûr de vouloir marquer tous les livres de cette série comme non terminés ?",
|
||||
"MessageConfirmDeleteMetadataProvider": "Êtes-vous sûr·e de vouloir supprimer le fournisseur de métadonnées personnalisées « {0} » ?",
|
||||
"MessageConfirmDeleteNotification": "Êtes-vous sûr·e de vouloir supprimer cette notification ?",
|
||||
"MessageConfirmDeleteSession": "Êtes-vous sûr·e de vouloir supprimer cette session ?",
|
||||
"MessageConfirmEmbedMetadataInAudioFiles": "Souhaitez-vous vraiment intégrer des métadonnées dans {0} fichiers audio ?",
|
||||
"MessageConfirmForceReScan": "Êtes-vous sûr·e de vouloir lancer une analyse forcée ?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr·e de marquer tous les épisodes comme terminés ?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr·e de vouloir marquer tous les épisodes comme non terminés ?",
|
||||
"MessageConfirmMarkItemFinished": "Êtes-vous sûr·e de vouloir marquer {0} comme terminé ?",
|
||||
"MessageConfirmMarkItemNotFinished": "Êtes-vous sûr·e de vouloir marquer {0} comme non terminé ?",
|
||||
"MessageConfirmMarkSeriesFinished": "Êtes-vous sûr·e de vouloir marquer tous les livres de cette série comme terminées ?",
|
||||
"MessageConfirmMarkSeriesNotFinished": "Êtes-vous sûr·e de vouloir marquer tous les livres de cette série comme non terminés ?",
|
||||
"MessageConfirmNotificationTestTrigger": "Déclencher cette notification avec des données de test ?",
|
||||
"MessageConfirmPurgeCache": "La purge du cache supprimera l’intégralité du répertoire à <code>/metadata/cache</code>.<br /><br />Êtes-vous sûr de vouloir supprimer le répertoire de cache ?",
|
||||
"MessageConfirmPurgeCache": "La purge du cache supprimera l’intégralité du répertoire à <code>/metadata/cache</code>.<br /><br />Êtes-vous sûr·e de vouloir supprimer le répertoire de cache ?",
|
||||
"MessageConfirmPurgeItemsCache": "Purger le cache des éléments supprimera l'ensemble du répertoire <code>/metadata/cache/items</code>.<br />Êtes-vous sûr ?",
|
||||
"MessageConfirmQuickEmbed": "Attention ! L'intégration rapide ne permet pas de sauvegarder vos fichiers audio. Assurez-vous d’avoir effectuer une sauvegarde de vos fichiers audio.<br><br>Souhaitez-vous continuer ?",
|
||||
"MessageConfirmReScanLibraryItems": "Êtes-vous sûr de vouloir re-analyser {0} éléments ?",
|
||||
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr de vouloir supprimer tous les chapitres ?",
|
||||
"MessageConfirmRemoveAuthor": "Êtes-vous sûr de vouloir supprimer l’auteur « {0} » ?",
|
||||
"MessageConfirmRemoveCollection": "Êtes-vous sûr de vouloir supprimer la collection « {0} » ?",
|
||||
"MessageConfirmRemoveEpisode": "Êtes-vous sûr de vouloir supprimer l’épisode « {0} » ?",
|
||||
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr de vouloir supprimer {0} épisodes ?",
|
||||
"MessageConfirmRemoveListeningSessions": "Êtes-vous sûr de vouloir supprimer {0} sessions d’écoute ?",
|
||||
"MessageConfirmRemoveNarrator": "Êtes-vous sûr de vouloir supprimer le narrateur « {0} » ?",
|
||||
"MessageConfirmRemovePlaylist": "Êtes-vous sûr de vouloir supprimer la liste de lecture « {0} » ?",
|
||||
"MessageConfirmRenameGenre": "Êtes-vous sûr de vouloir renommer le genre « {0} » en « {1} » pour tous les éléments ?",
|
||||
"MessageConfirmReScanLibraryItems": "Êtes-vous sûr·e de vouloir réanalyser {0} éléments ?",
|
||||
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr·e de vouloir supprimer tous les chapitres ?",
|
||||
"MessageConfirmRemoveAuthor": "Êtes-vous sûr·e de vouloir supprimer l’auteur « {0} » ?",
|
||||
"MessageConfirmRemoveCollection": "Êtes-vous sûr·e de vouloir supprimer la collection « {0} » ?",
|
||||
"MessageConfirmRemoveEpisode": "Êtes-vous sûr·e de vouloir supprimer l’épisode « {0} » ?",
|
||||
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr·e de vouloir supprimer {0} épisodes ?",
|
||||
"MessageConfirmRemoveListeningSessions": "Êtes-vous sûr·e de vouloir supprimer {0} sessions d’écoute ?",
|
||||
"MessageConfirmRemoveNarrator": "Êtes-vous sûr·e de vouloir supprimer le narrateur « {0} » ?",
|
||||
"MessageConfirmRemovePlaylist": "Êtes-vous sûr·e de vouloir supprimer la liste de lecture « {0} » ?",
|
||||
"MessageConfirmRenameGenre": "Êtes-vous sûr·e de vouloir renommer le genre « {0} » en « {1} » pour tous les éléments ?",
|
||||
"MessageConfirmRenameGenreMergeNote": "Information : ce genre existe déjà et sera fusionné.",
|
||||
"MessageConfirmRenameGenreWarning": "Attention ! Un genre similaire avec une casse différente existe déjà « {0} ».",
|
||||
"MessageConfirmRenameTag": "Êtes-vous sûr de vouloir renommer l’étiquette « {0} » en « {1} » pour tous les éléments ?",
|
||||
"MessageConfirmRenameTag": "Êtes-vous sûr·e de vouloir renommer l’étiquette « {0} » en « {1} » pour tous les éléments ?",
|
||||
"MessageConfirmRenameTagMergeNote": "Information : Cette étiquette existe déjà et sera fusionnée.",
|
||||
"MessageConfirmRenameTagWarning": "Attention ! Une étiquette similaire avec une casse différente existe déjà « {0} ».",
|
||||
"MessageConfirmResetProgress": "Êtes-vous sûr de vouloir réinitialiser votre progression ?",
|
||||
"MessageConfirmSendEbookToDevice": "Êtes-vous sûr de vouloir envoyer {0} livre numérique « {1} » à l'appareil « {2} » ?",
|
||||
"MessageConfirmUnlinkOpenId": "Êtes-vous sûr de vouloir dissocier cet utilisateur d’OpenID ?",
|
||||
"MessageConfirmResetProgress": "Êtes-vous sûr·e de vouloir réinitialiser votre progression ?",
|
||||
"MessageConfirmSendEbookToDevice": "Êtes-vous sûr·e de vouloir envoyer {0} livre numérique « {1} » à l'appareil « {2} » ?",
|
||||
"MessageConfirmUnlinkOpenId": "Êtes-vous sûr·e de vouloir dissocier cet utilisateur d’OpenID ?",
|
||||
"MessageDownloadingEpisode": "Téléchargement de l’épisode",
|
||||
"MessageDragFilesIntoTrackOrder": "Faites glisser les fichiers dans l’ordre correct des pistes",
|
||||
"MessageEmbedFailed": "Échec de l’intégration !",
|
||||
"MessageEmbedFinished": "Intégration terminée !",
|
||||
"MessageEmbedQueue": "En file d'attente pour l'intégration des métadonnées ({0} dans la file d'attente)",
|
||||
"MessageEpisodesQueuedForDownload": "{0} épisode(s) mis en file pour téléchargement",
|
||||
"MessageEreaderDevices": "Pour garantir la livraison des livres électroniques, vous devrez peut-être ajouter le courriel ci-dessus comme expéditeur valide pour chaque appareil répertorié ci-dessous.",
|
||||
"MessageEreaderDevices": "Pour garantir l’envoi des livres électroniques, vous devrez peut-être ajouter le courriel ci-dessus comme expéditeur valide pour chaque appareil répertorié ci-dessous.",
|
||||
"MessageFeedURLWillBe": "L’URL du flux sera {0}",
|
||||
"MessageFetching": "Récupération…",
|
||||
"MessageForceReScanDescription": "analysera de nouveau tous les fichiers. Les étiquettes ID3 des fichiers audio, les fichiers OPF et les fichiers texte seront analysés comme s’ils étaient nouveaux.",
|
||||
@@ -678,7 +735,7 @@
|
||||
"MessageLoading": "Chargement…",
|
||||
"MessageLoadingFolders": "Chargement des dossiers…",
|
||||
"MessageLogsDescription": "Les journaux sont stockés dans <code>/metadata/logs</code> sous forme de fichiers JSON. Les journaux d’incidents sont stockés dans <code>/metadata/logs/crash_logs.txt</code>.",
|
||||
"MessageM4BFailed": "M4B a échoué !",
|
||||
"MessageM4BFailed": "Échec de la conversion en M4B !",
|
||||
"MessageM4BFinished": "M4B terminé !",
|
||||
"MessageMapChapterTitles": "Faire correspondre les titres de chapitres avec ceux de vos livres audio existants sans ajuster les horodatages",
|
||||
"MessageMarkAllEpisodesFinished": "Marquer tous les épisodes terminés",
|
||||
@@ -708,6 +765,7 @@
|
||||
"MessageNoLogs": "Aucun journaux",
|
||||
"MessageNoMediaProgress": "Aucun média en cours",
|
||||
"MessageNoNotifications": "Aucune notification",
|
||||
"MessageNoPodcastFeed": "Podcast invalide : pas de flux",
|
||||
"MessageNoPodcastsFound": "Aucun podcast trouvé",
|
||||
"MessageNoResults": "Aucun résultat",
|
||||
"MessageNoSearchResultsFor": "Aucun résultat pour la recherche « {0} »",
|
||||
@@ -724,14 +782,17 @@
|
||||
"MessagePlaylistCreateFromCollection": "Créer une liste de lecture depuis la collection",
|
||||
"MessagePleaseWait": "Merci de patienter…",
|
||||
"MessagePodcastHasNoRSSFeedForMatching": "Le Podcast n’a pas d’URL de flux RSS à utiliser pour la correspondance",
|
||||
"MessagePodcastSearchField": "Saisissez le terme de recherche ou l'URL du flux RSS",
|
||||
"MessageQuickEmbedInProgress": "Intégration rapide en cours",
|
||||
"MessageQuickEmbedQueue": "En file d'attente pour une intégration rapide ({0} dans la file d'attente)",
|
||||
"MessageQuickMatchDescription": "Renseigne les détails manquants ainsi que la couverture avec la première correspondance de « {0} ». N’écrase pas les données présentes à moins que le paramètre « Préférer les Métadonnées par correspondance » soit activé.",
|
||||
"MessageRemoveChapter": "Supprimer le chapitre",
|
||||
"MessageRemoveEpisodes": "Suppression de {0} épisode(s)",
|
||||
"MessageRemoveFromPlayerQueue": "Supprimer de la liste d’écoute",
|
||||
"MessageRemoveUserWarning": "Êtes-vous sûr de vouloir supprimer définitivement l’utilisateur « {0} » ?",
|
||||
"MessageRemoveUserWarning": "Êtes-vous sûr·e de vouloir supprimer définitivement l’utilisateur « {0} » ?",
|
||||
"MessageReportBugsAndContribute": "Signalez des anomalies, demandez des fonctionnalités et contribuez sur",
|
||||
"MessageResetChaptersConfirm": "Êtes-vous sûr de vouloir réinitialiser les chapitres et annuler les changements effectués ?",
|
||||
"MessageRestoreBackupConfirm": "Êtes-vous sûr de vouloir restaurer la sauvegarde créée le",
|
||||
"MessageResetChaptersConfirm": "Êtes-vous sûr·e de vouloir réinitialiser les chapitres et annuler les changements effectués ?",
|
||||
"MessageRestoreBackupConfirm": "Êtes-vous sûr·e de vouloir restaurer la sauvegarde créée le",
|
||||
"MessageRestoreBackupWarning": "Restaurer la sauvegarde écrasera la base de donnée située dans le dossier /config ainsi que les images sur /metadata/items et /metadata/authors.<br><br>Les sauvegardes ne touchent pas aux fichiers de la bibliothèque. Si vous avez activé le paramètre pour sauvegarder les métadonnées et les images de couverture dans le même dossier que les fichiers, ceux-ci ne ni sauvegardés, ni écrasés lors de la restauration.<br><br>Tous les clients utilisant votre serveur seront automatiquement mis à jour.",
|
||||
"MessageSearchResultsFor": "Résultats de recherche pour",
|
||||
"MessageSelected": "{0} sélectionnés",
|
||||
@@ -741,6 +802,41 @@
|
||||
"MessageShareExpiresIn": "Expire dans {0}",
|
||||
"MessageShareURLWillBe": "L’adresse de partage sera <strong>{0}</strong>",
|
||||
"MessageStartPlaybackAtTime": "Démarrer la lecture pour « {0} » à {1} ?",
|
||||
"MessageTaskAudioFileNotWritable": "Le fichier audio « {0} » n’est pas accessible en écriture",
|
||||
"MessageTaskCanceledByUser": "Tâche annulée par l’utilisateur",
|
||||
"MessageTaskDownloadingEpisodeDescription": "Téléchargement de l'épisode « {0} »",
|
||||
"MessageTaskEmbeddingMetadata": "Intégration de métadonnées",
|
||||
"MessageTaskEmbeddingMetadataDescription": "Intégration de métadonnées dans le livre audio « {0} »",
|
||||
"MessageTaskEncodingM4b": "Encodage M4B",
|
||||
"MessageTaskEncodingM4bDescription": "Encodage du livre audio « {0} » dans un seul fichier M4B",
|
||||
"MessageTaskFailed": "Échec",
|
||||
"MessageTaskFailedToBackupAudioFile": "Échec de la sauvegarde du fichier audio « {0} »",
|
||||
"MessageTaskFailedToCreateCacheDirectory": "Échec de la création du répertoire de cache",
|
||||
"MessageTaskFailedToEmbedMetadataInFile": "Échec de l'intégration des métadonnées dans le fichier « {0} »",
|
||||
"MessageTaskFailedToMergeAudioFiles": "Échec de la fusion des fichiers audio",
|
||||
"MessageTaskFailedToMoveM4bFile": "Échec du déplacement du fichier M4B",
|
||||
"MessageTaskFailedToWriteMetadataFile": "Échec de l’écriture du fichier de métadonnées",
|
||||
"MessageTaskMatchingBooksInLibrary": "Livres correspondants dans la bibliothèque « {0} »",
|
||||
"MessageTaskNoFilesToScan": "Aucun fichier à analyser",
|
||||
"MessageTaskOpmlImport": "Importation OPML",
|
||||
"MessageTaskOpmlImportDescription": "Création de podcasts à partir de {0} flux RSS",
|
||||
"MessageTaskOpmlImportFeed": "Flux d’importation OPML",
|
||||
"MessageTaskOpmlImportFeedDescription": "Importation du flux RSS « {0} »",
|
||||
"MessageTaskOpmlImportFeedFailed": "Échec de l’obtention du flux de podcast",
|
||||
"MessageTaskOpmlImportFeedPodcastDescription": "Création du podcast « {0} »",
|
||||
"MessageTaskOpmlImportFeedPodcastExists": "Le podcast existe déjà à cet emplacement",
|
||||
"MessageTaskOpmlImportFeedPodcastFailed": "Échec de la création du podcast",
|
||||
"MessageTaskOpmlImportFinished": "Ajout de {0} podcasts",
|
||||
"MessageTaskOpmlParseFailed": "Échec de l'analyse du fichier OPML",
|
||||
"MessageTaskOpmlParseFastFail": "Balise <opml> de fichier OPML non valide introuvable OU une balise <outline> n’a pas été trouvée",
|
||||
"MessageTaskOpmlParseNoneFound": "Aucun flux trouvé dans le fichier OPML",
|
||||
"MessageTaskScanItemsAdded": "{0} ajouté",
|
||||
"MessageTaskScanItemsMissing": "{0} manquant",
|
||||
"MessageTaskScanItemsUpdated": "{0} mis à jour",
|
||||
"MessageTaskScanNoChangesNeeded": "Aucun changement nécessaire",
|
||||
"MessageTaskScanningFileChanges": "Analyse des modifications du fichier dans « {0} »",
|
||||
"MessageTaskScanningLibrary": "Analyse de la bibliothèque « {0} »",
|
||||
"MessageTaskTargetDirectoryNotWritable": "Le répertoire cible n’est pas accessible en écriture",
|
||||
"MessageThinking": "Je cherche…",
|
||||
"MessageUploaderItemFailed": "Échec du téléversement",
|
||||
"MessageUploaderItemSuccess": "Téléversement effectué !",
|
||||
@@ -758,6 +854,10 @@
|
||||
"NoteUploaderFoldersWithMediaFiles": "Les dossiers contenant des fichiers multimédias seront traités comme des éléments distincts de la bibliothèque.",
|
||||
"NoteUploaderOnlyAudioFiles": "Si vous téléversez uniquement des fichiers audio, chaque fichier audio sera traité comme un livre audio distinct.",
|
||||
"NoteUploaderUnsupportedFiles": "Les fichiers non pris en charge sont ignorés. Lorsque vous choisissez ou déposez un dossier, les autres fichiers qui ne sont pas dans un dossier d’élément sont ignorés.",
|
||||
"NotificationOnBackupCompletedDescription": "Déclenché lorsqu’une sauvegarde est terminée",
|
||||
"NotificationOnBackupFailedDescription": "Déclenché lorsqu'une sauvegarde échoue",
|
||||
"NotificationOnEpisodeDownloadedDescription": "Déclenché lorsqu’un épisode de podcast est téléchargé automatiquement",
|
||||
"NotificationOnTestDescription": "Événement pour tester le système de notification",
|
||||
"PlaceholderNewCollection": "Nom de la nouvelle collection",
|
||||
"PlaceholderNewFolderPath": "Nouveau chemin de dossier",
|
||||
"PlaceholderNewPlaylist": "Nouveau nom de liste de lecture",
|
||||
@@ -770,22 +870,23 @@
|
||||
"StatsBooksFinishedThisYear": "Quelques livres terminés cette année…",
|
||||
"StatsBooksListenedTo": "livres écoutés",
|
||||
"StatsCollectionGrewTo": "Votre collection de livres a atteint…",
|
||||
"StatsSessions": "sessions",
|
||||
"StatsSpentListening": "temps passé à écouter",
|
||||
"StatsTopAuthor": "TOP AUTEUR",
|
||||
"StatsTopAuthors": "TOP AUTEURS",
|
||||
"StatsTopGenre": "TOP GENRE",
|
||||
"StatsTopGenres": "TOP GENRES",
|
||||
"StatsTopMonth": "TOP MOIS",
|
||||
"StatsTopNarrator": "TOP NARRATEUR",
|
||||
"StatsTopNarrators": "TOP NARRATEURS",
|
||||
"StatsTotalDuration": "Pour une durée totale de…",
|
||||
"StatsYearInReview": "BILAN DE L’ANNÉE",
|
||||
"ToastAccountUpdateFailed": "Échec de la mise à jour du compte",
|
||||
"ToastAccountUpdateSuccess": "Compte mis à jour",
|
||||
"ToastAppriseUrlRequired": "Vous devez entrer une URL Apprise",
|
||||
"ToastAuthorImageRemoveSuccess": "Image de l’auteur supprimée",
|
||||
"ToastAuthorNotFound": "Auteur \"{0}\" non trouvé",
|
||||
"ToastAuthorRemoveSuccess": "Auteur supprimé",
|
||||
"ToastAuthorSearchNotFound": "Auteur non trouvé",
|
||||
"ToastAuthorUpdateFailed": "Échec de la mise à jour de l’auteur",
|
||||
"ToastAuthorUpdateMerged": "Auteur fusionné",
|
||||
"ToastAuthorUpdateSuccess": "Auteur mis à jour",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "Auteur mis à jour (aucune image trouvée)",
|
||||
@@ -796,7 +897,6 @@
|
||||
"ToastBackupDeleteSuccess": "Sauvegarde supprimée",
|
||||
"ToastBackupInvalidMaxKeep": "Nombre de sauvegardes à conserver invalide",
|
||||
"ToastBackupInvalidMaxSize": "Taille maximale de sauvegarde invalide",
|
||||
"ToastBackupPathUpdateFailed": "Échec de la mise à jour du chemin de sauvegarde",
|
||||
"ToastBackupRestoreFailed": "Échec de la restauration de sauvegarde",
|
||||
"ToastBackupUploadFailed": "Échec du téléversement de sauvegarde",
|
||||
"ToastBackupUploadSuccess": "Sauvegarde téléversée",
|
||||
@@ -807,7 +907,6 @@
|
||||
"ToastBookmarkCreateFailed": "Échec de la création de signet",
|
||||
"ToastBookmarkCreateSuccess": "Signet ajouté",
|
||||
"ToastBookmarkRemoveSuccess": "Signet supprimé",
|
||||
"ToastBookmarkUpdateFailed": "Échec de la mise à jour de signet",
|
||||
"ToastBookmarkUpdateSuccess": "Signet mis à jour",
|
||||
"ToastCachePurgeFailed": "Échec de la purge du cache",
|
||||
"ToastCachePurgeSuccess": "Cache purgé avec succès",
|
||||
@@ -818,7 +917,6 @@
|
||||
"ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi",
|
||||
"ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection",
|
||||
"ToastCollectionRemoveSuccess": "Collection supprimée",
|
||||
"ToastCollectionUpdateFailed": "Échec de la mise à jour de la collection",
|
||||
"ToastCollectionUpdateSuccess": "Collection mise à jour",
|
||||
"ToastCoverUpdateFailed": "Échec de la mise à jour de la couverture",
|
||||
"ToastDeleteFileFailed": "Échec de la suppression du fichier",
|
||||
@@ -827,50 +925,101 @@
|
||||
"ToastDeviceNameAlreadyExists": "Un appareil de lecture avec ce nom existe déjà",
|
||||
"ToastDeviceTestEmailFailed": "Échec de l’envoi du courriel de test",
|
||||
"ToastDeviceTestEmailSuccess": "Courriel de test envoyé",
|
||||
"ToastEmailSettingsUpdateFailed": "Échec de la mise à jour des paramètres de messagerie",
|
||||
"ToastEmailSettingsUpdateSuccess": "Paramètres de messagerie mis à jour",
|
||||
"ToastEncodeCancelFailed": "Échec de l’annulation de l’encodage",
|
||||
"ToastEncodeCancelSucces": "Encodage annulé",
|
||||
"ToastEpisodeDownloadQueueClearFailed": "Échec de la suppression de la file d'attente",
|
||||
"ToastEpisodeDownloadQueueClearSuccess": "File d’attente de téléchargement des épisodes effacée",
|
||||
"ToastErrorCannotShare": "Impossible de partager nativement sur cet appareil",
|
||||
"ToastFailedToLoadData": "Échec du chargement des données",
|
||||
"ToastItemCoverUpdateFailed": "Échec de la mise à jour de la couverture de l’élément",
|
||||
"ToastFailedToShare": "Échec du partage",
|
||||
"ToastFailedToUpdate": "Échec de la mise à jour",
|
||||
"ToastInvalidImageUrl": "URL de l'image invalide",
|
||||
"ToastInvalidUrl": "URL invalide",
|
||||
"ToastItemCoverUpdateSuccess": "Couverture mise à jour",
|
||||
"ToastItemDetailsUpdateFailed": "Échec de la mise à jour des détails de l’élément",
|
||||
"ToastItemDeletedFailed": "La suppression de l'élément à échouée",
|
||||
"ToastItemDeletedSuccess": "Élément supprimé",
|
||||
"ToastItemDetailsUpdateSuccess": "Détails de l’élément mis à jour",
|
||||
"ToastItemMarkedAsFinishedFailed": "Échec de l’annotation terminée",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Article marqué comme terminé",
|
||||
"ToastItemMarkedAsNotFinishedFailed": "Échec de l’annotation non-terminée",
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Article marqué comme non-terminé",
|
||||
"ToastItemUpdateSuccess": "Élément mis a jour",
|
||||
"ToastLibraryCreateFailed": "Échec de la création de bibliothèque",
|
||||
"ToastLibraryCreateSuccess": "Bibliothèque « {0} » créée",
|
||||
"ToastLibraryDeleteFailed": "Échec de la suppression de la bibliothèque",
|
||||
"ToastLibraryDeleteSuccess": "Bibliothèque supprimée",
|
||||
"ToastLibraryScanFailedToStart": "Échec du démarrage de l’analyse",
|
||||
"ToastLibraryScanStarted": "Analyse de la bibliothèque démarrée",
|
||||
"ToastLibraryUpdateFailed": "Échec de la mise à jour de la bibliothèque",
|
||||
"ToastLibraryUpdateSuccess": "Bibliothèque « {0} » mise à jour",
|
||||
"ToastMatchAllAuthorsFailed": "Tous les auteurs et autrices n’ont pas pu être classés",
|
||||
"ToastNameEmailRequired": "Le nom et le courriel sont requis",
|
||||
"ToastNameRequired": "Le nom est requis",
|
||||
"ToastNewUserCreatedFailed": "La création du compte à échouée : « {0} »",
|
||||
"ToastNewUserCreatedSuccess": "Nouveau compte créé",
|
||||
"ToastNewUserLibraryError": "Au moins une bibliothèque est requise",
|
||||
"ToastNewUserPasswordError": "Un mot de passe est requis, seul l’utilisateur root peut avoir un mot de passe vide",
|
||||
"ToastNewUserTagError": "Au moins une étiquette est requise",
|
||||
"ToastNewUserUsernameError": "Entrez un nom d’utilisateur",
|
||||
"ToastNoUpdatesNecessary": "Aucune mise à jour nécessaire",
|
||||
"ToastNotificationCreateFailed": "La création de la notification à échouée",
|
||||
"ToastNotificationDeleteFailed": "La suppression de la notification à échouée",
|
||||
"ToastNotificationFailedMaximum": "Le nombre maximum de tentatives échouées doit être >= 0",
|
||||
"ToastNotificationQueueMaximum": "Le nombre de notification maximum doit être >= 0",
|
||||
"ToastNotificationSettingsUpdateSuccess": "Paramètres de notification mis à jour",
|
||||
"ToastNotificationTestTriggerFailed": "L'envoi de la notification de test à échoué",
|
||||
"ToastNotificationTestTriggerSuccess": "Notification de test déclenchée",
|
||||
"ToastNotificationUpdateSuccess": "Notification mise à jour",
|
||||
"ToastPlaylistCreateFailed": "Échec de la création de la liste de lecture",
|
||||
"ToastPlaylistCreateSuccess": "Liste de lecture créée",
|
||||
"ToastPlaylistRemoveSuccess": "Liste de lecture supprimée",
|
||||
"ToastPlaylistUpdateFailed": "Échec de la mise à jour de la liste de lecture",
|
||||
"ToastPlaylistUpdateSuccess": "Liste de lecture mise à jour",
|
||||
"ToastPodcastCreateFailed": "Échec de la création du podcast",
|
||||
"ToastPodcastCreateSuccess": "Podcast créé avec succès",
|
||||
"ToastPodcastGetFeedFailed": "Échec de la récupération du flux du podcast",
|
||||
"ToastPodcastNoEpisodesInFeed": "Aucun épisode trouvé dans le flux RSS",
|
||||
"ToastPodcastNoRssFeed": "Le podcast n’a pas de flux RSS",
|
||||
"ToastProviderCreatedFailed": "Échec de l’ajout du fournisseur",
|
||||
"ToastProviderCreatedSuccess": "Nouveau fournisseur ajouté",
|
||||
"ToastProviderNameAndUrlRequired": "Nom et URL requis",
|
||||
"ToastProviderRemoveSuccess": "Fournisseur supprimé",
|
||||
"ToastRSSFeedCloseFailed": "Échec de la fermeture du flux RSS",
|
||||
"ToastRSSFeedCloseSuccess": "Flux RSS fermé",
|
||||
"ToastRemoveFailed": "Échec de la suppression",
|
||||
"ToastRemoveItemFromCollectionFailed": "Échec de la suppression d’un élément de la collection",
|
||||
"ToastRemoveItemFromCollectionSuccess": "Élément supprimé de la collection",
|
||||
"ToastRemoveItemsWithIssuesFailed": "Échec de la suppression des éléments de bibliothèque présentant des problèmes",
|
||||
"ToastRemoveItemsWithIssuesSuccess": "Éléments de bibliothèque supprimés avec des problèmes",
|
||||
"ToastRenameFailed": "Échec du renommage",
|
||||
"ToastRescanFailed": "Échec de la nouvelle analyse pour {0}",
|
||||
"ToastRescanRemoved": "Nouvelle analyse terminée, l’élément a été supprimé",
|
||||
"ToastRescanUpToDate": "Nouvelle analyse terminée, l’élément était déjà à jour",
|
||||
"ToastRescanUpdated": "Nouvelle analyse terminée, l’élément a été mis à jour",
|
||||
"ToastScanFailed": "Échec de l’analyse de l’élément de la bibliothèque",
|
||||
"ToastSelectAtLeastOneUser": "Sélectionnez au moins un utilisateur",
|
||||
"ToastSendEbookToDeviceFailed": "Échec de l’envoi du livre numérique à l’appareil",
|
||||
"ToastSendEbookToDeviceSuccess": "Livre numérique envoyé à l’appareil : {0}",
|
||||
"ToastSeriesUpdateFailed": "Échec de la mise à jour de la série",
|
||||
"ToastSeriesUpdateSuccess": "Mise à jour de la série réussie",
|
||||
"ToastServerSettingsUpdateFailed": "Échec de la mise à jour des paramètres du serveur",
|
||||
"ToastServerSettingsUpdateSuccess": "Mise à jour des paramètres du serveur",
|
||||
"ToastSessionCloseFailed": "Échec de la fermeture de la session",
|
||||
"ToastSessionDeleteFailed": "Échec de la suppression de session",
|
||||
"ToastSessionDeleteSuccess": "Session supprimée",
|
||||
"ToastSlugMustChange": "L’identifiant d’URL contient des caractères invalides",
|
||||
"ToastSlugRequired": "L’identifiant d’URL est requis",
|
||||
"ToastSocketConnected": "WebSocket connecté",
|
||||
"ToastSocketDisconnected": "WebSocket déconnecté",
|
||||
"ToastSocketFailedToConnect": "Échec de la connexion WebSocket",
|
||||
"ToastSortingPrefixesEmptyError": "Doit avoir au moins 1 préfixe de tri",
|
||||
"ToastSortingPrefixesUpdateFailed": "Échec de la mise à jour des préfixes de tri",
|
||||
"ToastSortingPrefixesUpdateSuccess": "Mise à jour des préfixes de tri ({0} élément)",
|
||||
"ToastTitleRequired": "Le titre est requis",
|
||||
"ToastUnknownError": "Erreur inconnue",
|
||||
"ToastUnlinkOpenIdFailed": "Échec de la dissociation de l’utilisateur l’OpenID",
|
||||
"ToastUnlinkOpenIdSuccess": "Utilisateur dissocié de OpenID",
|
||||
"ToastUserDeleteFailed": "Échec de la suppression de l’utilisateur",
|
||||
"ToastUserDeleteSuccess": "Utilisateur supprimé"
|
||||
"ToastUserDeleteSuccess": "Utilisateur supprimé",
|
||||
"ToastUserPasswordChangeSuccess": "Mot de passe modifié avec succès",
|
||||
"ToastUserPasswordMismatch": "Les mots de passe ne correspondent pas",
|
||||
"ToastUserPasswordMustChange": "Le nouveau mot de passe ne peut pas être identique à l’ancien",
|
||||
"ToastUserRootRequireName": "Vous devez entrer un nom d’utilisateur root"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"ButtonCheckAndDownloadNewEpisodes": "નવા એપિસોડ્સ ચેક કરો અને ડાઉનલોડ કરો",
|
||||
"ButtonChooseAFolder": "ફોલ્ડર પસંદ કરો",
|
||||
"ButtonChooseFiles": "ફાઇલો પસંદ કરો",
|
||||
"ButtonClearFilter": "ફિલ્ટર જતુ કરો ",
|
||||
"ButtonClearFilter": "ફિલ્ટર જતુ કરો",
|
||||
"ButtonCloseFeed": "ફીડ બંધ કરો",
|
||||
"ButtonCollections": "સંગ્રહ",
|
||||
"ButtonConfigureScanner": "સ્કેનર સેટિંગ બદલો",
|
||||
|
||||
@@ -701,10 +701,8 @@
|
||||
"PlaceholderNewPlaylist": "שם רשימת השמעה חדשה",
|
||||
"PlaceholderSearch": "חיפוש..",
|
||||
"PlaceholderSearchEpisode": "חיפוש פרק..",
|
||||
"ToastAccountUpdateFailed": "עדכון חשבון נכשל",
|
||||
"ToastAccountUpdateSuccess": "חשבון עודכן בהצלחה",
|
||||
"ToastAuthorImageRemoveSuccess": "תמונת המחבר הוסרה בהצלחה",
|
||||
"ToastAuthorUpdateFailed": "עדכון המחבר נכשל",
|
||||
"ToastAuthorUpdateMerged": "המחבר מוזג",
|
||||
"ToastAuthorUpdateSuccess": "המחבר עודכן בהצלחה",
|
||||
"ToastAuthorUpdateSuccessNoImageFound": "המחבר עודכן (תמונה לא נמצאה)",
|
||||
@@ -720,17 +718,13 @@
|
||||
"ToastBookmarkCreateFailed": "יצירת סימניה נכשלה",
|
||||
"ToastBookmarkCreateSuccess": "הסימניה נוספה בהצלחה",
|
||||
"ToastBookmarkRemoveSuccess": "הסימניה הוסרה בהצלחה",
|
||||
"ToastBookmarkUpdateFailed": "עדכון הסימניה נכשל",
|
||||
"ToastBookmarkUpdateSuccess": "הסימניה עודכנה בהצלחה",
|
||||
"ToastChaptersHaveErrors": "פרקים מכילים שגיאות",
|
||||
"ToastChaptersMustHaveTitles": "פרקים חייבים לכלול כותרות",
|
||||
"ToastCollectionItemsRemoveSuccess": "הפריט(ים) הוסרו מהאוסף בהצלחה",
|
||||
"ToastCollectionRemoveSuccess": "האוסף הוסר בהצלחה",
|
||||
"ToastCollectionUpdateFailed": "עדכון האוסף נכשל",
|
||||
"ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה",
|
||||
"ToastItemCoverUpdateFailed": "עדכון כריכת הפריט נכשל",
|
||||
"ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה",
|
||||
"ToastItemDetailsUpdateFailed": "עדכון פרטי הפריט נכשל",
|
||||
"ToastItemDetailsUpdateSuccess": "פרטי הפריט עודכנו בהצלחה",
|
||||
"ToastItemMarkedAsFinishedFailed": "סימון כפריט כהושלם נכשל",
|
||||
"ToastItemMarkedAsFinishedSuccess": "הפריט סומן כהושלם בהצלחה",
|
||||
@@ -742,12 +736,10 @@
|
||||
"ToastLibraryDeleteSuccess": "הספרייה נמחקה בהצלחה",
|
||||
"ToastLibraryScanFailedToStart": "הפעלת הסריקה נכשלה",
|
||||
"ToastLibraryScanStarted": "הסריקה של הספרייה החלה",
|
||||
"ToastLibraryUpdateFailed": "עדכון הספרייה נכשל",
|
||||
"ToastLibraryUpdateSuccess": "הספרייה \"{0}\" עודכנה בהצלחה",
|
||||
"ToastPlaylistCreateFailed": "יצירת רשימת השמעה נכשלה",
|
||||
"ToastPlaylistCreateSuccess": "רשימת השמעה נוצרה בהצלחה",
|
||||
"ToastPlaylistRemoveSuccess": "רשימת השמעה הוסרה בהצלחה",
|
||||
"ToastPlaylistUpdateFailed": "עדכון רשימת השמעה נכשל",
|
||||
"ToastPlaylistUpdateSuccess": "רשימת השמעה עודכנה בהצלחה",
|
||||
"ToastPodcastCreateFailed": "יצירת הפודקאסט נכשלה",
|
||||
"ToastPodcastCreateSuccess": "הפודקאסט נוצר בהצלחה",
|
||||
@@ -759,7 +751,6 @@
|
||||
"ToastSendEbookToDeviceSuccess": "הספר נשלח אל המכשיר \"{0}\"",
|
||||
"ToastSeriesUpdateFailed": "עדכון הסדרה נכשל",
|
||||
"ToastSeriesUpdateSuccess": "הסדרה עודכנה בהצלחה",
|
||||
"ToastServerSettingsUpdateFailed": "כשל בעדכון הגדרות שרת",
|
||||
"ToastServerSettingsUpdateSuccess": "הגדרות שרת עודכנו בהצלחה",
|
||||
"ToastSessionDeleteFailed": "מחיקת הפעולה נכשלה",
|
||||
"ToastSessionDeleteSuccess": "הפעולה נמחקה בהצלחה",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user