mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-01 16:30:39 +02:00
Compare commits
199 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ce1806359 | |||
| f05a513767 | |||
| d03c338b48 | |||
| 5e5a988f7a | |||
| 6d1f0b27df | |||
| d01a7cb756 | |||
| cae874ef05 | |||
| 733afc3e29 | |||
| 0772730336 | |||
| 8b02fe07c8 | |||
| 98f93a665c | |||
| 754566b221 | |||
| f4f9adad35 | |||
| 16f7f1166e | |||
| f527b0f4d5 | |||
| 4f41df53c9 | |||
| 8a15f775a2 | |||
| 5e83bcd283 | |||
| 2fd5dfcb66 | |||
| 872ce4fa38 | |||
| ba792d91e5 | |||
| 4997c716db | |||
| fd72d05280 | |||
| 241b56ad45 | |||
| 635c384952 | |||
| ef930fd1b4 | |||
| 49997a1336 | |||
| 8d0434143c | |||
| 8e0319994e | |||
| 0ed6045d1e | |||
| 25c7e95a64 | |||
| 1781c4bbcb | |||
| c4ce72d44e | |||
| 78813c4b28 | |||
| 990baa2dc6 | |||
| c85f4467d2 | |||
| 59f7609054 | |||
| 2ef827e3fa | |||
| 5cadc8d90f | |||
| 40e7e36ef6 | |||
| d60ad96f8a | |||
| 46ba342d49 | |||
| ace6b2b81f | |||
| fa7e2dfafe | |||
| 015310c15d | |||
| f624f04dec | |||
| 7c13cfcda2 | |||
| fc265dadae | |||
| f9905f887e | |||
| eb72bfbbc0 | |||
| c268cace09 | |||
| 9666caf7a3 | |||
| 9e01e5c24e | |||
| 25e613a867 | |||
| fe23a86eaa | |||
| cb5a7d6aef | |||
| 7deb89ce7a | |||
| 1e300c77c9 | |||
| ed7cc42959 | |||
| f681ff68a1 | |||
| ba112bf9c2 | |||
| 718434545a | |||
| 0e9a4c95a9 | |||
| 3c997c8468 | |||
| eb49646256 | |||
| c54b5eadfd | |||
| 659c671c25 | |||
| 0df5a7816d | |||
| 26c976b6b9 | |||
| bdeb22615e | |||
| 257bf2ebe0 | |||
| fc33da447a | |||
| df45347690 | |||
| b876256736 | |||
| 3ce6e45761 | |||
| 5ac6b85da1 | |||
| 69e0a0732a | |||
| 087835a9f3 | |||
| 1f7b181b7b | |||
| 1afb8840db | |||
| d9531166b6 | |||
| 336de49d8d | |||
| 3cc527484d | |||
| 45987ffd63 | |||
| 1a1ef9c378 | |||
| 342d100f3e | |||
| e0b90c6813 | |||
| 2706a9c4aa | |||
| 2cc9d1b7f8 | |||
| 2b7268c952 | |||
| e097fe1e88 | |||
| 6819c0b108 | |||
| 58cd751b43 | |||
| 9f834a5345 | |||
| 5eaf9c69ad | |||
| a1074e69ac | |||
| 65aec6a099 | |||
| 38957d4f32 | |||
| a2dc76e190 | |||
| fd84cd0d7f | |||
| db7744eb84 | |||
| af513a2fb6 | |||
| 4cb5c934d5 | |||
| 37f84a0f62 | |||
| 70595181f1 | |||
| b357bbed60 | |||
| f7a720c6ac | |||
| 6549605efd | |||
| 33952fb1fd | |||
| 7b207dc5d8 | |||
| cb24a9c1ec | |||
| 3b42af5213 | |||
| b56691f1a2 | |||
| ac3154093c | |||
| 01ef24f5e6 | |||
| 3fb73c7426 | |||
| bf3bc06322 | |||
| 2733c28784 | |||
| b3dac831e6 | |||
| 35702aa770 | |||
| b2ffb3b7b9 | |||
| c52fe4b583 | |||
| af8ace7d1f | |||
| de37e40a1e | |||
| 56f5df91dc | |||
| fc590abb09 | |||
| bc7bbc1b7d | |||
| 4345973213 | |||
| b4ff9f5944 | |||
| a9a253f769 | |||
| a9783efa34 | |||
| a380ee080f | |||
| eabefd099c | |||
| 97799919e6 | |||
| 35870a0158 | |||
| ec05bd36e4 | |||
| be041f93c2 | |||
| a156d3595b | |||
| a1d549a2b1 | |||
| 812cb5a160 | |||
| e6264540af | |||
| 79fe064c4a | |||
| 7e69713683 | |||
| 3bbeb8f27a | |||
| 04fb8fa61d | |||
| 2caa861b8a | |||
| d7f0815fb3 | |||
| e6ab05e177 | |||
| c2ecfd428b | |||
| 9f26274ca8 | |||
| 7764f1cf75 | |||
| bc1b99efd6 | |||
| 26309019e7 | |||
| b47d7b734d | |||
| 62194b8781 | |||
| 7c114a051a | |||
| 26c0c89b94 | |||
| c81071a7b3 | |||
| 31f48edcc3 | |||
| f7109a055c | |||
| 9d0e7759e0 | |||
| d15ccbd2fc | |||
| cfeb1743df | |||
| a7e0330b06 | |||
| 5f69e83d46 | |||
| fdde62896f | |||
| f1909d0fc7 | |||
| 28225618fd | |||
| 1a562a5f23 | |||
| bfbbcba160 | |||
| 21e4d17ef3 | |||
| 87faebc7d9 | |||
| 5d868d1355 | |||
| ac0fd41740 | |||
| 1202e95b66 | |||
| c05cf9718b | |||
| d8f1e43e85 | |||
| ed7e87b168 | |||
| 4ca2c9e97f | |||
| 01dd1c0615 | |||
| cd387b8fed | |||
| c85cd69152 | |||
| 9e9b52a252 | |||
| 292d5783a9 | |||
| c7faafd0f3 | |||
| ca7388b14e | |||
| ddf2ca3670 | |||
| 96825c3c2b | |||
| 6ed66fea16 | |||
| ddcda197b4 | |||
| 8bea5d83f5 | |||
| dc3c978f8d | |||
| 13fac2d5bc | |||
| fd0af6b2dd | |||
| 121805ba39 | |||
| f9bbd71174 | |||
| 2fbb31e0ea | |||
| 89167543fa | |||
| 33e0987d73 |
@@ -23,7 +23,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ !contains(github.event.head_commit.message, 'skip ci') && github.repository == 'advplyr/audiobookshelf' }}
|
if: ${{ !contains(github.event.head_commit.message, 'skip ci') && github.repository == 'advplyr/audiobookshelf' }}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out
|
- name: Check out
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ sw.*
|
|||||||
.DS_STORE
|
.DS_STORE
|
||||||
.idea/*
|
.idea/*
|
||||||
tailwind.compiled.css
|
tailwind.compiled.css
|
||||||
|
tailwind.config.js
|
||||||
|
|||||||
+34
-16
@@ -1,34 +1,32 @@
|
|||||||
|
ARG NUSQLITE3_DIR="/usr/local/lib/nusqlite3"
|
||||||
|
ARG NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so"
|
||||||
|
|
||||||
### STAGE 0: Build client ###
|
### STAGE 0: Build client ###
|
||||||
FROM node:20-alpine AS build
|
FROM node:20-alpine AS build-client
|
||||||
|
|
||||||
WORKDIR /client
|
WORKDIR /client
|
||||||
COPY /client /client
|
COPY /client /client
|
||||||
RUN npm ci && npm cache clean --force
|
RUN npm ci && npm cache clean --force
|
||||||
RUN npm run generate
|
RUN npm run generate
|
||||||
|
|
||||||
### STAGE 1: Build server ###
|
### STAGE 1: Build server ###
|
||||||
FROM node:20-alpine
|
FROM node:20-alpine AS build-server
|
||||||
|
|
||||||
|
ARG NUSQLITE3_DIR
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
RUN apk update && \
|
RUN apk add --no-cache --update \
|
||||||
apk add --no-cache --update \
|
|
||||||
curl \
|
curl \
|
||||||
tzdata \
|
|
||||||
ffmpeg \
|
|
||||||
make \
|
make \
|
||||||
python3 \
|
python3 \
|
||||||
g++ \
|
g++ \
|
||||||
tini \
|
|
||||||
unzip
|
unzip
|
||||||
|
|
||||||
COPY --from=build /client/dist /client/dist
|
WORKDIR /server
|
||||||
COPY index.js package* /
|
COPY index.js package* /server
|
||||||
COPY server server
|
COPY /server /server/server
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
|
|
||||||
ENV NUSQLITE3_DIR="/usr/local/lib/nusqlite3"
|
|
||||||
ENV NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so"
|
|
||||||
|
|
||||||
RUN case "$TARGETPLATFORM" in \
|
RUN case "$TARGETPLATFORM" in \
|
||||||
"linux/amd64") \
|
"linux/amd64") \
|
||||||
@@ -42,14 +40,34 @@ RUN case "$TARGETPLATFORM" in \
|
|||||||
|
|
||||||
RUN npm ci --only=production
|
RUN npm ci --only=production
|
||||||
|
|
||||||
RUN apk del make python3 g++
|
### STAGE 2: Create minimal runtime image ###
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
ARG NUSQLITE3_DIR
|
||||||
|
ARG NUSQLITE3_PATH
|
||||||
|
|
||||||
|
# Install only runtime dependencies
|
||||||
|
RUN apk add --no-cache --update \
|
||||||
|
tzdata \
|
||||||
|
ffmpeg \
|
||||||
|
tini
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy compiled frontend and server from build stages
|
||||||
|
COPY --from=build-client /client/dist /app/client/dist
|
||||||
|
COPY --from=build-server /server /app
|
||||||
|
COPY --from=build-server /usr/local/lib/nusqlite3 /usr/local/lib/nusqlite3
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
ENV PORT=80
|
ENV PORT=80
|
||||||
|
ENV NODE_ENV=production
|
||||||
ENV CONFIG_PATH="/config"
|
ENV CONFIG_PATH="/config"
|
||||||
ENV METADATA_PATH="/metadata"
|
ENV METADATA_PATH="/metadata"
|
||||||
ENV SOURCE="docker"
|
ENV SOURCE="docker"
|
||||||
|
ENV NUSQLITE3_DIR=${NUSQLITE3_DIR}
|
||||||
|
ENV NUSQLITE3_PATH=${NUSQLITE3_PATH}
|
||||||
|
|
||||||
ENTRYPOINT ["tini", "--"]
|
ENTRYPOINT ["tini", "--"]
|
||||||
CMD ["node", "index.js"]
|
CMD ["node", "index.js"]
|
||||||
|
|||||||
@@ -217,6 +217,16 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.results.episodes?.length) {
|
||||||
|
shelves.push({
|
||||||
|
id: 'episodes',
|
||||||
|
label: 'Episodes',
|
||||||
|
labelStringKey: 'LabelEpisodes',
|
||||||
|
type: 'episode',
|
||||||
|
entities: this.results.episodes.map((res) => res.libraryItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (this.results.series?.length) {
|
if (this.results.series?.length) {
|
||||||
shelves.push({
|
shelves.push({
|
||||||
id: 'series',
|
id: 'series',
|
||||||
|
|||||||
@@ -274,15 +274,10 @@ export default {
|
|||||||
isAuthorsPage() {
|
isAuthorsPage() {
|
||||||
return this.page === 'authors'
|
return this.page === 'authors'
|
||||||
},
|
},
|
||||||
isAlbumsPage() {
|
|
||||||
return this.page === 'albums'
|
|
||||||
},
|
|
||||||
numShowing() {
|
numShowing() {
|
||||||
return this.totalEntities
|
return this.totalEntities
|
||||||
},
|
},
|
||||||
entityName() {
|
entityName() {
|
||||||
if (this.isAlbumsPage) return 'Albums'
|
|
||||||
|
|
||||||
if (this.isPodcastLibrary) return this.$strings.LabelPodcasts
|
if (this.isPodcastLibrary) return this.$strings.LabelPodcasts
|
||||||
if (!this.page) return this.$strings.LabelBooks
|
if (!this.page) return this.$strings.LabelBooks
|
||||||
if (this.isSeriesPage) return this.$strings.LabelSeries
|
if (this.isSeriesPage) return this.$strings.LabelSeries
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export default {
|
|||||||
return this.mediaMetadata.authors || []
|
return this.mediaMetadata.authors || []
|
||||||
},
|
},
|
||||||
libraryId() {
|
libraryId() {
|
||||||
return this.streamLibraryItem ? this.streamLibraryItem.libraryId : null
|
return this.streamLibraryItem?.libraryId || null
|
||||||
},
|
},
|
||||||
totalDurationPretty() {
|
totalDurationPretty() {
|
||||||
// Adjusted by playback rate
|
// Adjusted by playback rate
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex items-center h-full px-1 overflow-hidden">
|
||||||
|
<covers-book-cover :library-item="libraryItem" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
||||||
|
<div class="grow px-2 episodeSearchCardContent">
|
||||||
|
<p class="truncate text-sm">{{ episodeTitle }}</p>
|
||||||
|
<p class="text-xs text-gray-200 truncate">{{ podcastTitle }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
libraryItem: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
episode: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
bookCoverAspectRatio() {
|
||||||
|
return this.$store.getters['libraries/getBookCoverAspectRatio']
|
||||||
|
},
|
||||||
|
coverWidth() {
|
||||||
|
if (this.bookCoverAspectRatio === 1) return 50 * 1.2
|
||||||
|
return 50
|
||||||
|
},
|
||||||
|
media() {
|
||||||
|
return this.libraryItem?.media || {}
|
||||||
|
},
|
||||||
|
mediaMetadata() {
|
||||||
|
return this.media.metadata || {}
|
||||||
|
},
|
||||||
|
episodeTitle() {
|
||||||
|
return this.episode.title || 'No Title'
|
||||||
|
},
|
||||||
|
podcastTitle() {
|
||||||
|
return this.mediaMetadata.title || 'No Title'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.episodeSearchCardContent {
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
height: 75px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
<div class="w-1/2 px-2">
|
<div class="w-1/2 px-2">
|
||||||
<div v-if="!isPodcast" class="flex items-end">
|
<div v-if="!isPodcast" class="flex items-end">
|
||||||
<ui-text-input-with-label v-model.trim="itemData.author" :disabled="processing" :label="$strings.LabelAuthor" />
|
<ui-text-input-with-label v-model.trim="itemData.author" :disabled="processing" :label="$strings.LabelAuthor" />
|
||||||
<ui-tooltip :text="$strings.LabelUploaderItemFetchMetadataHelp">
|
<ui-tooltip direction="top" :text="$strings.LabelUploaderItemFetchMetadataHelp">
|
||||||
<div class="ml-2 mb-1 w-8 h-8 bg-bg border border-white/10 flex items-center justify-center rounded-full hover:bg-primary cursor-pointer" @click="fetchMetadata">
|
<button type="button" class="ml-2 mb-1 w-8 h-8 bg-bg border border-white/10 flex items-center justify-center rounded-full hover:bg-primary cursor-pointer" @click="fetchMetadata">
|
||||||
<span class="text-base text-white/80 font-mono material-symbols">sync</span>
|
<span class="text-base text-white/80 font-mono material-symbols">sync</span>
|
||||||
</div>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="w-full">
|
<div v-else class="w-full">
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div ref="card" :id="`album-card-${index}`" :style="{ width: cardWidth + 'px' }" class="absolute top-0 left-0 rounded-xs z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
|
|
||||||
<div class="relative" :style="{ height: coverHeight + 'px' }">
|
|
||||||
<div class="absolute top-0 left-0 w-full box-shadow-book shadow-height" />
|
|
||||||
<div class="w-full h-full bg-primary relative rounded-sm overflow-hidden">
|
|
||||||
<covers-preview-cover ref="cover" :src="coverSrc" :width="cardWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="relative w-full">
|
|
||||||
<div v-if="!isAlternativeBookshelfView" class="categoryPlacard absolute z-30 left-0 right-0 mx-auto -bottom-6e h-6e rounded-md text-center" :style="{ width: Math.min(200, cardWidth) + 'px' }">
|
|
||||||
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-xs border" :style="{ padding: `0em ${0.5}em` }">
|
|
||||||
<p class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ title }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="absolute z-30 left-0 right-0 mx-auto -bottom-8e h-8e py-1e rounded-md text-center">
|
|
||||||
<p class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ title }}</p>
|
|
||||||
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ artist || ' ' }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
index: Number,
|
|
||||||
width: Number,
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 192
|
|
||||||
},
|
|
||||||
bookshelfView: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
albumMount: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
album: null,
|
|
||||||
isSelectionMode: false,
|
|
||||||
selected: false,
|
|
||||||
isHovering: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
bookCoverAspectRatio() {
|
|
||||||
return this.store.getters['libraries/getBookCoverAspectRatio']
|
|
||||||
},
|
|
||||||
cardWidth() {
|
|
||||||
return this.width || this.coverHeight
|
|
||||||
},
|
|
||||||
coverHeight() {
|
|
||||||
return this.height * this.sizeMultiplier
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
cardHeight() {
|
|
||||||
return this.coverHeight + this.bottomTextHeight
|
|
||||||
},
|
|
||||||
bottomTextHeight() {
|
|
||||||
if (!this.isAlternativeBookshelfView) return 0
|
|
||||||
const lineHeight = 1.5
|
|
||||||
const remSize = 16
|
|
||||||
const baseHeight = this.sizeMultiplier * lineHeight * remSize
|
|
||||||
const titleHeight = this.labelFontSize * baseHeight
|
|
||||||
const paddingHeight = 4 * 2 * this.sizeMultiplier // py-1
|
|
||||||
return titleHeight + paddingHeight
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
coverSrc() {
|
|
||||||
const config = this.$config || this.$nuxt.$config
|
|
||||||
if (!this.album || !this.album.libraryItemId) return `${config.routerBasePath}/book_placeholder.jpg`
|
|
||||||
return this.store.getters['globals/getLibraryItemCoverSrcById'](this.album.libraryItemId)
|
|
||||||
},
|
|
||||||
labelFontSize() {
|
|
||||||
if (this.width < 160) return 0.75
|
|
||||||
return 0.9
|
|
||||||
},
|
|
||||||
sizeMultiplier() {
|
|
||||||
return this.store.getters['user/getSizeMultiplier']
|
|
||||||
},
|
|
||||||
title() {
|
|
||||||
return this.album ? this.album.title : ''
|
|
||||||
},
|
|
||||||
artist() {
|
|
||||||
return this.album ? this.album.artist : ''
|
|
||||||
},
|
|
||||||
store() {
|
|
||||||
return this.$store || this.$nuxt.$store
|
|
||||||
},
|
|
||||||
currentLibraryId() {
|
|
||||||
return this.store.state.libraries.currentLibraryId
|
|
||||||
},
|
|
||||||
isAlternativeBookshelfView() {
|
|
||||||
const constants = this.$constants || this.$nuxt.$constants
|
|
||||||
return this.bookshelfView == constants.BookshelfView.DETAIL
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setEntity(album) {
|
|
||||||
this.album = album
|
|
||||||
},
|
|
||||||
setSelectionMode(val) {
|
|
||||||
this.isSelectionMode = val
|
|
||||||
},
|
|
||||||
mouseover() {
|
|
||||||
this.isHovering = true
|
|
||||||
},
|
|
||||||
mouseleave() {
|
|
||||||
this.isHovering = false
|
|
||||||
},
|
|
||||||
clickCard() {
|
|
||||||
if (!this.album) return
|
|
||||||
// const router = this.$router || this.$nuxt.$router
|
|
||||||
// router.push(`/album/${this.$encode(this.title)}`)
|
|
||||||
},
|
|
||||||
clickEdit() {
|
|
||||||
this.$emit('edit', this.album)
|
|
||||||
},
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (this.albumMount) {
|
|
||||||
this.setEntity(this.albumMount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="narrators?.length" class="flex py-0.5 mt-4">
|
<div v-if="narrators?.length" class="flex py-0.5 mt-4">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelNarrators }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelNarrators }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
|
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="publishedYear" role="paragraph" class="flex py-0.5">
|
<div v-if="publishedYear" role="paragraph" class="flex py-0.5">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublishYear }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublishYear }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="publisher" role="paragraph" class="flex py-0.5">
|
<div v-if="publisher" role="paragraph" class="flex py-0.5">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublisher }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublisher }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="podcastType" role="paragraph" class="flex py-0.5">
|
<div v-if="podcastType" role="paragraph" class="flex py-0.5">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="capitalize">
|
<div class="capitalize">
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex py-0.5" v-if="genres.length">
|
<div class="flex py-0.5" v-if="genres.length">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelGenres }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelGenres }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
|
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex py-0.5" v-if="tags.length">
|
<div class="flex py-0.5" v-if="tags.length">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelTags }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelTags }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
|
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="language" class="flex py-0.5">
|
<div v-if="language" class="flex py-0.5">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelLanguage }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelLanguage }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tracks.length || (isPodcast && totalPodcastDuration)" role="paragraph" class="flex py-0.5">
|
<div v-if="tracks.length || (isPodcast && totalPodcastDuration)" role="paragraph" class="flex py-0.5">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div role="paragraph" class="flex py-0.5">
|
<div role="paragraph" class="flex py-0.5">
|
||||||
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
|
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
|
||||||
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelSize }}</span>
|
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelSize }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -39,6 +39,15 @@
|
|||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<p v-if="episodeResults.length" class="uppercase text-xs text-gray-400 my-1 px-1 font-semibold">{{ $strings.LabelEpisodes }}</p>
|
||||||
|
<template v-for="item in episodeResults">
|
||||||
|
<li :key="item.libraryItem.recentEpisode.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
|
||||||
|
<nuxt-link :to="`/item/${item.libraryItem.id}`">
|
||||||
|
<cards-episode-search-card :episode="item.libraryItem.recentEpisode" :library-item="item.libraryItem" />
|
||||||
|
</nuxt-link>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
<p v-if="authorResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelAuthors }}</p>
|
<p v-if="authorResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelAuthors }}</p>
|
||||||
<template v-for="item in authorResults">
|
<template v-for="item in authorResults">
|
||||||
<li :key="item.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
|
<li :key="item.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
|
||||||
@@ -100,6 +109,7 @@ export default {
|
|||||||
isFetching: false,
|
isFetching: false,
|
||||||
search: null,
|
search: null,
|
||||||
podcastResults: [],
|
podcastResults: [],
|
||||||
|
episodeResults: [],
|
||||||
bookResults: [],
|
bookResults: [],
|
||||||
authorResults: [],
|
authorResults: [],
|
||||||
seriesResults: [],
|
seriesResults: [],
|
||||||
@@ -115,7 +125,7 @@ export default {
|
|||||||
return this.$store.state.libraries.currentLibraryId
|
return this.$store.state.libraries.currentLibraryId
|
||||||
},
|
},
|
||||||
totalResults() {
|
totalResults() {
|
||||||
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.genreResults.length + this.podcastResults.length + this.narratorResults.length
|
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.genreResults.length + this.podcastResults.length + this.narratorResults.length + this.episodeResults.length
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -132,6 +142,7 @@ export default {
|
|||||||
this.search = null
|
this.search = null
|
||||||
this.lastSearch = null
|
this.lastSearch = null
|
||||||
this.podcastResults = []
|
this.podcastResults = []
|
||||||
|
this.episodeResults = []
|
||||||
this.bookResults = []
|
this.bookResults = []
|
||||||
this.authorResults = []
|
this.authorResults = []
|
||||||
this.seriesResults = []
|
this.seriesResults = []
|
||||||
@@ -175,6 +186,7 @@ export default {
|
|||||||
if (!this.isFetching) return
|
if (!this.isFetching) return
|
||||||
|
|
||||||
this.podcastResults = searchResults.podcast || []
|
this.podcastResults = searchResults.podcast || []
|
||||||
|
this.episodeResults = searchResults.episodes || []
|
||||||
this.bookResults = searchResults.book || []
|
this.bookResults = searchResults.book || []
|
||||||
this.authorResults = searchResults.authors || []
|
this.authorResults = searchResults.authors || []
|
||||||
this.seriesResults = searchResults.series || []
|
this.seriesResults = searchResults.series || []
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<ui-text-input-with-label ref="sequenceInput" v-model="selectedSeries.sequence" :label="$strings.LabelSequence" />
|
<ui-text-input-with-label ref="sequenceInput" v-model="selectedSeries.sequence" :label="$strings.LabelSequence" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="error" class="text-error text-sm mt-2 p-1">{{ error }}</div>
|
||||||
<div class="flex justify-end mt-2 p-1">
|
<div class="flex justify-end mt-2 p-1">
|
||||||
<ui-btn type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
|
<ui-btn type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,12 +35,17 @@ export default {
|
|||||||
existingSeriesNames: {
|
existingSeriesNames: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
|
},
|
||||||
|
originalSeriesSequence: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
el: null,
|
el: null,
|
||||||
content: null
|
content: null,
|
||||||
|
error: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -85,10 +91,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
submitSeriesForm() {
|
submitSeriesForm() {
|
||||||
|
this.error = null
|
||||||
|
|
||||||
if (this.$refs.newSeriesSelect) {
|
if (this.$refs.newSeriesSelect) {
|
||||||
this.$refs.newSeriesSelect.blur()
|
this.$refs.newSeriesSelect.blur()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.selectedSeries.sequence !== this.originalSeriesSequence && this.selectedSeries.sequence.includes(' ')) {
|
||||||
|
this.error = this.$strings.MessageSeriesSequenceCannotContainSpaces
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.$emit('submit')
|
this.$emit('submit')
|
||||||
},
|
},
|
||||||
clickClose() {
|
clickClose() {
|
||||||
@@ -100,6 +113,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setShow() {
|
setShow() {
|
||||||
|
this.error = null
|
||||||
if (!this.el || !this.content) {
|
if (!this.el || !this.content) {
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,19 +74,12 @@ export default {
|
|||||||
mediaTracks() {
|
mediaTracks() {
|
||||||
return this.media.tracks || []
|
return this.media.tracks || []
|
||||||
},
|
},
|
||||||
isSingleM4b() {
|
|
||||||
return this.mediaTracks.length === 1 && this.mediaTracks[0].metadata.ext.toLowerCase() === '.m4b'
|
|
||||||
},
|
|
||||||
chapters() {
|
chapters() {
|
||||||
return this.media.chapters || []
|
return this.media.chapters || []
|
||||||
},
|
},
|
||||||
showM4bDownload() {
|
showM4bDownload() {
|
||||||
if (!this.mediaTracks.length) return false
|
if (!this.mediaTracks.length) return false
|
||||||
return !this.isSingleM4b
|
return true
|
||||||
},
|
|
||||||
showMp3Split() {
|
|
||||||
if (!this.mediaTracks.length) return false
|
|
||||||
return this.isSingleM4b && this.chapters.length
|
|
||||||
},
|
},
|
||||||
queuedEmbedLIds() {
|
queuedEmbedLIds() {
|
||||||
return this.$store.state.tasks.queuedEmbedLIds || []
|
return this.$store.state.tasks.queuedEmbedLIds || []
|
||||||
|
|||||||
@@ -10,6 +10,12 @@
|
|||||||
<form @submit.prevent="submit" class="flex grow">
|
<form @submit.prevent="submit" class="flex grow">
|
||||||
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="grow mr-2 text-sm md:text-base" />
|
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="grow mr-2 text-sm md:text-base" />
|
||||||
</form>
|
</form>
|
||||||
|
<ui-btn :padding-x="4" @click="toggleSort">
|
||||||
|
<span class="pr-4">{{ $strings.LabelSortPubDate }}</span>
|
||||||
|
<span class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<span class="material-symbols text-xl" :aria-label="sortDescending ? $strings.LabelSortDescending : $strings.LabelSortAscending">{{ sortDescending ? 'expand_more' : 'expand_less' }}</span>
|
||||||
|
</span>
|
||||||
|
</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
<div ref="episodeContainer" id="episodes-scroll" class="w-full overflow-x-hidden overflow-y-auto">
|
<div ref="episodeContainer" id="episodes-scroll" class="w-full overflow-x-hidden overflow-y-auto">
|
||||||
<div v-for="(episode, index) in episodesList" :key="index" class="relative" :class="episode.isDownloaded || episode.isDownloading ? 'bg-primary/40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success/10' : index % 2 == 0 ? 'cursor-pointer bg-primary/25 hover:bg-primary/40' : 'cursor-pointer bg-primary/5 hover:bg-primary/25'" @click="toggleSelectEpisode(episode)">
|
<div v-for="(episode, index) in episodesList" :key="index" class="relative" :class="episode.isDownloaded || episode.isDownloading ? 'bg-primary/40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success/10' : index % 2 == 0 ? 'cursor-pointer bg-primary/25 hover:bg-primary/40' : 'cursor-pointer bg-primary/5 hover:bg-primary/25'" @click="toggleSelectEpisode(episode)">
|
||||||
@@ -73,7 +79,8 @@ export default {
|
|||||||
searchTimeout: null,
|
searchTimeout: null,
|
||||||
searchText: null,
|
searchText: null,
|
||||||
downloadedEpisodeGuidMap: {},
|
downloadedEpisodeGuidMap: {},
|
||||||
downloadedEpisodeUrlMap: {}
|
downloadedEpisodeUrlMap: {},
|
||||||
|
sortDescending: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -141,6 +148,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleSort() {
|
||||||
|
this.sortDescending = !this.sortDescending
|
||||||
|
this.episodesCleaned = this.episodesCleaned.toSorted((a, b) => {
|
||||||
|
if (this.sortDescending) {
|
||||||
|
return a.publishedAt < b.publishedAt ? 1 : -1
|
||||||
|
}
|
||||||
|
return a.publishedAt > b.publishedAt ? 1 : -1
|
||||||
|
})
|
||||||
|
this.selectedEpisodes = {}
|
||||||
|
this.selectAll = false
|
||||||
|
},
|
||||||
getIsEpisodeDownloaded(episode) {
|
getIsEpisodeDownloaded(episode) {
|
||||||
if (episode.guid && !!this.downloadedEpisodeGuidMap[episode.guid]) {
|
if (episode.guid && !!this.downloadedEpisodeGuidMap[episode.guid]) {
|
||||||
return true
|
return true
|
||||||
@@ -226,8 +244,8 @@ export default {
|
|||||||
const sizeInMb = payloadSize / 1024 / 1024
|
const sizeInMb = payloadSize / 1024 / 1024
|
||||||
const sizeInMbPretty = sizeInMb.toFixed(2) + 'MB'
|
const sizeInMbPretty = sizeInMb.toFixed(2) + 'MB'
|
||||||
console.log('Request size', sizeInMb)
|
console.log('Request size', sizeInMb)
|
||||||
if (sizeInMb > 4.99) {
|
if (sizeInMb > 9.99) {
|
||||||
return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 5Mb`)
|
return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 10Mb`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processing = true
|
this.processing = true
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ export default {
|
|||||||
currentChapterStart() {
|
currentChapterStart() {
|
||||||
if (!this.currentChapter) return 0
|
if (!this.currentChapter) return 0
|
||||||
return this.currentChapter.start
|
return this.currentChapter.start
|
||||||
|
},
|
||||||
|
isMobile() {
|
||||||
|
return this.$store.state.globals.isMobile
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -145,6 +148,9 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
mousemoveTrack(e) {
|
mousemoveTrack(e) {
|
||||||
|
if (this.isMobile) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const offsetX = e.offsetX
|
const offsetX = e.offsetX
|
||||||
|
|
||||||
const baseTime = this.useChapterTrack ? this.currentChapterStart : 0
|
const baseTime = this.useChapterTrack ? this.currentChapterStart : 0
|
||||||
@@ -198,6 +204,7 @@ export default {
|
|||||||
setTrackWidth() {
|
setTrackWidth() {
|
||||||
if (this.$refs.track) {
|
if (this.$refs.track) {
|
||||||
this.trackWidth = this.$refs.track.clientWidth
|
this.trackWidth = this.$refs.track.clientWidth
|
||||||
|
this.trackOffsetLeft = this.$refs.track.getBoundingClientRect().left
|
||||||
} else {
|
} else {
|
||||||
console.error('Track not loaded', this.$refs)
|
console.error('Track not loaded', this.$refs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,14 +164,15 @@ export default {
|
|||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.yearInReviewYear = new Date().getFullYear()
|
this.yearInReviewYear = new Date().getFullYear()
|
||||||
|
|
||||||
// When not December show previous year
|
this.availableYears = this.getAvailableYears()
|
||||||
if (new Date().getMonth() < 11) {
|
const availableYearValues = this.availableYears.map((y) => y.value)
|
||||||
|
|
||||||
|
// When not December show previous year if data is available
|
||||||
|
if (new Date().getMonth() < 11 && availableYearValues.includes(this.yearInReviewYear - 1)) {
|
||||||
this.yearInReviewYear--
|
this.yearInReviewYear--
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.availableYears = this.getAvailableYears()
|
|
||||||
|
|
||||||
if (typeof navigator.share !== 'undefined' && navigator.share) {
|
if (typeof navigator.share !== 'undefined' && navigator.share) {
|
||||||
this.showShareButton = true
|
this.showShareButton = true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -26,9 +26,9 @@
|
|||||||
<span class="material-symbols text-2xl text-error">error_outline</span>
|
<span class="material-symbols text-2xl text-error">error_outline</span>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<button aria-label="Download Backup" class="inline-flex material-symbols text-xl mx-1 mt-1 text-white/70 hover:text-white/100" @click.stop="downloadBackup(backup)">download</button>
|
<button aria-label="Download backup" class="inline-flex material-symbols text-xl mx-1 mt-1 text-white/70 hover:text-white/100" @click.stop="downloadBackup(backup)">download</button>
|
||||||
|
|
||||||
<button aria-label="Delete Backup" class="inline-flex material-symbols text-xl mx-1 text-white/70 hover:text-error" @click.stop="deleteBackupClick(backup)">delete</button>
|
<button aria-label="Delete backup" class="inline-flex material-symbols text-xl mx-1 text-white/70 hover:text-error" @click.stop="deleteBackupClick(backup)">delete</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="lazy-episodes-table" class="w-full py-6">
|
<div id="lazy-episodes-table" class="w-full py-6">
|
||||||
<div class="flex flex-wrap flex-col md:flex-row md:items-center mb-4">
|
<div class="flex flex-wrap flex-col md:flex-row md:items-center mb-4">
|
||||||
@@ -176,6 +175,13 @@ export default {
|
|||||||
return episodeProgress && !episodeProgress.isFinished
|
return episodeProgress && !episodeProgress.isFinished
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
|
// Swap values if sort descending
|
||||||
|
if (this.sortDesc) {
|
||||||
|
const temp = a
|
||||||
|
a = b
|
||||||
|
b = temp
|
||||||
|
}
|
||||||
|
|
||||||
let aValue
|
let aValue
|
||||||
let bValue
|
let bValue
|
||||||
|
|
||||||
@@ -194,10 +200,23 @@ export default {
|
|||||||
if (!bValue) bValue = Number.MAX_VALUE
|
if (!bValue) bValue = Number.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sortDesc) {
|
const primaryCompare = String(aValue).localeCompare(String(bValue), undefined, { numeric: true, sensitivity: 'base' })
|
||||||
return String(bValue).localeCompare(String(aValue), undefined, { numeric: true, sensitivity: 'base' })
|
if (primaryCompare !== 0 || this.sortKey === 'publishedAt') return primaryCompare
|
||||||
|
|
||||||
|
// When sorting by season, secondary sort is by episode number
|
||||||
|
if (this.sortKey === 'season') {
|
||||||
|
const aEpisode = a.episode || ''
|
||||||
|
const bEpisode = b.episode || ''
|
||||||
|
|
||||||
|
const secondaryCompare = String(aEpisode).localeCompare(String(bEpisode), undefined, { numeric: true, sensitivity: 'base' })
|
||||||
|
if (secondaryCompare !== 0) return secondaryCompare
|
||||||
}
|
}
|
||||||
return String(aValue).localeCompare(String(bValue), undefined, { numeric: true, sensitivity: 'base' })
|
|
||||||
|
// Final sort by publishedAt
|
||||||
|
let aPubDate = a.publishedAt || Number.MAX_VALUE
|
||||||
|
let bPubDate = b.publishedAt || Number.MAX_VALUE
|
||||||
|
|
||||||
|
return String(aPubDate).localeCompare(String(bPubDate), undefined, { numeric: true, sensitivity: 'base' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
episodesList() {
|
episodesList() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="inline-flex toggle-btn-wrapper shadow-md">
|
<div class="inline-flex toggle-btn-wrapper shadow-md">
|
||||||
<button v-for="item in items" :key="item.value" type="button" class="toggle-btn outline-hidden relative border border-gray-600 px-4 py-1" :class="{ selected: item.value === value }" @click.stop="clickBtn(item.value)">
|
<button v-for="item in items" :key="item.value" type="button" :disabled="disabled" class="toggle-btn outline-hidden relative border border-gray-600 px-4 py-1" :class="{ selected: item.value === value }" @click.stop="clickBtn(item.value)">
|
||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -9,13 +9,17 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: String,
|
value: [String, Number],
|
||||||
/**
|
/**
|
||||||
* [{ "text", "", "value": "" }]
|
* [{ "text", "", "value": "" }]
|
||||||
*/
|
*/
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: Object
|
default: Object
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -76,10 +80,19 @@ export default {
|
|||||||
.toggle-btn.selected {
|
.toggle-btn.selected {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
.toggle-btn.selected:disabled {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
.toggle-btn.selected::before {
|
.toggle-btn.selected::before {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
button.toggle-btn.selected:disabled::before {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
button.toggle-btn:disabled::before {
|
button.toggle-btn:disabled::before {
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
button.toggle-btn:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full py-2">
|
||||||
|
<div class="flex -mb-px">
|
||||||
|
<button type="button" :disabled="disabled" class="w-1/2 h-8 rounded-tl-md relative border border-black-200 flex items-center justify-center disabled:cursor-not-allowed" :class="!showAdvancedView ? 'text-white bg-bg hover:bg-bg/60 border-b-bg' : 'text-gray-400 hover:text-gray-300 bg-primary/70 hover:bg-primary/60'" @click="showAdvancedView = false">
|
||||||
|
<p class="text-sm">{{ $strings.HeaderPresets }}</p>
|
||||||
|
</button>
|
||||||
|
<button type="button" :disabled="disabled" class="w-1/2 h-8 rounded-tr-md relative border border-black-200 flex items-center justify-center -ml-px disabled:cursor-not-allowed" :class="showAdvancedView ? 'text-white bg-bg hover:bg-bg/60 border-b-bg' : 'text-gray-400 hover:text-gray-300 bg-primary/70 hover:bg-primary/60'" @click="showAdvancedView = true">
|
||||||
|
<p class="text-sm">{{ $strings.HeaderAdvanced }}</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 md:p-8 border border-black-200 rounded-b-md mr-px bg-bg">
|
||||||
|
<template v-if="!showAdvancedView">
|
||||||
|
<div class="flex flex-wrap gap-4 sm:gap-8 justify-start sm:justify-center">
|
||||||
|
<div class="flex flex-col items-start gap-2">
|
||||||
|
<p class="text-sm w-40">{{ $strings.LabelCodec }}</p>
|
||||||
|
<ui-toggle-btns v-model="selectedCodec" :items="codecItems" :disabled="disabled" />
|
||||||
|
<p class="text-xs text-gray-300">
|
||||||
|
{{ $strings.LabelCurrently }} <span class="text-white">{{ currentCodec }}</span> <span v-if="isCodecsDifferent" class="text-warning">(mixed)</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-start gap-2">
|
||||||
|
<p class="text-sm w-40">{{ $strings.LabelBitrate }}</p>
|
||||||
|
<ui-toggle-btns v-model="selectedBitrate" :items="bitrateItems" :disabled="disabled" />
|
||||||
|
<p class="text-xs text-gray-300">
|
||||||
|
{{ $strings.LabelCurrently }} <span class="text-white">{{ currentBitrate }} KB/s</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-start gap-2">
|
||||||
|
<p class="text-sm w-40">{{ $strings.LabelChannels }}</p>
|
||||||
|
<ui-toggle-btns v-model="selectedChannels" :items="channelsItems" :disabled="disabled" />
|
||||||
|
<p class="text-xs text-gray-300">
|
||||||
|
{{ $strings.LabelCurrently }} <span class="text-white">{{ currentChannels }} ({{ currentChanelLayout }})</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-wrap gap-4 sm:gap-8 justify-start sm:justify-center mb-4">
|
||||||
|
<div class="w-40">
|
||||||
|
<ui-text-input-with-label v-model="customCodec" :label="$strings.LabelAudioCodec" :disabled="disabled" @input="customCodecChanged" />
|
||||||
|
</div>
|
||||||
|
<div class="w-40">
|
||||||
|
<ui-text-input-with-label v-model="customBitrate" :label="$strings.LabelAudioBitrate" :disabled="disabled" @input="customBitrateChanged" />
|
||||||
|
</div>
|
||||||
|
<div class="w-40">
|
||||||
|
<ui-text-input-with-label v-model="customChannels" :label="$strings.LabelAudioChannels" type="number" :disabled="disabled" @input="customChannelsChanged" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs sm:text-sm text-warning sm:text-center">{{ $strings.LabelEncodingWarningAdvancedSettings }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
audioTracks: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showAdvancedView: false,
|
||||||
|
selectedCodec: 'aac',
|
||||||
|
selectedBitrate: '128k',
|
||||||
|
selectedChannels: 2,
|
||||||
|
customCodec: 'aac',
|
||||||
|
customBitrate: '128k',
|
||||||
|
customChannels: 2,
|
||||||
|
currentCodec: '',
|
||||||
|
currentBitrate: '',
|
||||||
|
currentChannels: '',
|
||||||
|
currentChanelLayout: '',
|
||||||
|
isCodecsDifferent: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
codecItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: 'Copy',
|
||||||
|
value: 'copy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'AAC',
|
||||||
|
value: 'aac'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'OPUS',
|
||||||
|
value: 'opus'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
bitrateItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: '32k',
|
||||||
|
value: '32k'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '64k',
|
||||||
|
value: '64k'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '128k',
|
||||||
|
value: '128k'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '192k',
|
||||||
|
value: '192k'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
channelsItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: '1 (mono)',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '2 (stereo)',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
customBitrateChanged(val) {
|
||||||
|
localStorage.setItem('embedMetadataBitrate', val)
|
||||||
|
},
|
||||||
|
customChannelsChanged(val) {
|
||||||
|
localStorage.setItem('embedMetadataChannels', val)
|
||||||
|
},
|
||||||
|
customCodecChanged(val) {
|
||||||
|
localStorage.setItem('embedMetadataCodec', val)
|
||||||
|
},
|
||||||
|
getEncodingOptions() {
|
||||||
|
return {
|
||||||
|
codec: this.selectedCodec || 'aac',
|
||||||
|
bitrate: this.selectedBitrate || '128k',
|
||||||
|
channels: this.selectedChannels || 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setPreset() {
|
||||||
|
// If already AAC and not mixed, set copy
|
||||||
|
if (this.currentCodec === 'aac' && !this.isCodecsDifferent) {
|
||||||
|
this.selectedCodec = 'copy'
|
||||||
|
} else {
|
||||||
|
this.selectedCodec = 'aac'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.currentBitrate) {
|
||||||
|
this.selectedBitrate = '128k'
|
||||||
|
} else {
|
||||||
|
// Find closest bitrate rounding up
|
||||||
|
const bitratesToMatch = [32, 64, 128, 192]
|
||||||
|
const closestBitrate = bitratesToMatch.find((bitrate) => bitrate >= this.currentBitrate) || 192
|
||||||
|
this.selectedBitrate = closestBitrate + 'k'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.currentChannels || isNaN(this.currentChannels)) {
|
||||||
|
this.selectedChannels = 2
|
||||||
|
} else {
|
||||||
|
// Either 1 or 2
|
||||||
|
this.selectedChannels = Math.max(Math.min(Number(this.currentChannels), 2), 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setCurrentValues() {
|
||||||
|
if (this.audioTracks.length === 0) return
|
||||||
|
|
||||||
|
this.currentChannels = this.audioTracks[0].channels
|
||||||
|
this.currentChanelLayout = this.audioTracks[0].channelLayout
|
||||||
|
this.currentCodec = this.audioTracks[0].codec
|
||||||
|
|
||||||
|
let totalBitrate = 0
|
||||||
|
for (const track of this.audioTracks) {
|
||||||
|
const trackBitrate = !isNaN(track.bitRate) ? track.bitRate : 0
|
||||||
|
totalBitrate += trackBitrate
|
||||||
|
|
||||||
|
if (track.channels > this.currentChannels) this.currentChannels = track.channels
|
||||||
|
if (track.codec !== this.currentCodec) {
|
||||||
|
console.warn('Audio track codec is different from the first track', track.codec)
|
||||||
|
this.isCodecsDifferent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentBitrate = Math.round(totalBitrate / this.audioTracks.length / 1000)
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.customBitrate = localStorage.getItem('embedMetadataBitrate') || '128k'
|
||||||
|
this.customChannels = localStorage.getItem('embedMetadataChannels') || 2
|
||||||
|
this.customCodec = localStorage.getItem('embedMetadataCodec') || 'aac'
|
||||||
|
|
||||||
|
this.setCurrentValues()
|
||||||
|
|
||||||
|
this.setPreset()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<ui-multi-select-query-input v-model="seriesItems" text-key="displayName" :label="$strings.LabelSeries" :disabled="disabled" readonly show-edit @edit="editSeriesItem" @add="addNewSeries" />
|
<ui-multi-select-query-input v-model="seriesItems" text-key="displayName" :label="$strings.LabelSeries" :disabled="disabled" readonly show-edit @edit="editSeriesItem" @add="addNewSeries" />
|
||||||
|
|
||||||
<modals-edit-series-input-inner-modal v-model="showSeriesForm" :selected-series="selectedSeries" :existing-series-names="existingSeriesNames" @submit="submitSeriesForm" />
|
<modals-edit-series-input-inner-modal v-model="showSeriesForm" :selected-series="selectedSeries" :existing-series-names="existingSeriesNames" :original-series-sequence="originalSeriesSequence" @submit="submitSeriesForm" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedSeries: null,
|
selectedSeries: null,
|
||||||
|
originalSeriesSequence: null,
|
||||||
showSeriesForm: false
|
showSeriesForm: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -59,6 +60,7 @@ export default {
|
|||||||
..._series
|
..._series
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.originalSeriesSequence = _series.sequence
|
||||||
this.showSeriesForm = true
|
this.showSeriesForm = true
|
||||||
},
|
},
|
||||||
addNewSeries() {
|
addNewSeries() {
|
||||||
@@ -68,6 +70,7 @@ export default {
|
|||||||
sequence: ''
|
sequence: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.originalSeriesSequence = null
|
||||||
this.showSeriesForm = true
|
this.showSeriesForm = true
|
||||||
},
|
},
|
||||||
submitSeriesForm() {
|
submitSeriesForm() {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export default {
|
|||||||
this.$store.commit('libraries/updateFilterDataWithItem', libraryItem)
|
this.$store.commit('libraries/updateFilterDataWithItem', libraryItem)
|
||||||
},
|
},
|
||||||
libraryItemUpdated(libraryItem) {
|
libraryItemUpdated(libraryItem) {
|
||||||
if (this.$store.state.selectedLibraryItem && this.$store.state.selectedLibraryItem.id === libraryItem.id) {
|
if (this.$store.state.selectedLibraryItem?.id === libraryItem.id) {
|
||||||
this.$store.commit('setSelectedLibraryItem', libraryItem)
|
this.$store.commit('setSelectedLibraryItem', libraryItem)
|
||||||
if (this.$store.state.globals.selectedEpisode && libraryItem.mediaType === 'podcast') {
|
if (this.$store.state.globals.selectedEpisode && libraryItem.mediaType === 'podcast') {
|
||||||
const episode = libraryItem.media.episodes.find((ep) => ep.id === this.$store.state.globals.selectedEpisode.id)
|
const episode = libraryItem.media.episodes.find((ep) => ep.id === this.$store.state.globals.selectedEpisode.id)
|
||||||
@@ -192,6 +192,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.$store.state.streamLibraryItem?.id === libraryItem.id) {
|
||||||
|
this.$store.commit('updateStreamLibraryItem', libraryItem)
|
||||||
|
}
|
||||||
this.$eventBus.$emit(`${libraryItem.id}_updated`, libraryItem)
|
this.$eventBus.$emit(`${libraryItem.id}_updated`, libraryItem)
|
||||||
this.$store.commit('libraries/updateFilterDataWithItem', libraryItem)
|
this.$store.commit('libraries/updateFilterDataWithItem', libraryItem)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import LazyBookCard from '@/components/cards/LazyBookCard'
|
|||||||
import LazySeriesCard from '@/components/cards/LazySeriesCard'
|
import LazySeriesCard from '@/components/cards/LazySeriesCard'
|
||||||
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
|
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
|
||||||
import LazyPlaylistCard from '@/components/cards/LazyPlaylistCard'
|
import LazyPlaylistCard from '@/components/cards/LazyPlaylistCard'
|
||||||
import LazyAlbumCard from '@/components/cards/LazyAlbumCard'
|
|
||||||
import AuthorCard from '@/components/cards/AuthorCard'
|
import AuthorCard from '@/components/cards/AuthorCard'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -20,7 +19,6 @@ export default {
|
|||||||
if (this.entityName === 'series') return Vue.extend(LazySeriesCard)
|
if (this.entityName === 'series') return Vue.extend(LazySeriesCard)
|
||||||
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
|
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
|
||||||
if (this.entityName === 'playlists') return Vue.extend(LazyPlaylistCard)
|
if (this.entityName === 'playlists') return Vue.extend(LazyPlaylistCard)
|
||||||
if (this.entityName === 'albums') return Vue.extend(LazyAlbumCard)
|
|
||||||
if (this.entityName === 'authors') return Vue.extend(AuthorCard)
|
if (this.entityName === 'authors') return Vue.extend(AuthorCard)
|
||||||
return Vue.extend(LazyBookCard)
|
return Vue.extend(LazyBookCard)
|
||||||
},
|
},
|
||||||
@@ -28,7 +26,6 @@ export default {
|
|||||||
if (this.entityName === 'series') return 'cards-lazy-series-card'
|
if (this.entityName === 'series') return 'cards-lazy-series-card'
|
||||||
if (this.entityName === 'collections') return 'cards-lazy-collection-card'
|
if (this.entityName === 'collections') return 'cards-lazy-collection-card'
|
||||||
if (this.entityName === 'playlists') return 'cards-lazy-playlist-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'
|
if (this.entityName === 'authors') return 'cards-author-card'
|
||||||
return 'cards-lazy-book-card'
|
return 'cards-lazy-book-card'
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.20.0",
|
"version": "2.23.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.20.0",
|
"version": "2.23.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/axios": "^5.13.6",
|
"@nuxtjs/axios": "^5.13.6",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.20.0",
|
"version": "2.23.0",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast client",
|
"description": "Self-hosted audiobook and podcast client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="page-wrapper" class="bg-bg page overflow-y-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
<div id="page-wrapper" class="bg-bg page overflow-y-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
||||||
<div class="flex items-center py-4 px-2 md:px-0 max-w-7xl mx-auto">
|
<div class="flex items-center py-4 px-4 max-w-7xl mx-auto">
|
||||||
<nuxt-link :to="`/item/${libraryItem.id}`" class="hover:underline">
|
<nuxt-link :to="`/item/${libraryItem.id}`" class="hover:underline">
|
||||||
<h1 class="text-lg lg:text-xl">{{ title }}</h1>
|
<h1 class="text-lg lg:text-xl">{{ title }}</h1>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<p class="text-base font-mono ml-4 hidden md:block">{{ $secondsToTimestamp(mediaDurationRounded) }}</p>
|
<p class="text-base font-mono ml-4 hidden md:block">{{ $secondsToTimestamp(mediaDurationRounded) }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap-reverse justify-center py-4 px-2">
|
<div class="flex flex-wrap-reverse lg:flex-nowrap justify-center py-4 px-4">
|
||||||
<div class="w-full max-w-3xl py-4">
|
<div class="w-full max-w-3xl py-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="w-12 hidden lg:block" />
|
<div class="w-12 hidden lg:block" />
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mb-3 py-1 -mx-1">
|
<div class="flex items-center mb-3 py-1 -mx-1">
|
||||||
<div class="w-12 hidden lg:block" />
|
<div class="w-12 hidden lg:block" />
|
||||||
<ui-btn v-if="chapters.length" color="bg-primary" small class="mx-1" @click.stop="removeAllChaptersClick">{{ $strings.ButtonRemoveAll }}</ui-btn>
|
<ui-btn v-if="chapters.length" color="bg-primary" small class="mx-1 whitespace-nowrap" @click.stop="removeAllChaptersClick">{{ $strings.ButtonRemoveAll }}</ui-btn>
|
||||||
<ui-btn v-if="newChapters.length > 1" :color="showShiftTimes ? 'bg' : 'primary'" class="mx-1" small @click="showShiftTimes = !showShiftTimes">{{ $strings.ButtonShiftTimes }}</ui-btn>
|
<ui-btn v-if="newChapters.length > 1" :color="showShiftTimes ? 'bg-bg' : 'bg-primary'" class="mx-1 whitespace-nowrap" small @click="showShiftTimes = !showShiftTimes">{{ $strings.ButtonShiftTimes }}</ui-btn>
|
||||||
<ui-btn color="bg-primary" small :class="{ 'mx-1': newChapters.length > 1 }" @click="showFindChaptersModal = true">{{ $strings.ButtonLookup }}</ui-btn>
|
<ui-btn color="bg-primary" small :class="{ 'mx-1': newChapters.length > 1 }" @click="showFindChaptersModal = true">{{ $strings.ButtonLookup }}</ui-btn>
|
||||||
<div class="grow" />
|
<div class="grow" />
|
||||||
<ui-btn v-if="hasChanges" small class="mx-1" @click.stop="resetChapters">{{ $strings.ButtonReset }}</ui-btn>
|
<ui-btn v-if="hasChanges" small class="mx-1" @click.stop="resetChapters">{{ $strings.ButtonReset }}</ui-btn>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<ui-time-picker v-else class="text-xs" v-model="chapter.start" :show-three-digit-hour="mediaDuration >= 360000" @change="checkChapters" />
|
<ui-time-picker v-else class="text-xs" v-model="chapter.start" :show-three-digit-hour="mediaDuration >= 360000" @change="checkChapters" />
|
||||||
</div>
|
</div>
|
||||||
<div class="grow px-1">
|
<div class="grow px-1">
|
||||||
<ui-text-input v-model="chapter.title" @change="checkChapters" class="text-xs" />
|
<ui-text-input v-model="chapter.title" @change="checkChapters" class="text-xs min-w-52" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-32 min-w-32 px-2 py-1">
|
<div class="w-32 min-w-32 px-2 py-1">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -141,10 +141,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="w-full h-full max-h-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative">
|
<div class="w-full h-full max-h-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative">
|
||||||
<div v-if="!chapterData" class="flex p-20">
|
<div v-if="!chapterData" class="flex flex-col items-center justify-center p-20">
|
||||||
<ui-text-input-with-label v-model.trim="asinInput" label="ASIN" />
|
<div class="relative">
|
||||||
<ui-dropdown v-model="regionInput" :label="$strings.LabelRegion" small :items="audibleRegions" class="w-32 mx-1" />
|
<div class="flex items-end space-x-2">
|
||||||
<ui-btn small color="bg-primary" class="mt-5" @click="findChapters">{{ $strings.ButtonSearch }}</ui-btn>
|
<ui-text-input-with-label v-model.trim="asinInput" label="ASIN" class="flex-grow" />
|
||||||
|
<ui-dropdown v-model="regionInput" :label="$strings.LabelRegion" small :items="audibleRegions" class="w-20 max-w-20" />
|
||||||
|
<ui-btn color="bg-primary" @click="findChapters">{{ $strings.ButtonSearch }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<ui-checkbox v-model="removeBranding" :label="$strings.LabelRemoveAudibleBranding" small checkbox-bg="bg" label-class="pl-2 text-base text-sm" @click="toggleRemoveBranding" />
|
||||||
|
</div>
|
||||||
|
<div class="absolute left-0 mt-1.5 text-error text-s h-5">
|
||||||
|
<p v-if="asinError">{{ asinError }}</p>
|
||||||
|
<p v-if="asinError">{{ $strings.MessageAsinCheck }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="invisible mt-1 text-xs"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="w-full p-4">
|
<div v-else class="w-full p-4">
|
||||||
<div class="flex justify-between mb-4">
|
<div class="flex justify-between mb-4">
|
||||||
@@ -221,6 +233,11 @@ export default {
|
|||||||
return redirect('/')
|
return redirect('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch and set library if this items library does not match the current
|
||||||
|
if (store.state.libraries.currentLibraryId !== libraryItem.libraryId || !store.state.libraries.filterData) {
|
||||||
|
await store.dispatch('libraries/fetch', libraryItem.libraryId)
|
||||||
|
}
|
||||||
|
|
||||||
var previousRoute = from ? from.fullPath : null
|
var previousRoute = from ? from.fullPath : null
|
||||||
if (from && from.path === '/login') previousRoute = null
|
if (from && from.path === '/login') previousRoute = null
|
||||||
return {
|
return {
|
||||||
@@ -244,6 +261,8 @@ export default {
|
|||||||
findingChapters: false,
|
findingChapters: false,
|
||||||
showFindChaptersModal: false,
|
showFindChaptersModal: false,
|
||||||
chapterData: null,
|
chapterData: null,
|
||||||
|
asinError: null,
|
||||||
|
removeBranding: false,
|
||||||
showSecondInputs: false,
|
showSecondInputs: false,
|
||||||
audibleRegions: ['US', 'CA', 'UK', 'AU', 'FR', 'DE', 'JP', 'IT', 'IN', 'ES'],
|
audibleRegions: ['US', 'CA', 'UK', 'AU', 'FR', 'DE', 'JP', 'IT', 'IN', 'ES'],
|
||||||
hasChanges: false
|
hasChanges: false
|
||||||
@@ -305,6 +324,9 @@ export default {
|
|||||||
|
|
||||||
this.checkChapters()
|
this.checkChapters()
|
||||||
},
|
},
|
||||||
|
toggleRemoveBranding() {
|
||||||
|
this.removeBranding = !this.removeBranding
|
||||||
|
},
|
||||||
shiftChapterTimes() {
|
shiftChapterTimes() {
|
||||||
if (!this.shiftAmount || isNaN(this.shiftAmount) || this.newChapters.length <= 1) {
|
if (!this.shiftAmount || isNaN(this.shiftAmount) || this.newChapters.length <= 1) {
|
||||||
return
|
return
|
||||||
@@ -314,12 +336,12 @@ export default {
|
|||||||
|
|
||||||
const lastChapter = this.newChapters[this.newChapters.length - 1]
|
const lastChapter = this.newChapters[this.newChapters.length - 1]
|
||||||
if (lastChapter.start + amount > this.mediaDurationRounded) {
|
if (lastChapter.start + amount > this.mediaDurationRounded) {
|
||||||
this.$toast.error('Invalid shift amount. Last chapter start time would extend beyond the duration of this audiobook.')
|
this.$toast.error(this.$strings.ToastChaptersInvalidShiftAmountLast)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.newChapters[0].end + amount <= 0) {
|
if (this.newChapters[1].start + amount <= 0) {
|
||||||
this.$toast.error('Invalid shift amount. First chapter would have zero or negative length.')
|
this.$toast.error(this.$strings.ToastChaptersInvalidShiftAmountStart)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,17 +563,17 @@ export default {
|
|||||||
|
|
||||||
this.findingChapters = true
|
this.findingChapters = true
|
||||||
this.chapterData = null
|
this.chapterData = null
|
||||||
|
this.asinError = null // used to show warning about audible vs amazon ASIN
|
||||||
this.$axios
|
this.$axios
|
||||||
.$get(`/api/search/chapters?asin=${this.asinInput}®ion=${this.regionInput}`)
|
.$get(`/api/search/chapters?asin=${this.asinInput}®ion=${this.regionInput}`)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.findingChapters = false
|
this.findingChapters = false
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
this.$toast.error(data.error)
|
this.asinError = this.$getString(data.stringKey)
|
||||||
this.showFindChaptersModal = false
|
|
||||||
} else {
|
} else {
|
||||||
console.log('Chapter data', data)
|
console.log('Chapter data', data)
|
||||||
this.chapterData = data
|
this.chapterData = this.removeBranding ? this.removeBrandingFromData(data) : data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -561,6 +583,37 @@ export default {
|
|||||||
this.showFindChaptersModal = false
|
this.showFindChaptersModal = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
removeBrandingFromData(data) {
|
||||||
|
if (!data) return data
|
||||||
|
try {
|
||||||
|
const introDuration = data.brandIntroDurationMs
|
||||||
|
const outroDuration = data.brandOutroDurationMs
|
||||||
|
|
||||||
|
for (let i = 0; i < data.chapters.length; i++) {
|
||||||
|
const chapter = data.chapters[i]
|
||||||
|
if (chapter.startOffsetMs < introDuration) {
|
||||||
|
// This should never happen, as the intro is not longer than the first chapter
|
||||||
|
// If this happens set to the next second
|
||||||
|
// Will be 0 for the first chapter anayways
|
||||||
|
chapter.startOffsetMs = i * 1000
|
||||||
|
chapter.startOffsetSec = i
|
||||||
|
} else {
|
||||||
|
chapter.startOffsetMs -= introDuration
|
||||||
|
chapter.startOffsetSec = Math.floor(chapter.startOffsetMs / 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastChapter = data.chapters[data.chapters.length - 1]
|
||||||
|
// If there is an outro that's in the outro duration, remove it
|
||||||
|
if (lastChapter && lastChapter.lengthMs <= outroDuration) {
|
||||||
|
data.chapters.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
} catch {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
},
|
||||||
resetChapters() {
|
resetChapters() {
|
||||||
const payload = {
|
const payload = {
|
||||||
message: this.$strings.MessageResetChaptersConfirm,
|
message: this.$strings.MessageResetChaptersConfirm,
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ export default {
|
|||||||
console.error('No need to edit library item that is 1 file...')
|
console.error('No need to edit library item that is 1 file...')
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch and set library if this items library does not match the current
|
||||||
|
if (store.state.libraries.currentLibraryId !== libraryItem.libraryId || !store.state.libraries.filterData) {
|
||||||
|
await store.dispatch('libraries/fetch', libraryItem.libraryId)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
libraryItem,
|
libraryItem,
|
||||||
files: libraryItem.media.audioFiles ? libraryItem.media.audioFiles.map((af) => ({ ...af, include: !af.exclude })) : []
|
files: libraryItem.media.audioFiles ? libraryItem.media.audioFiles.map((af) => ({ ...af, include: !af.exclude })) : []
|
||||||
|
|||||||
@@ -2,7 +2,14 @@
|
|||||||
<div id="page-wrapper" class="bg-bg page p-8 overflow-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
<div id="page-wrapper" class="bg-bg page p-8 overflow-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
||||||
<div class="flex items-center justify-center mb-6">
|
<div class="flex items-center justify-center mb-6">
|
||||||
<div class="w-full max-w-2xl">
|
<div class="w-full max-w-2xl">
|
||||||
<p class="text-2xl mb-2">{{ $strings.HeaderAudiobookTools }}</p>
|
<div class="flex items-center mb-4">
|
||||||
|
<nuxt-link :to="`/item/${libraryItem.id}`" class="hover:underline">
|
||||||
|
<h1 class="text-lg lg:text-xl">{{ mediaMetadata.title }}</h1>
|
||||||
|
</nuxt-link>
|
||||||
|
<button class="w-7 h-7 flex items-center justify-center mx-4 hover:scale-110 duration-100 transform text-gray-200 hover:text-white" @click="editItem">
|
||||||
|
<span class="material-symbols text-base">edit</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-2xl">
|
<div class="w-full max-w-2xl">
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
@@ -13,13 +20,13 @@
|
|||||||
|
|
||||||
<div class="flex justify-center mb-2">
|
<div class="flex justify-center mb-2">
|
||||||
<div class="w-full max-w-2xl">
|
<div class="w-full max-w-2xl">
|
||||||
<p class="text-xl">{{ $strings.HeaderMetadataToEmbed }}</p>
|
<p class="text-lg">{{ $strings.HeaderMetadataToEmbed }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-2xl"></div>
|
<div class="w-full max-w-2xl"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center flex-wrap">
|
<div class="flex justify-center flex-wrap lg:flex-nowrap gap-4">
|
||||||
<div class="w-full max-w-2xl border border-white/10 bg-bg mx-2">
|
<div class="w-full max-w-2xl border border-white/10 bg-bg">
|
||||||
<div class="flex py-2 px-4">
|
<div class="flex py-2 px-4">
|
||||||
<div class="w-1/3 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelMetaTag }}</div>
|
<div class="w-1/3 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelMetaTag }}</div>
|
||||||
<div class="w-2/3 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelValue }}</div>
|
<div class="w-2/3 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelValue }}</div>
|
||||||
@@ -35,7 +42,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-2xl border border-white/10 bg-bg mx-2">
|
<div class="w-full max-w-2xl border border-white/10 bg-bg">
|
||||||
<div class="flex py-2 px-4 bg-primary/25">
|
<div class="flex py-2 px-4 bg-primary/25">
|
||||||
<div class="grow text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelChapterTitle }}</div>
|
<div class="grow text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelChapterTitle }}</div>
|
||||||
<div class="w-24 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelStart }}</div>
|
<div class="w-24 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelStart }}</div>
|
||||||
@@ -77,10 +84,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- m4b embed action buttons -->
|
<!-- m4b embed action buttons -->
|
||||||
<div v-else class="w-full flex items-center mb-4">
|
<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">{{ $strings.LabelUseAdvancedOptions }}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="grow" />
|
<div class="grow" />
|
||||||
|
|
||||||
<ui-btn v-if="!isTaskFinished && processing" color="bg-error" :loading="isCancelingEncode" class="mr-2" @click.stop="cancelEncodeClick">{{ $strings.ButtonCancelEncode }}</ui-btn>
|
<ui-btn v-if="!isTaskFinished && processing" color="bg-error" :loading="isCancelingEncode" class="mr-2" @click.stop="cancelEncodeClick">{{ $strings.ButtonCancelEncode }}</ui-btn>
|
||||||
@@ -89,18 +92,16 @@
|
|||||||
<p v-else class="text-success text-lg font-semibold">{{ $strings.MessageM4BFinished }}</p>
|
<p v-else class="text-success text-lg font-semibold">{{ $strings.MessageM4BFinished }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- advanced encoding options -->
|
<!-- show encoding options for running task -->
|
||||||
<div v-if="isM4BTool" class="overflow-hidden">
|
<div v-if="encodeTaskHasEncodingOptions" class="mb-4 pb-4 border-b border-white/10">
|
||||||
<transition name="slide">
|
<div class="flex flex-wrap -mx-2">
|
||||||
<div v-if="showEncodeOptions || usingCustomEncodeOptions" class="mb-4 pb-4 border-b border-white/10">
|
<ui-text-input-with-label ref="bitrateInput" v-model="encodingOptions.bitrate" readonly :label="$strings.LabelAudioBitrate" class="m-2 max-w-40" @input="bitrateChanged" />
|
||||||
<div class="flex flex-wrap -mx-2">
|
<ui-text-input-with-label ref="channelsInput" v-model="encodingOptions.channels" readonly :label="$strings.LabelAudioChannels" class="m-2 max-w-40" @input="channelsChanged" />
|
||||||
<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="codecInput" v-model="encodingOptions.codec" readonly :label="$strings.LabelAudioCodec" class="m-2 max-w-40" @input="codecChanged" />
|
||||||
<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" />
|
</div>
|
||||||
<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>
|
||||||
</div>
|
<div v-else-if="isM4BTool" class="mb-4">
|
||||||
<p class="text-sm text-warning">{{ $strings.LabelEncodingWarningAdvancedSettings }}</p>
|
<widgets-encoder-options-card ref="encoderOptionsCard" :audio-tracks="audioFiles" :disabled="processing || isTaskFinished" />
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
@@ -146,19 +147,29 @@
|
|||||||
<div class="flex py-2 px-4 bg-primary/25">
|
<div class="flex py-2 px-4 bg-primary/25">
|
||||||
<div class="w-10 text-xs font-semibold text-gray-200">#</div>
|
<div class="w-10 text-xs font-semibold text-gray-200">#</div>
|
||||||
<div class="grow text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelFilename }}</div>
|
<div class="grow text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelFilename }}</div>
|
||||||
|
<div class="w-20 text-xs font-semibold uppercase text-gray-200 hidden lg:block">{{ $strings.LabelChannels }}</div>
|
||||||
|
<div class="w-16 text-xs font-semibold uppercase text-gray-200 hidden md:block">{{ $strings.LabelCodec }}</div>
|
||||||
|
<div class="w-16 text-xs font-semibold uppercase text-gray-200 hidden md:block">{{ $strings.LabelBitrate }}</div>
|
||||||
<div class="w-16 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelSize }}</div>
|
<div class="w-16 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelSize }}</div>
|
||||||
<div class="w-24"></div>
|
<div class="w-24"></div>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="file in audioFiles">
|
<template v-for="file in audioFiles">
|
||||||
<div :key="file.index" class="flex py-2 px-4 text-sm" :class="file.index % 2 === 0 ? 'bg-primary/25' : ''">
|
<div :key="file.index" class="flex py-2 px-4 text-xs sm:text-sm" :class="file.index % 2 === 0 ? 'bg-primary/25' : ''">
|
||||||
<div class="w-10">{{ file.index }}</div>
|
<div class="w-10 min-w-10">{{ file.index }}</div>
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
{{ file.metadata.filename }}
|
{{ file.metadata.filename }}
|
||||||
</div>
|
</div>
|
||||||
<div class="w-16 font-mono text-gray-200">
|
<div class="w-20 min-w-20 text-gray-200 hidden lg:block">{{ file.channels || 'unknown' }} ({{ file.channelLayout || 'unknown' }})</div>
|
||||||
|
<div class="w-16 min-w-16 text-gray-200 hidden md:block">
|
||||||
|
{{ file.codec || 'unknown' }}
|
||||||
|
</div>
|
||||||
|
<div class="w-16 min-w-16 text-gray-200 hidden md:block">
|
||||||
|
{{ $bytesPretty(file.bitRate || 0, 0) }}
|
||||||
|
</div>
|
||||||
|
<div class="w-16 min-w-16 text-gray-200">
|
||||||
{{ $bytesPretty(file.metadata.size) }}
|
{{ $bytesPretty(file.metadata.size) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="w-24">
|
<div class="w-24 min-w-24">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<span v-if="audioFilesFinished[file.ino]" class="material-symbols text-xl text-success leading-none">check_circle</span>
|
<span v-if="audioFilesFinished[file.ino]" class="material-symbols text-xl text-success leading-none">check_circle</span>
|
||||||
<div v-else-if="audioFilesEncoding[file.ino]">
|
<div v-else-if="audioFilesEncoding[file.ino]">
|
||||||
@@ -195,10 +206,15 @@ export default {
|
|||||||
return redirect('/?error=invalid media type')
|
return redirect('/?error=invalid media type')
|
||||||
}
|
}
|
||||||
if (!libraryItem.media.audioFiles.length) {
|
if (!libraryItem.media.audioFiles.length) {
|
||||||
cnosole.error('No audio files')
|
console.error('No audio files')
|
||||||
return redirect('/?error=no audio files')
|
return redirect('/?error=no audio files')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch and set library if this items library does not match the current
|
||||||
|
if (store.state.libraries.currentLibraryId !== libraryItem.libraryId || !store.state.libraries.filterData) {
|
||||||
|
await store.dispatch('libraries/fetch', libraryItem.libraryId)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
libraryItem
|
libraryItem
|
||||||
}
|
}
|
||||||
@@ -209,7 +225,6 @@ export default {
|
|||||||
metadataObject: null,
|
metadataObject: null,
|
||||||
selectedTool: 'embed',
|
selectedTool: 'embed',
|
||||||
isCancelingEncode: false,
|
isCancelingEncode: false,
|
||||||
showEncodeOptions: false,
|
|
||||||
shouldBackupAudioFiles: true,
|
shouldBackupAudioFiles: true,
|
||||||
encodingOptions: {
|
encodingOptions: {
|
||||||
bitrate: '128k',
|
bitrate: '128k',
|
||||||
@@ -258,9 +273,6 @@ export default {
|
|||||||
audioFiles() {
|
audioFiles() {
|
||||||
return (this.media.audioFiles || []).filter((af) => !af.exclude)
|
return (this.media.audioFiles || []).filter((af) => !af.exclude)
|
||||||
},
|
},
|
||||||
isSingleM4b() {
|
|
||||||
return this.audioFiles.length === 1 && this.audioFiles[0].metadata.ext.toLowerCase() === '.m4b'
|
|
||||||
},
|
|
||||||
streamLibraryItem() {
|
streamLibraryItem() {
|
||||||
return this.$store.state.streamLibraryItem
|
return this.$store.state.streamLibraryItem
|
||||||
},
|
},
|
||||||
@@ -268,14 +280,10 @@ export default {
|
|||||||
return this.media.chapters || []
|
return this.media.chapters || []
|
||||||
},
|
},
|
||||||
availableTools() {
|
availableTools() {
|
||||||
if (this.isSingleM4b) {
|
return [
|
||||||
return [{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata }]
|
{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata },
|
||||||
} else {
|
{ value: 'm4b', text: this.$strings.LabelToolsM4bEncoder }
|
||||||
return [
|
]
|
||||||
{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata },
|
|
||||||
{ value: 'm4b', text: this.$strings.LabelToolsM4bEncoder }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
taskFailed() {
|
taskFailed() {
|
||||||
return this.isTaskFinished && this.task.isFailed
|
return this.isTaskFinished && this.task.isFailed
|
||||||
@@ -309,8 +317,8 @@ export default {
|
|||||||
isMetadataEmbedQueued() {
|
isMetadataEmbedQueued() {
|
||||||
return this.queuedEmbedLIds.some((lid) => lid === this.libraryItemId)
|
return this.queuedEmbedLIds.some((lid) => lid === this.libraryItemId)
|
||||||
},
|
},
|
||||||
usingCustomEncodeOptions() {
|
encodeTaskHasEncodingOptions() {
|
||||||
return this.isM4BTool && this.encodeTask && this.encodeTask.data.encodeOptions && Object.keys(this.encodeTask.data.encodeOptions).length > 0
|
return this.isM4BTool && !!this.encodeTask?.data.encodeOptions && Object.keys(this.encodeTask.data.encodeOptions).length > 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -346,19 +354,13 @@ export default {
|
|||||||
if (this.$refs.channelsInput) this.$refs.channelsInput.blur()
|
if (this.$refs.channelsInput) this.$refs.channelsInput.blur()
|
||||||
if (this.$refs.codecInput) this.$refs.codecInput.blur()
|
if (this.$refs.codecInput) this.$refs.codecInput.blur()
|
||||||
|
|
||||||
let queryStr = ''
|
const encodeOptions = this.$refs.encoderOptionsCard.getEncodingOptions()
|
||||||
if (this.showEncodeOptions) {
|
|
||||||
const options = []
|
const queryParams = new URLSearchParams(encodeOptions)
|
||||||
if (this.encodingOptions.bitrate) options.push(`bitrate=${this.encodingOptions.bitrate}`)
|
|
||||||
if (this.encodingOptions.channels) options.push(`channels=${this.encodingOptions.channels}`)
|
|
||||||
if (this.encodingOptions.codec) options.push(`codec=${this.encodingOptions.codec}`)
|
|
||||||
if (options.length) {
|
|
||||||
queryStr = `?${options.join('&')}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.processing = true
|
this.processing = true
|
||||||
this.$axios
|
this.$axios
|
||||||
.$post(`/api/tools/item/${this.libraryItemId}/encode-m4b${queryStr}`)
|
.$post(`/api/tools/item/${this.libraryItemId}/encode-m4b?${queryParams.toString()}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('Ab m4b merge started')
|
console.log('Ab m4b merge started')
|
||||||
})
|
})
|
||||||
@@ -411,14 +413,10 @@ export default {
|
|||||||
const shouldBackupAudioFiles = localStorage.getItem('embedMetadataShouldBackup')
|
const shouldBackupAudioFiles = localStorage.getItem('embedMetadataShouldBackup')
|
||||||
this.shouldBackupAudioFiles = shouldBackupAudioFiles != 0
|
this.shouldBackupAudioFiles = shouldBackupAudioFiles != 0
|
||||||
|
|
||||||
if (this.usingCustomEncodeOptions) {
|
if (this.encodeTaskHasEncodingOptions) {
|
||||||
if (this.encodeTask.data.encodeOptions.bitrate) this.encodingOptions.bitrate = this.encodeTask.data.encodeOptions.bitrate
|
if (this.encodeTask.data.encodeOptions.bitrate) this.encodingOptions.bitrate = this.encodeTask.data.encodeOptions.bitrate
|
||||||
if (this.encodeTask.data.encodeOptions.channels) this.encodingOptions.channels = this.encodeTask.data.encodeOptions.channels
|
if (this.encodeTask.data.encodeOptions.channels) this.encodingOptions.channels = this.encodeTask.data.encodeOptions.channels
|
||||||
if (this.encodeTask.data.encodeOptions.codec) this.encodingOptions.codec = this.encodeTask.data.encodeOptions.codec
|
if (this.encodeTask.data.encodeOptions.codec) this.encodingOptions.codec = this.encodeTask.data.encodeOptions.codec
|
||||||
} else {
|
|
||||||
this.encodingOptions.bitrate = localStorage.getItem('embedMetadataBitrate') || '128k'
|
|
||||||
this.encodingOptions.channels = localStorage.getItem('embedMetadataChannels') || '2'
|
|
||||||
this.encodingOptions.codec = localStorage.getItem('embedMetadataCodec') || 'aac'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchMetadataEmbedObject() {
|
fetchMetadataEmbedObject() {
|
||||||
@@ -433,10 +431,24 @@ export default {
|
|||||||
},
|
},
|
||||||
taskUpdated(task) {
|
taskUpdated(task) {
|
||||||
this.processing = !task.isFinished
|
this.processing = !task.isFinished
|
||||||
|
},
|
||||||
|
editItem() {
|
||||||
|
this.$store.commit('showEditModal', this.libraryItem)
|
||||||
|
},
|
||||||
|
libraryItemUpdated(libraryItem) {
|
||||||
|
if (libraryItem.id === this.libraryItem.id) {
|
||||||
|
this.libraryItem = libraryItem
|
||||||
|
this.fetchMetadataEmbedObject()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init()
|
this.init()
|
||||||
|
|
||||||
|
this.$eventBus.$on(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$eventBus.$off(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -122,7 +122,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full flex items-center justify-end p-4">
|
<div class="w-full flex items-center justify-between p-4">
|
||||||
|
<p v-if="enableOpenIDAuth" class="text-sm text-warning">{{ $strings.MessageAuthenticationOIDCChangesRestart }}</p>
|
||||||
<ui-btn color="bg-success" :padding-x="8" small class="text-base" :loading="savingSettings" @click="saveSettings">{{ $strings.ButtonSave }}</ui-btn>
|
<ui-btn color="bg-success" :padding-x="8" small class="text-base" :loading="savingSettings" @click="saveSettings">{{ $strings.ButtonSave }}</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
</app-settings-content>
|
</app-settings-content>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<p class="truncate">{{ feed.meta.title }}</p>
|
<p class="truncate">{{ feed.meta.title }}</p>
|
||||||
</td>
|
</td>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<td class="hidden xl:table-cell">
|
<td class="hidden xl:table-cell max-w-48">
|
||||||
<p class="truncate">{{ feed.slug }}</p>
|
<p class="truncate">{{ feed.slug }}</p>
|
||||||
</td>
|
</td>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<ui-icon-btn icon="delete" class="mx-0.5" :size="7" bg-color="bg-error" outlined @click.stop="deleteFeedClick(feed)" />
|
<ui-icon-btn icon="delete" class="mx-0.5 text-white/70" borderless :size="7" iconFontSize="1.25rem" outlined @click.stop="deleteFeedClick(feed)" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -91,15 +91,15 @@
|
|||||||
{{ isMissing ? $strings.LabelMissing : $strings.LabelIncomplete }}
|
{{ isMissing ? $strings.LabelMissing : $strings.LabelIncomplete }}
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
|
|
||||||
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
|
|
||||||
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_play'" :bg-color="isQueued ? 'bg-primary' : 'bg-success/60'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
|
|
||||||
</ui-tooltip>
|
|
||||||
|
|
||||||
<ui-btn v-if="showReadButton" color="bg-info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
|
<ui-btn v-if="showReadButton" color="bg-info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
|
||||||
<span class="material-symbols text-2xl -ml-2 pr-2 text-white" aria-hidden="true">auto_stories</span>
|
<span class="material-symbols text-2xl -ml-2 pr-2 text-white" aria-hidden="true">auto_stories</span>
|
||||||
{{ $strings.ButtonRead }}
|
{{ $strings.ButtonRead }}
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
|
|
||||||
|
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
|
||||||
|
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_play'" :bg-color="isQueued ? 'bg-primary' : 'bg-success/60'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
|
||||||
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-tooltip v-if="userCanUpdate" :text="$strings.LabelEdit" direction="top">
|
<ui-tooltip v-if="userCanUpdate" :text="$strings.LabelEdit" direction="top">
|
||||||
<ui-icon-btn icon="" outlined class="mx-0.5" :aria-label="$strings.LabelEdit" @click="editClick" />
|
<ui-icon-btn icon="" outlined class="mx-0.5" :aria-label="$strings.LabelEdit" @click="editClick" />
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async loadRecentEpisodes(page = 0) {
|
async loadRecentEpisodes(page = 0) {
|
||||||
this.processing = true
|
this.processing = true
|
||||||
const episodePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/recent-episodes?limit=25&page=${page}`).catch((error) => {
|
const episodePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/recent-episodes?limit=50&page=${page}`).catch((error) => {
|
||||||
console.error('Failed to get recent episodes', error)
|
console.error('Failed to get recent episodes', error)
|
||||||
this.$toast.error(this.$strings.ToastFailedToLoadData)
|
this.$toast.error(this.$strings.ToastFailedToLoadData)
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default {
|
|||||||
})
|
})
|
||||||
results = {
|
results = {
|
||||||
podcasts: results?.podcast || [],
|
podcasts: results?.podcast || [],
|
||||||
|
episodes: results?.episodes || [],
|
||||||
books: results?.book || [],
|
books: results?.book || [],
|
||||||
authors: results?.authors || [],
|
authors: results?.authors || [],
|
||||||
series: results?.series || [],
|
series: results?.series || [],
|
||||||
@@ -61,6 +62,7 @@ export default {
|
|||||||
})
|
})
|
||||||
this.results = {
|
this.results = {
|
||||||
podcasts: results?.podcast || [],
|
podcasts: results?.podcast || [],
|
||||||
|
episodes: results?.episodes || [],
|
||||||
books: results?.book || [],
|
books: results?.book || [],
|
||||||
authors: results?.authors || [],
|
authors: results?.authors || [],
|
||||||
series: results?.series || [],
|
series: results?.series || [],
|
||||||
|
|||||||
@@ -89,14 +89,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
asyncData({ redirect, store }) {
|
async asyncData({ redirect, store, params }) {
|
||||||
if (!store.getters['user/getIsAdminOrUp']) {
|
if (!store.getters['user/getIsAdminOrUp']) {
|
||||||
redirect('/')
|
redirect('/')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!store.state.libraries.currentLibraryId) {
|
const libraryId = params.library
|
||||||
return redirect('/config')
|
const library = await store.dispatch('libraries/fetch', libraryId)
|
||||||
|
if (!library) {
|
||||||
|
return redirect(`/oops?message=Library "${libraryId}" not found`)
|
||||||
}
|
}
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full h-dvh max-h-dvh overflow-hidden" :style="{ backgroundColor: coverRgb }">
|
<div class="w-full max-w-full h-dvh max-h-dvh overflow-hidden" :style="{ backgroundColor: coverRgb }">
|
||||||
<div class="w-screen h-screen absolute inset-0 pointer-events-none" style="background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(38, 38, 38, 1) 80%)"></div>
|
<div class="w-screen h-screen absolute inset-0 pointer-events-none" style="background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(38, 38, 38, 1) 80%)"></div>
|
||||||
<div class="absolute inset-0 w-screen h-dvh flex items-center justify-center z-10">
|
<div class="absolute inset-0 w-screen h-dvh flex items-center justify-center z-10">
|
||||||
<div class="w-full p-2 sm:p-4 md:p-8">
|
<div class="w-full p-2 sm:p-4 md:p-8">
|
||||||
@@ -335,8 +335,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
resize() {
|
resize() {
|
||||||
this.windowWidth = window.innerWidth
|
setTimeout(() => {
|
||||||
this.windowHeight = window.innerHeight
|
this.windowWidth = window.innerWidth
|
||||||
|
this.windowHeight = window.innerHeight
|
||||||
|
this.$store.commit('globals/updateWindowSize', { width: window.innerWidth, height: window.innerHeight })
|
||||||
|
}, 100)
|
||||||
},
|
},
|
||||||
playerError(error) {
|
playerError(error) {
|
||||||
console.error('Player error', error)
|
console.error('Player error', error)
|
||||||
|
|||||||
@@ -316,9 +316,8 @@ export default {
|
|||||||
.$post('/api/upload', form)
|
.$post('/api/upload', form)
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed', error)
|
console.error('Failed to upload item', error)
|
||||||
var errorMessage = error.response && error.response.data ? error.response.data : 'Oops, something went wrong...'
|
this.$toast.error(error.response?.data || 'Oops, something went wrong...')
|
||||||
this.$toast.error(errorMessage)
|
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -360,15 +359,14 @@ export default {
|
|||||||
// Check if path already exists before starting upload
|
// Check if path already exists before starting upload
|
||||||
// uploading fails if path already exists
|
// uploading fails if path already exists
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const filepath = Path.join(this.selectedFolder.fullPath, item.directory)
|
|
||||||
const exists = await this.$axios
|
const exists = await this.$axios
|
||||||
.$post(`/api/filesystem/pathexists`, { filepath, directory: item.directory, folderPath: this.selectedFolder.fullPath })
|
.$post(`/api/filesystem/pathexists`, { directory: item.directory, folderPath: this.selectedFolder.fullPath })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.exists) {
|
if (data.exists) {
|
||||||
if (data.libraryItemTitle) {
|
if (data.libraryItemTitle) {
|
||||||
this.$toast.error(this.$getString('ToastUploaderItemExistsInSubdirectoryError', [data.libraryItemTitle]))
|
this.$toast.error(this.$getString('ToastUploaderItemExistsInSubdirectoryError', [data.libraryItemTitle]))
|
||||||
} else {
|
} else {
|
||||||
this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [filepath]))
|
this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [Path.join(this.selectedFolder.fullPath, item.directory)]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data.exists
|
return data.exists
|
||||||
@@ -382,13 +380,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemsUploaded = 0
|
|
||||||
let itemsFailed = 0
|
|
||||||
for (const item of itemsToUpload) {
|
for (const item of itemsToUpload) {
|
||||||
this.updateItemCardStatus(item.index, 'uploading')
|
this.updateItemCardStatus(item.index, 'uploading')
|
||||||
const result = await this.uploadItem(item)
|
const result = await this.uploadItem(item)
|
||||||
if (result) itemsUploaded++
|
|
||||||
else itemsFailed++
|
|
||||||
this.updateItemCardStatus(item.index, result ? 'success' : 'failed')
|
this.updateItemCardStatus(item.index, result ? 'success' : 'failed')
|
||||||
}
|
}
|
||||||
this.processing = false
|
this.processing = false
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default class AudioTrack {
|
export default class AudioTrack {
|
||||||
constructor(track, userToken, routerBasePath) {
|
constructor(track, sessionId, routerBasePath) {
|
||||||
this.index = track.index || 0
|
this.index = track.index || 0
|
||||||
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
||||||
this.duration = track.duration || 0
|
this.duration = track.duration || 0
|
||||||
@@ -8,28 +8,29 @@ export default class AudioTrack {
|
|||||||
this.mimeType = track.mimeType
|
this.mimeType = track.mimeType
|
||||||
this.metadata = track.metadata || {}
|
this.metadata = track.metadata || {}
|
||||||
|
|
||||||
this.userToken = userToken
|
this.sessionId = sessionId
|
||||||
this.routerBasePath = routerBasePath || ''
|
this.routerBasePath = routerBasePath || ''
|
||||||
|
if (this.contentUrl?.startsWith('/hls')) {
|
||||||
|
this.sessionTrackUrl = this.contentUrl
|
||||||
|
} else {
|
||||||
|
this.sessionTrackUrl = `/public/session/${sessionId}/track/${this.index}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for CastPlayer
|
* Used for CastPlayer
|
||||||
*/
|
*/
|
||||||
get fullContentUrl() {
|
get fullContentUrl() {
|
||||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
return `${process.env.serverUrl}${this.contentUrl}?token=${this.userToken}`
|
return `${process.env.serverUrl}${this.sessionTrackUrl}`
|
||||||
}
|
}
|
||||||
return `${window.location.origin}${this.routerBasePath}${this.contentUrl}?token=${this.userToken}`
|
return `${window.location.origin}${this.routerBasePath}${this.sessionTrackUrl}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for LocalPlayer
|
* Used for LocalPlayer
|
||||||
*/
|
*/
|
||||||
get relativeContentUrl() {
|
get relativeContentUrl() {
|
||||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
return `${this.routerBasePath}${this.sessionTrackUrl}`
|
||||||
|
|
||||||
return `${this.routerBasePath}${this.contentUrl}?token=${this.userToken}`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ export default class PlayerHandler {
|
|||||||
get isPlayingLocalItem() {
|
get isPlayingLocalItem() {
|
||||||
return this.libraryItem && this.player instanceof LocalAudioPlayer
|
return this.libraryItem && this.player instanceof LocalAudioPlayer
|
||||||
}
|
}
|
||||||
get userToken() {
|
|
||||||
return this.ctx.$store.getters['user/getToken']
|
|
||||||
}
|
|
||||||
get playerPlaying() {
|
get playerPlaying() {
|
||||||
return this.playerState === 'PLAYING'
|
return this.playerState === 'PLAYING'
|
||||||
}
|
}
|
||||||
@@ -226,7 +223,7 @@ export default class PlayerHandler {
|
|||||||
|
|
||||||
console.log('[PlayerHandler] Preparing Session', session)
|
console.log('[PlayerHandler] Preparing Session', session)
|
||||||
|
|
||||||
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, this.userToken, this.ctx.$config.routerBasePath))
|
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, session.id, this.ctx.$config.routerBasePath))
|
||||||
|
|
||||||
this.ctx.playerLoading = true
|
this.ctx.playerLoading = true
|
||||||
this.isHlsTranscode = true
|
this.isHlsTranscode = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const SupportedFileTypes = {
|
const SupportedFileTypes = {
|
||||||
image: ['png', 'jpg', 'jpeg', 'webp'],
|
image: ['png', 'jpg', 'jpeg', 'webp'],
|
||||||
audio: ['m4b', 'mp3', 'm4a', 'flac', 'opus', 'ogg', 'oga', 'mp4', 'aac', 'wma', 'aiff', 'wav', 'webm', 'webma', 'mka', 'awb', 'caf', 'mpeg', 'mpg'],
|
audio: ['m4b', 'mp3', 'm4a', 'flac', 'opus', 'ogg', 'oga', 'mp4', 'aac', 'wma', 'aiff', 'aif','wav', 'webm', 'webma', 'mka', 'awb', 'caf', 'mpeg', 'mpg'],
|
||||||
ebook: ['epub', 'pdf', 'mobi', 'azw3', 'cbr', 'cbz'],
|
ebook: ['epub', 'pdf', 'mobi', 'azw3', 'cbr', 'cbz'],
|
||||||
info: ['nfo'],
|
info: ['nfo'],
|
||||||
text: ['txt'],
|
text: ['txt'],
|
||||||
|
|||||||
@@ -171,6 +171,10 @@ export const mutations = {
|
|||||||
state.playerQueueItems = payload.queueItems || []
|
state.playerQueueItems = payload.queueItems || []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateStreamLibraryItem(state, libraryItem) {
|
||||||
|
if (!libraryItem) return
|
||||||
|
state.streamLibraryItem = libraryItem
|
||||||
|
},
|
||||||
setIsPlaying(state, isPlaying) {
|
setIsPlaying(state, isPlaying) {
|
||||||
state.streamIsPlaying = isPlaying
|
state.streamIsPlaying = isPlaying
|
||||||
},
|
},
|
||||||
|
|||||||
+123
-8
@@ -39,7 +39,7 @@
|
|||||||
"ButtonIssues": "مشاكل",
|
"ButtonIssues": "مشاكل",
|
||||||
"ButtonJumpBackward": "اقفز للخلف",
|
"ButtonJumpBackward": "اقفز للخلف",
|
||||||
"ButtonJumpForward": "اقفز للأمام",
|
"ButtonJumpForward": "اقفز للأمام",
|
||||||
"ButtonLatest": "أحدث",
|
"ButtonLatest": "الأحدث",
|
||||||
"ButtonLibrary": "المكتبة",
|
"ButtonLibrary": "المكتبة",
|
||||||
"ButtonLogout": "تسجيل الخروج",
|
"ButtonLogout": "تسجيل الخروج",
|
||||||
"ButtonLookup": "البحث",
|
"ButtonLookup": "البحث",
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
"ButtonNext": "التالي",
|
"ButtonNext": "التالي",
|
||||||
"ButtonNextChapter": "الفصل التالي",
|
"ButtonNextChapter": "الفصل التالي",
|
||||||
"ButtonNextItemInQueue": "العنصر التالي في قائمة الانتظار",
|
"ButtonNextItemInQueue": "العنصر التالي في قائمة الانتظار",
|
||||||
"ButtonOk": "نعم",
|
"ButtonOk": "موافق",
|
||||||
"ButtonOpenFeed": "فتح التغذية",
|
"ButtonOpenFeed": "فتح التغذية",
|
||||||
"ButtonOpenManager": "فتح الإدارة",
|
"ButtonOpenManager": "فتح الإدارة",
|
||||||
"ButtonPause": "تَوَقَّف",
|
"ButtonPause": "إيقاف مؤقت",
|
||||||
"ButtonPlay": "تشغيل",
|
"ButtonPlay": "تشغيل",
|
||||||
"ButtonPlayAll": "تشغيل الكل",
|
"ButtonPlayAll": "تشغيل الكل",
|
||||||
"ButtonPlaying": "مشغل الآن",
|
"ButtonPlaying": "مشغل الآن",
|
||||||
@@ -71,8 +71,8 @@
|
|||||||
"ButtonQuickMatch": "مطابقة سريعة",
|
"ButtonQuickMatch": "مطابقة سريعة",
|
||||||
"ButtonReScan": "إعادة البحث",
|
"ButtonReScan": "إعادة البحث",
|
||||||
"ButtonRead": "اقرأ",
|
"ButtonRead": "اقرأ",
|
||||||
"ButtonReadLess": "قلص",
|
"ButtonReadLess": "اقرأ أقل",
|
||||||
"ButtonReadMore": "المزيد",
|
"ButtonReadMore": "اقرأ أكثر",
|
||||||
"ButtonRefresh": "تحديث",
|
"ButtonRefresh": "تحديث",
|
||||||
"ButtonRemove": "إزالة",
|
"ButtonRemove": "إزالة",
|
||||||
"ButtonRemoveAll": "إزالة الكل",
|
"ButtonRemoveAll": "إزالة الكل",
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
"ButtonStartM4BEncode": "ابدأ ترميز M4B",
|
"ButtonStartM4BEncode": "ابدأ ترميز M4B",
|
||||||
"ButtonStartMetadataEmbed": "ابدأ تضمين البيانات الوصفية",
|
"ButtonStartMetadataEmbed": "ابدأ تضمين البيانات الوصفية",
|
||||||
"ButtonStats": "الإحصائيات",
|
"ButtonStats": "الإحصائيات",
|
||||||
"ButtonSubmit": "تقديم",
|
"ButtonSubmit": "إرسال",
|
||||||
"ButtonTest": "اختبار",
|
"ButtonTest": "اختبار",
|
||||||
"ButtonUnlinkOpenId": "إلغاء ربط المعرف",
|
"ButtonUnlinkOpenId": "إلغاء ربط المعرف",
|
||||||
"ButtonUpload": "رفع",
|
"ButtonUpload": "رفع",
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
"HeaderAddCustomMetadataProvider": "إضافة موفر بيانات تعريفية مخصص",
|
"HeaderAddCustomMetadataProvider": "إضافة موفر بيانات تعريفية مخصص",
|
||||||
"HeaderAdvanced": "متقدم",
|
"HeaderAdvanced": "متقدم",
|
||||||
"HeaderAppriseNotificationSettings": "إعدادات الإشعارات",
|
"HeaderAppriseNotificationSettings": "إعدادات الإشعارات",
|
||||||
"HeaderAudioTracks": "المسارات الصوتية",
|
"HeaderAudioTracks": "المقاطع الصوتية",
|
||||||
"HeaderAudiobookTools": "أدوات إدارة ملفات الكتب الصوتية",
|
"HeaderAudiobookTools": "أدوات إدارة ملفات الكتب الصوتية",
|
||||||
"HeaderAuthentication": "المصادقة",
|
"HeaderAuthentication": "المصادقة",
|
||||||
"HeaderBackups": "النسخ الاحتياطية",
|
"HeaderBackups": "النسخ الاحتياطية",
|
||||||
@@ -152,5 +152,120 @@
|
|||||||
"HeaderLogin": "تسجيل الدخول",
|
"HeaderLogin": "تسجيل الدخول",
|
||||||
"HeaderLogs": "السجلات",
|
"HeaderLogs": "السجلات",
|
||||||
"HeaderManageGenres": "إدارة الانواع",
|
"HeaderManageGenres": "إدارة الانواع",
|
||||||
"HeaderManageTags": "إدارة العلامات"
|
"HeaderManageTags": "إدارة العلامات",
|
||||||
|
"HeaderOpenRSSFeed": "فتح تغذية RSS",
|
||||||
|
"HeaderPlaylist": "قائمة تشغيل",
|
||||||
|
"HeaderPlaylistItems": "عناصر قائمة التشغيل",
|
||||||
|
"HeaderRSSFeedGeneral": "تفاصيل RSS",
|
||||||
|
"HeaderRSSFeedIsOpen": "مغذي RSS مفتوح",
|
||||||
|
"HeaderSettings": "إعدادات",
|
||||||
|
"HeaderSleepTimer": "مؤقت النوم",
|
||||||
|
"HeaderStatsMinutesListeningChart": "الدقائق المسموعة (آخر 7 أيام)",
|
||||||
|
"HeaderStatsRecentSessions": "الجلسات الأخيرة",
|
||||||
|
"HeaderTableOfContents": "جدول المحتويات",
|
||||||
|
"HeaderYourStats": "إحصائياتك",
|
||||||
|
"LabelAddToPlaylist": "أضف إلى قائمة التشغيل",
|
||||||
|
"LabelAddedAt": "أضيفت على",
|
||||||
|
"LabelAddedDate": "تمت الإضافة",
|
||||||
|
"LabelAll": "الكل",
|
||||||
|
"LabelAuthor": "المؤلف",
|
||||||
|
"LabelAuthorFirstLast": "المؤلف (الاسم الأول الأخير)",
|
||||||
|
"LabelAuthorLastFirst": "المؤلف (الاسم الأخير، الأول)",
|
||||||
|
"LabelAuthors": "المؤلفون",
|
||||||
|
"LabelAutoDownloadEpisodes": "تنزيل الحلقات تلقائيًا",
|
||||||
|
"LabelBooks": "الكتب",
|
||||||
|
"LabelChapters": "الفصول",
|
||||||
|
"LabelClosePlayer": "إغلاق المشغل",
|
||||||
|
"LabelCollapseSeries": "إخفاء المسلسلات",
|
||||||
|
"LabelComplete": "مكتمل",
|
||||||
|
"LabelContinueListening": "استمرار الاستماع",
|
||||||
|
"LabelContinueReading": "استمرار القراءة",
|
||||||
|
"LabelContinueSeries": "استمرار المسلسلات",
|
||||||
|
"LabelDescription": "الوصف",
|
||||||
|
"LabelDiscover": "استكشف",
|
||||||
|
"LabelDownload": "تنزيل",
|
||||||
|
"LabelDuration": "المدة",
|
||||||
|
"LabelEbook": "الكتاب الإلكتروني",
|
||||||
|
"LabelEbooks": "الكتب الإلكترونية",
|
||||||
|
"LabelEnable": "تمكين",
|
||||||
|
"LabelEnd": "انهاء",
|
||||||
|
"LabelEndOfChapter": "نهاية الفصل",
|
||||||
|
"LabelEpisode": "الحلقة",
|
||||||
|
"LabelFeedURL": "عنوان التغذية",
|
||||||
|
"LabelFile": "الملف",
|
||||||
|
"LabelFileBirthtime": "وقت انشاء الملف",
|
||||||
|
"LabelFileModified": "تم تعديل الملف",
|
||||||
|
"LabelFilename": "اسم الملف",
|
||||||
|
"LabelFinished": "المنجزة",
|
||||||
|
"LabelFolder": "المجلد",
|
||||||
|
"LabelFontBoldness": "تعريض الخط",
|
||||||
|
"LabelFontScale": "نطاق الخط",
|
||||||
|
"LabelGenre": "التصنيف",
|
||||||
|
"LabelGenres": "التصانيف",
|
||||||
|
"LabelHasEbook": "يحتوي كتاب إلكتروني",
|
||||||
|
"LabelHasSupplementaryEbook": "يحتوي كتاب إلكتروني تكميلي",
|
||||||
|
"LabelHost": "المضيف",
|
||||||
|
"LabelInProgress": "تحت التنفيذ",
|
||||||
|
"LabelIncomplete": "غير مكتمل",
|
||||||
|
"LabelLanguage": "اللغة",
|
||||||
|
"LabelLayout": "التنسيق",
|
||||||
|
"LabelLayoutSinglePage": "صفحة واحدة",
|
||||||
|
"LabelLineSpacing": "تباعد الأسطر",
|
||||||
|
"LabelListenAgain": "الاستماع مجدداً",
|
||||||
|
"LabelMediaType": "نوع الوسائط",
|
||||||
|
"LabelMissing": "مفقود",
|
||||||
|
"LabelMore": "أكثر",
|
||||||
|
"LabelMoreInfo": "معلومات أكثر",
|
||||||
|
"LabelName": "الاسم",
|
||||||
|
"LabelNarrator": "الراوي",
|
||||||
|
"LabelNarrators": "الرواة",
|
||||||
|
"LabelNewestAuthors": "أجدد المؤلفين",
|
||||||
|
"LabelNewestEpisodes": "أجدد الحلقات",
|
||||||
|
"LabelNotFinished": "لم يتم الانتهاء",
|
||||||
|
"LabelNotStarted": "لم يتم البدء",
|
||||||
|
"LabelNumberOfEpisodes": "# من الحلقات",
|
||||||
|
"LabelPassword": "كلمة المرور",
|
||||||
|
"LabelPath": "مسار",
|
||||||
|
"LabelPodcast": "مدونة صوتية",
|
||||||
|
"LabelPodcasts": "مدونات صوتية",
|
||||||
|
"LabelPreventIndexing": "منع فهرسة تغذيتك بواسطة دليل آيتونز وقوقل بودكاست",
|
||||||
|
"LabelProgress": "تقدم",
|
||||||
|
"LabelPubDate": "تاريخ النشر",
|
||||||
|
"LabelPublishYear": "سنة النشر",
|
||||||
|
"LabelPublishedDate": "منشور {0}",
|
||||||
|
"LabelRSSFeedCustomOwnerEmail": "البريد الالكتروني المخصص للمالك",
|
||||||
|
"LabelRSSFeedCustomOwnerName": "الاسم المخصص للمالك",
|
||||||
|
"LabelRSSFeedPreventIndexing": "منع الفهرسة",
|
||||||
|
"LabelRSSFeedSlug": "رابط تغذية RSS",
|
||||||
|
"LabelRandomly": "عشوائياً",
|
||||||
|
"LabelRead": "اقرأ",
|
||||||
|
"LabelReadAgain": "اقرأ مرة أخرى",
|
||||||
|
"LabelRecentSeries": "المسلسلات الحديثة",
|
||||||
|
"LabelRecentlyAdded": "المضافة حديثاً",
|
||||||
|
"LabelSeason": "الموسم",
|
||||||
|
"LabelSeries": "المسلسلات",
|
||||||
|
"LabelSetEbookAsPrimary": "تعيين كرئيسي",
|
||||||
|
"LabelSetEbookAsSupplementary": "تعيين كتكميلي",
|
||||||
|
"LabelShowAll": "اظهار الكل",
|
||||||
|
"LabelSize": "الحجم",
|
||||||
|
"LabelSleepTimer": "مؤقت النوم",
|
||||||
|
"LabelStart": "ابدأ",
|
||||||
|
"LabelStatsBestDay": "أفضل يوم",
|
||||||
|
"LabelStatsDailyAverage": "المتوسط اليومي",
|
||||||
|
"LabelStatsDays": "أيام",
|
||||||
|
"LabelStatsDaysListened": "أيام الاستماع",
|
||||||
|
"LabelStatsInARow": "على التوالي",
|
||||||
|
"LabelStatsItemsFinished": "العناصر المنتهية",
|
||||||
|
"LabelStatsMinutes": "دقائق",
|
||||||
|
"LabelStatsMinutesListening": "دقائق الاستماع",
|
||||||
|
"NoteRSSFeedPodcastAppsHttps": "تحذير: تتطلب معظم تطبيقات البث الصوتي أن يكون عنوان URL لخلاصة RSS يستخدم HTTPS",
|
||||||
|
"NoteRSSFeedPodcastAppsPubDate": "تحذير: حلقة واحدة أو أكثر من حلقاتك ليس لها تاريخ نشر. بعض تطبيقات البودكاست تتطلب هذا.",
|
||||||
|
"ToastBookmarkCreateFailed": "فشل في إنشاء الإشارة المرجعية",
|
||||||
|
"ToastItemMarkedAsFinishedFailed": "فشل في وضع علامة على الانتهاء",
|
||||||
|
"ToastItemMarkedAsNotFinishedFailed": "فشل في وضع علامة \"غير مكتمل\"",
|
||||||
|
"ToastPlaylistCreateFailed": "فشل إنشاء قائمة التشغيل",
|
||||||
|
"ToastPodcastCreateFailed": "فشل في إنشاء البودكاست",
|
||||||
|
"ToastPodcastCreateSuccess": "تم إنشاء البودكاست بنجاح",
|
||||||
|
"ToastRSSFeedCloseFailed": "فشل في إغلاق موجز RSS",
|
||||||
|
"ToastRSSFeedCloseSuccess": "تم إغلاق موجز RSS"
|
||||||
}
|
}
|
||||||
|
|||||||
+142
-118
@@ -1,33 +1,35 @@
|
|||||||
{
|
{
|
||||||
"ButtonAdd": "Afegeix",
|
"ButtonAdd": "Afegeix",
|
||||||
"ButtonAddChapters": "Afegeix",
|
"ButtonAddChapters": "Afegeix capítols",
|
||||||
"ButtonAddDevice": "Afegeix Dispositiu",
|
"ButtonAddDevice": "Afegeix un aparell",
|
||||||
"ButtonAddLibrary": "Crea Biblioteca",
|
"ButtonAddLibrary": "Afegeix una biblioteca",
|
||||||
"ButtonAddPodcasts": "Afegeix pòdcasts",
|
"ButtonAddPodcasts": "Afegeix pòdcasts",
|
||||||
"ButtonAddUser": "Crea Usuari",
|
"ButtonAddUser": "Afegeix un usuari",
|
||||||
"ButtonAddYourFirstLibrary": "Crea la teva Primera Biblioteca",
|
"ButtonAddYourFirstLibrary": "Afegiu la vostra primera biblioteca",
|
||||||
"ButtonApply": "Aplica",
|
"ButtonApply": "Aplica",
|
||||||
"ButtonApplyChapters": "Aplica Capítols",
|
"ButtonApplyChapters": "Aplica capítols",
|
||||||
"ButtonAuthors": "Autors",
|
"ButtonAuthors": "Autors",
|
||||||
"ButtonBack": "Enrere",
|
"ButtonBack": "Enrere",
|
||||||
"ButtonBrowseForFolder": "Cerca Carpeta",
|
"ButtonBatchEditPopulateFromExisting": "Omplir des d'existent",
|
||||||
|
"ButtonBatchEditPopulateMapDetails": "Omple els detalls del mapa",
|
||||||
|
"ButtonBrowseForFolder": "Cerca una carpeta",
|
||||||
"ButtonCancel": "Cancel·la",
|
"ButtonCancel": "Cancel·la",
|
||||||
"ButtonCancelEncode": "Cancel·la Codificador",
|
"ButtonCancelEncode": "Cancel·la la codificació",
|
||||||
"ButtonChangeRootPassword": "Canvia Contrasenya Root",
|
"ButtonChangeRootPassword": "Canvia Contrasenya Root",
|
||||||
"ButtonCheckAndDownloadNewEpisodes": "Verifica i Descarrega Nous Episodis",
|
"ButtonCheckAndDownloadNewEpisodes": "Verifica i Descarrega Nous Episodis",
|
||||||
"ButtonChooseAFolder": "Tria una Carpeta",
|
"ButtonChooseAFolder": "Trieu una carpeta",
|
||||||
"ButtonChooseFiles": "Tria un Fitxer",
|
"ButtonChooseFiles": "Trieu fitxers",
|
||||||
"ButtonClearFilter": "Elimina Filtres",
|
"ButtonClearFilter": "Neteja el filtre",
|
||||||
"ButtonCloseFeed": "Tanca Font",
|
"ButtonCloseFeed": "Tanca el canal",
|
||||||
"ButtonCloseSession": "Tanca la sessió oberta",
|
"ButtonCloseSession": "Tanca la sessió oberta",
|
||||||
"ButtonCollections": "Col·leccions",
|
"ButtonCollections": "Col·leccions",
|
||||||
"ButtonConfigureScanner": "Configura Escàner",
|
"ButtonConfigureScanner": "Configura Escàner",
|
||||||
"ButtonCreate": "Crea",
|
"ButtonCreate": "Crea",
|
||||||
"ButtonCreateBackup": "Crea Còpia de Seguretat",
|
"ButtonCreateBackup": "Crea Còpia de Seguretat",
|
||||||
"ButtonDelete": "Elimina",
|
"ButtonDelete": "Suprimeix",
|
||||||
"ButtonDownloadQueue": "Cua",
|
"ButtonDownloadQueue": "Cua",
|
||||||
"ButtonEdit": "Edita",
|
"ButtonEdit": "Edita",
|
||||||
"ButtonEditChapters": "Edita Capítol",
|
"ButtonEditChapters": "Edita capítols",
|
||||||
"ButtonEditPodcast": "Edita el pòdcast",
|
"ButtonEditPodcast": "Edita el pòdcast",
|
||||||
"ButtonEnable": "Habilita",
|
"ButtonEnable": "Habilita",
|
||||||
"ButtonFireAndFail": "Executat i fallat",
|
"ButtonFireAndFail": "Executat i fallat",
|
||||||
@@ -117,7 +119,7 @@
|
|||||||
"HeaderAccount": "Compte",
|
"HeaderAccount": "Compte",
|
||||||
"HeaderAddCustomMetadataProvider": "Afegeix un proveïdor de metadades personalitzat",
|
"HeaderAddCustomMetadataProvider": "Afegeix un proveïdor de metadades personalitzat",
|
||||||
"HeaderAdvanced": "Avançat",
|
"HeaderAdvanced": "Avançat",
|
||||||
"HeaderAppriseNotificationSettings": "Configuració de Notificacions Apprise",
|
"HeaderAppriseNotificationSettings": "Paràmetres de notificacions Apprise",
|
||||||
"HeaderAudioTracks": "Pistes d'àudio",
|
"HeaderAudioTracks": "Pistes d'àudio",
|
||||||
"HeaderAudiobookTools": "Eines de gestió de fitxers de l'audiollibre",
|
"HeaderAudiobookTools": "Eines de gestió de fitxers de l'audiollibre",
|
||||||
"HeaderAuthentication": "Autenticació",
|
"HeaderAuthentication": "Autenticació",
|
||||||
@@ -133,9 +135,9 @@
|
|||||||
"HeaderCustomMetadataProviders": "Proveïdors de metadades personalitzats",
|
"HeaderCustomMetadataProviders": "Proveïdors de metadades personalitzats",
|
||||||
"HeaderDetails": "Detalls",
|
"HeaderDetails": "Detalls",
|
||||||
"HeaderDownloadQueue": "Cua de baixades",
|
"HeaderDownloadQueue": "Cua de baixades",
|
||||||
"HeaderEbookFiles": "Fitxers de Llibres Digitals",
|
"HeaderEbookFiles": "Fitxers de llibres digitals",
|
||||||
"HeaderEmail": "Correu electrònic",
|
"HeaderEmail": "Correu electrònic",
|
||||||
"HeaderEmailSettings": "Configuració de Correu Electrònic",
|
"HeaderEmailSettings": "Paràmetres de correu electrònic",
|
||||||
"HeaderEpisodes": "Episodis",
|
"HeaderEpisodes": "Episodis",
|
||||||
"HeaderEreaderDevices": "Dispositius Ereader",
|
"HeaderEreaderDevices": "Dispositius Ereader",
|
||||||
"HeaderEreaderSettings": "Paràmetres del lector",
|
"HeaderEreaderSettings": "Paràmetres del lector",
|
||||||
@@ -171,7 +173,7 @@
|
|||||||
"HeaderPasswordAuthentication": "Autenticació per Contrasenya",
|
"HeaderPasswordAuthentication": "Autenticació per Contrasenya",
|
||||||
"HeaderPermissions": "Permisos",
|
"HeaderPermissions": "Permisos",
|
||||||
"HeaderPlayerQueue": "Cua del Reproductor",
|
"HeaderPlayerQueue": "Cua del Reproductor",
|
||||||
"HeaderPlayerSettings": "Configuració del Reproductor",
|
"HeaderPlayerSettings": "Paràmetres del reproductor",
|
||||||
"HeaderPlaylist": "Llista de Reproducció",
|
"HeaderPlaylist": "Llista de Reproducció",
|
||||||
"HeaderPlaylistItems": "Elements de la Llista de Reproducció",
|
"HeaderPlaylistItems": "Elements de la Llista de Reproducció",
|
||||||
"HeaderPodcastsToAdd": "Pòdcasts a afegir",
|
"HeaderPodcastsToAdd": "Pòdcasts a afegir",
|
||||||
@@ -190,7 +192,7 @@
|
|||||||
"HeaderSettings": "Paràmetres",
|
"HeaderSettings": "Paràmetres",
|
||||||
"HeaderSettingsDisplay": "Interfície",
|
"HeaderSettingsDisplay": "Interfície",
|
||||||
"HeaderSettingsExperimental": "Funcionalitats experimentals",
|
"HeaderSettingsExperimental": "Funcionalitats experimentals",
|
||||||
"HeaderSettingsGeneral": "General",
|
"HeaderSettingsGeneral": "Generals",
|
||||||
"HeaderSettingsScanner": "Escàner",
|
"HeaderSettingsScanner": "Escàner",
|
||||||
"HeaderSettingsWebClient": "Client web",
|
"HeaderSettingsWebClient": "Client web",
|
||||||
"HeaderSleepTimer": "Temporitzador de son",
|
"HeaderSleepTimer": "Temporitzador de son",
|
||||||
@@ -219,10 +221,10 @@
|
|||||||
"LabelAccountTypeUser": "Usuari",
|
"LabelAccountTypeUser": "Usuari",
|
||||||
"LabelActivities": "Activitats",
|
"LabelActivities": "Activitats",
|
||||||
"LabelActivity": "Activitat",
|
"LabelActivity": "Activitat",
|
||||||
"LabelAddToCollection": "Afegit a la Col·lecció",
|
"LabelAddToCollection": "Afegeix a la col·lecció",
|
||||||
"LabelAddToCollectionBatch": "S'han Afegit {0} Llibres a la Col·lecció",
|
"LabelAddToCollectionBatch": "Afegeix {0} llibres a la col·lecció",
|
||||||
"LabelAddToPlaylist": "Afegit a la llista de reproducció",
|
"LabelAddToPlaylist": "Afegeix a la llista de reproducció",
|
||||||
"LabelAddToPlaylistBatch": "S'han Afegit {0} Elements a la Llista de Reproducció",
|
"LabelAddToPlaylistBatch": "Afegeix {0} elements a la llista de reproducció",
|
||||||
"LabelAddedAt": "Afegit",
|
"LabelAddedAt": "Afegit",
|
||||||
"LabelAddedDate": "{0} Afegit",
|
"LabelAddedDate": "{0} Afegit",
|
||||||
"LabelAdminUsersOnly": "Només usuaris administradors",
|
"LabelAdminUsersOnly": "Només usuaris administradors",
|
||||||
@@ -231,7 +233,7 @@
|
|||||||
"LabelAllUsers": "Tots els usuaris",
|
"LabelAllUsers": "Tots els usuaris",
|
||||||
"LabelAllUsersExcludingGuests": "Tots els usuaris excepte convidats",
|
"LabelAllUsersExcludingGuests": "Tots els usuaris excepte convidats",
|
||||||
"LabelAllUsersIncludingGuests": "Tots els usuaris i convidats",
|
"LabelAllUsersIncludingGuests": "Tots els usuaris i convidats",
|
||||||
"LabelAlreadyInYourLibrary": "Ja existeix a la Biblioteca",
|
"LabelAlreadyInYourLibrary": "Ja existeix a la biblioteca",
|
||||||
"LabelApiToken": "Testimoni de l'API",
|
"LabelApiToken": "Testimoni de l'API",
|
||||||
"LabelAppend": "Adjuntar",
|
"LabelAppend": "Adjuntar",
|
||||||
"LabelAudioBitrate": "Taxa de bits d'àudio (per exemple, 128k)",
|
"LabelAudioBitrate": "Taxa de bits d'àudio (per exemple, 128k)",
|
||||||
@@ -288,14 +290,14 @@
|
|||||||
"LabelCronExpression": "Expressió de Cron",
|
"LabelCronExpression": "Expressió de Cron",
|
||||||
"LabelCurrent": "Actual",
|
"LabelCurrent": "Actual",
|
||||||
"LabelCurrently": "En aquest moment:",
|
"LabelCurrently": "En aquest moment:",
|
||||||
"LabelCustomCronExpression": "Expressió de Cron Personalitzada:",
|
"LabelCustomCronExpression": "Expressió del Cron personalitzada:",
|
||||||
"LabelDatetime": "Hora i Data",
|
"LabelDatetime": "Data i hora",
|
||||||
"LabelDays": "Dies",
|
"LabelDays": "Dies",
|
||||||
"LabelDeleteFromFileSystemCheckbox": "Suprimeix del sistema de fitxers (desmarqueu per a eliminar de la base de dades només)",
|
"LabelDeleteFromFileSystemCheckbox": "Suprimeix del sistema de fitxers (desmarqueu per a eliminar de la base de dades només)",
|
||||||
"LabelDescription": "Descripció",
|
"LabelDescription": "Descripció",
|
||||||
"LabelDeselectAll": "Desseleccionar Tots",
|
"LabelDeselectAll": "Desseleccionar Tots",
|
||||||
"LabelDevice": "Dispositiu",
|
"LabelDevice": "Dispositiu",
|
||||||
"LabelDeviceInfo": "Informació del Dispositiu",
|
"LabelDeviceInfo": "Informació de l'aparell",
|
||||||
"LabelDeviceIsAvailableTo": "El dispositiu està disponible per a...",
|
"LabelDeviceIsAvailableTo": "El dispositiu està disponible per a...",
|
||||||
"LabelDirectory": "Directori",
|
"LabelDirectory": "Directori",
|
||||||
"LabelDiscFromFilename": "Disc a partir del nom de fitxer",
|
"LabelDiscFromFilename": "Disc a partir del nom de fitxer",
|
||||||
@@ -333,11 +335,11 @@
|
|||||||
"LabelEnd": "Fi",
|
"LabelEnd": "Fi",
|
||||||
"LabelEndOfChapter": "Fi del capítol",
|
"LabelEndOfChapter": "Fi del capítol",
|
||||||
"LabelEpisode": "Episodi",
|
"LabelEpisode": "Episodi",
|
||||||
"LabelEpisodeNotLinkedToRssFeed": "Episodi no enllaçat al feed RSS",
|
"LabelEpisodeNotLinkedToRssFeed": "Episodi no enllaçat al canal RSS",
|
||||||
"LabelEpisodeNumber": "Episodi #{0}",
|
"LabelEpisodeNumber": "Episodi #{0}",
|
||||||
"LabelEpisodeTitle": "Títol de l'Episodi",
|
"LabelEpisodeTitle": "Títol de l'Episodi",
|
||||||
"LabelEpisodeType": "Tipus d'Episodi",
|
"LabelEpisodeType": "Tipus d'Episodi",
|
||||||
"LabelEpisodeUrlFromRssFeed": "URL de l'episodi del feed RSS",
|
"LabelEpisodeUrlFromRssFeed": "URL de l'episodi del canal RSS",
|
||||||
"LabelEpisodes": "Episodis",
|
"LabelEpisodes": "Episodis",
|
||||||
"LabelEpisodic": "Episodis",
|
"LabelEpisodic": "Episodis",
|
||||||
"LabelExample": "Exemple",
|
"LabelExample": "Exemple",
|
||||||
@@ -350,7 +352,7 @@
|
|||||||
"LabelFeedURL": "Font de URL",
|
"LabelFeedURL": "Font de URL",
|
||||||
"LabelFetchingMetadata": "Obtenció de metadades",
|
"LabelFetchingMetadata": "Obtenció de metadades",
|
||||||
"LabelFile": "Fitxer",
|
"LabelFile": "Fitxer",
|
||||||
"LabelFileBirthtime": "Arxiu creat a",
|
"LabelFileBirthtime": "Fitxer creat a",
|
||||||
"LabelFileBornDate": "Creat {0}",
|
"LabelFileBornDate": "Creat {0}",
|
||||||
"LabelFileModified": "Fitxer modificat",
|
"LabelFileModified": "Fitxer modificat",
|
||||||
"LabelFileModifiedDate": "Modificat {0}",
|
"LabelFileModifiedDate": "Modificat {0}",
|
||||||
@@ -471,6 +473,7 @@
|
|||||||
"LabelOpenIDAdvancedPermsClaimDescription": "Nom de la notificació de OpenID que conté permisos avançats per accions d'usuari dins l'aplicació que s'aplicaran a rols que no siguin d'administrador (<b>si estan configurats</b>). Si el reclam no apareix en la resposta, es denegarà l'accés a ABS. Si manca una sola opció, es tractarà com a <code>falsa</code>. Assegura't que la notificació del proveïdor d'identitats coincideixi amb l'estructura esperada:",
|
"LabelOpenIDAdvancedPermsClaimDescription": "Nom de la notificació de OpenID que conté permisos avançats per accions d'usuari dins l'aplicació que s'aplicaran a rols que no siguin d'administrador (<b>si estan configurats</b>). Si el reclam no apareix en la resposta, es denegarà l'accés a ABS. Si manca una sola opció, es tractarà com a <code>falsa</code>. Assegura't que la notificació del proveïdor d'identitats coincideixi amb l'estructura esperada:",
|
||||||
"LabelOpenIDClaims": "Deixa les següents opcions buides per desactivar l'assignació avançada de grups i permisos, el que assignaria automàticament al grup 'Usuari'.",
|
"LabelOpenIDClaims": "Deixa les següents opcions buides per desactivar l'assignació avançada de grups i permisos, el que assignaria automàticament al grup 'Usuari'.",
|
||||||
"LabelOpenIDGroupClaimDescription": "Nom de la declaració OpenID que conté una llista de grups de l'usuari. Comunament coneguts com <code>grups</code>. <b>Si es configura</b>, l'aplicació assignarà automàticament rols basats en la pertinença a grups de l'usuari, sempre que aquests grups es denominen 'admin', 'user' o 'guest' en la notificació. La sol·licitud ha de contenir una llista, i si un usuari pertany a diversos grups, l'aplicació assignarà el rol corresponent al major nivell d'accés. Si cap grup coincideix, es denegarà l'accés.",
|
"LabelOpenIDGroupClaimDescription": "Nom de la declaració OpenID que conté una llista de grups de l'usuari. Comunament coneguts com <code>grups</code>. <b>Si es configura</b>, l'aplicació assignarà automàticament rols basats en la pertinença a grups de l'usuari, sempre que aquests grups es denominen 'admin', 'user' o 'guest' en la notificació. La sol·licitud ha de contenir una llista, i si un usuari pertany a diversos grups, l'aplicació assignarà el rol corresponent al major nivell d'accés. Si cap grup coincideix, es denegarà l'accés.",
|
||||||
|
"LabelOpenRSSFeed": "Obre el canal RSS",
|
||||||
"LabelOverwrite": "Sobreescriure",
|
"LabelOverwrite": "Sobreescriure",
|
||||||
"LabelPaginationPageXOfY": "Pàgina {0} de {1}",
|
"LabelPaginationPageXOfY": "Pàgina {0} de {1}",
|
||||||
"LabelPassword": "Contrasenya",
|
"LabelPassword": "Contrasenya",
|
||||||
@@ -495,7 +498,7 @@
|
|||||||
"LabelPodcasts": "Pòdcasts",
|
"LabelPodcasts": "Pòdcasts",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Prefixos per Ignorar (no distingeix entre majúscules i minúscules.)",
|
"LabelPrefixesToIgnore": "Prefixos per Ignorar (no distingeix entre majúscules i minúscules.)",
|
||||||
"LabelPreventIndexing": "Evita que la teva font sigui indexada pels directoris de podcasts d'iTunes i Google",
|
"LabelPreventIndexing": "Evita que el vostre canal l'indexin els directoris de pòdcasts de l'iTunes i Google",
|
||||||
"LabelPrimaryEbook": "Ebook Principal",
|
"LabelPrimaryEbook": "Ebook Principal",
|
||||||
"LabelProgress": "Progrés",
|
"LabelProgress": "Progrés",
|
||||||
"LabelProvider": "Proveïdor",
|
"LabelProvider": "Proveïdor",
|
||||||
@@ -528,7 +531,7 @@
|
|||||||
"LabelRemoveAllMetadataJson": "Eliminar tots els fitxers metadata.json",
|
"LabelRemoveAllMetadataJson": "Eliminar tots els fitxers metadata.json",
|
||||||
"LabelRemoveCover": "Eliminar Coberta",
|
"LabelRemoveCover": "Eliminar Coberta",
|
||||||
"LabelRemoveMetadataFile": "Eliminar fitxers de metadades en carpetes d'elements de biblioteca",
|
"LabelRemoveMetadataFile": "Eliminar fitxers de metadades en carpetes d'elements de biblioteca",
|
||||||
"LabelRemoveMetadataFileHelp": "Elimina tots els fitxers metadata.json i metadata.abs de les teves carpetes {0}.",
|
"LabelRemoveMetadataFileHelp": "Elimina tots els fitxers metadata.json i metadata.abs de les vostres carpetes {0}.",
|
||||||
"LabelRowsPerPage": "Files per Pàgina",
|
"LabelRowsPerPage": "Files per Pàgina",
|
||||||
"LabelSearchTerm": "Cercar Terme",
|
"LabelSearchTerm": "Cercar Terme",
|
||||||
"LabelSearchTitle": "Cercar Títol",
|
"LabelSearchTitle": "Cercar Títol",
|
||||||
@@ -560,7 +563,13 @@
|
|||||||
"LabelSettingsExperimentalFeatures": "Funcions Experimentals",
|
"LabelSettingsExperimentalFeatures": "Funcions Experimentals",
|
||||||
"LabelSettingsExperimentalFeaturesHelp": "Funcions en desenvolupament que es beneficiarien dels teus comentaris i experiències de prova. Feu clic aquí per obrir una conversa a Github.",
|
"LabelSettingsExperimentalFeaturesHelp": "Funcions en desenvolupament que es beneficiarien dels teus comentaris i experiències de prova. Feu clic aquí per obrir una conversa a Github.",
|
||||||
"LabelSettingsFindCovers": "Troba cobertes",
|
"LabelSettingsFindCovers": "Troba cobertes",
|
||||||
|
"LabelSettingsHideSingleBookSeries": "Amaga les sèries amb un sol llibre",
|
||||||
|
"LabelSettingsParseSubtitles": "Analitza els subtítols",
|
||||||
"LabelSettingsSortingIgnorePrefixes": "Ignora els prefixos en ordenar",
|
"LabelSettingsSortingIgnorePrefixes": "Ignora els prefixos en ordenar",
|
||||||
|
"LabelSettingsTimeFormat": "Format d'hora",
|
||||||
|
"LabelShare": "Comparteix",
|
||||||
|
"LabelShareDownloadableHelp": "Permet els usuaris amb l'enllaç de compartició de baixar un fitxer ZIP amb l'element de la biblioteca.",
|
||||||
|
"LabelShareURL": "URL de compartició",
|
||||||
"LabelShowAll": "Mostra-ho tot",
|
"LabelShowAll": "Mostra-ho tot",
|
||||||
"LabelShowSeconds": "Mostra segons",
|
"LabelShowSeconds": "Mostra segons",
|
||||||
"LabelShowSubtitles": "Mostra subtítols",
|
"LabelShowSubtitles": "Mostra subtítols",
|
||||||
@@ -654,88 +663,98 @@
|
|||||||
"LabelViewPlayerSettings": "Mostra els ajustaments del reproductor",
|
"LabelViewPlayerSettings": "Mostra els ajustaments del reproductor",
|
||||||
"LabelViewQueue": "Mostra cua del reproductor",
|
"LabelViewQueue": "Mostra cua del reproductor",
|
||||||
"LabelVolume": "Volum",
|
"LabelVolume": "Volum",
|
||||||
"LabelWebRedirectURLsDescription": "Autoritza aquestes URL al teu proveïdor OAuth per permetre redirecció a l'aplicació web després d'iniciar sessió:",
|
"LabelWebRedirectURLsDescription": "Autoritzeu aquests URL al vostre proveïdor OAuth per a permetre redirigir a l’aplicació web després d'iniciar sessió:",
|
||||||
"LabelWebRedirectURLsSubfolder": "Subcarpeta per a URL de redirecció",
|
"LabelWebRedirectURLsSubfolder": "Subcarpeta per a URL de redirecció",
|
||||||
"LabelWeekdaysToRun": "Executar en dies de la setmana",
|
"LabelWeekdaysToRun": "Executar en dies de la setmana",
|
||||||
"LabelXBooks": "{0} llibres",
|
"LabelXBooks": "{0} llibres",
|
||||||
"LabelXItems": "{0} elements",
|
"LabelXItems": "{0} elements",
|
||||||
"LabelYearReviewHide": "Oculta resum de l'any",
|
"LabelYearReviewHide": "Oculta resum de l'any",
|
||||||
"LabelYearReviewShow": "Mostra resum de l'any",
|
"LabelYearReviewShow": "Mostra resum de l'any",
|
||||||
"LabelYourAudiobookDuration": "Duració del teu audiollibre",
|
"LabelYourAudiobookDuration": "Duració del vostre audiollibre",
|
||||||
"LabelYourBookmarks": "Els vostres marcadors",
|
"LabelYourBookmarks": "Els vostres marcadors",
|
||||||
"LabelYourPlaylists": "Les teves llistes",
|
"LabelYourPlaylists": "Les vostres llistes",
|
||||||
"LabelYourProgress": "El vostre progrés",
|
"LabelYourProgress": "El vostre progrés",
|
||||||
"MessageAddToPlayerQueue": "Afegeix a la cua del reproductor",
|
"MessageAddToPlayerQueue": "Afegeix a la cua del reproductor",
|
||||||
"MessageAppriseDescription": "Per utilitzar aquesta funció, hauràs de tenir l'<a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API d'Apprise</a> en funcionament o una API que gestioni resultats similars. <br/>La URL de l'API d'Apprise ha de tenir la mateixa ruta d'arxius que on s'envien les notificacions. Per exemple: si la teva API és a <code>http://192.168.1.1:8337</code>, llavors posaries <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Per utilitzar aquesta funció, hauràs de tenir l'<a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API d'Apprise</a> en funcionament o una API que gestioni resultats similars. <br/>La URL de l'API d'Apprise ha de tenir la mateixa ruta d'arxius que on s'envien les notificacions. Per exemple: si la teva API és a <code>http://192.168.1.1:8337</code>, llavors posaries <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Reengegueu el servidor després de desar perquè s'hi apliquin els canvis d'OIDC.",
|
||||||
"MessageBackupsDescription": "Les còpies de seguretat inclouen: usuaris, progrés dels usuaris, detalls dels elements de la biblioteca, configuració del servidor i imatges a <code>/metadata/items</code> i <code>/metadata/authors</code>. Les còpies de seguretat <strong>NO</strong> inclouen cap fitxer guardat a la carpeta de la teva biblioteca.",
|
"MessageBackupsDescription": "Les còpies de seguretat inclouen: usuaris, progrés dels usuaris, detalls dels elements de la biblioteca, configuració del servidor i imatges a <code>/metadata/items</code> i <code>/metadata/authors</code>. Les còpies de seguretat <strong>NO</strong> inclouen cap fitxer guardat a la carpeta de la teva biblioteca.",
|
||||||
"MessageBackupsLocationEditNote": "Nota: Actualitzar la ubicació de la còpia de seguretat no mourà ni modificarà les còpies existents",
|
"MessageBackupsLocationEditNote": "Nota: Actualitzar la ubicació de la còpia de seguretat no mourà ni modificarà les còpies existents",
|
||||||
"MessageBackupsLocationNoEditNote": "Nota: La ubicació de la còpia de seguretat es defineix mitjançant una variable d'entorn i no es pot modificar aquí.",
|
"MessageBackupsLocationNoEditNote": "Nota: La ubicació de la còpia de seguretat es defineix mitjançant una variable d'entorn i no es pot modificar aquí.",
|
||||||
"MessageBackupsLocationPathEmpty": "La ruta de la còpia de seguretat no pot estar buida",
|
"MessageBackupsLocationPathEmpty": "La ruta de la còpia de seguretat no pot estar buida",
|
||||||
"MessageBatchQuickMatchDescription": "La funció \"Troba Ràpid\" intentarà afegir portades i metadades que falten als elements seleccionats. Activa l'opció següent perquè \"Troba Ràpid\" pugui sobreescriure portades i/o metadades existents.",
|
"MessageBatchQuickMatchDescription": "La funció \"Troba Ràpid\" intentarà afegir portades i metadades que falten als elements seleccionats. Activa l'opció següent perquè \"Troba Ràpid\" pugui sobreescriure portades i/o metadades existents.",
|
||||||
"MessageBookshelfNoCollections": "No tens cap col·lecció",
|
"MessageBookshelfNoCollections": "Encara no heu fet cap col·lecció",
|
||||||
|
"MessageBookshelfNoCollectionsHelp": "Les col·leccions són públiques. Tots els usuaris amb accés a la biblioteca les podran veure.",
|
||||||
"MessageBookshelfNoRSSFeeds": "Cap font RSS està oberta",
|
"MessageBookshelfNoRSSFeeds": "Cap font RSS està oberta",
|
||||||
"MessageBookshelfNoResultsForFilter": "Cap resultat per al filtre \"{0}: {1}\"",
|
"MessageBookshelfNoResultsForFilter": "Cap resultat per al filtre «{0}: {1}»",
|
||||||
"MessageBookshelfNoResultsForQuery": "Cap resultat per a la consulta",
|
"MessageBookshelfNoResultsForQuery": "Cap resultat per a la consulta",
|
||||||
"MessageBookshelfNoSeries": "No tens cap sèrie",
|
"MessageBookshelfNoSeries": "No teniu cap sèrie",
|
||||||
"MessageChapterEndIsAfter": "El final del capítol és després del final del teu audiollibre",
|
"MessageChapterEndIsAfter": "El final del capítol és després del final del teu audiollibre",
|
||||||
"MessageChapterErrorFirstNotZero": "El primer capítol ha de començar a 0",
|
"MessageChapterErrorFirstNotZero": "El primer capítol ha de començar a 0",
|
||||||
"MessageChapterErrorStartGteDuration": "El temps d'inici no és vàlid: ha de ser inferior a la durada de l'audiollibre",
|
"MessageChapterErrorStartGteDuration": "El temps d'inici no és vàlid: ha de ser inferior a la durada de l'audiollibre",
|
||||||
"MessageChapterErrorStartLtPrev": "El temps d'inici no és vàlid: ha de ser igual o més gran que el temps d'inici del capítol anterior",
|
"MessageChapterErrorStartLtPrev": "El temps d'inici no és vàlid: ha de ser igual o més gran que el temps d'inici del capítol anterior",
|
||||||
"MessageChapterStartIsAfter": "L'inici del capítol és després del final del teu audiollibre",
|
"MessageChapterStartIsAfter": "L'inici del capítol és després del final del teu audiollibre",
|
||||||
|
"MessageChaptersNotFound": "No s'han trobat els capítols",
|
||||||
"MessageCheckingCron": "Comprovant cron...",
|
"MessageCheckingCron": "Comprovant cron...",
|
||||||
"MessageConfirmCloseFeed": "Estàs segur que vols tancar aquesta font?",
|
"MessageConfirmCloseFeed": "Segur que voleu tancar aquest canal?",
|
||||||
"MessageConfirmDeleteBackup": "Estàs segur que vols eliminar la còpia de seguretat {0}?",
|
"MessageConfirmDeleteBackup": "Segur que voleu suprimir la còpia de seguretat de {0}?",
|
||||||
"MessageConfirmDeleteDevice": "Estàs segur que vols eliminar el lector electrònic \"{0}\"?",
|
"MessageConfirmDeleteDevice": "Segur que voleu suprimir el lector electrònic «{0}»?",
|
||||||
"MessageConfirmDeleteFile": "Això eliminarà el fitxer del teu sistema. Estàs segur?",
|
"MessageConfirmDeleteFile": "Això suprimirà el fitxer del vostre sistema de fitxers. N'esteu segur?",
|
||||||
"MessageConfirmDeleteLibrary": "Estàs segur que vols eliminar permanentment la biblioteca \"{0}\"?",
|
"MessageConfirmDeleteLibrary": "Segur que voleu suprimir permanentment la biblioteca «{0}»?",
|
||||||
"MessageConfirmDeleteLibraryItem": "Això eliminarà l'element de la base de dades i del sistema. Estàs segur?",
|
"MessageConfirmDeleteLibraryItem": "Això suprimirà l’element de la base de dades i del sistema de fitxers. N’esteu segur?",
|
||||||
"MessageConfirmDeleteLibraryItems": "Això eliminarà {0} element(s) de la base de dades i del sistema. Estàs segur?",
|
"MessageConfirmDeleteLibraryItems": "Això suprimirà {0} element(s) de la base de dades i del sistema de fitxers. N'esteu segur?",
|
||||||
"MessageConfirmDeleteMetadataProvider": "Estàs segur que vols eliminar el proveïdor de metadades personalitzat \"{0}\"?",
|
"MessageConfirmDeleteMetadataProvider": "Segur que voleu suprimir el proveïdor de metadades personalitzat «{0}»?",
|
||||||
"MessageConfirmDeleteNotification": "Estàs segur que vols eliminar aquesta notificació?",
|
"MessageConfirmDeleteNotification": "Segur que voleu suprimir aquesta notificació?",
|
||||||
"MessageConfirmDeleteSession": "Estàs segur que vols eliminar aquesta sessió?",
|
"MessageConfirmDeleteSession": "Segur que voleu suprimir aquesta sessió?",
|
||||||
"MessageConfirmEmbedMetadataInAudioFiles": "Estàs segur que vols incrustar metadades a {0} fitxer(s) d'àudio?",
|
"MessageConfirmEmbedMetadataInAudioFiles": "Segur que voleu incrustar metadades a {0} fitxer(s) d'àudio?",
|
||||||
"MessageConfirmForceReScan": "Estàs segur que vols forçar un reescaneig?",
|
"MessageConfirmForceReScan": "Segur que voleu forçar un reescaneig?",
|
||||||
"MessageConfirmMarkAllEpisodesFinished": "Estàs segur que vols marcar tots els episodis com a acabats?",
|
"MessageConfirmMarkAllEpisodesFinished": "Segur que voleu marcar tots els episodis com a acabats?",
|
||||||
"MessageConfirmMarkAllEpisodesNotFinished": "Estàs segur que vols marcar tots els episodis com a no acabats?",
|
"MessageConfirmMarkAllEpisodesNotFinished": "Segur que voleu marcar tots els episodis com a no acabats?",
|
||||||
"MessageConfirmMarkItemFinished": "Estàs segur que vols marcar \"{0}\" com a acabat?",
|
"MessageConfirmMarkItemFinished": "Segur que voleu marcar «{0}» com a acabat?",
|
||||||
"MessageConfirmMarkItemNotFinished": "Estàs segur que vols marcar \"{0}\" com a no acabat?",
|
"MessageConfirmMarkItemNotFinished": "Segur que voleu marcar «{0}» com a no acabat?",
|
||||||
"MessageConfirmMarkSeriesFinished": "Estàs segur que vols marcar tots els llibres d'aquesta sèrie com a acabats?",
|
"MessageConfirmMarkSeriesFinished": "Segur que voleu marcar tots els llibres d'aquesta sèrie com a acabats?",
|
||||||
"MessageConfirmMarkSeriesNotFinished": "Estàs segur que vols marcar tots els llibres d'aquesta sèrie com a no acabats?",
|
"MessageConfirmMarkSeriesNotFinished": "Segur que voleu marcar tots els llibres d'aquesta sèrie com a no acabats?",
|
||||||
"MessageConfirmNotificationTestTrigger": "Vols activar aquesta notificació amb dades de prova?",
|
"MessageConfirmNotificationTestTrigger": "Voleu activar aquesta notificació amb dades de prova?",
|
||||||
"MessageConfirmPurgeCache": "Esborrar la memòria cau eliminarà tot el directori localitzat a <code>/metadata/cache</code>. <br /><br />Estàs segur que vols eliminar-lo?",
|
"MessageConfirmPurgeCache": "Purgar la memòria cau suprimirà tot el directori localitzat a <code>/metadata/cache</code>. <br /><br />Segur que voleu eliminar-lo?",
|
||||||
"MessageConfirmPurgeItemsCache": "Esborrar la memòria cau dels elements eliminarà el directori <code>/metadata/cache/items</code>.<br />Estàs segur?",
|
"MessageConfirmPurgeItemsCache": "Esborrar la memòria cau dels elements eliminarà el directori <code>/metadata/cache/items</code>.<br />Estàs segur?",
|
||||||
"MessageConfirmQuickEmbed": "Advertència! La integració ràpida no fa còpies de seguretat dels teus fitxers d'àudio. Assegura't d'haver-ne fet una còpia abans. <br><br>Vols continuar?",
|
"MessageConfirmQuickEmbed": "Avís: la incrustació ràpida no fa còpies de seguretat dels vostres fitxers d'àudio. Assegureu-vos d'haver-ne fet una còpia abans. <br><br>Voleu continuar?",
|
||||||
"MessageConfirmQuickMatchEpisodes": "El reconeixement ràpid sobreescriurà els detalls si es troba una coincidència. Estàs segur?",
|
"MessageConfirmQuickMatchEpisodes": "El reconeixement ràpid sobreescriurà els detalls si es troba una coincidència. Estàs segur?",
|
||||||
"MessageConfirmReScanLibraryItems": "Estàs segur que vols reescanejar {0} element(s)?",
|
"MessageConfirmReScanLibraryItems": "Segur que voleu reescanejar {0} element(s)?",
|
||||||
"MessageConfirmRemoveAllChapters": "Estàs segur que vols eliminar tots els capítols?",
|
"MessageConfirmRemoveAllChapters": "Segur que voleu eliminar tots els capítols?",
|
||||||
"MessageConfirmRemoveAuthor": "Estàs segur que vols eliminar l'autor \"{0}\"?",
|
"MessageConfirmRemoveAuthor": "Segur que voleu eliminar l'autor «{0}»?",
|
||||||
"MessageConfirmRemoveCollection": "Estàs segur que vols eliminar la col·lecció \"{0}\"?",
|
"MessageConfirmRemoveCollection": "Segur que voleu eliminar la col·lecció «{0}»?",
|
||||||
"MessageConfirmRemoveEpisode": "Estàs segur que vols eliminar l'episodi \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Segur que voleu eliminar l'episodi «{0}»?",
|
||||||
"MessageConfirmRemoveEpisodes": "Estàs segur que vols eliminar {0} episodis?",
|
"MessageConfirmRemoveEpisodes": "Segur que voleu eliminar {0} episodis?",
|
||||||
"MessageConfirmRemoveListeningSessions": "Estàs segur que vols eliminar {0} sessions d'escolta?",
|
"MessageConfirmRemoveListeningSessions": "Segur que voleu eliminar {0} sessions d'escolta?",
|
||||||
"MessageConfirmRemoveMetadataFiles": "Estàs segur que vols eliminar tots els fitxers de metadades.{0} de les carpetes dels elements de la teva biblioteca?",
|
"MessageConfirmRemoveMetadataFiles": "Segur que voleu eliminar tots els fitxers metadata.{0} de les carpetes dels elements de la vostra biblioteca?",
|
||||||
"MessageConfirmRemoveNarrator": "Estàs segur que vols eliminar el narrador \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Segur que voleu eliminar el narrador «{0}»?",
|
||||||
"MessageConfirmRemovePlaylist": "Estàs segur que vols eliminar la llista de reproducció \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Segur que voleu eliminar la llista de reproducció «{0}»?",
|
||||||
"MessageConfirmRenameGenre": "Estàs segur que vols canviar el gènere \"{0}\" a \"{1}\" per a tots els elements?",
|
"MessageConfirmRenameGenre": "Segur que voleu canviar el nom del gènere «{0}» a «{1}» per a tots els elements?",
|
||||||
"MessageConfirmRenameGenreMergeNote": "Nota: Aquest gènere ja existeix, i es fusionarà.",
|
"MessageConfirmRenameGenreMergeNote": "Nota: Aquest gènere ja existeix, i es fusionarà.",
|
||||||
"MessageConfirmRenameGenreWarning": "Advertència! Ja existeix un gènere similar \"{0}\".",
|
"MessageConfirmRenameGenreWarning": "Advertència! Ja existeix un gènere similar \"{0}\".",
|
||||||
"MessageConfirmRenameTag": "Estàs segur que vols canviar l'etiqueta \"{0}\" a \"{1}\" per a tots els elements?",
|
"MessageConfirmRenameTag": "Segur que voleu canviar el nom de l'etiqueta «{0}» a «{1}» per a tots els elements?",
|
||||||
"MessageConfirmRenameTagMergeNote": "Nota: Aquesta etiqueta ja existeix, i es fusionarà.",
|
"MessageConfirmRenameTagMergeNote": "Nota: Aquesta etiqueta ja existeix, i es fusionarà.",
|
||||||
"MessageConfirmRenameTagWarning": "Advertència! Ja existeix una etiqueta similar \"{0}\".",
|
"MessageConfirmRenameTagWarning": "Advertència! Ja existeix una etiqueta similar \"{0}\".",
|
||||||
"MessageConfirmResetProgress": "Estàs segur que vols reiniciar el teu progrés?",
|
"MessageConfirmResetProgress": "Segur que voleu reinicialitzar el vostre progrés?",
|
||||||
"MessageConfirmSendEbookToDevice": "Estàs segur que vols enviar {0} ebook(s) \"{1}\" al dispositiu \"{2}\"?",
|
"MessageConfirmSendEbookToDevice": "Segur que voleu enviar {0} llibre(s) «{1}» al dispositiu «{2}»?",
|
||||||
"MessageConfirmUnlinkOpenId": "Estàs segur que vols desvincular aquest usuari d'OpenID?",
|
"MessageConfirmUnlinkOpenId": "Segur que voleu desenllaçar aquest usuari d'OpenID?",
|
||||||
"MessageDownloadingEpisode": "Descarregant capítol",
|
"MessageDaysListenedInTheLastYear": "{0} dies escoltats l'any passat",
|
||||||
|
"MessageDownloadingEpisode": "S'està baixant l'episodi",
|
||||||
"MessageDragFilesIntoTrackOrder": "Arrossega els fitxers en l'ordre correcte de les pistes",
|
"MessageDragFilesIntoTrackOrder": "Arrossega els fitxers en l'ordre correcte de les pistes",
|
||||||
"MessageEmbedFailed": "Error en incrustar!",
|
"MessageEmbedFailed": "Error en incrustar!",
|
||||||
"MessageEmbedFinished": "Incrustació acabada!",
|
"MessageEmbedFinished": "Incrustació acabada!",
|
||||||
"MessageEmbedQueue": "En cua per incrustar metadades ({0} en cua)",
|
"MessageEmbedQueue": "En cua per incrustar metadades ({0} en cua)",
|
||||||
|
"MessageFeedURLWillBe": "L'URL del canal serà {0}",
|
||||||
"MessageFetching": "S'està recuperant...",
|
"MessageFetching": "S'està recuperant...",
|
||||||
"MessageImportantNotice": "Avís important",
|
"MessageImportantNotice": "Avís important",
|
||||||
|
"MessageInsertChapterBelow": "Insereix un capítol a sota",
|
||||||
|
"MessageInvalidAsin": "L'ASIN no és vàlid",
|
||||||
"MessageItemsSelected": "{0} elements seleccionats",
|
"MessageItemsSelected": "{0} elements seleccionats",
|
||||||
"MessageItemsUpdated": "{0} elements actualitzats",
|
"MessageItemsUpdated": "{0} elements actualitzats",
|
||||||
|
"MessageJoinUsOn": "Uniu-vos a nosaltres a",
|
||||||
"MessageLoading": "S'està carregant...",
|
"MessageLoading": "S'està carregant...",
|
||||||
"MessageLoadingFolders": "S'estan carregant les carpetes...",
|
"MessageLoadingFolders": "S'estan carregant les carpetes...",
|
||||||
|
"MessageMarkAllEpisodesFinished": "Marca tots els episodis com a acabats",
|
||||||
|
"MessageMarkAllEpisodesNotFinished": "Marca tots els episodis com a inacabats",
|
||||||
"MessageMarkAsFinished": "Marcar com acabat",
|
"MessageMarkAsFinished": "Marcar com acabat",
|
||||||
"MessageMarkAsNotFinished": "Marcar com no acabat",
|
"MessageMarkAsNotFinished": "Marcar com no acabat",
|
||||||
"MessageMatchBooksDescription": "S'intentarà fer coincidir els llibres de la biblioteca amb un llibre del proveïdor de cerca seleccionat, i s'ompliran els detalls buits i la portada. No sobreescriu els detalls.",
|
"MessageMatchBooksDescription": "S'intentarà fer coincidir els llibres de la biblioteca amb un llibre del proveïdor de cerca seleccionat, i s'ompliran els detalls buits i la portada. No sobreescriu els detalls.",
|
||||||
@@ -776,9 +795,9 @@
|
|||||||
"MessagePauseChapter": "Pausar la reproducció del capítol",
|
"MessagePauseChapter": "Pausar la reproducció del capítol",
|
||||||
"MessagePlayChapter": "Escoltar l'inici del capítol",
|
"MessagePlayChapter": "Escoltar l'inici del capítol",
|
||||||
"MessagePlaylistCreateFromCollection": "Crear una llista de reproducció a partir d'una col·lecció",
|
"MessagePlaylistCreateFromCollection": "Crear una llista de reproducció a partir d'una col·lecció",
|
||||||
"MessagePleaseWait": "Espera si us plau...",
|
"MessagePleaseWait": "Espereu...",
|
||||||
"MessagePodcastHasNoRSSFeedForMatching": "El podcast no té una URL de font RSS que es pugui utilitzar",
|
"MessagePodcastHasNoRSSFeedForMatching": "El pòdcast no té un URL de canal RSS que es pugui utilitzar",
|
||||||
"MessagePodcastSearchField": "Introdueix el terme de cerca o la URL de la font RSS",
|
"MessagePodcastSearchField": "Introduïu el terme de cerca o l'URL del canal RSS",
|
||||||
"MessageQuickEmbedInProgress": "Integració ràpida en procés",
|
"MessageQuickEmbedInProgress": "Integració ràpida en procés",
|
||||||
"MessageQuickEmbedQueue": "En cua per a inserció ràpida ({0} en cua)",
|
"MessageQuickEmbedQueue": "En cua per a inserció ràpida ({0} en cua)",
|
||||||
"MessageQuickMatchAllEpisodes": "Combina ràpidament tots els episodis",
|
"MessageQuickMatchAllEpisodes": "Combina ràpidament tots els episodis",
|
||||||
@@ -786,10 +805,10 @@
|
|||||||
"MessageRemoveChapter": "Eliminar capítols",
|
"MessageRemoveChapter": "Eliminar capítols",
|
||||||
"MessageRemoveEpisodes": "Eliminar {0} episodi(s)",
|
"MessageRemoveEpisodes": "Eliminar {0} episodi(s)",
|
||||||
"MessageRemoveFromPlayerQueue": "Eliminar de la cua del reproductor",
|
"MessageRemoveFromPlayerQueue": "Eliminar de la cua del reproductor",
|
||||||
"MessageRemoveUserWarning": "Estàs segur que vols eliminar l'usuari \"{0}\"?",
|
"MessageRemoveUserWarning": "Segur que voleu suprimir permanentment l'usuari «{0}»?",
|
||||||
"MessageReportBugsAndContribute": "Informa d'errors, sol·licita funcions i contribueix a",
|
"MessageReportBugsAndContribute": "Informa d'errors, sol·licita funcions i contribueix a",
|
||||||
"MessageResetChaptersConfirm": "Estàs segur que vols desfer els canvis i revertir els capítols al seu estat original?",
|
"MessageResetChaptersConfirm": "Segur que voleu desfer els canvis i revertir els capítols al seu estat original?",
|
||||||
"MessageRestoreBackupConfirm": "Estàs segur que vols restaurar la còpia de seguretat creada a",
|
"MessageRestoreBackupConfirm": "Segur que voleu restaurar la còpia de seguretat creada a",
|
||||||
"MessageRestoreBackupWarning": "Restaurar sobreescriurà tota la base de dades situada a /config i les imatges de portades a /metadata/items i /metadata/authors.<br /><br />La còpia de seguretat no modifica cap fitxer a les carpetes de la teva biblioteca. Si has activat l'opció del servidor per guardar portades i metadades a les carpetes de la biblioteca, aquests fitxers no es guarden ni sobreescriuen.<br /><br />Tots els clients que utilitzin el teu servidor s'actualitzaran automàticament.",
|
"MessageRestoreBackupWarning": "Restaurar sobreescriurà tota la base de dades situada a /config i les imatges de portades a /metadata/items i /metadata/authors.<br /><br />La còpia de seguretat no modifica cap fitxer a les carpetes de la teva biblioteca. Si has activat l'opció del servidor per guardar portades i metadades a les carpetes de la biblioteca, aquests fitxers no es guarden ni sobreescriuen.<br /><br />Tots els clients que utilitzin el teu servidor s'actualitzaran automàticament.",
|
||||||
"MessageSearchResultsFor": "Resultats de la cerca de",
|
"MessageSearchResultsFor": "Resultats de la cerca de",
|
||||||
"MessageSelected": "{0} seleccionat(s)",
|
"MessageSelected": "{0} seleccionat(s)",
|
||||||
@@ -799,15 +818,15 @@
|
|||||||
"MessageShareExpiresIn": "Caduca en {0}",
|
"MessageShareExpiresIn": "Caduca en {0}",
|
||||||
"MessageShareURLWillBe": "La URL per compartir serà <strong>{0}</strong>",
|
"MessageShareURLWillBe": "La URL per compartir serà <strong>{0}</strong>",
|
||||||
"MessageStartPlaybackAtTime": "Començar la reproducció per a \"{0}\" a {1}?",
|
"MessageStartPlaybackAtTime": "Començar la reproducció per a \"{0}\" a {1}?",
|
||||||
"MessageTaskAudioFileNotWritable": "El fitxer d'àudio \"{0}\" no es pot escriure",
|
"MessageTaskAudioFileNotWritable": "El fitxer d'àudio «{0}» no es pot escriure",
|
||||||
"MessageTaskCanceledByUser": "Tasca cancel·lada per l'usuari",
|
"MessageTaskCanceledByUser": "Tasca cancel·lada per l'usuari",
|
||||||
"MessageTaskDownloadingEpisodeDescription": "Descarregant l'episodi \"{0}\"",
|
"MessageTaskDownloadingEpisodeDescription": "S'està baixant l'episodi «{0}»",
|
||||||
"MessageTaskEmbeddingMetadata": "Inserint metadades",
|
"MessageTaskEmbeddingMetadata": "Inserint metadades",
|
||||||
"MessageTaskEmbeddingMetadataDescription": "Inserint metadades en l'audiollibre \"{0}\"",
|
"MessageTaskEmbeddingMetadataDescription": "Inserint metadades en l'audiollibre \"{0}\"",
|
||||||
"MessageTaskEncodingM4b": "Codificant M4B",
|
"MessageTaskEncodingM4b": "Codificant M4B",
|
||||||
"MessageTaskEncodingM4bDescription": "Codificant l'audiollibre \"{0}\" en un únic fitxer M4B",
|
"MessageTaskEncodingM4bDescription": "S'està codificant l'audiollibre «{0}» en un únic fitxer M4B",
|
||||||
"MessageTaskFailed": "Fallada",
|
"MessageTaskFailed": "Fallada",
|
||||||
"MessageTaskFailedToBackupAudioFile": "Error en fer una còpia de seguretat del fitxer d'àudio \"{0}\"",
|
"MessageTaskFailedToBackupAudioFile": "No s'ha pogut fer una còpia de seguretat del fitxer d'àudio «{0}»",
|
||||||
"MessageTaskFailedToCreateCacheDirectory": "Error en crear el directori de la memòria cau",
|
"MessageTaskFailedToCreateCacheDirectory": "Error en crear el directori de la memòria cau",
|
||||||
"MessageTaskFailedToEmbedMetadataInFile": "Error en incrustar metadades en el fitxer \"{0}\"",
|
"MessageTaskFailedToEmbedMetadataInFile": "Error en incrustar metadades en el fitxer \"{0}\"",
|
||||||
"MessageTaskFailedToMergeAudioFiles": "Error en fusionar fitxers d'àudio",
|
"MessageTaskFailedToMergeAudioFiles": "Error en fusionar fitxers d'àudio",
|
||||||
@@ -816,14 +835,14 @@
|
|||||||
"MessageTaskMatchingBooksInLibrary": "Coincidint llibres a la biblioteca \"{0}\"",
|
"MessageTaskMatchingBooksInLibrary": "Coincidint llibres a la biblioteca \"{0}\"",
|
||||||
"MessageTaskNoFilesToScan": "Sense fitxers per escanejar",
|
"MessageTaskNoFilesToScan": "Sense fitxers per escanejar",
|
||||||
"MessageTaskOpmlImport": "Importar OPML",
|
"MessageTaskOpmlImport": "Importar OPML",
|
||||||
"MessageTaskOpmlImportDescription": "Creant podcasts a partir de {0} fonts RSS",
|
"MessageTaskOpmlImportDescription": "S'estan creant pòdcasts a partir de {0} canals RSS",
|
||||||
"MessageTaskOpmlImportFeed": "Importació de feed OPML",
|
"MessageTaskOpmlImportFeed": "Importació d'un canal OPML",
|
||||||
"MessageTaskOpmlImportFeedDescription": "Importació del feed RSS \"{0}\"",
|
"MessageTaskOpmlImportFeedDescription": "S'està important el canal RSS «{0}»",
|
||||||
"MessageTaskOpmlImportFeedFailed": "No es pot obtenir el podcast",
|
"MessageTaskOpmlImportFeedFailed": "No s'ha pogut obtenir el canal del pòdcast",
|
||||||
"MessageTaskOpmlImportFeedPodcastDescription": "Creant el podcast \"{0}\"",
|
"MessageTaskOpmlImportFeedPodcastDescription": "S'està creant el pòdcast «{0}»",
|
||||||
"MessageTaskOpmlImportFeedPodcastExists": "El podcast ja existeix a la ruta",
|
"MessageTaskOpmlImportFeedPodcastExists": "El pòdcast ja existeix al camí",
|
||||||
"MessageTaskOpmlImportFeedPodcastFailed": "Error en crear el podcast",
|
"MessageTaskOpmlImportFeedPodcastFailed": "No s'ha pogut crear el pòdcast",
|
||||||
"MessageTaskOpmlImportFinished": "Afegit {0} podcasts",
|
"MessageTaskOpmlImportFinished": "S'han afegit {0} pòdcasts",
|
||||||
"MessageTaskOpmlParseFailed": "No s'ha pogut analitzar el fitxer OPML",
|
"MessageTaskOpmlParseFailed": "No s'ha pogut analitzar el fitxer OPML",
|
||||||
"MessageTaskOpmlParseFastFail": "No s'ha trobat l'etiqueta <opml> o <outline> al fitxer OPML",
|
"MessageTaskOpmlParseFastFail": "No s'ha trobat l'etiqueta <opml> o <outline> al fitxer OPML",
|
||||||
"MessageTaskOpmlParseNoneFound": "No s'han trobat fonts al fitxer OPML",
|
"MessageTaskOpmlParseNoneFound": "No s'han trobat fonts al fitxer OPML",
|
||||||
@@ -841,13 +860,13 @@
|
|||||||
"MessageValidCronExpression": "Expressió de cron vàlida",
|
"MessageValidCronExpression": "Expressió de cron vàlida",
|
||||||
"MessageWatcherIsDisabledGlobally": "El watcher està desactivat globalment a la configuració del servidor",
|
"MessageWatcherIsDisabledGlobally": "El watcher està desactivat globalment a la configuració del servidor",
|
||||||
"MessageXLibraryIsEmpty": "La biblioteca {0} està buida!",
|
"MessageXLibraryIsEmpty": "La biblioteca {0} està buida!",
|
||||||
"MessageYourAudiobookDurationIsLonger": "La durada del teu audiollibre és més llarga que la durada trobada",
|
"MessageYourAudiobookDurationIsLonger": "La durada del vostre audiollibre és major que la durada trobada",
|
||||||
"MessageYourAudiobookDurationIsShorter": "La durada del teu audiollibre és més curta que la durada trobada",
|
"MessageYourAudiobookDurationIsShorter": "La durada del vostre audiollibre és menor que la durada trobada",
|
||||||
"NoteChangeRootPassword": "L'usuari Root és l'únic usuari que pot no tenir una contrasenya",
|
"NoteChangeRootPassword": "L'usuari Root és l'únic usuari que pot no tenir una contrasenya",
|
||||||
"NoteChapterEditorTimes": "Nota: El temps d'inici del primer capítol ha de romandre a 0:00, i el temps d'inici de l'últim capítol no pot superar la durada de l'audiollibre.",
|
"NoteChapterEditorTimes": "Nota: el temps d'inici del primer capítol ha de romandre a 0:00, i el temps d'inici de l'últim capítol no pot superar la durada de l'audiollibre.",
|
||||||
"NoteFolderPicker": "Nota: Les carpetes ja assignades no es mostraran",
|
"NoteFolderPicker": "Nota: les carpetes ja assignades no es mostraran",
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Advertència: La majoria d'aplicacions de podcast requereixen que la URL de la font RSS utilitzi HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Avís: la majoria d'aplicacions de pòdcast requereixen que l'URL del canal RSS utilitzi HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Advertència: Un o més dels teus episodis no tenen data de publicació. Algunes aplicacions de podcast ho requereixen.",
|
"NoteRSSFeedPodcastAppsPubDate": "Avís: un o més dels vostres episodis no tenen data de publicació. Algunes aplicacions de pòdcast ho requereixen.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Les carpetes amb fitxers multimèdia es gestionaran com a elements separats a la biblioteca.",
|
"NoteUploaderFoldersWithMediaFiles": "Les carpetes amb fitxers multimèdia es gestionaran com a elements separats a la biblioteca.",
|
||||||
"NoteUploaderOnlyAudioFiles": "Si només puges fitxers d'àudio, cada fitxer es gestionarà com un audiollibre separat.",
|
"NoteUploaderOnlyAudioFiles": "Si només puges fitxers d'àudio, cada fitxer es gestionarà com un audiollibre separat.",
|
||||||
"NoteUploaderUnsupportedFiles": "Els fitxers no compatibles seran ignorats. Si selecciona o arrossega una carpeta, els fitxers que no estiguin dins d'una subcarpeta seran ignorats.",
|
"NoteUploaderUnsupportedFiles": "Els fitxers no compatibles seran ignorats. Si selecciona o arrossega una carpeta, els fitxers que no estiguin dins d'una subcarpeta seran ignorats.",
|
||||||
@@ -856,7 +875,7 @@
|
|||||||
"NotificationOnEpisodeDownloadedDescription": "S'activa quan es descarrega automàticament un episodi d'un podcast",
|
"NotificationOnEpisodeDownloadedDescription": "S'activa quan es descarrega automàticament un episodi d'un podcast",
|
||||||
"NotificationOnTestDescription": "Esdeveniment per provar el sistema de notificacions",
|
"NotificationOnTestDescription": "Esdeveniment per provar el sistema de notificacions",
|
||||||
"PlaceholderNewCollection": "Nou nom de la col·lecció",
|
"PlaceholderNewCollection": "Nou nom de la col·lecció",
|
||||||
"PlaceholderNewFolderPath": "Nova ruta de carpeta",
|
"PlaceholderNewFolderPath": "Camí de carpeta nou",
|
||||||
"PlaceholderNewPlaylist": "Nou nom de la llista de reproducció",
|
"PlaceholderNewPlaylist": "Nou nom de la llista de reproducció",
|
||||||
"PlaceholderSearch": "Cerca...",
|
"PlaceholderSearch": "Cerca...",
|
||||||
"PlaceholderSearchEpisode": "Cerca d'episodis...",
|
"PlaceholderSearchEpisode": "Cerca d'episodis...",
|
||||||
@@ -882,7 +901,7 @@
|
|||||||
"ToastAppriseUrlRequired": "Cal introduir una URL de Apprise",
|
"ToastAppriseUrlRequired": "Cal introduir una URL de Apprise",
|
||||||
"ToastAsinRequired": "ASIN requerit",
|
"ToastAsinRequired": "ASIN requerit",
|
||||||
"ToastAuthorImageRemoveSuccess": "S'ha eliminat la imatge de l'autor",
|
"ToastAuthorImageRemoveSuccess": "S'ha eliminat la imatge de l'autor",
|
||||||
"ToastAuthorNotFound": "No s'ha trobat l'autor \"{0}\"",
|
"ToastAuthorNotFound": "No s'ha trobat l'autor «{0}»",
|
||||||
"ToastAuthorRemoveSuccess": "Autor eliminat",
|
"ToastAuthorRemoveSuccess": "Autor eliminat",
|
||||||
"ToastAuthorSearchNotFound": "No s'ha trobat l'autor",
|
"ToastAuthorSearchNotFound": "No s'ha trobat l'autor",
|
||||||
"ToastAuthorUpdateMerged": "Autor combinat",
|
"ToastAuthorUpdateMerged": "Autor combinat",
|
||||||
@@ -910,6 +929,7 @@
|
|||||||
"ToastCachePurgeFailed": "Error en purgar la memòria cau",
|
"ToastCachePurgeFailed": "Error en purgar la memòria cau",
|
||||||
"ToastCachePurgeSuccess": "Memòria cau purgada amb èxit",
|
"ToastCachePurgeSuccess": "Memòria cau purgada amb èxit",
|
||||||
"ToastChaptersHaveErrors": "Els capítols tenen errors",
|
"ToastChaptersHaveErrors": "Els capítols tenen errors",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "La quantitat de desplaçament no és vàlida. L'hora d'inici de l'últim capítol s'estendria més enllà de la durada d'aquest audiollibre.",
|
||||||
"ToastChaptersMustHaveTitles": "Els capítols han de tenir un títol",
|
"ToastChaptersMustHaveTitles": "Els capítols han de tenir un títol",
|
||||||
"ToastChaptersRemoved": "Capítols eliminats",
|
"ToastChaptersRemoved": "Capítols eliminats",
|
||||||
"ToastChaptersUpdated": "Capítols actualitzats",
|
"ToastChaptersUpdated": "Capítols actualitzats",
|
||||||
@@ -947,34 +967,35 @@
|
|||||||
"ToastItemMarkedAsNotFinishedSuccess": "Element marcat com a no acabat",
|
"ToastItemMarkedAsNotFinishedSuccess": "Element marcat com a no acabat",
|
||||||
"ToastItemUpdateSuccess": "Element actualitzat",
|
"ToastItemUpdateSuccess": "Element actualitzat",
|
||||||
"ToastLibraryCreateFailed": "Error en crear la biblioteca",
|
"ToastLibraryCreateFailed": "Error en crear la biblioteca",
|
||||||
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada",
|
"ToastLibraryCreateSuccess": "S'ha creat la biblioteca «{0}»",
|
||||||
"ToastLibraryDeleteFailed": "Error en eliminar la biblioteca",
|
"ToastLibraryDeleteFailed": "Error en eliminar la biblioteca",
|
||||||
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
|
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
|
||||||
"ToastLibraryScanFailedToStart": "Error en iniciar l'escaneig",
|
"ToastLibraryScanFailedToStart": "Error en iniciar l'escaneig",
|
||||||
"ToastLibraryScanStarted": "S'ha iniciat l'escaneig de la biblioteca",
|
"ToastLibraryScanStarted": "S'ha iniciat l'escaneig de la biblioteca",
|
||||||
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualitzada",
|
"ToastLibraryUpdateSuccess": "S'ha actualitzat la biblioteca «{0}»",
|
||||||
"ToastMatchAllAuthorsFailed": "No coincideix amb tots els autors",
|
"ToastMatchAllAuthorsFailed": "No coincideix amb tots els autors",
|
||||||
"ToastMetadataFilesRemovedError": "Error en eliminar metadades de {0} arxius",
|
"ToastMetadataFilesRemovedError": "S’ha produït un error en eliminar els fitxers metadata.{0}",
|
||||||
"ToastMetadataFilesRemovedNoneFound": "No s'han trobat metadades en {0} arxius",
|
"ToastMetadataFilesRemovedNoneFound": "No hi ha cap fitxer metadata.{0} a la biblioteca",
|
||||||
"ToastMetadataFilesRemovedNoneRemoved": "Cap metadada eliminada en {0} arxius",
|
"ToastMetadataFilesRemovedNoneRemoved": "No s'ha eliminat cap fitxer metadata.{0}",
|
||||||
"ToastMetadataFilesRemovedSuccess": "{0} metadades eliminades en {1} arxius",
|
"ToastMetadataFilesRemovedSuccess": "{0} metadades eliminades en {1} arxius",
|
||||||
"ToastMustHaveAtLeastOnePath": "Ha de tenir almenys una ruta",
|
"ToastMustHaveAtLeastOnePath": "Ha de tenir almenys una ruta",
|
||||||
"ToastNameEmailRequired": "El nom i el correu electrònic són obligatoris",
|
"ToastNameEmailRequired": "El nom i el correu electrònic són obligatoris",
|
||||||
"ToastNameRequired": "Nom obligatori",
|
"ToastNameRequired": "Nom obligatori",
|
||||||
"ToastNewEpisodesFound": "{0} episodi(s) nou(s) trobat(s)",
|
"ToastNewEpisodesFound": "{0} episodi(s) nou(s) trobat(s)",
|
||||||
"ToastNewUserCreatedFailed": "Error en crear el compte: \"{0}\"",
|
"ToastNewUserCreatedFailed": "No s'ha pogut crear el compte: «{0}»",
|
||||||
"ToastNewUserCreatedSuccess": "Nou compte creat",
|
"ToastNewUserCreatedSuccess": "Nou compte creat",
|
||||||
"ToastNewUserLibraryError": "Ha de seleccionar almenys una biblioteca",
|
"ToastNewUserLibraryError": "S'ha de seleccionar almenys una biblioteca",
|
||||||
"ToastNewUserPasswordError": "Necessites una contrasenya, només el root pot estar sense contrasenya",
|
"ToastNewUserPasswordError": "Necessites una contrasenya, només el root pot estar sense contrasenya",
|
||||||
"ToastNewUserTagError": "Selecciona almenys una etiqueta",
|
"ToastNewUserTagError": "S'ha de seleccionar almenys una etiqueta",
|
||||||
"ToastNewUserUsernameError": "Introdueix un nom d'usuari",
|
"ToastNewUserUsernameError": "Introduïu un nom d'usuari",
|
||||||
"ToastNoNewEpisodesFound": "No s'han trobat nous episodis",
|
"ToastNoNewEpisodesFound": "No s'han trobat nous episodis",
|
||||||
|
"ToastNoRSSFeed": "El pòdcast no té canal RSS",
|
||||||
"ToastNoUpdatesNecessary": "No cal actualitzar",
|
"ToastNoUpdatesNecessary": "No cal actualitzar",
|
||||||
"ToastNotificationCreateFailed": "Error en crear la notificació",
|
"ToastNotificationCreateFailed": "No s'ha pogut crear la notificació",
|
||||||
"ToastNotificationDeleteFailed": "Error en eliminar la notificació",
|
"ToastNotificationDeleteFailed": "No s'ha pogut suprimir la notificació",
|
||||||
"ToastNotificationFailedMaximum": "El nombre màxim d'intents fallits ha de ser ≥ 0",
|
"ToastNotificationFailedMaximum": "El nombre màxim d'intents fallits ha de ser ≥ 0",
|
||||||
"ToastNotificationQueueMaximum": "La cua de notificació màxima ha de ser ≥ 0",
|
"ToastNotificationQueueMaximum": "La cua de notificació màxima ha de ser ≥ 0",
|
||||||
"ToastNotificationSettingsUpdateSuccess": "Configuració de notificació actualitzada",
|
"ToastNotificationSettingsUpdateSuccess": "S'han actualitzat els paràmetres de notificacions",
|
||||||
"ToastNotificationTestTriggerFailed": "No s'ha pogut activar la notificació de prova",
|
"ToastNotificationTestTriggerFailed": "No s'ha pogut activar la notificació de prova",
|
||||||
"ToastNotificationTestTriggerSuccess": "Notificació de prova activada",
|
"ToastNotificationTestTriggerSuccess": "Notificació de prova activada",
|
||||||
"ToastNotificationUpdateSuccess": "Notificació actualitzada",
|
"ToastNotificationUpdateSuccess": "Notificació actualitzada",
|
||||||
@@ -984,16 +1005,16 @@
|
|||||||
"ToastPlaylistUpdateSuccess": "Llista de reproducció actualitzada",
|
"ToastPlaylistUpdateSuccess": "Llista de reproducció actualitzada",
|
||||||
"ToastPodcastCreateFailed": "No s'ha pogut crear el pòdcast",
|
"ToastPodcastCreateFailed": "No s'ha pogut crear el pòdcast",
|
||||||
"ToastPodcastCreateSuccess": "S'ha creat el pòdcast correctament",
|
"ToastPodcastCreateSuccess": "S'ha creat el pòdcast correctament",
|
||||||
"ToastPodcastGetFeedFailed": "No s'ha pogut obtenir el podcast",
|
"ToastPodcastGetFeedFailed": "No s'ha pogut obtenir el canal del pòdcast",
|
||||||
"ToastPodcastNoEpisodesInFeed": "No s'han trobat episodis en el feed RSS",
|
"ToastPodcastNoEpisodesInFeed": "No s'ha trobat cap episodi al canal RSS",
|
||||||
"ToastPodcastNoRssFeed": "El podcast no té un feed RSS",
|
"ToastPodcastNoRssFeed": "El pòdcast no té un canal RSS",
|
||||||
"ToastProgressIsNotBeingSynced": "El progrés no s'està sincronitzant, reinicia la reproducció",
|
"ToastProgressIsNotBeingSynced": "El progrés no s'està sincronitzant, reinicia la reproducció",
|
||||||
"ToastProviderCreatedFailed": "Error en afegir el proveïdor",
|
"ToastProviderCreatedFailed": "Error en afegir el proveïdor",
|
||||||
"ToastProviderCreatedSuccess": "Nou proveïdor afegit",
|
"ToastProviderCreatedSuccess": "Nou proveïdor afegit",
|
||||||
"ToastProviderNameAndUrlRequired": "Nom i URL obligatoris",
|
"ToastProviderNameAndUrlRequired": "Nom i URL obligatoris",
|
||||||
"ToastProviderRemoveSuccess": "Proveïdor eliminat",
|
"ToastProviderRemoveSuccess": "Proveïdor eliminat",
|
||||||
"ToastRSSFeedCloseFailed": "Error en tancar el feed RSS",
|
"ToastRSSFeedCloseFailed": "No s'ha pogut tancar el canal RSS",
|
||||||
"ToastRSSFeedCloseSuccess": "Feed RSS tancat",
|
"ToastRSSFeedCloseSuccess": "Canal RSS tancat",
|
||||||
"ToastRemoveFailed": "Error en eliminar",
|
"ToastRemoveFailed": "Error en eliminar",
|
||||||
"ToastRemoveItemFromCollectionFailed": "Error en eliminar l'element de la col·lecció",
|
"ToastRemoveItemFromCollectionFailed": "Error en eliminar l'element de la col·lecció",
|
||||||
"ToastRemoveItemFromCollectionSuccess": "Element eliminat de la col·lecció",
|
"ToastRemoveItemFromCollectionSuccess": "Element eliminat de la col·lecció",
|
||||||
@@ -1008,6 +1029,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Selecciona almenys un usuari",
|
"ToastSelectAtLeastOneUser": "Selecciona almenys un usuari",
|
||||||
"ToastSendEbookToDeviceFailed": "Error en enviar l'ebook al dispositiu",
|
"ToastSendEbookToDeviceFailed": "Error en enviar l'ebook al dispositiu",
|
||||||
"ToastSendEbookToDeviceSuccess": "Ebook enviat al dispositiu \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "Ebook enviat al dispositiu \"{0}\"",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "No és possible afegir dues sèries amb el mateix nom",
|
||||||
"ToastSeriesUpdateFailed": "Error en actualitzar la sèrie",
|
"ToastSeriesUpdateFailed": "Error en actualitzar la sèrie",
|
||||||
"ToastSeriesUpdateSuccess": "Sèrie actualitzada",
|
"ToastSeriesUpdateSuccess": "Sèrie actualitzada",
|
||||||
"ToastServerSettingsUpdateSuccess": "Configuració del servidor actualitzada",
|
"ToastServerSettingsUpdateSuccess": "Configuració del servidor actualitzada",
|
||||||
@@ -1026,6 +1048,8 @@
|
|||||||
"ToastUnknownError": "Error desconegut",
|
"ToastUnknownError": "Error desconegut",
|
||||||
"ToastUnlinkOpenIdFailed": "Error en desvincular l'usuari d'OpenID",
|
"ToastUnlinkOpenIdFailed": "Error en desvincular l'usuari d'OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Usuari desvinculat d'OpenID",
|
"ToastUnlinkOpenIdSuccess": "Usuari desvinculat d'OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "El camí del fitxer «{0}» ja existeix al servidor",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "L'element «{0}» usa un subdirectori del camí de pujada.",
|
||||||
"ToastUserDeleteFailed": "Error en eliminar l'usuari",
|
"ToastUserDeleteFailed": "Error en eliminar l'usuari",
|
||||||
"ToastUserDeleteSuccess": "Usuari eliminat",
|
"ToastUserDeleteSuccess": "Usuari eliminat",
|
||||||
"ToastUserPasswordChangeSuccess": "Contrasenya canviada correctament",
|
"ToastUserPasswordChangeSuccess": "Contrasenya canviada correctament",
|
||||||
|
|||||||
+18
-4
@@ -10,6 +10,8 @@
|
|||||||
"ButtonApplyChapters": "Aplikovat kapitoly",
|
"ButtonApplyChapters": "Aplikovat kapitoly",
|
||||||
"ButtonAuthors": "Autoři",
|
"ButtonAuthors": "Autoři",
|
||||||
"ButtonBack": "Zpět",
|
"ButtonBack": "Zpět",
|
||||||
|
"ButtonBatchEditPopulateFromExisting": "Vytvořit z existujících",
|
||||||
|
"ButtonBatchEditPopulateMapDetails": "Předvyplnit podrobnosti mapování",
|
||||||
"ButtonBrowseForFolder": "Vyhledat složku",
|
"ButtonBrowseForFolder": "Vyhledat složku",
|
||||||
"ButtonCancel": "Zrušit",
|
"ButtonCancel": "Zrušit",
|
||||||
"ButtonCancelEncode": "Zrušit kódování",
|
"ButtonCancelEncode": "Zrušit kódování",
|
||||||
@@ -145,7 +147,7 @@
|
|||||||
"HeaderItemFiles": "Soubory položek",
|
"HeaderItemFiles": "Soubory položek",
|
||||||
"HeaderItemMetadataUtils": "Nástroje metadat položek",
|
"HeaderItemMetadataUtils": "Nástroje metadat položek",
|
||||||
"HeaderLastListeningSession": "Poslední poslechová relace",
|
"HeaderLastListeningSession": "Poslední poslechová relace",
|
||||||
"HeaderLatestEpisodes": "Nejnovější epizody",
|
"HeaderLatestEpisodes": "Nové epizody",
|
||||||
"HeaderLibraries": "Knihovny",
|
"HeaderLibraries": "Knihovny",
|
||||||
"HeaderLibraryFiles": "Soubory knihovny",
|
"HeaderLibraryFiles": "Soubory knihovny",
|
||||||
"HeaderLibraryStats": "Statistiky knihovny",
|
"HeaderLibraryStats": "Statistiky knihovny",
|
||||||
@@ -227,6 +229,7 @@
|
|||||||
"LabelAddedDate": "Přidáno {0}",
|
"LabelAddedDate": "Přidáno {0}",
|
||||||
"LabelAdminUsersOnly": "Pouze administrátoři",
|
"LabelAdminUsersOnly": "Pouze administrátoři",
|
||||||
"LabelAll": "Vše",
|
"LabelAll": "Vše",
|
||||||
|
"LabelAllEpisodesDownloaded": "Všechny epizody staženy",
|
||||||
"LabelAllUsers": "Všichni uživatelé",
|
"LabelAllUsers": "Všichni uživatelé",
|
||||||
"LabelAllUsersExcludingGuests": "Všichni uživatelé kromě hostů",
|
"LabelAllUsersExcludingGuests": "Všichni uživatelé kromě hostů",
|
||||||
"LabelAllUsersIncludingGuests": "Všichni uživatelé včetně hostů",
|
"LabelAllUsersIncludingGuests": "Všichni uživatelé včetně hostů",
|
||||||
@@ -250,7 +253,7 @@
|
|||||||
"LabelBackToUser": "Zpět k uživateli",
|
"LabelBackToUser": "Zpět k uživateli",
|
||||||
"LabelBackupAudioFiles": "Zálohovat zvukové soubory",
|
"LabelBackupAudioFiles": "Zálohovat zvukové soubory",
|
||||||
"LabelBackupLocation": "Umístění zálohy",
|
"LabelBackupLocation": "Umístění zálohy",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Povolit automatické zálohování",
|
"LabelBackupsEnableAutomaticBackups": "Automatické zálohování",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Zálohy uložené v /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Zálohy uložené v /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Maximální velikost zálohy (v GB) (0 bez omezení)",
|
"LabelBackupsMaxBackupSize": "Maximální velikost zálohy (v GB) (0 bez omezení)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Ochrana proti chybné konfiguraci: Zálohování se nezdaří, pokud překročí nastavenou velikost.",
|
"LabelBackupsMaxBackupSizeHelp": "Ochrana proti chybné konfiguraci: Zálohování se nezdaří, pokud překročí nastavenou velikost.",
|
||||||
@@ -282,6 +285,7 @@
|
|||||||
"LabelContinueSeries": "Pokračovat v sérii",
|
"LabelContinueSeries": "Pokračovat v sérii",
|
||||||
"LabelCover": "Obálka",
|
"LabelCover": "Obálka",
|
||||||
"LabelCoverImageURL": "URL obrázku obálky",
|
"LabelCoverImageURL": "URL obrázku obálky",
|
||||||
|
"LabelCoverProvider": "Poskytovatel obálky",
|
||||||
"LabelCreatedAt": "Vytvořeno v",
|
"LabelCreatedAt": "Vytvořeno v",
|
||||||
"LabelCronExpression": "Výraz Cronu",
|
"LabelCronExpression": "Výraz Cronu",
|
||||||
"LabelCurrent": "Aktuální",
|
"LabelCurrent": "Aktuální",
|
||||||
@@ -421,7 +425,7 @@
|
|||||||
"LabelLookForNewEpisodesAfterDate": "Hledat nové epizody po tomto datu",
|
"LabelLookForNewEpisodesAfterDate": "Hledat nové epizody po tomto datu",
|
||||||
"LabelLowestPriority": "Nejnižší priorita",
|
"LabelLowestPriority": "Nejnižší priorita",
|
||||||
"LabelMatchExistingUsersBy": "Přiřadit stávající uživatele podle",
|
"LabelMatchExistingUsersBy": "Přiřadit stávající uživatele podle",
|
||||||
"LabelMatchExistingUsersByDescription": "Slouží k propojení stávajících uživatelů. Po propojení budou uživatelé přiřazeni k jedinečnému ID od poskytovatele SSO.",
|
"LabelMatchExistingUsersByDescription": "Slouží k propojení stávajících uživatelů. Po propojení budou uživatelé přiřazeni k jedinečnému ID od poskytovatele SSO",
|
||||||
"LabelMaxEpisodesToDownload": "Maximální # epizod pro stažení. Použijte 0 pro bez omezení.",
|
"LabelMaxEpisodesToDownload": "Maximální # epizod pro stažení. Použijte 0 pro bez omezení.",
|
||||||
"LabelMaxEpisodesToDownloadPerCheck": "Maximální počet nových epizod ke stažení při jedné kontrole",
|
"LabelMaxEpisodesToDownloadPerCheck": "Maximální počet nových epizod ke stažení při jedné kontrole",
|
||||||
"LabelMaxEpisodesToKeep": "Maximální počet epizod k zachování",
|
"LabelMaxEpisodesToKeep": "Maximální počet epizod k zachování",
|
||||||
@@ -430,7 +434,7 @@
|
|||||||
"LabelMediaType": "Typ média",
|
"LabelMediaType": "Typ média",
|
||||||
"LabelMetaTag": "Metaznačka",
|
"LabelMetaTag": "Metaznačka",
|
||||||
"LabelMetaTags": "Metaznačky",
|
"LabelMetaTags": "Metaznačky",
|
||||||
"LabelMetadataOrderOfPrecedenceDescription": "Zdroje metadat s vyšší prioritou budou mít přednost před zdroji metadat s nižší prioritou.",
|
"LabelMetadataOrderOfPrecedenceDescription": "Zdroje metadat s vyšší prioritou budou mít přednost před zdroji metadat s nižší prioritou",
|
||||||
"LabelMetadataProvider": "Poskytovatel metadat",
|
"LabelMetadataProvider": "Poskytovatel metadat",
|
||||||
"LabelMinute": "Minuta",
|
"LabelMinute": "Minuta",
|
||||||
"LabelMinutes": "Minuty",
|
"LabelMinutes": "Minuty",
|
||||||
@@ -555,6 +559,8 @@
|
|||||||
"LabelSettingsBookshelfViewHelp": "Skeumorfní design s dřevěnými policemi",
|
"LabelSettingsBookshelfViewHelp": "Skeumorfní design s dřevěnými policemi",
|
||||||
"LabelSettingsChromecastSupport": "Podpora Chromecastu",
|
"LabelSettingsChromecastSupport": "Podpora Chromecastu",
|
||||||
"LabelSettingsDateFormat": "Formát data",
|
"LabelSettingsDateFormat": "Formát data",
|
||||||
|
"LabelSettingsEnableWatcher": "Automaticky skenovat změny v knihovnách",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Automaticky skenovat změny v knihovně",
|
||||||
"LabelSettingsEnableWatcherHelp": "Povoluje automatické přidávání/aktualizaci položek, když jsou zjištěny změny souborů. *Vyžaduje restart serveru",
|
"LabelSettingsEnableWatcherHelp": "Povoluje automatické přidávání/aktualizaci položek, když jsou zjištěny změny souborů. *Vyžaduje restart serveru",
|
||||||
"LabelSettingsEpubsAllowScriptedContent": "Povolení skriptovaného obsahu v epubu",
|
"LabelSettingsEpubsAllowScriptedContent": "Povolení skriptovaného obsahu v epubu",
|
||||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Povolení spouštění skriptů v souborech epub. Doporučujeme toto nastavení vypnout, pokud nedůvěřujete zdroji souborů epub.",
|
"LabelSettingsEpubsAllowScriptedContentHelp": "Povolení spouštění skriptů v souborech epub. Doporučujeme toto nastavení vypnout, pokud nedůvěřujete zdroji souborů epub.",
|
||||||
@@ -598,6 +604,7 @@
|
|||||||
"LabelSlug": "URL název",
|
"LabelSlug": "URL název",
|
||||||
"LabelSortAscending": "Vzestupně",
|
"LabelSortAscending": "Vzestupně",
|
||||||
"LabelSortDescending": "Sestupně",
|
"LabelSortDescending": "Sestupně",
|
||||||
|
"LabelSortPubDate": "Seřadit podle datumu publikování",
|
||||||
"LabelStart": "Spustit",
|
"LabelStart": "Spustit",
|
||||||
"LabelStartTime": "Čas Spuštění",
|
"LabelStartTime": "Čas Spuštění",
|
||||||
"LabelStarted": "Spuštěno",
|
"LabelStarted": "Spuštěno",
|
||||||
@@ -702,6 +709,8 @@
|
|||||||
"MessageBackupsLocationEditNote": "Poznámka: Změna umístění záloh nepřesune ani nezmění existující zálohy",
|
"MessageBackupsLocationEditNote": "Poznámka: Změna umístění záloh nepřesune ani nezmění existující zálohy",
|
||||||
"MessageBackupsLocationNoEditNote": "Poznámka: Umístění záloh je nastavené z proměnných prostředí a nelze zde změnit.",
|
"MessageBackupsLocationNoEditNote": "Poznámka: Umístění záloh je nastavené z proměnných prostředí a nelze zde změnit.",
|
||||||
"MessageBackupsLocationPathEmpty": "Umístění záloh nemůže být prázdné",
|
"MessageBackupsLocationPathEmpty": "Umístění záloh nemůže být prázdné",
|
||||||
|
"MessageBatchEditPopulateMapDetailsAllHelp": "Předvyplnit vybraná pole datami ze všech položek. Pole s více hodnotami budou sloučena",
|
||||||
|
"MessageBatchEditPopulateMapDetailsItemHelp": "Předvyplnit povolená pole mapování daty z této položky",
|
||||||
"MessageBatchQuickMatchDescription": "Rychlá párování se pokusí přidat chybějící obálky a metadata pro vybrané položky. Povolením níže uvedených možností umožníte funkci Rychlé párování přepsat stávající obálky a/nebo metadata.",
|
"MessageBatchQuickMatchDescription": "Rychlá párování se pokusí přidat chybějící obálky a metadata pro vybrané položky. Povolením níže uvedených možností umožníte funkci Rychlé párování přepsat stávající obálky a/nebo metadata.",
|
||||||
"MessageBookshelfNoCollections": "Ještě jste nevytvořili žádnou sbírku",
|
"MessageBookshelfNoCollections": "Ještě jste nevytvořili žádnou sbírku",
|
||||||
"MessageBookshelfNoCollectionsHelp": "Kolekce jsou veřejné. Mohou je zobrazit všichni uživatelé s přístupem do knihovny.",
|
"MessageBookshelfNoCollectionsHelp": "Kolekce jsou veřejné. Mohou je zobrazit všichni uživatelé s přístupem do knihovny.",
|
||||||
@@ -838,6 +847,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Opravdu chcete obnovit zálohu vytvořenou dne",
|
"MessageRestoreBackupConfirm": "Opravdu chcete obnovit zálohu vytvořenou dne",
|
||||||
"MessageRestoreBackupWarning": "Obnovení zálohy přepíše celou databázi umístěnou v /config a obálku obrázků v /metadata/items & /metadata/authors.<br /><br />Backups nezmění žádné soubory ve složkách knihovny. Pokud jste povolili nastavení serveru pro ukládání obrázků obalu a metadat do složek knihovny, nebudou zálohovány ani přepsány.<br /><br />Všichni klienti používající váš server budou automaticky obnoveni.",
|
"MessageRestoreBackupWarning": "Obnovení zálohy přepíše celou databázi umístěnou v /config a obálku obrázků v /metadata/items & /metadata/authors.<br /><br />Backups nezmění žádné soubory ve složkách knihovny. Pokud jste povolili nastavení serveru pro ukládání obrázků obalu a metadat do složek knihovny, nebudou zálohovány ani přepsány.<br /><br />Všichni klienti používající váš server budou automaticky obnoveni.",
|
||||||
"MessageScheduleLibraryScanNote": "Většině uživatelů se doporučuje ponechat tuto funkci vypnutou a ponechat zapnuté nastavení sledování složek. Sledování složek automaticky zjistí změny ve složkách vaší knihovny. Sledování složek nefunguje pro každý souborový systém (jako je NFS), takže místo toho lze použít plánované skenování knihoven.",
|
"MessageScheduleLibraryScanNote": "Většině uživatelů se doporučuje ponechat tuto funkci vypnutou a ponechat zapnuté nastavení sledování složek. Sledování složek automaticky zjistí změny ve složkách vaší knihovny. Sledování složek nefunguje pro každý souborový systém (jako je NFS), takže místo toho lze použít plánované skenování knihoven.",
|
||||||
|
"MessageScheduleRunEveryWeekdayAtTime": "Spusť každý {0} v {1}",
|
||||||
"MessageSearchResultsFor": "Výsledky hledání pro",
|
"MessageSearchResultsFor": "Výsledky hledání pro",
|
||||||
"MessageSelected": "{0} vybráno",
|
"MessageSelected": "{0} vybráno",
|
||||||
"MessageServerCouldNotBeReached": "Server je nedostupný",
|
"MessageServerCouldNotBeReached": "Server je nedostupný",
|
||||||
@@ -945,6 +955,7 @@
|
|||||||
"ToastBackupRestoreFailed": "Nepodařilo se obnovit zálohu",
|
"ToastBackupRestoreFailed": "Nepodařilo se obnovit zálohu",
|
||||||
"ToastBackupUploadFailed": "Nepodařilo se nahrát zálohu",
|
"ToastBackupUploadFailed": "Nepodařilo se nahrát zálohu",
|
||||||
"ToastBackupUploadSuccess": "Záloha nahrána",
|
"ToastBackupUploadSuccess": "Záloha nahrána",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Detaily aplikované na položky",
|
||||||
"ToastBatchDeleteFailed": "Hromadné smazání selhalo",
|
"ToastBatchDeleteFailed": "Hromadné smazání selhalo",
|
||||||
"ToastBatchDeleteSuccess": "Hromadné smazání proběhlo úspěšně",
|
"ToastBatchDeleteSuccess": "Hromadné smazání proběhlo úspěšně",
|
||||||
"ToastBatchQuickMatchFailed": "Rychlá schoda dávky se nezdařila!",
|
"ToastBatchQuickMatchFailed": "Rychlá schoda dávky se nezdařila!",
|
||||||
@@ -1057,6 +1068,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Vyberte alespoň jednoho uživatele",
|
"ToastSelectAtLeastOneUser": "Vyberte alespoň jednoho uživatele",
|
||||||
"ToastSendEbookToDeviceFailed": "Odeslání e-knihy do zařízení se nezdařilo",
|
"ToastSendEbookToDeviceFailed": "Odeslání e-knihy do zařízení se nezdařilo",
|
||||||
"ToastSendEbookToDeviceSuccess": "E-kniha odeslána do zařízení \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "E-kniha odeslána do zařízení \"{0}\"",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Nelze přidat dvě série se stejným názvem",
|
||||||
"ToastSeriesUpdateFailed": "Aktualizace série se nezdařila",
|
"ToastSeriesUpdateFailed": "Aktualizace série se nezdařila",
|
||||||
"ToastSeriesUpdateSuccess": "Aktualizace série byla úspěšná",
|
"ToastSeriesUpdateSuccess": "Aktualizace série byla úspěšná",
|
||||||
"ToastServerSettingsUpdateSuccess": "Nastavení serveru aktualizováno",
|
"ToastServerSettingsUpdateSuccess": "Nastavení serveru aktualizováno",
|
||||||
@@ -1075,6 +1087,8 @@
|
|||||||
"ToastUnknownError": "Neznámý error",
|
"ToastUnknownError": "Neznámý error",
|
||||||
"ToastUnlinkOpenIdFailed": "Chyba při odpárování uživatele z OpenID",
|
"ToastUnlinkOpenIdFailed": "Chyba při odpárování uživatele z OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Uživatel odpárován z uživatele z OpenID",
|
"ToastUnlinkOpenIdSuccess": "Uživatel odpárován z uživatele z OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Soubor \"{0}\" na serveru již existuje",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Položka \"{0}\" používá podsložku nahrávané cesty.",
|
||||||
"ToastUserDeleteFailed": "Nepodařilo se smazat uživatele",
|
"ToastUserDeleteFailed": "Nepodařilo se smazat uživatele",
|
||||||
"ToastUserDeleteSuccess": "Uživatel smazán",
|
"ToastUserDeleteSuccess": "Uživatel smazán",
|
||||||
"ToastUserPasswordChangeSuccess": "Heslo bylo změněno úspěšně",
|
"ToastUserPasswordChangeSuccess": "Heslo bylo změněno úspěšně",
|
||||||
|
|||||||
@@ -229,6 +229,7 @@
|
|||||||
"LabelAddedDate": "Tilføjet {0}",
|
"LabelAddedDate": "Tilføjet {0}",
|
||||||
"LabelAdminUsersOnly": "Kun Administratorer",
|
"LabelAdminUsersOnly": "Kun Administratorer",
|
||||||
"LabelAll": "Alle",
|
"LabelAll": "Alle",
|
||||||
|
"LabelAllEpisodesDownloaded": "Alle episoder hentet",
|
||||||
"LabelAllUsers": "Alle Brugere",
|
"LabelAllUsers": "Alle Brugere",
|
||||||
"LabelAllUsersExcludingGuests": "Alle bruger eksklusiv gæster",
|
"LabelAllUsersExcludingGuests": "Alle bruger eksklusiv gæster",
|
||||||
"LabelAllUsersIncludingGuests": "Alle bruger inklusiv gæster",
|
"LabelAllUsersIncludingGuests": "Alle bruger inklusiv gæster",
|
||||||
@@ -252,7 +253,7 @@
|
|||||||
"LabelBackToUser": "Tilbage til Bruger",
|
"LabelBackToUser": "Tilbage til Bruger",
|
||||||
"LabelBackupAudioFiles": "Sikkerhedskopier lydfiler",
|
"LabelBackupAudioFiles": "Sikkerhedskopier lydfiler",
|
||||||
"LabelBackupLocation": "Backup Placering",
|
"LabelBackupLocation": "Backup Placering",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Aktivér automatisk sikkerhedskopiering",
|
"LabelBackupsEnableAutomaticBackups": "Automatisk sikkerhedskopiering",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Sikkerhedskopier gemt i /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Sikkerhedskopier gemt i /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Maksimal sikkerhedskopistørrelse (i GB) (0 for ubegrænset)",
|
"LabelBackupsMaxBackupSize": "Maksimal sikkerhedskopistørrelse (i GB) (0 for ubegrænset)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Som en beskyttelse mod fejlkonfiguration fejler sikkerhedskopier, hvis de overstiger den konfigurerede størrelse.",
|
"LabelBackupsMaxBackupSizeHelp": "Som en beskyttelse mod fejlkonfiguration fejler sikkerhedskopier, hvis de overstiger den konfigurerede størrelse.",
|
||||||
@@ -558,6 +559,8 @@
|
|||||||
"LabelSettingsBookshelfViewHelp": "Skeumorfisk design med træhylder",
|
"LabelSettingsBookshelfViewHelp": "Skeumorfisk design med træhylder",
|
||||||
"LabelSettingsChromecastSupport": "Chromecast-understøttelse",
|
"LabelSettingsChromecastSupport": "Chromecast-understøttelse",
|
||||||
"LabelSettingsDateFormat": "Datoformat",
|
"LabelSettingsDateFormat": "Datoformat",
|
||||||
|
"LabelSettingsEnableWatcher": "Scan automatisk bibliotek for ændringer",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Scan automatisk bibliotek for ændringer",
|
||||||
"LabelSettingsEnableWatcherHelp": "Aktiverer automatisk tilføjelse/opdatering af elementer, når filændringer registreres. *Kræver servergenstart",
|
"LabelSettingsEnableWatcherHelp": "Aktiverer automatisk tilføjelse/opdatering af elementer, når filændringer registreres. *Kræver servergenstart",
|
||||||
"LabelSettingsEpubsAllowScriptedContent": "Tillad scriptet indhold i epub",
|
"LabelSettingsEpubsAllowScriptedContent": "Tillad scriptet indhold i epub",
|
||||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillad epub filer at køre scripts. Det anbefales at holde denne indstilling deaktiveret med mindre du stoler på kilderne af epub filerne.",
|
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillad epub filer at køre scripts. Det anbefales at holde denne indstilling deaktiveret med mindre du stoler på kilderne af epub filerne.",
|
||||||
@@ -1063,6 +1066,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Vælg mindst en bruger",
|
"ToastSelectAtLeastOneUser": "Vælg mindst en bruger",
|
||||||
"ToastSendEbookToDeviceFailed": "Mislykkedes afsendelse af e-bog til enhed",
|
"ToastSendEbookToDeviceFailed": "Mislykkedes afsendelse af e-bog til enhed",
|
||||||
"ToastSendEbookToDeviceSuccess": "E-bog afsendt til enhed \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "E-bog afsendt til enhed \"{0}\"",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Kan ikke tilføje to serier med samme navn",
|
||||||
"ToastSeriesUpdateFailed": "Mislykkedes opdatering af serie",
|
"ToastSeriesUpdateFailed": "Mislykkedes opdatering af serie",
|
||||||
"ToastSeriesUpdateSuccess": "Serieopdatering lykkedes",
|
"ToastSeriesUpdateSuccess": "Serieopdatering lykkedes",
|
||||||
"ToastServerSettingsUpdateSuccess": "Server indstillinger opdateret",
|
"ToastServerSettingsUpdateSuccess": "Server indstillinger opdateret",
|
||||||
@@ -1081,6 +1085,8 @@
|
|||||||
"ToastUnknownError": "Ukendt fejl",
|
"ToastUnknownError": "Ukendt fejl",
|
||||||
"ToastUnlinkOpenIdFailed": "Fejlede i af afkoble bruger fra OpenID",
|
"ToastUnlinkOpenIdFailed": "Fejlede i af afkoble bruger fra OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Bruger afkoblet fra OpenID",
|
"ToastUnlinkOpenIdSuccess": "Bruger afkoblet fra OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Filsti \"{0}\" findes allerede på serveren",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Genstand \"{0}\" benytter en undermappe af upload stien",
|
||||||
"ToastUserDeleteFailed": "Mislykkedes sletning af bruger",
|
"ToastUserDeleteFailed": "Mislykkedes sletning af bruger",
|
||||||
"ToastUserDeleteSuccess": "Bruger slettet",
|
"ToastUserDeleteSuccess": "Bruger slettet",
|
||||||
"ToastUserPasswordChangeSuccess": "Password ændret",
|
"ToastUserPasswordChangeSuccess": "Password ændret",
|
||||||
|
|||||||
+13
-1
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Wiedergabeliste",
|
"HeaderPlaylist": "Wiedergabeliste",
|
||||||
"HeaderPlaylistItems": "Einträge in der Wiedergabeliste",
|
"HeaderPlaylistItems": "Einträge in der Wiedergabeliste",
|
||||||
"HeaderPodcastsToAdd": "Podcasts zum Hinzufügen",
|
"HeaderPodcastsToAdd": "Podcasts zum Hinzufügen",
|
||||||
|
"HeaderPresets": "Voreinstellungen",
|
||||||
"HeaderPreviewCover": "Vorschau Titelbild",
|
"HeaderPreviewCover": "Vorschau Titelbild",
|
||||||
"HeaderRSSFeedGeneral": "RSS Details",
|
"HeaderRSSFeedGeneral": "RSS Details",
|
||||||
"HeaderRSSFeedIsOpen": "RSS-Feed ist geöffnet",
|
"HeaderRSSFeedIsOpen": "RSS-Feed ist geöffnet",
|
||||||
@@ -229,6 +230,7 @@
|
|||||||
"LabelAddedDate": "{0} Hinzugefügt",
|
"LabelAddedDate": "{0} Hinzugefügt",
|
||||||
"LabelAdminUsersOnly": "Nur Admin Benutzer",
|
"LabelAdminUsersOnly": "Nur Admin Benutzer",
|
||||||
"LabelAll": "Alle",
|
"LabelAll": "Alle",
|
||||||
|
"LabelAllEpisodesDownloaded": "Alle Episoden heruntergeladen",
|
||||||
"LabelAllUsers": "Alle Benutzer",
|
"LabelAllUsers": "Alle Benutzer",
|
||||||
"LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen",
|
"LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen",
|
||||||
"LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste",
|
"LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste",
|
||||||
@@ -397,7 +399,7 @@
|
|||||||
"LabelInvert": "Umkehren",
|
"LabelInvert": "Umkehren",
|
||||||
"LabelItem": "Medium",
|
"LabelItem": "Medium",
|
||||||
"LabelJumpBackwardAmount": "Zurückspringen Zeit",
|
"LabelJumpBackwardAmount": "Zurückspringen Zeit",
|
||||||
"LabelJumpForwardAmount": "Vorwärtsspringn Zeit",
|
"LabelJumpForwardAmount": "Vorwärtsspringen Zeit",
|
||||||
"LabelLanguage": "Sprache",
|
"LabelLanguage": "Sprache",
|
||||||
"LabelLanguageDefaultServer": "Standard-Server-Sprache",
|
"LabelLanguageDefaultServer": "Standard-Server-Sprache",
|
||||||
"LabelLanguages": "Sprachen",
|
"LabelLanguages": "Sprachen",
|
||||||
@@ -529,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Veröffentlichungsdatum",
|
"LabelReleaseDate": "Veröffentlichungsdatum",
|
||||||
"LabelRemoveAllMetadataAbs": "Alle metadata.abs Dateien löschen",
|
"LabelRemoveAllMetadataAbs": "Alle metadata.abs Dateien löschen",
|
||||||
"LabelRemoveAllMetadataJson": "Alle metadata.json Dateien löschen",
|
"LabelRemoveAllMetadataJson": "Alle metadata.json Dateien löschen",
|
||||||
|
"LabelRemoveAudibleBranding": "Audible Intro sowie Outro aus Kapiteln entfernen",
|
||||||
"LabelRemoveCover": "Entferne Titelbild",
|
"LabelRemoveCover": "Entferne Titelbild",
|
||||||
"LabelRemoveMetadataFile": "Metadaten-Dateien in Bibliotheksordnern löschen",
|
"LabelRemoveMetadataFile": "Metadaten-Dateien in Bibliotheksordnern löschen",
|
||||||
"LabelRemoveMetadataFileHelp": "Alle metadata.json und metadata.abs Dateien aus den Ordnern {0} löschen.",
|
"LabelRemoveMetadataFileHelp": "Alle metadata.json und metadata.abs Dateien aus den Ordnern {0} löschen.",
|
||||||
@@ -603,6 +606,7 @@
|
|||||||
"LabelSlug": "URL Teil",
|
"LabelSlug": "URL Teil",
|
||||||
"LabelSortAscending": "Aufsteigend",
|
"LabelSortAscending": "Aufsteigend",
|
||||||
"LabelSortDescending": "Absteigend",
|
"LabelSortDescending": "Absteigend",
|
||||||
|
"LabelSortPubDate": "Erscheinungsdatum",
|
||||||
"LabelStart": "Start",
|
"LabelStart": "Start",
|
||||||
"LabelStartTime": "Startzeit",
|
"LabelStartTime": "Startzeit",
|
||||||
"LabelStarted": "Gestartet",
|
"LabelStarted": "Gestartet",
|
||||||
@@ -703,6 +707,8 @@
|
|||||||
"LabelYourProgress": "Fortschritt",
|
"LabelYourProgress": "Fortschritt",
|
||||||
"MessageAddToPlayerQueue": "Zur Abspielwarteliste hinzufügen",
|
"MessageAddToPlayerQueue": "Zur Abspielwarteliste hinzufügen",
|
||||||
"MessageAppriseDescription": "Um diese Funktion nutzen zu können, musst du eine Instanz von <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> laufen haben oder eine API verwenden welche dieselbe Anfragen bearbeiten kann. <br />Die Apprise API Url muss der vollständige URL-Pfad sein, an den die Benachrichtigung gesendet werden soll, z.B. wenn Ihre API-Instanz unter <code>http://192.168.1.1:8337</code> läuft, würdest du <code>http://192.168.1.1:8337/notify</code> eingeben.",
|
"MessageAppriseDescription": "Um diese Funktion nutzen zu können, musst du eine Instanz von <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> laufen haben oder eine API verwenden welche dieselbe Anfragen bearbeiten kann. <br />Die Apprise API Url muss der vollständige URL-Pfad sein, an den die Benachrichtigung gesendet werden soll, z.B. wenn Ihre API-Instanz unter <code>http://192.168.1.1:8337</code> läuft, würdest du <code>http://192.168.1.1:8337/notify</code> eingeben.",
|
||||||
|
"MessageAsinCheck": "Stellen Sie sicher, dass Sie die ASIN aus der richtigen Audible Region verwenden, nicht Amazon.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Nach dem Speichern muß der Server neugestartet werden um die OIDC Änderungen zu übernehmen.",
|
||||||
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in <code>/metadata/items</code> & <code>/metadata/authors</code> gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
|
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in <code>/metadata/items</code> & <code>/metadata/authors</code> gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
|
||||||
"MessageBackupsLocationEditNote": "Hinweis: Durch das Aktualisieren des Backup-Speicherorts werden vorhandene Sicherungen nicht verschoben oder geändert",
|
"MessageBackupsLocationEditNote": "Hinweis: Durch das Aktualisieren des Backup-Speicherorts werden vorhandene Sicherungen nicht verschoben oder geändert",
|
||||||
"MessageBackupsLocationNoEditNote": "Hinweis: Der Sicherungsspeicherort wird über eine Umgebungsvariable festgelegt und kann hier nicht geändert werden.",
|
"MessageBackupsLocationNoEditNote": "Hinweis: Der Sicherungsspeicherort wird über eine Umgebungsvariable festgelegt und kann hier nicht geändert werden.",
|
||||||
@@ -721,6 +727,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumlänge (Kapitelanfang liegt zeitlich nach dem Ende des Mediums -> Lösung: Kapitelanfang < Mediumlänge)",
|
"MessageChapterErrorStartGteDuration": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumlänge (Kapitelanfang liegt zeitlich nach dem Ende des Mediums -> Lösung: Kapitelanfang < Mediumlänge)",
|
||||||
"MessageChapterErrorStartLtPrev": "Ungültige Kapitelstartzeit: Kapitelanfang < Kapitelanfang vorheriges Kapitel (Kapitelanfang liegt zeitlich vor dem Beginn des vorherigen Kapitels -> Lösung: Kapitelanfang >= Startzeit des vorherigen Kapitels)",
|
"MessageChapterErrorStartLtPrev": "Ungültige Kapitelstartzeit: Kapitelanfang < Kapitelanfang vorheriges Kapitel (Kapitelanfang liegt zeitlich vor dem Beginn des vorherigen Kapitels -> Lösung: Kapitelanfang >= Startzeit des vorherigen Kapitels)",
|
||||||
"MessageChapterStartIsAfter": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumende (Kapitelanfang liegt nach dem Ende des Mediums)",
|
"MessageChapterStartIsAfter": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumende (Kapitelanfang liegt nach dem Ende des Mediums)",
|
||||||
|
"MessageChaptersNotFound": "Kapitel gefunden nicht",
|
||||||
"MessageCheckingCron": "Überprüfe Cron...",
|
"MessageCheckingCron": "Überprüfe Cron...",
|
||||||
"MessageConfirmCloseFeed": "Feed wird geschlossen! Bist du dir sicher?",
|
"MessageConfirmCloseFeed": "Feed wird geschlossen! Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteBackup": "Sicherung für {0} wird gelöscht! Bist du dir sicher?",
|
"MessageConfirmDeleteBackup": "Sicherung für {0} wird gelöscht! Bist du dir sicher?",
|
||||||
@@ -777,6 +784,7 @@
|
|||||||
"MessageForceReScanDescription": "Durchsucht alle Dateien erneut, wie bei einem frischen Scan. ID3-Tags von Audiodateien, OPF-Dateien und Textdateien werden neu durchsucht.",
|
"MessageForceReScanDescription": "Durchsucht alle Dateien erneut, wie bei einem frischen Scan. ID3-Tags von Audiodateien, OPF-Dateien und Textdateien werden neu durchsucht.",
|
||||||
"MessageImportantNotice": "Wichtiger Hinweis!",
|
"MessageImportantNotice": "Wichtiger Hinweis!",
|
||||||
"MessageInsertChapterBelow": "Kapitel unten einfügen",
|
"MessageInsertChapterBelow": "Kapitel unten einfügen",
|
||||||
|
"MessageInvalidAsin": "Ungültige ASIN",
|
||||||
"MessageItemsSelected": "{0} ausgewählte Medien",
|
"MessageItemsSelected": "{0} ausgewählte Medien",
|
||||||
"MessageItemsUpdated": "{0} Medien aktualisiert",
|
"MessageItemsUpdated": "{0} Medien aktualisiert",
|
||||||
"MessageJoinUsOn": "Besuche uns auf",
|
"MessageJoinUsOn": "Besuche uns auf",
|
||||||
@@ -953,6 +961,7 @@
|
|||||||
"ToastBackupRestoreFailed": "Sicherung konnte nicht wiederhergestellt werden",
|
"ToastBackupRestoreFailed": "Sicherung konnte nicht wiederhergestellt werden",
|
||||||
"ToastBackupUploadFailed": "Sicherung konnte nicht hochgeladen werden",
|
"ToastBackupUploadFailed": "Sicherung konnte nicht hochgeladen werden",
|
||||||
"ToastBackupUploadSuccess": "Sicherung hochgeladen",
|
"ToastBackupUploadSuccess": "Sicherung hochgeladen",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Details auf Medien anwenden",
|
||||||
"ToastBatchDeleteFailed": "Batch-Löschen fehlgeschlagen",
|
"ToastBatchDeleteFailed": "Batch-Löschen fehlgeschlagen",
|
||||||
"ToastBatchDeleteSuccess": "Batch-Löschung erfolgreich",
|
"ToastBatchDeleteSuccess": "Batch-Löschung erfolgreich",
|
||||||
"ToastBatchQuickMatchFailed": "Batch-Schnellabgleich fehlgeschlagen!",
|
"ToastBatchQuickMatchFailed": "Batch-Schnellabgleich fehlgeschlagen!",
|
||||||
@@ -1065,6 +1074,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Wähle mindestens einen Benutzer aus",
|
"ToastSelectAtLeastOneUser": "Wähle mindestens einen Benutzer aus",
|
||||||
"ToastSendEbookToDeviceFailed": "E-Buch konnte nicht auf Gerät übertragen werden",
|
"ToastSendEbookToDeviceFailed": "E-Buch konnte nicht auf Gerät übertragen werden",
|
||||||
"ToastSendEbookToDeviceSuccess": "E-Buch an Gerät „{0}“ gesendet",
|
"ToastSendEbookToDeviceSuccess": "E-Buch an Gerät „{0}“ gesendet",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Serien mit dem selben Namen können nicht hinzugefügt werden",
|
||||||
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
|
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
|
||||||
"ToastSeriesUpdateSuccess": "Serien aktualisiert",
|
"ToastSeriesUpdateSuccess": "Serien aktualisiert",
|
||||||
"ToastServerSettingsUpdateSuccess": "Die Server-Einstellungen wurden geupdated",
|
"ToastServerSettingsUpdateSuccess": "Die Server-Einstellungen wurden geupdated",
|
||||||
@@ -1083,6 +1093,8 @@
|
|||||||
"ToastUnknownError": "Unbekannter Fehler",
|
"ToastUnknownError": "Unbekannter Fehler",
|
||||||
"ToastUnlinkOpenIdFailed": "Fehler beim entkoppeln des Benutzers von OpenID",
|
"ToastUnlinkOpenIdFailed": "Fehler beim entkoppeln des Benutzers von OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Benutzer entkoppelt von OpenID",
|
"ToastUnlinkOpenIdSuccess": "Benutzer entkoppelt von OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Dateipfad \"{0}\" ist bereits auf dem Server horhanden",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Element \"{0}\" verwendet ein Unterverzeichnis des Upload-Pfads.",
|
||||||
"ToastUserDeleteFailed": "Benutzer konnte nicht gelöscht werden",
|
"ToastUserDeleteFailed": "Benutzer konnte nicht gelöscht werden",
|
||||||
"ToastUserDeleteSuccess": "Benutzer gelöscht",
|
"ToastUserDeleteSuccess": "Benutzer gelöscht",
|
||||||
"ToastUserPasswordChangeSuccess": "Passwort erfolgreich verändert",
|
"ToastUserPasswordChangeSuccess": "Passwort erfolgreich verändert",
|
||||||
|
|||||||
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Playlist",
|
"HeaderPlaylist": "Playlist",
|
||||||
"HeaderPlaylistItems": "Playlist Items",
|
"HeaderPlaylistItems": "Playlist Items",
|
||||||
"HeaderPodcastsToAdd": "Podcasts to Add",
|
"HeaderPodcastsToAdd": "Podcasts to Add",
|
||||||
|
"HeaderPresets": "Presets",
|
||||||
"HeaderPreviewCover": "Preview Cover",
|
"HeaderPreviewCover": "Preview Cover",
|
||||||
"HeaderRSSFeedGeneral": "RSS Details",
|
"HeaderRSSFeedGeneral": "RSS Details",
|
||||||
"HeaderRSSFeedIsOpen": "RSS Feed is Open",
|
"HeaderRSSFeedIsOpen": "RSS Feed is Open",
|
||||||
@@ -530,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Release Date",
|
"LabelReleaseDate": "Release Date",
|
||||||
"LabelRemoveAllMetadataAbs": "Remove all metadata.abs files",
|
"LabelRemoveAllMetadataAbs": "Remove all metadata.abs files",
|
||||||
"LabelRemoveAllMetadataJson": "Remove all metadata.json files",
|
"LabelRemoveAllMetadataJson": "Remove all metadata.json files",
|
||||||
|
"LabelRemoveAudibleBranding": "Remove Audible intro and outro from chapters",
|
||||||
"LabelRemoveCover": "Remove cover",
|
"LabelRemoveCover": "Remove cover",
|
||||||
"LabelRemoveMetadataFile": "Remove metadata files in library item folders",
|
"LabelRemoveMetadataFile": "Remove metadata files in library item folders",
|
||||||
"LabelRemoveMetadataFileHelp": "Remove all metadata.json and metadata.abs files in your {0} folders.",
|
"LabelRemoveMetadataFileHelp": "Remove all metadata.json and metadata.abs files in your {0} folders.",
|
||||||
@@ -604,6 +606,7 @@
|
|||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "Ascending",
|
"LabelSortAscending": "Ascending",
|
||||||
"LabelSortDescending": "Descending",
|
"LabelSortDescending": "Descending",
|
||||||
|
"LabelSortPubDate": "Sort Pub Date",
|
||||||
"LabelStart": "Start",
|
"LabelStart": "Start",
|
||||||
"LabelStartTime": "Start Time",
|
"LabelStartTime": "Start Time",
|
||||||
"LabelStarted": "Started",
|
"LabelStarted": "Started",
|
||||||
@@ -704,6 +707,8 @@
|
|||||||
"LabelYourProgress": "Your Progress",
|
"LabelYourProgress": "Your Progress",
|
||||||
"MessageAddToPlayerQueue": "Add to player queue",
|
"MessageAddToPlayerQueue": "Add to player queue",
|
||||||
"MessageAppriseDescription": "To use this feature you will need to have an instance of <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> running or an api that will handle those same requests. <br />The Apprise API Url should be the full URL path to send the notification, e.g., if your API instance is served at <code>http://192.168.1.1:8337</code> then you would put <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "To use this feature you will need to have an instance of <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> running or an api that will handle those same requests. <br />The Apprise API Url should be the full URL path to send the notification, e.g., if your API instance is served at <code>http://192.168.1.1:8337</code> then you would put <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Ensure you are using the ASIN from the correct Audible region, not Amazon.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Restart your server after saving to apply OIDC changes.",
|
||||||
"MessageBackupsDescription": "Backups include users, user progress, library item details, server settings, and images stored in <code>/metadata/items</code> & <code>/metadata/authors</code>. Backups <strong>do not</strong> include any files stored in your library folders.",
|
"MessageBackupsDescription": "Backups include users, user progress, library item details, server settings, and images stored in <code>/metadata/items</code> & <code>/metadata/authors</code>. Backups <strong>do not</strong> include any files stored in your library folders.",
|
||||||
"MessageBackupsLocationEditNote": "Note: Updating the backup location will not move or modify existing backups",
|
"MessageBackupsLocationEditNote": "Note: Updating the backup location will not move or modify existing backups",
|
||||||
"MessageBackupsLocationNoEditNote": "Note: The backup location is set through an environment variable and cannot be changed here.",
|
"MessageBackupsLocationNoEditNote": "Note: The backup location is set through an environment variable and cannot be changed here.",
|
||||||
@@ -722,6 +727,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Invalid start time must be less than audiobook duration",
|
"MessageChapterErrorStartGteDuration": "Invalid start time must be less than audiobook duration",
|
||||||
"MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time",
|
"MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time",
|
||||||
"MessageChapterStartIsAfter": "Chapter start is after the end of your audiobook",
|
"MessageChapterStartIsAfter": "Chapter start is after the end of your audiobook",
|
||||||
|
"MessageChaptersNotFound": "Chapters not found",
|
||||||
"MessageCheckingCron": "Checking cron...",
|
"MessageCheckingCron": "Checking cron...",
|
||||||
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
|
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
|
||||||
"MessageConfirmDeleteBackup": "Are you sure you want to delete backup for {0}?",
|
"MessageConfirmDeleteBackup": "Are you sure you want to delete backup for {0}?",
|
||||||
@@ -778,6 +784,7 @@
|
|||||||
"MessageForceReScanDescription": "will scan all files again like a fresh scan. Audio file ID3 tags, OPF files, and text files will be scanned as new.",
|
"MessageForceReScanDescription": "will scan all files again like a fresh scan. Audio file ID3 tags, OPF files, and text files will be scanned as new.",
|
||||||
"MessageImportantNotice": "Important Notice!",
|
"MessageImportantNotice": "Important Notice!",
|
||||||
"MessageInsertChapterBelow": "Insert chapter below",
|
"MessageInsertChapterBelow": "Insert chapter below",
|
||||||
|
"MessageInvalidAsin": "Invalid ASIN",
|
||||||
"MessageItemsSelected": "{0} items selected",
|
"MessageItemsSelected": "{0} items selected",
|
||||||
"MessageItemsUpdated": "{0} items updated",
|
"MessageItemsUpdated": "{0} items updated",
|
||||||
"MessageJoinUsOn": "Join us on",
|
"MessageJoinUsOn": "Join us on",
|
||||||
@@ -849,6 +856,7 @@
|
|||||||
"MessageScheduleRunEveryWeekdayAtTime": "Run every {0} at {1}",
|
"MessageScheduleRunEveryWeekdayAtTime": "Run every {0} at {1}",
|
||||||
"MessageSearchResultsFor": "Search results for",
|
"MessageSearchResultsFor": "Search results for",
|
||||||
"MessageSelected": "{0} selected",
|
"MessageSelected": "{0} selected",
|
||||||
|
"MessageSeriesSequenceCannotContainSpaces": "Series sequence cannot contain spaces",
|
||||||
"MessageServerCouldNotBeReached": "Server could not be reached",
|
"MessageServerCouldNotBeReached": "Server could not be reached",
|
||||||
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
||||||
"MessageShareExpirationWillBe": "Expiration will be <strong>{0}</strong>",
|
"MessageShareExpirationWillBe": "Expiration will be <strong>{0}</strong>",
|
||||||
@@ -967,6 +975,8 @@
|
|||||||
"ToastCachePurgeFailed": "Failed to purge cache",
|
"ToastCachePurgeFailed": "Failed to purge cache",
|
||||||
"ToastCachePurgeSuccess": "Cache purged successfully",
|
"ToastCachePurgeSuccess": "Cache purged successfully",
|
||||||
"ToastChaptersHaveErrors": "Chapters have errors",
|
"ToastChaptersHaveErrors": "Chapters have errors",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "Invalid shift amount. The last chapter start time would extend beyond the duration of this audiobook.",
|
||||||
|
"ToastChaptersInvalidShiftAmountStart": "Invalid shift amount. The first chapter would have zero or negative length and would be overwritten by the second chapter. Increase the start duration of second chapter.",
|
||||||
"ToastChaptersMustHaveTitles": "Chapters must have titles",
|
"ToastChaptersMustHaveTitles": "Chapters must have titles",
|
||||||
"ToastChaptersRemoved": "Chapters removed",
|
"ToastChaptersRemoved": "Chapters removed",
|
||||||
"ToastChaptersUpdated": "Chapters updated",
|
"ToastChaptersUpdated": "Chapters updated",
|
||||||
|
|||||||
+162
-146
@@ -10,13 +10,15 @@
|
|||||||
"ButtonApplyChapters": "Aplicar capítulos",
|
"ButtonApplyChapters": "Aplicar capítulos",
|
||||||
"ButtonAuthors": "Autores",
|
"ButtonAuthors": "Autores",
|
||||||
"ButtonBack": "Atrás",
|
"ButtonBack": "Atrás",
|
||||||
|
"ButtonBatchEditPopulateFromExisting": "Rellenar desde existentes",
|
||||||
|
"ButtonBatchEditPopulateMapDetails": "Rellenar detalles de mapa",
|
||||||
"ButtonBrowseForFolder": "Buscar carpeta",
|
"ButtonBrowseForFolder": "Buscar carpeta",
|
||||||
"ButtonCancel": "Cancelar",
|
"ButtonCancel": "Cancelar",
|
||||||
"ButtonCancelEncode": "Cancelar Codificador",
|
"ButtonCancelEncode": "Cancelar codificación",
|
||||||
"ButtonChangeRootPassword": "Cambiar contraseña administrativa",
|
"ButtonChangeRootPassword": "Cambiar contraseña administrativa",
|
||||||
"ButtonCheckAndDownloadNewEpisodes": "Comprobar y descargar episodios nuevos",
|
"ButtonCheckAndDownloadNewEpisodes": "Comprobar y descargar episodios nuevos",
|
||||||
"ButtonChooseAFolder": "Escoger una Carpeta",
|
"ButtonChooseAFolder": "Elegir una carpeta",
|
||||||
"ButtonChooseFiles": "Escoger un Archivo",
|
"ButtonChooseFiles": "Elegir archivos",
|
||||||
"ButtonClearFilter": "Quitar filtros",
|
"ButtonClearFilter": "Quitar filtros",
|
||||||
"ButtonCloseFeed": "Cerrar suministro",
|
"ButtonCloseFeed": "Cerrar suministro",
|
||||||
"ButtonCloseSession": "Cerrar la sesión abierta",
|
"ButtonCloseSession": "Cerrar la sesión abierta",
|
||||||
@@ -77,9 +79,9 @@
|
|||||||
"ButtonRemove": "Quitar",
|
"ButtonRemove": "Quitar",
|
||||||
"ButtonRemoveAll": "Quitar todo",
|
"ButtonRemoveAll": "Quitar todo",
|
||||||
"ButtonRemoveAllLibraryItems": "Quitar todos los elementos de la biblioteca",
|
"ButtonRemoveAllLibraryItems": "Quitar todos los elementos de la biblioteca",
|
||||||
"ButtonRemoveFromContinueListening": "Remover de Continuar Escuchando",
|
"ButtonRemoveFromContinueListening": "Quitar de Continuar escuchando",
|
||||||
"ButtonRemoveFromContinueReading": "Remover de Continuar Leyendo",
|
"ButtonRemoveFromContinueReading": "Quitar de Continuar leyendo",
|
||||||
"ButtonRemoveSeriesFromContinueSeries": "Remover Serie de Continuar Series",
|
"ButtonRemoveSeriesFromContinueSeries": "Quitar serie de Continuar serie",
|
||||||
"ButtonReset": "Restablecer",
|
"ButtonReset": "Restablecer",
|
||||||
"ButtonResetToDefault": "Restaurar valores predeterminados",
|
"ButtonResetToDefault": "Restaurar valores predeterminados",
|
||||||
"ButtonRestore": "Restaurar",
|
"ButtonRestore": "Restaurar",
|
||||||
@@ -87,7 +89,7 @@
|
|||||||
"ButtonSaveAndClose": "Guardar y cerrar",
|
"ButtonSaveAndClose": "Guardar y cerrar",
|
||||||
"ButtonSaveTracklist": "Guardar lista de pistas",
|
"ButtonSaveTracklist": "Guardar lista de pistas",
|
||||||
"ButtonScan": "Escanear",
|
"ButtonScan": "Escanear",
|
||||||
"ButtonScanLibrary": "Escanear Biblioteca",
|
"ButtonScanLibrary": "Escanear biblioteca",
|
||||||
"ButtonScrollLeft": "Desplazarse hacia la izquierda",
|
"ButtonScrollLeft": "Desplazarse hacia la izquierda",
|
||||||
"ButtonScrollRight": "Desplazarse hacia la derecha",
|
"ButtonScrollRight": "Desplazarse hacia la derecha",
|
||||||
"ButtonSearch": "Buscar",
|
"ButtonSearch": "Buscar",
|
||||||
@@ -147,7 +149,7 @@
|
|||||||
"HeaderLastListeningSession": "Última sesión de escucha",
|
"HeaderLastListeningSession": "Última sesión de escucha",
|
||||||
"HeaderLatestEpisodes": "Episodios más recientes",
|
"HeaderLatestEpisodes": "Episodios más recientes",
|
||||||
"HeaderLibraries": "Bibliotecas",
|
"HeaderLibraries": "Bibliotecas",
|
||||||
"HeaderLibraryFiles": "Archivos de Biblioteca",
|
"HeaderLibraryFiles": "Archivos de biblioteca",
|
||||||
"HeaderLibraryStats": "Estadísticas de biblioteca",
|
"HeaderLibraryStats": "Estadísticas de biblioteca",
|
||||||
"HeaderListeningSessions": "Sesión",
|
"HeaderListeningSessions": "Sesión",
|
||||||
"HeaderListeningStats": "Estadísticas de Tiempo Escuchado",
|
"HeaderListeningStats": "Estadísticas de Tiempo Escuchado",
|
||||||
@@ -175,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Lista de reproducción",
|
"HeaderPlaylist": "Lista de reproducción",
|
||||||
"HeaderPlaylistItems": "Elementos de lista de reproducción",
|
"HeaderPlaylistItems": "Elementos de lista de reproducción",
|
||||||
"HeaderPodcastsToAdd": "Pódcast para añadir",
|
"HeaderPodcastsToAdd": "Pódcast para añadir",
|
||||||
|
"HeaderPresets": "Preconfiguraciones",
|
||||||
"HeaderPreviewCover": "Previsualizar cubierta",
|
"HeaderPreviewCover": "Previsualizar cubierta",
|
||||||
"HeaderRSSFeedGeneral": "Detalles de RSS",
|
"HeaderRSSFeedGeneral": "Detalles de RSS",
|
||||||
"HeaderRSSFeedIsOpen": "El suministro RSS está abierto",
|
"HeaderRSSFeedIsOpen": "El suministro RSS está abierto",
|
||||||
@@ -190,7 +193,7 @@
|
|||||||
"HeaderSettings": "Configuración",
|
"HeaderSettings": "Configuración",
|
||||||
"HeaderSettingsDisplay": "Interfaz",
|
"HeaderSettingsDisplay": "Interfaz",
|
||||||
"HeaderSettingsExperimental": "Funcionalidades experimentales",
|
"HeaderSettingsExperimental": "Funcionalidades experimentales",
|
||||||
"HeaderSettingsGeneral": "General",
|
"HeaderSettingsGeneral": "Generales",
|
||||||
"HeaderSettingsScanner": "Escáner",
|
"HeaderSettingsScanner": "Escáner",
|
||||||
"HeaderSettingsWebClient": "Cliente web",
|
"HeaderSettingsWebClient": "Cliente web",
|
||||||
"HeaderSleepTimer": "Temporizador de apagado",
|
"HeaderSleepTimer": "Temporizador de apagado",
|
||||||
@@ -227,6 +230,7 @@
|
|||||||
"LabelAddedDate": "{0} Añadido",
|
"LabelAddedDate": "{0} Añadido",
|
||||||
"LabelAdminUsersOnly": "Solamente usuarios administradores",
|
"LabelAdminUsersOnly": "Solamente usuarios administradores",
|
||||||
"LabelAll": "Todos",
|
"LabelAll": "Todos",
|
||||||
|
"LabelAllEpisodesDownloaded": "Todos los episodios descargados",
|
||||||
"LabelAllUsers": "Todos los usuarios",
|
"LabelAllUsers": "Todos los usuarios",
|
||||||
"LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados",
|
"LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados",
|
||||||
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
|
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
|
||||||
@@ -261,14 +265,14 @@
|
|||||||
"LabelBooks": "Libros",
|
"LabelBooks": "Libros",
|
||||||
"LabelButtonText": "Texto del botón",
|
"LabelButtonText": "Texto del botón",
|
||||||
"LabelByAuthor": "por {0}",
|
"LabelByAuthor": "por {0}",
|
||||||
"LabelChangePassword": "Cambiar Contraseña",
|
"LabelChangePassword": "Cambiar contraseña",
|
||||||
"LabelChannels": "Canales",
|
"LabelChannels": "Canales",
|
||||||
"LabelChapterCount": "{0} capítulos",
|
"LabelChapterCount": "{0} capítulos",
|
||||||
"LabelChapterTitle": "Titulo del Capítulo",
|
"LabelChapterTitle": "Título del capítulo",
|
||||||
"LabelChapters": "Capítulos",
|
"LabelChapters": "Capítulos",
|
||||||
"LabelChaptersFound": "Capítulo Encontrado",
|
"LabelChaptersFound": "capítulos encontrados",
|
||||||
"LabelClickForMoreInfo": "Click para más información",
|
"LabelClickForMoreInfo": "Pulse para más información",
|
||||||
"LabelClickToUseCurrentValue": "Haz clic para utilizar el valor actual",
|
"LabelClickToUseCurrentValue": "Pulse para utilizar el valor actual",
|
||||||
"LabelClosePlayer": "Cerrar reproductor",
|
"LabelClosePlayer": "Cerrar reproductor",
|
||||||
"LabelCodec": "Codec",
|
"LabelCodec": "Codec",
|
||||||
"LabelCollapseSeries": "Colapsar serie",
|
"LabelCollapseSeries": "Colapsar serie",
|
||||||
@@ -276,7 +280,7 @@
|
|||||||
"LabelCollection": "Colección",
|
"LabelCollection": "Colección",
|
||||||
"LabelCollections": "Colecciones",
|
"LabelCollections": "Colecciones",
|
||||||
"LabelComplete": "Completo",
|
"LabelComplete": "Completo",
|
||||||
"LabelConfirmPassword": "Confirmar Contraseña",
|
"LabelConfirmPassword": "Confirmar contraseña",
|
||||||
"LabelContinueListening": "Seguir escuchando",
|
"LabelContinueListening": "Seguir escuchando",
|
||||||
"LabelContinueReading": "Continuar leyendo",
|
"LabelContinueReading": "Continuar leyendo",
|
||||||
"LabelContinueSeries": "Continuar series",
|
"LabelContinueSeries": "Continuar series",
|
||||||
@@ -287,8 +291,8 @@
|
|||||||
"LabelCronExpression": "Expresión de Cron",
|
"LabelCronExpression": "Expresión de Cron",
|
||||||
"LabelCurrent": "Actual",
|
"LabelCurrent": "Actual",
|
||||||
"LabelCurrently": "En este momento:",
|
"LabelCurrently": "En este momento:",
|
||||||
"LabelCustomCronExpression": "Expresión de Cron Personalizada:",
|
"LabelCustomCronExpression": "Expresión de Cron personalizada:",
|
||||||
"LabelDatetime": "Hora y Fecha",
|
"LabelDatetime": "Hora y fecha",
|
||||||
"LabelDays": "Días",
|
"LabelDays": "Días",
|
||||||
"LabelDeleteFromFileSystemCheckbox": "Eliminar del sistema de archivos (desmarque para quitar de la base de datos solamente)",
|
"LabelDeleteFromFileSystemCheckbox": "Eliminar del sistema de archivos (desmarque para quitar de la base de datos solamente)",
|
||||||
"LabelDescription": "Descripción",
|
"LabelDescription": "Descripción",
|
||||||
@@ -367,7 +371,7 @@
|
|||||||
"LabelFontStrikethrough": "Tachado",
|
"LabelFontStrikethrough": "Tachado",
|
||||||
"LabelFormat": "Formato",
|
"LabelFormat": "Formato",
|
||||||
"LabelFull": "Completo",
|
"LabelFull": "Completo",
|
||||||
"LabelGenre": "Genero",
|
"LabelGenre": "Género",
|
||||||
"LabelGenres": "Géneros",
|
"LabelGenres": "Géneros",
|
||||||
"LabelHardDeleteFile": "Eliminar Definitivamente",
|
"LabelHardDeleteFile": "Eliminar Definitivamente",
|
||||||
"LabelHasEbook": "Tiene un libro",
|
"LabelHasEbook": "Tiene un libro",
|
||||||
@@ -436,8 +440,8 @@
|
|||||||
"LabelMinute": "Minuto",
|
"LabelMinute": "Minuto",
|
||||||
"LabelMinutes": "Minutos",
|
"LabelMinutes": "Minutos",
|
||||||
"LabelMissing": "Falta",
|
"LabelMissing": "Falta",
|
||||||
"LabelMissingEbook": "No tiene ebook",
|
"LabelMissingEbook": "No tiene libro electrónico",
|
||||||
"LabelMissingSupplementaryEbook": "No tiene ebook suplementario",
|
"LabelMissingSupplementaryEbook": "No tiene libro electrónico suplementario",
|
||||||
"LabelMobileRedirectURIs": "URIs de redirección a móviles permitidos",
|
"LabelMobileRedirectURIs": "URIs de redirección a móviles permitidos",
|
||||||
"LabelMobileRedirectURIsDescription": "Esta es una lista blanca de URI de redireccionamiento válidos para aplicaciones móviles. El predeterminado es <code> audiobookshelf</code> , que puede eliminar o complementar con URI adicionales para la integración de aplicaciones de terceros. Usando un asterisco (<code> *</code> ) como única entrada que permite cualquier URI.",
|
"LabelMobileRedirectURIsDescription": "Esta es una lista blanca de URI de redireccionamiento válidos para aplicaciones móviles. El predeterminado es <code> audiobookshelf</code> , que puede eliminar o complementar con URI adicionales para la integración de aplicaciones de terceros. Usando un asterisco (<code> *</code> ) como única entrada que permite cualquier URI.",
|
||||||
"LabelMore": "Más",
|
"LabelMore": "Más",
|
||||||
@@ -458,17 +462,17 @@
|
|||||||
"LabelNotes": "Notas",
|
"LabelNotes": "Notas",
|
||||||
"LabelNotificationAppriseURL": "URL(s) de Apprise",
|
"LabelNotificationAppriseURL": "URL(s) de Apprise",
|
||||||
"LabelNotificationAvailableVariables": "Variables disponibles",
|
"LabelNotificationAvailableVariables": "Variables disponibles",
|
||||||
"LabelNotificationBodyTemplate": "Plantilla de Cuerpo",
|
"LabelNotificationBodyTemplate": "Plantilla de cuerpo",
|
||||||
"LabelNotificationEvent": "Evento de Notificación",
|
"LabelNotificationEvent": "Evento de notificación",
|
||||||
"LabelNotificationTitleTemplate": "Plantilla de Titulo",
|
"LabelNotificationTitleTemplate": "Plantilla de título",
|
||||||
"LabelNotificationsMaxFailedAttempts": "Máximo de Intentos Fallidos",
|
"LabelNotificationsMaxFailedAttempts": "Máximo de intentos fallidos",
|
||||||
"LabelNotificationsMaxFailedAttemptsHelp": "Las notificaciones se desactivan después de fallar este número de veces",
|
"LabelNotificationsMaxFailedAttemptsHelp": "Las notificaciones se desactivan después de fallar este número de veces",
|
||||||
"LabelNotificationsMaxQueueSize": "Tamaño máximo de la cola de notificaciones",
|
"LabelNotificationsMaxQueueSize": "Tamaño máximo de la cola de notificaciones",
|
||||||
"LabelNotificationsMaxQueueSizeHelp": "Las notificaciones están limitadas a 1 por segundo. Las notificaciones serán ignoradas si llegan al numero máximo de cola para prevenir spam de eventos.",
|
"LabelNotificationsMaxQueueSizeHelp": "Las notificaciones están limitadas a 1 por segundo. Las notificaciones serán ignoradas si llegan al numero máximo de cola para prevenir spam de eventos.",
|
||||||
"LabelNumberOfBooks": "Número de libros",
|
"LabelNumberOfBooks": "Número de libros",
|
||||||
"LabelNumberOfEpisodes": "N.º de episodios",
|
"LabelNumberOfEpisodes": "N.º 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:",
|
"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 desactivar 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.",
|
"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 suministro RSS",
|
"LabelOpenRSSFeed": "Abrir suministro RSS",
|
||||||
"LabelOverwrite": "Sobrescribir",
|
"LabelOverwrite": "Sobrescribir",
|
||||||
@@ -497,7 +501,7 @@
|
|||||||
"LabelPort": "Puerto",
|
"LabelPort": "Puerto",
|
||||||
"LabelPrefixesToIgnore": "Prefijos para Ignorar (no distingue entre mayúsculas y minúsculas.)",
|
"LabelPrefixesToIgnore": "Prefijos para Ignorar (no distingue entre mayúsculas y minúsculas.)",
|
||||||
"LabelPreventIndexing": "Evite que los directorios de pódcast de iTunes y Google indicen su suministro",
|
"LabelPreventIndexing": "Evite que los directorios de pódcast de iTunes y Google indicen su suministro",
|
||||||
"LabelPrimaryEbook": "Ebook principal",
|
"LabelPrimaryEbook": "Libro electrónico principal",
|
||||||
"LabelProgress": "Progreso",
|
"LabelProgress": "Progreso",
|
||||||
"LabelProvider": "Proveedor",
|
"LabelProvider": "Proveedor",
|
||||||
"LabelProviderAuthorizationValue": "Valor del encabezado de autorización",
|
"LabelProviderAuthorizationValue": "Valor del encabezado de autorización",
|
||||||
@@ -575,7 +579,7 @@
|
|||||||
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Saltar libros anteriores de la serie Continuada",
|
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Saltar libros anteriores de la serie Continuada",
|
||||||
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "El estante de la página de inicio de Continuar Serie muestra el primer libro no iniciado de una serie que tenga por lo menos un libro finalizado y no tenga libros en progreso. Habilitar esta opción le permitirá continuar series desde el último libro que ha completado en vez del primer libro que no ha empezado.",
|
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "El estante de la página de inicio de Continuar Serie muestra el primer libro no iniciado de una serie que tenga por lo menos un libro finalizado y no tenga libros en progreso. Habilitar esta opción le permitirá continuar series desde el último libro que ha completado en vez del primer libro que no ha empezado.",
|
||||||
"LabelSettingsParseSubtitles": "Extraer Subtítulos",
|
"LabelSettingsParseSubtitles": "Extraer Subtítulos",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extraer subtítulos de los nombres de las carpetas de los audiolibros.<br>Los subtítulos deben estar separados por \" - \"<br>Por ejemplo: \"Ejemplo de Título - Subtítulo Aquí\" tiene el subtítulo \"Subtítulo Aquí\"",
|
"LabelSettingsParseSubtitlesHelp": "Extraer subtítulos de los nombres de las carpetas de los audiolibros.<br>Los subtítulos deben estar separados por « - »<br>Así, «Título de libro - Un subtítulo aquí» tiene el subtítulo «Un subtítulo aquí»",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Preferir metadatos encontrados",
|
"LabelSettingsPreferMatchedMetadata": "Preferir metadatos encontrados",
|
||||||
"LabelSettingsPreferMatchedMetadataHelp": "Los datos encontrados sobreescribirán los detalles del elemento cuando se use \"Encontrar Rápido\". Por defecto, \"Encontrar Rápido\" sólo completará los detalles faltantes.",
|
"LabelSettingsPreferMatchedMetadataHelp": "Los datos encontrados sobreescribirán los detalles del elemento cuando se use \"Encontrar Rápido\". Por defecto, \"Encontrar Rápido\" sólo completará los detalles faltantes.",
|
||||||
"LabelSettingsSkipMatchingBooksWithASIN": "Omitir libros coincidentes que ya tengan un ASIN",
|
"LabelSettingsSkipMatchingBooksWithASIN": "Omitir libros coincidentes que ya tengan un ASIN",
|
||||||
@@ -593,7 +597,7 @@
|
|||||||
"LabelShareDownloadableHelp": "Permite a quienes posean el enlace de compartición descargar un archivo ZIP del elemento de la biblioteca.",
|
"LabelShareDownloadableHelp": "Permite a quienes posean el enlace de compartición descargar un archivo ZIP del elemento de la biblioteca.",
|
||||||
"LabelShareOpen": "abrir un recurso compartido",
|
"LabelShareOpen": "abrir un recurso compartido",
|
||||||
"LabelShareURL": "Compartir la URL",
|
"LabelShareURL": "Compartir la URL",
|
||||||
"LabelShowAll": "Mostrar Todos",
|
"LabelShowAll": "Mostrar todo",
|
||||||
"LabelShowSeconds": "Mostrar segundos",
|
"LabelShowSeconds": "Mostrar segundos",
|
||||||
"LabelShowSubtitles": "Mostrar subtítulos",
|
"LabelShowSubtitles": "Mostrar subtítulos",
|
||||||
"LabelSize": "Tamaño",
|
"LabelSize": "Tamaño",
|
||||||
@@ -601,33 +605,34 @@
|
|||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "Ascendente",
|
"LabelSortAscending": "Ascendente",
|
||||||
"LabelSortDescending": "Descendente",
|
"LabelSortDescending": "Descendente",
|
||||||
|
"LabelSortPubDate": "Ord. fecha pub.",
|
||||||
"LabelStart": "Iniciar",
|
"LabelStart": "Iniciar",
|
||||||
"LabelStartTime": "Tiempo de Inicio",
|
"LabelStartTime": "Tiempo de Inicio",
|
||||||
"LabelStarted": "Iniciado",
|
"LabelStarted": "Iniciado",
|
||||||
"LabelStartedAt": "Iniciado En",
|
"LabelStartedAt": "Iniciado En",
|
||||||
"LabelStatsAudioTracks": "Pistas de Audio",
|
"LabelStatsAudioTracks": "Pistas de Audio",
|
||||||
"LabelStatsAuthors": "Autores",
|
"LabelStatsAuthors": "Autores",
|
||||||
"LabelStatsBestDay": "Mejor Día",
|
"LabelStatsBestDay": "Mejor día",
|
||||||
"LabelStatsDailyAverage": "Promedio Diario",
|
"LabelStatsDailyAverage": "Promedio diario",
|
||||||
"LabelStatsDays": "Días",
|
"LabelStatsDays": "Días",
|
||||||
"LabelStatsDaysListened": "Días Escuchando",
|
"LabelStatsDaysListened": "Días escuchando",
|
||||||
"LabelStatsHours": "Horas",
|
"LabelStatsHours": "Horas",
|
||||||
"LabelStatsInARow": "seguidos",
|
"LabelStatsInARow": "seguidos",
|
||||||
"LabelStatsItemsFinished": "Elementos Terminados",
|
"LabelStatsItemsFinished": "Elementos terminados",
|
||||||
"LabelStatsItemsInLibrary": "Elementos en biblioteca",
|
"LabelStatsItemsInLibrary": "Elementos en biblioteca",
|
||||||
"LabelStatsMinutes": "minutos",
|
"LabelStatsMinutes": "minutos",
|
||||||
"LabelStatsMinutesListening": "Minutos Escuchando",
|
"LabelStatsMinutesListening": "Minutos escuchando",
|
||||||
"LabelStatsOverallDays": "Total de Dias",
|
"LabelStatsOverallDays": "Total de días",
|
||||||
"LabelStatsOverallHours": "Total de Horas",
|
"LabelStatsOverallHours": "Total de horas",
|
||||||
"LabelStatsWeekListening": "Tiempo escuchando en la Semana",
|
"LabelStatsWeekListening": "Tiempo escuchando en la semana",
|
||||||
"LabelSubtitle": "Subtítulo",
|
"LabelSubtitle": "Subtítulo",
|
||||||
"LabelSupportedFileTypes": "Tipos de archivo admitidos",
|
"LabelSupportedFileTypes": "Tipos de archivo admitidos",
|
||||||
"LabelTag": "Etiqueta",
|
"LabelTag": "Etiqueta",
|
||||||
"LabelTags": "Etiquetas",
|
"LabelTags": "Etiquetas",
|
||||||
"LabelTagsAccessibleToUser": "Etiquetas Accessibles al Usuario",
|
"LabelTagsAccessibleToUser": "Etiquetas accessibles al usuario",
|
||||||
"LabelTagsNotAccessibleToUser": "Etiquetas no Accesibles al Usuario",
|
"LabelTagsNotAccessibleToUser": "Etiquetas no accesibles al usuario",
|
||||||
"LabelTasks": "Tareas Corriendo",
|
"LabelTasks": "Tareas en ejecución",
|
||||||
"LabelTextEditorBulletedList": "Lista con viñetas",
|
"LabelTextEditorBulletedList": "Lista con bolos",
|
||||||
"LabelTextEditorLink": "Enlazar",
|
"LabelTextEditorLink": "Enlazar",
|
||||||
"LabelTextEditorNumberedList": "Lista numerada",
|
"LabelTextEditorNumberedList": "Lista numerada",
|
||||||
"LabelTextEditorUnlink": "Desenlazar",
|
"LabelTextEditorUnlink": "Desenlazar",
|
||||||
@@ -680,7 +685,7 @@
|
|||||||
"LabelUseFullTrack": "Usar pista completa",
|
"LabelUseFullTrack": "Usar pista completa",
|
||||||
"LabelUseZeroForUnlimited": "Utilice 0 para ilimitado",
|
"LabelUseZeroForUnlimited": "Utilice 0 para ilimitado",
|
||||||
"LabelUser": "Usuario",
|
"LabelUser": "Usuario",
|
||||||
"LabelUsername": "Nombre de Usuario",
|
"LabelUsername": "Nombre de usuario",
|
||||||
"LabelValue": "Valor",
|
"LabelValue": "Valor",
|
||||||
"LabelVersion": "Versión",
|
"LabelVersion": "Versión",
|
||||||
"LabelViewBookmarks": "Ver Marcadores",
|
"LabelViewBookmarks": "Ver Marcadores",
|
||||||
@@ -693,89 +698,95 @@
|
|||||||
"LabelWeekdaysToRun": "Correr en Días de la Semana",
|
"LabelWeekdaysToRun": "Correr en Días de la Semana",
|
||||||
"LabelXBooks": "{0} libros",
|
"LabelXBooks": "{0} libros",
|
||||||
"LabelXItems": "{0} elementos",
|
"LabelXItems": "{0} elementos",
|
||||||
"LabelYearReviewHide": "Ocultar Resumen del año",
|
"LabelYearReviewHide": "Ocultar resumen del año",
|
||||||
"LabelYearReviewShow": "Resumen del año",
|
"LabelYearReviewShow": "Ver resumen del año",
|
||||||
"LabelYourAudiobookDuration": "Duración de tu Audiolibro",
|
"LabelYourAudiobookDuration": "Duración de tu Audiolibro",
|
||||||
"LabelYourBookmarks": "Tus Marcadores",
|
"LabelYourBookmarks": "Sus marcadores",
|
||||||
"LabelYourPlaylists": "Tus Listas",
|
"LabelYourPlaylists": "Tus Listas",
|
||||||
"LabelYourProgress": "Su progreso",
|
"LabelYourProgress": "Su progreso",
|
||||||
"MessageAddToPlayerQueue": "Agregar a fila del Reproductor",
|
"MessageAddToPlayerQueue": "Agregar a fila del Reproductor",
|
||||||
"MessageAppriseDescription": "Para usar esta función deberás tener <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">la API de Apprise</a> corriendo o una API que maneje los mismos resultados. <br/>La URL de la API de Apprise debe tener la misma ruta de archivos que donde se envían las notificaciones. Por ejemplo: si su API esta en <code>http://192.168.1.1:8337</code> entonces pondría <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Para usar esta función deberás tener <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">la API de Apprise</a> corriendo o una API que maneje los mismos resultados. <br/>La URL de la API de Apprise debe tener la misma ruta de archivos que donde se envían las notificaciones. Por ejemplo: si su API esta en <code>http://192.168.1.1:8337</code> entonces pondría <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Cerciórese de usar el ASIN de la región correcta de Audible, no de Amazon.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Reinicie el servidor tras el guardado para aplicar los cambios de OIDC.",
|
||||||
"MessageBackupsDescription": "Los respaldos incluyen: usuarios, el progreso del los usuarios, los detalles de los elementos de la biblioteca, la configuración del servidor y las imágenes en <code>/metadata/items</code> y <code>/metadata/authors</code>. Los Respaldos <strong>NO</strong> incluyen ningún archivo guardado en la carpeta de tu biblioteca.",
|
"MessageBackupsDescription": "Los respaldos incluyen: usuarios, el progreso del los usuarios, los detalles de los elementos de la biblioteca, la configuración del servidor y las imágenes en <code>/metadata/items</code> y <code>/metadata/authors</code>. Los Respaldos <strong>NO</strong> incluyen ningún archivo guardado en la carpeta de tu biblioteca.",
|
||||||
"MessageBackupsLocationEditNote": "Nota: actualizar la ubicación de la copia de respaldo no moverá ni modificará los respaldos existentes",
|
"MessageBackupsLocationEditNote": "Nota: actualizar la ubicación de la copia de respaldo no moverá ni modificará los respaldos existentes",
|
||||||
"MessageBackupsLocationNoEditNote": "Nota: la ubicación de la copia de respaldo se establece a través de una variable de entorno y no se puede cambiar aquí.",
|
"MessageBackupsLocationNoEditNote": "Nota: la ubicación de la copia de respaldo 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",
|
"MessageBackupsLocationPathEmpty": "La ruta de la copia de seguridad no puede estar vacía",
|
||||||
|
"MessageBatchEditPopulateMapDetailsAllHelp": "Rellenar campos activados con datos de todos los elementos. Los campos con varios valores se combinarán",
|
||||||
|
"MessageBatchEditPopulateMapDetailsItemHelp": "Rellenar campos de detalles de mapa con datos de este elemento",
|
||||||
"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.",
|
"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": "Aún no ha hecho ninguna colección",
|
"MessageBookshelfNoCollections": "Aún no ha hecho ninguna colección",
|
||||||
"MessageBookshelfNoCollectionsHelp": "Las colecciones son públicas. Cualquiera que pueda acceder a la biblioteca las podrá ver.",
|
"MessageBookshelfNoCollectionsHelp": "Las colecciones son públicas. Cualquiera que pueda acceder a la biblioteca las podrá ver.",
|
||||||
"MessageBookshelfNoRSSFeeds": "Ningún suministro RSS está abierto",
|
"MessageBookshelfNoRSSFeeds": "Ningún suministro RSS está abierto",
|
||||||
"MessageBookshelfNoResultsForFilter": "Ningún Resultado para el filtro \"{0}: {1}\"",
|
"MessageBookshelfNoResultsForFilter": "El filtro «{0}: {1}» no produjo ningún resultado",
|
||||||
"MessageBookshelfNoResultsForQuery": "No hay resultados para la consulta",
|
"MessageBookshelfNoResultsForQuery": "No hay resultados para la consulta",
|
||||||
"MessageBookshelfNoSeries": "No tiene ninguna serie",
|
"MessageBookshelfNoSeries": "No tiene ninguna serie",
|
||||||
"MessageChapterEndIsAfter": "El final del capítulo es después del final de tu audiolibro",
|
"MessageChapterEndIsAfter": "El final del capítulo es después del final de tu audiolibro",
|
||||||
"MessageChapterErrorFirstNotZero": "El primer capitulo debe iniciar en 0",
|
"MessageChapterErrorFirstNotZero": "El primer capítulo debe iniciar en 0",
|
||||||
"MessageChapterErrorStartGteDuration": "El tiempo de inicio no es válido: debe ser inferior a la duración del audiolibro",
|
"MessageChapterErrorStartGteDuration": "El tiempo de inicio no es válido: debe ser inferior a la duración del audiolibro",
|
||||||
"MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válido: debe ser mayor o igual que el tiempo de inicio del capítulo anterior",
|
"MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válido: debe ser mayor o igual que el tiempo de inicio del capítulo anterior",
|
||||||
"MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro",
|
"MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro",
|
||||||
|
"MessageChaptersNotFound": "Capítulos no encontrados",
|
||||||
"MessageCheckingCron": "Revisando cron...",
|
"MessageCheckingCron": "Revisando cron...",
|
||||||
"MessageConfirmCloseFeed": "¿Confirma que quiere cerrar este suministro?",
|
"MessageConfirmCloseFeed": "¿Confirma que quiere cerrar este suministro?",
|
||||||
"MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?",
|
"MessageConfirmDeleteBackup": "¿Confirma que quiere eliminar el respaldo de {0}?",
|
||||||
"MessageConfirmDeleteDevice": "¿Estás seguro de que deseas eliminar el lector electrónico \"{0}\"?",
|
"MessageConfirmDeleteDevice": "¿Confirma que quiere eliminar el lector electrónico «{0}»?",
|
||||||
"MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?",
|
"MessageConfirmDeleteFile": "Esto eliminará el archivo del sistema de archivos. ¿Quiere continuar?",
|
||||||
"MessageConfirmDeleteLibrary": "¿Está seguro de que desea eliminar permanentemente la biblioteca \"{0}\"?",
|
"MessageConfirmDeleteLibrary": "¿Confirma que quiere eliminar permanentemente la biblioteca «{0}»?",
|
||||||
"MessageConfirmDeleteLibraryItem": "Esto eliminará el elemento de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?",
|
"MessageConfirmDeleteLibraryItem": "Esto eliminará el elemento de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?",
|
||||||
"MessageConfirmDeleteLibraryItems": "Esto eliminará {0} elementos de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?",
|
"MessageConfirmDeleteLibraryItems": "Esto eliminará {0} elementos de la biblioteca de la base de datos y del sistema de archivos. ¿Confirma que quiere hacerlo?",
|
||||||
"MessageConfirmDeleteMetadataProvider": "¿Estás seguro de que deseas eliminar el proveedor de metadatos personalizado \"{0}\"?",
|
"MessageConfirmDeleteMetadataProvider": "¿Confirma que quiere eliminar el proveedor de metadatos personalizado «{0}»?",
|
||||||
"MessageConfirmDeleteNotification": "¿Estás seguro de que deseas eliminar esta notificación?",
|
"MessageConfirmDeleteNotification": "¿Confirma que quiere eliminar esta notificación?",
|
||||||
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
|
"MessageConfirmDeleteSession": "¿Confirma que quiere eliminar esta sesión?",
|
||||||
"MessageConfirmEmbedMetadataInAudioFiles": "¿Está seguro de que desea incrustar metadatos en {0} archivos de audio?",
|
"MessageConfirmEmbedMetadataInAudioFiles": "¿Confirma que quiere incrustar metadatos en {0} archivos de audio?",
|
||||||
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
|
"MessageConfirmForceReScan": "¿Confirma que quiere forzar un reescaneo?",
|
||||||
"MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
|
"MessageConfirmMarkAllEpisodesFinished": "¿Confirma que quiere marcar todos los episodios como terminados?",
|
||||||
"MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
|
"MessageConfirmMarkAllEpisodesNotFinished": "¿Confirma que quiere marcar todos los episodios como no terminados?",
|
||||||
"MessageConfirmMarkItemFinished": "¿Estás seguro de que deseas marcar \"{0}\" como terminado?",
|
"MessageConfirmMarkItemFinished": "¿Confirma que quiere marcar «{0}» como terminado?",
|
||||||
"MessageConfirmMarkItemNotFinished": "¿Estás seguro de que deseas marcar \"{0}\" como no acabado?",
|
"MessageConfirmMarkItemNotFinished": "¿Confirma que quiere marcar «{0}» como no terminado?",
|
||||||
"MessageConfirmMarkSeriesFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como terminados?",
|
"MessageConfirmMarkSeriesFinished": "¿Confirma que quiere marcar todos los libros de esta serie como terminados?",
|
||||||
"MessageConfirmMarkSeriesNotFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como no terminados?",
|
"MessageConfirmMarkSeriesNotFinished": "¿Confirma que quiere marcar todos los libros de esta serie como no terminados?",
|
||||||
"MessageConfirmNotificationTestTrigger": "¿Activar esta notificación con datos de prueba?",
|
"MessageConfirmNotificationTestTrigger": "¿Activar esta notificación con datos de prueba?",
|
||||||
"MessageConfirmPurgeCache": "Purgar el caché eliminará el directorio completo ubicado en <code>/metadata/cache</code>. <br /><br />¿Está seguro que desea eliminar el directorio del caché?",
|
"MessageConfirmPurgeCache": "Purgar la antememoria eliminará el directorio completo ubicado en <code>/metadata/cache</code>. <br /><br />¿Confirma que quiere eliminar el directorio de antememoria?",
|
||||||
"MessageConfirmPurgeItemsCache": "Purgar la caché de los elementos eliminará todo el directorio <code>/metadata/cache/items</code>.<br />¿Estás seguro?",
|
"MessageConfirmPurgeItemsCache": "Purgar la antememoria de elementos eliminará el directorio completo ubicado en <code>/metadata/cache/items</code>.<br />¿Lo confirma?",
|
||||||
"MessageConfirmQuickEmbed": "¡Advertencia! La integración rápida no realiza copias de seguridad a ninguno de tus archivos de audio. Asegúrate de haber realizado una copia de los mismos previamente. <br><br>¿Deseas continuar?",
|
"MessageConfirmQuickEmbed": "¡Advertencia! La integración rápida no realiza copias de seguridad a ninguno de tus archivos de audio. Asegúrate de haber realizado una copia de los mismos previamente. <br><br>¿Deseas continuar?",
|
||||||
"MessageConfirmQuickMatchEpisodes": "El reconocimiento rápido de extensiones sobrescribirá los detalles si se encuentra una coincidencia. Se actualizarán las extensiones no reconocidas. ¿Está seguro?",
|
"MessageConfirmQuickMatchEpisodes": "El reconocimiento rápido de extensiones sobrescribirá los detalles si se encuentra una coincidencia. Se actualizarán las extensiones no reconocidas. ¿Quiere continuar?",
|
||||||
"MessageConfirmReScanLibraryItems": "¿Estás seguro de querer re escanear {0} elemento(s)?",
|
"MessageConfirmReScanLibraryItems": "¿Confirma que quiere volver a analizar {0} elementos?",
|
||||||
"MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?",
|
"MessageConfirmRemoveAllChapters": "¿Confirma que quiere quitar todos los capítulos?",
|
||||||
"MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?",
|
"MessageConfirmRemoveAuthor": "¿Confirma que quiere quitar el autor «{0}»?",
|
||||||
"MessageConfirmRemoveCollection": "¿Está seguro de que desea remover la colección \"{0}\"?",
|
"MessageConfirmRemoveCollection": "¿Confirma que quiere quitar la colección «{0}»?",
|
||||||
"MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "¿Confirma que quiere quitar el episodio «{0}»?",
|
||||||
"MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?",
|
"MessageConfirmRemoveEpisodes": "¿Confirma que quiere quitar {0} episodios?",
|
||||||
"MessageConfirmRemoveListeningSessions": "¿Está seguro que desea remover {0} sesiones de escuchar?",
|
"MessageConfirmRemoveListeningSessions": "¿Confirma que quiere quitar {0} sesiones de escucha?",
|
||||||
"MessageConfirmRemoveMetadataFiles": "¿Está seguro de que desea eliminar todos los archivos de metadatos.{0} en las carpetas de elementos de su biblioteca?",
|
"MessageConfirmRemoveMetadataFiles": "¿Confirma que quiere quitar todos los archivos metadata.{0} en las carpetas de elementos de su biblioteca?",
|
||||||
"MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "¿Confirma que quiere quitar el narrador «{0}»?",
|
||||||
"MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "¿Confirma que quiere quitar la lista de reproducción «{0}»?",
|
||||||
"MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
|
"MessageConfirmRenameGenre": "¿Confirma que quiere cambiar el nombre del género «{0}» a «{1}» en todos los elementos?",
|
||||||
"MessageConfirmRenameGenreMergeNote": "Nota: Este género ya existe, por lo que se fusionarán.",
|
"MessageConfirmRenameGenreMergeNote": "Nota: Este género ya existe, por lo que se fusionarán.",
|
||||||
"MessageConfirmRenameGenreWarning": "Advertencia! Un genero similar ya existe \"{0}\".",
|
"MessageConfirmRenameGenreWarning": "¡Atención! Ya existe un género similar con distinta mayusculación, «{0}».",
|
||||||
"MessageConfirmRenameTag": "¿Está seguro de que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?",
|
"MessageConfirmRenameTag": "¿Confirma que quiere cambiar el nombre de la etiqueta «{0}» a «{1}» en todos los elementos?",
|
||||||
"MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe, por lo que se fusionarán.",
|
"MessageConfirmRenameTagMergeNote": "Nota: esta etiqueta ya existe, por lo que se fusionarán.",
|
||||||
"MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".",
|
"MessageConfirmRenameTagWarning": "¡Atención! Ya existe una etiqueta similar con distinta mayusculación, «{0}».",
|
||||||
"MessageConfirmResetProgress": "¿Estás seguro de que quieres reiniciar tu progreso?",
|
"MessageConfirmResetProgress": "¿Confirma que quiere restablecer su progreso?",
|
||||||
"MessageConfirmSendEbookToDevice": "¿Está seguro de que enviar {0} ebook(s) \"{1}\" al dispositivo \"{2}\"?",
|
"MessageConfirmSendEbookToDevice": "¿Confirma que quiere enviar el libro electrónico {0} «{1}» al dispositivo «{2}»?",
|
||||||
"MessageConfirmUnlinkOpenId": "¿Estás seguro de que deseas desvincular este usuario de OpenID?",
|
"MessageConfirmUnlinkOpenId": "¿Confirma que quiere desenlazar este usuario de OpenID?",
|
||||||
"MessageDaysListenedInTheLastYear": "{0} dies escoltats en l'últim any",
|
"MessageDaysListenedInTheLastYear": "{0} días escuchados el año pasado",
|
||||||
"MessageDownloadingEpisode": "Descargando Capitulo",
|
"MessageDownloadingEpisode": "Descargando episodio",
|
||||||
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas",
|
"MessageDragFilesIntoTrackOrder": "Arrastre los archivos al orden correcto de las pistas",
|
||||||
"MessageEmbedFailed": "¡Error al insertar!",
|
"MessageEmbedFailed": "Falló la incrustación.",
|
||||||
"MessageEmbedFinished": "Incrustación Terminada!",
|
"MessageEmbedFinished": "Finalizó la incrustación.",
|
||||||
"MessageEmbedQueue": "En cola para incrustar metadatos ({0} en cola)",
|
"MessageEmbedQueue": "En cola para incrustar metadatos ({0} en cola)",
|
||||||
"MessageEpisodesQueuedForDownload": "{0} Episodio(s) en cola para descargar",
|
"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.",
|
"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": "El URL del suministro será {0}",
|
"MessageFeedURLWillBe": "El URL del suministro será {0}",
|
||||||
"MessageFetching": "Buscando...",
|
"MessageFetching": "Recuperando...",
|
||||||
"MessageForceReScanDescription": "Escaneará todos los archivos como un nuevo escaneo. Archivos de audio con etiquetas ID3, archivos OPF y archivos de texto serán escaneados como nuevos.",
|
"MessageForceReScanDescription": "Escaneará todos los archivos como un nuevo escaneo. Archivos de audio con etiquetas ID3, archivos OPF y archivos de texto serán escaneados como nuevos.",
|
||||||
"MessageImportantNotice": "¡Notificación importante!",
|
"MessageImportantNotice": "¡Notificación importante!",
|
||||||
"MessageInsertChapterBelow": "Insertar Capítulo Abajo",
|
"MessageInsertChapterBelow": "Insertar capítulo debajo",
|
||||||
|
"MessageInvalidAsin": "ASIN no válido",
|
||||||
"MessageItemsSelected": "{0} elementos seleccionados",
|
"MessageItemsSelected": "{0} elementos seleccionados",
|
||||||
"MessageItemsUpdated": "{0} elementos actualizados",
|
"MessageItemsUpdated": "{0} elementos actualizados",
|
||||||
"MessageJoinUsOn": "Únetenos en",
|
"MessageJoinUsOn": "Únase a nosotros en",
|
||||||
"MessageLoading": "Cargando...",
|
"MessageLoading": "Cargando...",
|
||||||
"MessageLoadingFolders": "Cargando archivos...",
|
"MessageLoadingFolders": "Cargando archivos...",
|
||||||
"MessageLogsDescription": "Logs son almacenados en <code>/metadata/logs</code> en archivos bajo formato JSON. Logs de fallos son almacenados en <code>/metadata/logs/crash_logs.txt</code>.",
|
"MessageLogsDescription": "Logs son almacenados en <code>/metadata/logs</code> en archivos bajo formato JSON. Logs de fallos son almacenados en <code>/metadata/logs/crash_logs.txt</code>.",
|
||||||
@@ -799,19 +810,19 @@
|
|||||||
"MessageNoDownloadsInProgress": "No hay descargas actualmente en curso",
|
"MessageNoDownloadsInProgress": "No hay descargas actualmente en curso",
|
||||||
"MessageNoDownloadsQueued": "Sin Lista de Descarga",
|
"MessageNoDownloadsQueued": "Sin Lista de Descarga",
|
||||||
"MessageNoEpisodeMatchesFound": "No se encontraron episodios que coinciden",
|
"MessageNoEpisodeMatchesFound": "No se encontraron episodios que coinciden",
|
||||||
"MessageNoEpisodes": "Sin Episodios",
|
"MessageNoEpisodes": "Ningún episodio",
|
||||||
"MessageNoFoldersAvailable": "No Hay Carpetas Disponibles",
|
"MessageNoFoldersAvailable": "Ninguna carpeta disponible",
|
||||||
"MessageNoGenres": "Sin Géneros",
|
"MessageNoGenres": "Ningún género",
|
||||||
"MessageNoIssues": "Sin Problemas",
|
"MessageNoIssues": "Ningún número",
|
||||||
"MessageNoItems": "Sin elementos",
|
"MessageNoItems": "Ningún elemento",
|
||||||
"MessageNoItemsFound": "Ningún elemento encontrado",
|
"MessageNoItemsFound": "Ningún elemento encontrado",
|
||||||
"MessageNoListeningSessions": "Ninguna sesión escuchada",
|
"MessageNoListeningSessions": "Ninguna sesión de escucha",
|
||||||
"MessageNoLogs": "Ningún registro",
|
"MessageNoLogs": "Ningún registro",
|
||||||
"MessageNoMediaProgress": "Multimedia sin Progreso",
|
"MessageNoMediaProgress": "Multimedia sin Progreso",
|
||||||
"MessageNoNotifications": "Ninguna notificación",
|
"MessageNoNotifications": "Ninguna notificación",
|
||||||
"MessageNoPodcastFeed": "Podcast no válido: Sin feed",
|
"MessageNoPodcastFeed": "Pódcast no válido: no hay suministro",
|
||||||
"MessageNoPodcastsFound": "Ningún podcast encontrado",
|
"MessageNoPodcastsFound": "No se encontró ningún pódcast",
|
||||||
"MessageNoResults": "Sin Resultados",
|
"MessageNoResults": "Ningún resultado",
|
||||||
"MessageNoSearchResultsFor": "La búsqueda «{0}» no produjo ningún resultado",
|
"MessageNoSearchResultsFor": "La búsqueda «{0}» no produjo ningún resultado",
|
||||||
"MessageNoSeries": "Ninguna serie",
|
"MessageNoSeries": "Ninguna serie",
|
||||||
"MessageNoTags": "Ninguna etiqueta",
|
"MessageNoTags": "Ninguna etiqueta",
|
||||||
@@ -819,28 +830,29 @@
|
|||||||
"MessageNoUpdatesWereNecessary": "No fue necesario actualizar",
|
"MessageNoUpdatesWereNecessary": "No fue necesario actualizar",
|
||||||
"MessageNoUserPlaylists": "No tiene ninguna lista de reproducción",
|
"MessageNoUserPlaylists": "No tiene ninguna lista de reproducción",
|
||||||
"MessageNoUserPlaylistsHelp": "Las listas de reproducción son privadas. Solo quien las cree podrá verlas.",
|
"MessageNoUserPlaylistsHelp": "Las listas de reproducción son privadas. Solo quien las cree podrá verlas.",
|
||||||
"MessageNotYetImplemented": "Aun no implementado",
|
"MessageNotYetImplemented": "Aún no implementado",
|
||||||
"MessageOpmlPreviewNote": "Nota: Esta es una vista previa del archivo OPML analizado. El título real del podcast se obtendrá del canal RSS.",
|
"MessageOpmlPreviewNote": "Nota: Esta es una vista previa del archivo OPML analizado. El título real del podcast se obtendrá del canal RSS.",
|
||||||
"MessageOr": "o",
|
"MessageOr": "o",
|
||||||
"MessagePauseChapter": "Pausar la reproducción del capítulo",
|
"MessagePauseChapter": "Pausar la reproducción del capítulo",
|
||||||
"MessagePlayChapter": "Escuchar el comienzo del capítulo",
|
"MessagePlayChapter": "Escuchar el comienzo del capítulo",
|
||||||
"MessagePlaylistCreateFromCollection": "Crear una lista de reproducción a partir de una colección",
|
"MessagePlaylistCreateFromCollection": "Crear una lista de reproducción a partir de una colección",
|
||||||
"MessagePleaseWait": "Por favor, espera...",
|
"MessagePleaseWait": "Espere…",
|
||||||
"MessagePodcastHasNoRSSFeedForMatching": "El pódcast no tiene un URL de suministro RSS que pueda usarse para encontrar correspondencias",
|
"MessagePodcastHasNoRSSFeedForMatching": "El pódcast no tiene un URL de suministro RSS que pueda usarse para encontrar correspondencias",
|
||||||
"MessagePodcastSearchField": "Introduzca el término de búsqueda o el URL del suministro RSS",
|
"MessagePodcastSearchField": "Introduzca el término de búsqueda o el URL del suministro RSS",
|
||||||
"MessageQuickEmbedInProgress": "Integración rápida en proceso",
|
"MessageQuickEmbedInProgress": "Integración rápida en proceso",
|
||||||
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
|
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
|
||||||
"MessageQuickMatchAllEpisodes": "Combina rápidamente todos los episodios",
|
"MessageQuickMatchAllEpisodes": "Combina rápidamente todos los episodios",
|
||||||
"MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.",
|
"MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.",
|
||||||
"MessageRemoveChapter": "Remover capítulos",
|
"MessageRemoveChapter": "Quitar capítulo",
|
||||||
"MessageRemoveEpisodes": "Remover {0} episodio(s)",
|
"MessageRemoveEpisodes": "Quitar {0} episodio(s)",
|
||||||
"MessageRemoveFromPlayerQueue": "Remover la cola de reproducción",
|
"MessageRemoveFromPlayerQueue": "Quitar de la cola de reproducción",
|
||||||
"MessageRemoveUserWarning": "¿Está seguro de que desea eliminar el usuario \"{0}\"?",
|
"MessageRemoveUserWarning": "¿Confirma que quiere eliminar permanentemente el usuario «{0}»?",
|
||||||
"MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuya en",
|
"MessageReportBugsAndContribute": "Informe de defectos, solicite funciones y contribuya en",
|
||||||
"MessageResetChaptersConfirm": "¿Está seguro de que desea deshacer los cambios y revertir los capítulos a su estado original?",
|
"MessageResetChaptersConfirm": "¿Confirma que quiere deshacer los cambios y restablecer los capítulos a su estado original?",
|
||||||
"MessageRestoreBackupConfirm": "¿Está seguro de que desea para restaurar del respaldo creado en",
|
"MessageRestoreBackupConfirm": "¿Confirma que quiere restaurar el respaldo creado el",
|
||||||
"MessageRestoreBackupWarning": "Restaurar sobrescribirá toda la base de datos localizada en /config y las imágenes de portadas en /metadata/items y /metadata/authors.<br /><br />El respaldo no modifica ningún archivo en las carpetas de su biblioteca. Si ha habilitado la opción del servidor para almacenar portadas y metadata en las carpetas de su biblioteca, esos archivos no se respaldan o sobrescriben.<br /><br />Todos los clientes que usen su servidor se actualizarán automáticamente.",
|
"MessageRestoreBackupWarning": "Restaurar sobrescribirá toda la base de datos localizada en /config y las imágenes de portadas en /metadata/items y /metadata/authors.<br /><br />El respaldo no modifica ningún archivo en las carpetas de su biblioteca. Si ha habilitado la opción del servidor para almacenar portadas y metadata en las carpetas de su biblioteca, esos archivos no se respaldan o sobrescriben.<br /><br />Todos los clientes que usen su servidor se actualizarán automáticamente.",
|
||||||
"MessageScheduleLibraryScanNote": "Para la mayoría de los usuarios, se recomienda dejar esta función desactivada y mantener activada la configuración del observador de carpetas. El observador de carpetas detectará automáticamente los cambios en las carpetas de la biblioteca. El observador de carpetas no funciona para todos los sistemas de archivos (como NFS), por lo que se pueden utilizar exploraciones programadas de la biblioteca en su lugar.",
|
"MessageScheduleLibraryScanNote": "Para la mayoría de los usuarios, se recomienda dejar esta función desactivada y mantener activada la configuración del observador de carpetas. El observador de carpetas detectará automáticamente los cambios en las carpetas de la biblioteca. El observador de carpetas no funciona para todos los sistemas de archivos (como NFS), por lo que se pueden utilizar exploraciones programadas de la biblioteca en su lugar.",
|
||||||
|
"MessageScheduleRunEveryWeekdayAtTime": "Ejecutar cada {0} a las {1}",
|
||||||
"MessageSearchResultsFor": "Resultados de la búsqueda de",
|
"MessageSearchResultsFor": "Resultados de la búsqueda de",
|
||||||
"MessageSelected": "{0} seleccionado(s)",
|
"MessageSelected": "{0} seleccionado(s)",
|
||||||
"MessageServerCouldNotBeReached": "No se pudo establecer la conexión con el servidor",
|
"MessageServerCouldNotBeReached": "No se pudo establecer la conexión con el servidor",
|
||||||
@@ -848,31 +860,31 @@
|
|||||||
"MessageShareExpirationWillBe": "La caducidad será <strong>{0}</strong>",
|
"MessageShareExpirationWillBe": "La caducidad será <strong>{0}</strong>",
|
||||||
"MessageShareExpiresIn": "Caduduca en {0}",
|
"MessageShareExpiresIn": "Caduduca en {0}",
|
||||||
"MessageShareURLWillBe": "La URL para compartir será <strong> {0} </strong>",
|
"MessageShareURLWillBe": "La URL para compartir será <strong> {0} </strong>",
|
||||||
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
|
"MessageStartPlaybackAtTime": "¿Iniciar reproducción para «{0}» en {1}?",
|
||||||
"MessageTaskAudioFileNotWritable": "El archivo de audio \"{0}\" no se puede grabar",
|
"MessageTaskAudioFileNotWritable": "El archivo de audio «{0}» no se puede grabar",
|
||||||
"MessageTaskCanceledByUser": "Tarea cancelada por el usuario",
|
"MessageTaskCanceledByUser": "Tarea cancelada por el usuario",
|
||||||
"MessageTaskDownloadingEpisodeDescription": "Descargando el episodio \"{0}\"",
|
"MessageTaskDownloadingEpisodeDescription": "Descargando el episodio «{0}»",
|
||||||
"MessageTaskEmbeddingMetadata": "Inserción de metadatos",
|
"MessageTaskEmbeddingMetadata": "Inserción de metadatos",
|
||||||
"MessageTaskEmbeddingMetadataDescription": "Inserción de metadatos en el audiolibro \"{0}\"",
|
"MessageTaskEmbeddingMetadataDescription": "Incrustando metadatos en el audiolibro «{0}»",
|
||||||
"MessageTaskEncodingM4b": "Codificación M4B",
|
"MessageTaskEncodingM4b": "Codificación M4B",
|
||||||
"MessageTaskEncodingM4bDescription": "Codificación del audiolibro \"{0}\" en un único archivo m4b",
|
"MessageTaskEncodingM4bDescription": "Codificando el audiolibro «{0}» en un único archivo m4b",
|
||||||
"MessageTaskFailed": "Fallida",
|
"MessageTaskFailed": "Fallida",
|
||||||
"MessageTaskFailedToBackupAudioFile": "Error en la copia de seguridad del archivo de audio \"{0}\"",
|
"MessageTaskFailedToBackupAudioFile": "No se pudo respaldar el archivo de audio «{0}»",
|
||||||
"MessageTaskFailedToCreateCacheDirectory": "Error al crear el directorio de la caché",
|
"MessageTaskFailedToCreateCacheDirectory": "Error al crear el directorio de la caché",
|
||||||
"MessageTaskFailedToEmbedMetadataInFile": "Error al incrustar metadatos en el archivo \"{0}\"",
|
"MessageTaskFailedToEmbedMetadataInFile": "No se pudieron incrustar metadatos en el archivo «{0}»",
|
||||||
"MessageTaskFailedToMergeAudioFiles": "Error al fusionar archivos de audio",
|
"MessageTaskFailedToMergeAudioFiles": "Error al fusionar archivos de audio",
|
||||||
"MessageTaskFailedToMoveM4bFile": "Error al mover el archivo m4b",
|
"MessageTaskFailedToMoveM4bFile": "Error al mover el archivo m4b",
|
||||||
"MessageTaskFailedToWriteMetadataFile": "Error al escribir el archivo de metadatos",
|
"MessageTaskFailedToWriteMetadataFile": "Error al escribir el archivo de metadatos",
|
||||||
"MessageTaskMatchingBooksInLibrary": "Libros coincidentes en la biblioteca \"{0}\"",
|
"MessageTaskMatchingBooksInLibrary": "Libros coincidentes en la biblioteca «{0}»",
|
||||||
"MessageTaskNoFilesToScan": "Sin archivos para escanear",
|
"MessageTaskNoFilesToScan": "Sin archivos para escanear",
|
||||||
"MessageTaskOpmlImport": "Importar OPML",
|
"MessageTaskOpmlImport": "Importar OPML",
|
||||||
"MessageTaskOpmlImportDescription": "Creando pódcast a partir de {0} suministros RSS",
|
"MessageTaskOpmlImportDescription": "Creando pódcast a partir de {0} suministros RSS",
|
||||||
"MessageTaskOpmlImportFeed": "Feed de importación OPML",
|
"MessageTaskOpmlImportFeed": "Feed de importación OPML",
|
||||||
"MessageTaskOpmlImportFeedDescription": "Importando el feed RSS \"{0}\"",
|
"MessageTaskOpmlImportFeedDescription": "Importando el suministro RSS «{0}»",
|
||||||
"MessageTaskOpmlImportFeedFailed": "No se puede obtener el podcast",
|
"MessageTaskOpmlImportFeedFailed": "No se pudo obtener el suministro del pódcast",
|
||||||
"MessageTaskOpmlImportFeedPodcastDescription": "Creando podcast \"{0}\"",
|
"MessageTaskOpmlImportFeedPodcastDescription": "Creando pódcast «{0}»",
|
||||||
"MessageTaskOpmlImportFeedPodcastExists": "Podcast ya existe en la ruta",
|
"MessageTaskOpmlImportFeedPodcastExists": "El pódcast ya existe en la ruta",
|
||||||
"MessageTaskOpmlImportFeedPodcastFailed": "Error al crear podcast",
|
"MessageTaskOpmlImportFeedPodcastFailed": "No se pudo crear el pódcast",
|
||||||
"MessageTaskOpmlImportFinished": "Añadido {0} podcasts",
|
"MessageTaskOpmlImportFinished": "Añadido {0} podcasts",
|
||||||
"MessageTaskOpmlParseFailed": "No se pudo analizar el archivo OPML",
|
"MessageTaskOpmlParseFailed": "No se pudo analizar el archivo OPML",
|
||||||
"MessageTaskOpmlParseFastFail": "No se encontró la etiqueta <opml> del archivo OPML no válido O no se encontró la etiqueta <outline>",
|
"MessageTaskOpmlParseFastFail": "No se encontró la etiqueta <opml> del archivo OPML no válido O no se encontró la etiqueta <outline>",
|
||||||
@@ -881,8 +893,8 @@
|
|||||||
"MessageTaskScanItemsMissing": "Falta {0}",
|
"MessageTaskScanItemsMissing": "Falta {0}",
|
||||||
"MessageTaskScanItemsUpdated": "{0} actualizado",
|
"MessageTaskScanItemsUpdated": "{0} actualizado",
|
||||||
"MessageTaskScanNoChangesNeeded": "No se necesitan cambios",
|
"MessageTaskScanNoChangesNeeded": "No se necesitan cambios",
|
||||||
"MessageTaskScanningFileChanges": "Escaneando cambios en el archivo en \"{0}\"",
|
"MessageTaskScanningFileChanges": "Escaneando cambios en archivos en «{0}»",
|
||||||
"MessageTaskScanningLibrary": "Escaneando la biblioteca \"{0}\"",
|
"MessageTaskScanningLibrary": "Escaneando la biblioteca «{0}»",
|
||||||
"MessageTaskTargetDirectoryNotWritable": "El directorio de destino no se puede escribir",
|
"MessageTaskTargetDirectoryNotWritable": "El directorio de destino no se puede escribir",
|
||||||
"MessageThinking": "Pensando...",
|
"MessageThinking": "Pensando...",
|
||||||
"MessageUploaderItemFailed": "Error al Subir",
|
"MessageUploaderItemFailed": "Error al Subir",
|
||||||
@@ -932,7 +944,7 @@
|
|||||||
"ToastAppriseUrlRequired": "Debes ingresar una URL de Apprise",
|
"ToastAppriseUrlRequired": "Debes ingresar una URL de Apprise",
|
||||||
"ToastAsinRequired": "Se requiere ASIN",
|
"ToastAsinRequired": "Se requiere ASIN",
|
||||||
"ToastAuthorImageRemoveSuccess": "Se eliminó la imagen del autor",
|
"ToastAuthorImageRemoveSuccess": "Se eliminó la imagen del autor",
|
||||||
"ToastAuthorNotFound": "No se encontró el autor \"{0}\"",
|
"ToastAuthorNotFound": "No se encontró el autor «{0}»",
|
||||||
"ToastAuthorRemoveSuccess": "Autor eliminado",
|
"ToastAuthorRemoveSuccess": "Autor eliminado",
|
||||||
"ToastAuthorSearchNotFound": "No se encontró al autor",
|
"ToastAuthorSearchNotFound": "No se encontró al autor",
|
||||||
"ToastAuthorUpdateMerged": "Autor combinado",
|
"ToastAuthorUpdateMerged": "Autor combinado",
|
||||||
@@ -948,27 +960,29 @@
|
|||||||
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
|
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
|
||||||
"ToastBackupUploadFailed": "Error al subir el respaldo",
|
"ToastBackupUploadFailed": "Error al subir el respaldo",
|
||||||
"ToastBackupUploadSuccess": "Respaldo cargado",
|
"ToastBackupUploadSuccess": "Respaldo cargado",
|
||||||
"ToastBatchDeleteFailed": "Error al eliminar por lotes",
|
"ToastBatchApplyDetailsToItemsSuccess": "Detalles aplicados a los elementos",
|
||||||
|
"ToastBatchDeleteFailed": "Falló la eliminación por lotes",
|
||||||
"ToastBatchDeleteSuccess": "Borrado por lotes correcto",
|
"ToastBatchDeleteSuccess": "Borrado por lotes correcto",
|
||||||
"ToastBatchQuickMatchFailed": "¡Error en la sincronización rápida por lotes!",
|
"ToastBatchQuickMatchFailed": "¡Error en la sincronización rápida por lotes!",
|
||||||
"ToastBatchQuickMatchStarted": "¡Se inició el lote de búsqueda rápida de {0} libros!",
|
"ToastBatchQuickMatchStarted": "¡Se inició el lote de búsqueda rápida de {0} libros!",
|
||||||
"ToastBatchUpdateFailed": "Subida masiva fallida",
|
"ToastBatchUpdateFailed": "Falló la actualización por lotes",
|
||||||
"ToastBatchUpdateSuccess": "Subida masiva exitosa",
|
"ToastBatchUpdateSuccess": "Se actualizó por lotes correctamente",
|
||||||
"ToastBookmarkCreateFailed": "Error al crear marcador",
|
"ToastBookmarkCreateFailed": "No se pudo crear el marcador",
|
||||||
"ToastBookmarkCreateSuccess": "Marcador Agregado",
|
"ToastBookmarkCreateSuccess": "Marcador añadido",
|
||||||
"ToastBookmarkRemoveSuccess": "Marcador eliminado",
|
"ToastBookmarkRemoveSuccess": "Marcador eliminado",
|
||||||
"ToastCachePurgeFailed": "Error al purgar el caché",
|
"ToastCachePurgeFailed": "No se pudo purgar la antememoria",
|
||||||
"ToastCachePurgeSuccess": "Caché purgado de manera exitosa",
|
"ToastCachePurgeSuccess": "Se purgó la antememoria correctamente",
|
||||||
"ToastChaptersHaveErrors": "Los capítulos tienen errores",
|
"ToastChaptersHaveErrors": "Los capítulos tienen errores",
|
||||||
"ToastChaptersMustHaveTitles": "Los capítulos tienen que tener un título",
|
"ToastChaptersInvalidShiftAmountLast": "Cantidad de desplazamiento no válida. La hora de inicio del último capítulo se extendería más allá de la duración de este audiolibro.",
|
||||||
|
"ToastChaptersMustHaveTitles": "Los capítulos deben tener título",
|
||||||
"ToastChaptersRemoved": "Capítulos eliminados",
|
"ToastChaptersRemoved": "Capítulos eliminados",
|
||||||
"ToastChaptersUpdated": "Capítulos actualizados",
|
"ToastChaptersUpdated": "Capítulos actualizados",
|
||||||
"ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)",
|
"ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)",
|
||||||
"ToastCollectionRemoveSuccess": "Colección removida",
|
"ToastCollectionRemoveSuccess": "Colección quitada",
|
||||||
"ToastCollectionUpdateSuccess": "Colección actualizada",
|
"ToastCollectionUpdateSuccess": "Colección actualizada",
|
||||||
"ToastCoverUpdateFailed": "Error al actualizar la cubierta",
|
"ToastCoverUpdateFailed": "Error al actualizar la cubierta",
|
||||||
"ToastDateTimeInvalidOrIncomplete": "Fecha y hora inválidas o incompletas",
|
"ToastDateTimeInvalidOrIncomplete": "Fecha y hora no válidas o incompletas",
|
||||||
"ToastDeleteFileFailed": "Error el eliminar archivo",
|
"ToastDeleteFileFailed": "Falló la eliminación del archivo",
|
||||||
"ToastDeleteFileSuccess": "Archivo eliminado",
|
"ToastDeleteFileSuccess": "Archivo eliminado",
|
||||||
"ToastDeviceAddFailed": "Error al añadir dispositivo",
|
"ToastDeviceAddFailed": "Error al añadir dispositivo",
|
||||||
"ToastDeviceNameAlreadyExists": "Un libro electrónico ya existe con ese nombre",
|
"ToastDeviceNameAlreadyExists": "Un libro electrónico ya existe con ese nombre",
|
||||||
@@ -998,12 +1012,12 @@
|
|||||||
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
|
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
|
||||||
"ToastItemUpdateSuccess": "Elemento actualizado",
|
"ToastItemUpdateSuccess": "Elemento actualizado",
|
||||||
"ToastLibraryCreateFailed": "Error al crear biblioteca",
|
"ToastLibraryCreateFailed": "Error al crear biblioteca",
|
||||||
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada",
|
"ToastLibraryCreateSuccess": "Se creó la biblioteca «{0}»",
|
||||||
"ToastLibraryDeleteFailed": "Error al eliminar biblioteca",
|
"ToastLibraryDeleteFailed": "Error al eliminar biblioteca",
|
||||||
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
|
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
|
||||||
"ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
|
"ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
|
||||||
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
|
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
|
||||||
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada",
|
"ToastLibraryUpdateSuccess": "Se actualizó la biblioteca «{0}»",
|
||||||
"ToastMatchAllAuthorsFailed": "No se pudo encontrar a todos los autores",
|
"ToastMatchAllAuthorsFailed": "No se pudo encontrar a todos los autores",
|
||||||
"ToastMetadataFilesRemovedError": "Error al eliminar metadatos de {0} archivo(s)",
|
"ToastMetadataFilesRemovedError": "Error al eliminar metadatos de {0} archivo(s)",
|
||||||
"ToastMetadataFilesRemovedNoneFound": "No hay metadatos.{0} archivo(s) encontrado(s) en la biblioteca",
|
"ToastMetadataFilesRemovedNoneFound": "No hay metadatos.{0} archivo(s) encontrado(s) en la biblioteca",
|
||||||
@@ -1013,7 +1027,7 @@
|
|||||||
"ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico",
|
"ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico",
|
||||||
"ToastNameRequired": "Nombre obligatorio",
|
"ToastNameRequired": "Nombre obligatorio",
|
||||||
"ToastNewEpisodesFound": "{0} nuevo(s) episodio(s) encontrado(s)",
|
"ToastNewEpisodesFound": "{0} nuevo(s) episodio(s) encontrado(s)",
|
||||||
"ToastNewUserCreatedFailed": "Error al crear la cuenta: \"{0}\"",
|
"ToastNewUserCreatedFailed": "No se pudo crear la cuenta: «{0}»",
|
||||||
"ToastNewUserCreatedSuccess": "Nueva cuenta creada",
|
"ToastNewUserCreatedSuccess": "Nueva cuenta creada",
|
||||||
"ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca",
|
"ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca",
|
||||||
"ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña",
|
"ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña",
|
||||||
@@ -1034,11 +1048,11 @@
|
|||||||
"ToastPlaylistCreateSuccess": "Lista de reproducción creada",
|
"ToastPlaylistCreateSuccess": "Lista de reproducción creada",
|
||||||
"ToastPlaylistRemoveSuccess": "Lista de reproducción eliminada",
|
"ToastPlaylistRemoveSuccess": "Lista de reproducción eliminada",
|
||||||
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
|
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
|
||||||
"ToastPodcastCreateFailed": "Error al crear podcast",
|
"ToastPodcastCreateFailed": "No se pudo crear el pódcast",
|
||||||
"ToastPodcastCreateSuccess": "Podcast creado",
|
"ToastPodcastCreateSuccess": "Se creó el pódcast correctamente",
|
||||||
"ToastPodcastGetFeedFailed": "No se puede obtener el podcast",
|
"ToastPodcastGetFeedFailed": "No se puede obtener el podcast",
|
||||||
"ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS",
|
"ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS",
|
||||||
"ToastPodcastNoRssFeed": "El podcast no tiene feed RSS",
|
"ToastPodcastNoRssFeed": "El pódcast no tiene suministro RSS",
|
||||||
"ToastProgressIsNotBeingSynced": "El progreso no se sincroniza, reinicia la reproducción",
|
"ToastProgressIsNotBeingSynced": "El progreso no se sincroniza, reinicia la reproducción",
|
||||||
"ToastProviderCreatedFailed": "Error al añadir el proveedor",
|
"ToastProviderCreatedFailed": "Error al añadir el proveedor",
|
||||||
"ToastProviderCreatedSuccess": "Nuevo proveedor añadido",
|
"ToastProviderCreatedSuccess": "Nuevo proveedor añadido",
|
||||||
@@ -1057,9 +1071,9 @@
|
|||||||
"ToastRescanUpToDate": "Reescaneado del artículo completo, estaba actualizado",
|
"ToastRescanUpToDate": "Reescaneado del artículo completo, estaba actualizado",
|
||||||
"ToastRescanUpdated": "Reescaneado completado, el artículo ha sido actualizado",
|
"ToastRescanUpdated": "Reescaneado completado, el artículo ha sido actualizado",
|
||||||
"ToastScanFailed": "No se pudo escanear el elemento de la biblioteca",
|
"ToastScanFailed": "No se pudo escanear el elemento de la biblioteca",
|
||||||
"ToastSelectAtLeastOneUser": "Selecciona al menos un usuario",
|
"ToastSelectAtLeastOneUser": "Seleccione al menos un usuario",
|
||||||
"ToastSendEbookToDeviceFailed": "Error al enviar el ebook al dispositivo",
|
"ToastSendEbookToDeviceFailed": "No se pudo enviar el libro electrónico al dispositivo",
|
||||||
"ToastSendEbookToDeviceSuccess": "Ebook enviado al dispositivo \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "Libro electrónico enviado al dispositivo «{0}»",
|
||||||
"ToastSeriesSubmitFailedSameName": "No se puede añadir dos series con el mismo nombre",
|
"ToastSeriesSubmitFailedSameName": "No se puede añadir dos series con el mismo nombre",
|
||||||
"ToastSeriesUpdateFailed": "Error al actualizar la serie",
|
"ToastSeriesUpdateFailed": "Error al actualizar la serie",
|
||||||
"ToastSeriesUpdateSuccess": "Serie actualizada",
|
"ToastSeriesUpdateSuccess": "Serie actualizada",
|
||||||
@@ -1079,10 +1093,12 @@
|
|||||||
"ToastUnknownError": "Error desconocido",
|
"ToastUnknownError": "Error desconocido",
|
||||||
"ToastUnlinkOpenIdFailed": "Error al desvincular el usuario de OpenID",
|
"ToastUnlinkOpenIdFailed": "Error al desvincular el usuario de OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Usuario desvinculado de OpenID",
|
"ToastUnlinkOpenIdSuccess": "Usuario desvinculado de OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "La ruta de archivo «{0}» ya existe en el servidor",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "El elemento «{0}» usa un subdirectorio de la ruta de carga.",
|
||||||
"ToastUserDeleteFailed": "Error al eliminar el usuario",
|
"ToastUserDeleteFailed": "Error al eliminar el usuario",
|
||||||
"ToastUserDeleteSuccess": "Usuario eliminado",
|
"ToastUserDeleteSuccess": "Usuario eliminado",
|
||||||
"ToastUserPasswordChangeSuccess": "Contraseña modificada correctamente",
|
"ToastUserPasswordChangeSuccess": "Contraseña modificada correctamente",
|
||||||
"ToastUserPasswordMismatch": "No coinciden las contraseñas",
|
"ToastUserPasswordMismatch": "No coinciden las contraseñas",
|
||||||
"ToastUserPasswordMustChange": "La nueva contraseña no puede ser igual que la anterior",
|
"ToastUserPasswordMustChange": "La nueva contraseña no puede ser igual que la anterior",
|
||||||
"ToastUserRootRequireName": "Debes introducir un nombre de usuario root"
|
"ToastUserRootRequireName": "Debe introducir un nombre de usuario administrativo"
|
||||||
}
|
}
|
||||||
|
|||||||
+260
-9
@@ -69,7 +69,7 @@
|
|||||||
"ButtonQueueAddItem": "Lisää jonoon",
|
"ButtonQueueAddItem": "Lisää jonoon",
|
||||||
"ButtonQueueRemoveItem": "Poista jonosta",
|
"ButtonQueueRemoveItem": "Poista jonosta",
|
||||||
"ButtonQuickEmbed": "Pikaupota",
|
"ButtonQuickEmbed": "Pikaupota",
|
||||||
"ButtonQuickEmbedMetadata": "Upota kuvailutiedot nopeasti",
|
"ButtonQuickEmbedMetadata": "Upota metatiedot pikaisesti",
|
||||||
"ButtonQuickMatch": "Pikatäsmäys",
|
"ButtonQuickMatch": "Pikatäsmäys",
|
||||||
"ButtonReScan": "Uudelleenskannaa",
|
"ButtonReScan": "Uudelleenskannaa",
|
||||||
"ButtonRead": "Lue",
|
"ButtonRead": "Lue",
|
||||||
@@ -229,6 +229,7 @@
|
|||||||
"LabelAddedDate": "Lisätty {0}",
|
"LabelAddedDate": "Lisätty {0}",
|
||||||
"LabelAdminUsersOnly": "Vain järjestelmänvalvojat",
|
"LabelAdminUsersOnly": "Vain järjestelmänvalvojat",
|
||||||
"LabelAll": "Kaikki",
|
"LabelAll": "Kaikki",
|
||||||
|
"LabelAllEpisodesDownloaded": "Kaikki jaksot ladattu",
|
||||||
"LabelAllUsers": "Kaikki käyttäjät",
|
"LabelAllUsers": "Kaikki käyttäjät",
|
||||||
"LabelAllUsersExcludingGuests": "Kaikki käyttäjät vieraita lukuun ottamatta",
|
"LabelAllUsersExcludingGuests": "Kaikki käyttäjät vieraita lukuun ottamatta",
|
||||||
"LabelAllUsersIncludingGuests": "Kaikki käyttäjät mukaan lukien vieraat",
|
"LabelAllUsersIncludingGuests": "Kaikki käyttäjät mukaan lukien vieraat",
|
||||||
@@ -252,7 +253,7 @@
|
|||||||
"LabelBackToUser": "Takaisin käyttäjään",
|
"LabelBackToUser": "Takaisin käyttäjään",
|
||||||
"LabelBackupAudioFiles": "Varmuuskopioi äänitiedostot",
|
"LabelBackupAudioFiles": "Varmuuskopioi äänitiedostot",
|
||||||
"LabelBackupLocation": "Varmuuskopiointipaikka",
|
"LabelBackupLocation": "Varmuuskopiointipaikka",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Ota automaattinen varmuuskopiointi käyttöön",
|
"LabelBackupsEnableAutomaticBackups": "Automaattiset varmuuskopiot",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Varmuuskopiot tallennettu kansioon /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Varmuuskopiot tallennettu kansioon /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Varmuuskopion enimmäiskoko (Gt) (0 rajaton)",
|
"LabelBackupsMaxBackupSize": "Varmuuskopion enimmäiskoko (Gt) (0 rajaton)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Virheellisten asetusten estämiseksi varmuuskopiot epäonnistuvat, jos ne ovat asetettua kokoa suurempia.",
|
"LabelBackupsMaxBackupSizeHelp": "Virheellisten asetusten estämiseksi varmuuskopiot epäonnistuvat, jos ne ovat asetettua kokoa suurempia.",
|
||||||
@@ -344,6 +345,9 @@
|
|||||||
"LabelExample": "Esimerkki",
|
"LabelExample": "Esimerkki",
|
||||||
"LabelExpandSeries": "Laajenna sarja",
|
"LabelExpandSeries": "Laajenna sarja",
|
||||||
"LabelExpandSubSeries": "Laajenna alisarja",
|
"LabelExpandSubSeries": "Laajenna alisarja",
|
||||||
|
"LabelExplicit": "Yksiselitteinen",
|
||||||
|
"LabelExplicitChecked": "Yksiselitteinen (valittu)",
|
||||||
|
"LabelExplicitUnchecked": "Ei yksiselitteinen (ei valittu)",
|
||||||
"LabelExportOPML": "Vie OPML",
|
"LabelExportOPML": "Vie OPML",
|
||||||
"LabelFeedURL": "Syötteen URL",
|
"LabelFeedURL": "Syötteen URL",
|
||||||
"LabelFetchingMetadata": "Noudetaan kuvailutietoja",
|
"LabelFetchingMetadata": "Noudetaan kuvailutietoja",
|
||||||
@@ -371,8 +375,8 @@
|
|||||||
"LabelHardDeleteFile": "Kova tiedostojen poisto",
|
"LabelHardDeleteFile": "Kova tiedostojen poisto",
|
||||||
"LabelHasEbook": "Sillä on s-kirja",
|
"LabelHasEbook": "Sillä on s-kirja",
|
||||||
"LabelHasSupplementaryEbook": "Sillä on täydentävän s-kirjan",
|
"LabelHasSupplementaryEbook": "Sillä on täydentävän s-kirjan",
|
||||||
"LabelHideSubtitles": "Piilota tekstitykset",
|
"LabelHideSubtitles": "Piilota alaotsikot",
|
||||||
"LabelHighestPriority": "Tärkein",
|
"LabelHighestPriority": "Korkein etusija",
|
||||||
"LabelHost": "Isäntä",
|
"LabelHost": "Isäntä",
|
||||||
"LabelHour": "Tunti",
|
"LabelHour": "Tunti",
|
||||||
"LabelHours": "Tunnit",
|
"LabelHours": "Tunnit",
|
||||||
@@ -393,6 +397,8 @@
|
|||||||
"LabelIntervalEveryMinute": "Joka minuutti",
|
"LabelIntervalEveryMinute": "Joka minuutti",
|
||||||
"LabelInvert": "Saa käänteiseksi",
|
"LabelInvert": "Saa käänteiseksi",
|
||||||
"LabelItem": "Kohde",
|
"LabelItem": "Kohde",
|
||||||
|
"LabelJumpBackwardAmount": "Taaksepäin-hyppyjen määrä",
|
||||||
|
"LabelJumpForwardAmount": "Eteenpäin-hyppyjen määrä",
|
||||||
"LabelLanguage": "Kieli",
|
"LabelLanguage": "Kieli",
|
||||||
"LabelLanguageDefaultServer": "Palvelimen oletuskieli",
|
"LabelLanguageDefaultServer": "Palvelimen oletuskieli",
|
||||||
"LabelLanguages": "Kielet",
|
"LabelLanguages": "Kielet",
|
||||||
@@ -484,6 +490,7 @@
|
|||||||
"LabelPersonalYearReview": "Vuotesi katsauksessa ({0})",
|
"LabelPersonalYearReview": "Vuotesi katsauksessa ({0})",
|
||||||
"LabelPhotoPathURL": "Valokuvan polku/URL-osoite",
|
"LabelPhotoPathURL": "Valokuvan polku/URL-osoite",
|
||||||
"LabelPlayMethod": "Toistotapa",
|
"LabelPlayMethod": "Toistotapa",
|
||||||
|
"LabelPlaybackRateIncrementDecrement": "Toistonopeuden lisäys-/vähennysmäärä",
|
||||||
"LabelPlayerChapterNumberMarker": "{0}/{1}",
|
"LabelPlayerChapterNumberMarker": "{0}/{1}",
|
||||||
"LabelPlaylists": "Soittolistat",
|
"LabelPlaylists": "Soittolistat",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
@@ -523,9 +530,10 @@
|
|||||||
"LabelReleaseDate": "Julkaisupäivä",
|
"LabelReleaseDate": "Julkaisupäivä",
|
||||||
"LabelRemoveAllMetadataAbs": "Poista kaikki metadata.abs-tiedostot",
|
"LabelRemoveAllMetadataAbs": "Poista kaikki metadata.abs-tiedostot",
|
||||||
"LabelRemoveAllMetadataJson": "Poista kaikki metadata.json-tiedostot",
|
"LabelRemoveAllMetadataJson": "Poista kaikki metadata.json-tiedostot",
|
||||||
|
"LabelRemoveAudibleBranding": "Poista Audiblen intro ja outro kappaleista",
|
||||||
"LabelRemoveCover": "Poista kansikuva",
|
"LabelRemoveCover": "Poista kansikuva",
|
||||||
"LabelRemoveMetadataFile": "Poista metatietotiedostot kirjaston kohdekansioista",
|
"LabelRemoveMetadataFile": "Poista metatietotiedostot kirjaston kohdekansioista",
|
||||||
"LabelRemoveMetadataFileHelp": "Poista kaikki metadata.json- ja metadata.abs-tiedostot {0} kansiostasi.",
|
"LabelRemoveMetadataFileHelp": "Poista kaikki metadata.json- ja metadata.abs-tiedostot {0} kansioistasi.",
|
||||||
"LabelRowsPerPage": "Rivejä sivulla",
|
"LabelRowsPerPage": "Rivejä sivulla",
|
||||||
"LabelSearchTerm": "Hakusana",
|
"LabelSearchTerm": "Hakusana",
|
||||||
"LabelSearchTitle": "Etsi otsikko",
|
"LabelSearchTitle": "Etsi otsikko",
|
||||||
@@ -552,6 +560,8 @@
|
|||||||
"LabelSettingsBookshelfViewHelp": "Skeuomorfinen muotoilu puisilla hyllyillä",
|
"LabelSettingsBookshelfViewHelp": "Skeuomorfinen muotoilu puisilla hyllyillä",
|
||||||
"LabelSettingsChromecastSupport": "Chromecast-tuki",
|
"LabelSettingsChromecastSupport": "Chromecast-tuki",
|
||||||
"LabelSettingsDateFormat": "Päivämäärän muoto",
|
"LabelSettingsDateFormat": "Päivämäärän muoto",
|
||||||
|
"LabelSettingsEnableWatcher": "Skannaa kirjastot automaattisesti muutoksien varalta",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Skannaa kirjastot automaattisesti muutoksien varalta",
|
||||||
"LabelSettingsEnableWatcherHelp": "Ottaa käyttöön kohteiden automaattisen lisäämisen ja päivityksen kun tiedostomuutoksia havaitaan. *Tarvitsee palvelimen uudelleenkäynnistyksen",
|
"LabelSettingsEnableWatcherHelp": "Ottaa käyttöön kohteiden automaattisen lisäämisen ja päivityksen kun tiedostomuutoksia havaitaan. *Tarvitsee palvelimen uudelleenkäynnistyksen",
|
||||||
"LabelSettingsEpubsAllowScriptedContent": "Salli komentosarjamuotoinen sisältö epubissa",
|
"LabelSettingsEpubsAllowScriptedContent": "Salli komentosarjamuotoinen sisältö epubissa",
|
||||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Salli epub-tiedostojen suorittaa komentosarjoja. On suositeltavaa pitää tämä asetus pois käytöstä, ellet luota epub-tiedostojen lähteeseen.",
|
"LabelSettingsEpubsAllowScriptedContentHelp": "Salli epub-tiedostojen suorittaa komentosarjoja. On suositeltavaa pitää tämä asetus pois käytöstä, ellet luota epub-tiedostojen lähteeseen.",
|
||||||
@@ -568,8 +578,8 @@
|
|||||||
"LabelSettingsLibraryMarkAsFinishedWhen": "Merkitse mediakohde valmiiksi, kun",
|
"LabelSettingsLibraryMarkAsFinishedWhen": "Merkitse mediakohde valmiiksi, kun",
|
||||||
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Ohita aiemmat kirjat Jatka sarjassa",
|
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Ohita aiemmat kirjat Jatka sarjassa",
|
||||||
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Jatka sarja -kotisivun hyllyssä näkyy ensimmäinen kirja, jota ei ole aloitettu sarjoissa, joissa on vähintään yksi kirja valmiina eikä yhtään kirjaa kesken. Tämän asetuksen ottaminen käyttöön jatkaa sarjaa kauimpana valmistuneesta kirjasta ensimmäisen aloittamattoman kirjan sijaan.",
|
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Jatka sarja -kotisivun hyllyssä näkyy ensimmäinen kirja, jota ei ole aloitettu sarjoissa, joissa on vähintään yksi kirja valmiina eikä yhtään kirjaa kesken. Tämän asetuksen ottaminen käyttöön jatkaa sarjaa kauimpana valmistuneesta kirjasta ensimmäisen aloittamattoman kirjan sijaan.",
|
||||||
"LabelSettingsParseSubtitles": "Jäsennä tekstitykset",
|
"LabelSettingsParseSubtitles": "Jäsennä alaotsikot",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Pura tekstitykset äänikirjojen kansioiden nimistä.<br>Tekstitys on erotettava toisistaan merkillä \"-\"<br>ts. \"Kirjan otsikko - Tekstitys täällä\" on alaotsikko \"Tekstitys täällä\"",
|
"LabelSettingsParseSubtitlesHelp": "Pura alaotsikot äänikirjojen kansioiden nimistä.<br>Tekstitys on erotettava toisistaan merkillä \"-\"<br>ts. \"Kirjan otsikko - Tekstitys täällä\" on alaotsikko \"Tekstitys täällä\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Pidä mieluummin täsmäävät metatiedot",
|
"LabelSettingsPreferMatchedMetadata": "Pidä mieluummin täsmäävät metatiedot",
|
||||||
"LabelSettingsPreferMatchedMetadataHelp": "Täsmäävät tiedot ohittavat kohteen tiedot käytettäessä Pikatäsmäystä. Oletuksena Pikatäsmäys täyttää vain puuttuvat tiedot.",
|
"LabelSettingsPreferMatchedMetadataHelp": "Täsmäävät tiedot ohittavat kohteen tiedot käytettäessä Pikatäsmäystä. Oletuksena Pikatäsmäys täyttää vain puuttuvat tiedot.",
|
||||||
"LabelSettingsSkipMatchingBooksWithASIN": "Ohita täsmäävät kirjat, joilla on jo ASIN",
|
"LabelSettingsSkipMatchingBooksWithASIN": "Ohita täsmäävät kirjat, joilla on jo ASIN",
|
||||||
@@ -589,12 +599,13 @@
|
|||||||
"LabelShareURL": "Jaa URL-osoite",
|
"LabelShareURL": "Jaa URL-osoite",
|
||||||
"LabelShowAll": "Näytä kaikki",
|
"LabelShowAll": "Näytä kaikki",
|
||||||
"LabelShowSeconds": "Näytä sekunnit",
|
"LabelShowSeconds": "Näytä sekunnit",
|
||||||
"LabelShowSubtitles": "Näytä tekstitykset",
|
"LabelShowSubtitles": "Näytä alaotsikot",
|
||||||
"LabelSize": "Koko",
|
"LabelSize": "Koko",
|
||||||
"LabelSleepTimer": "Uniajastin",
|
"LabelSleepTimer": "Uniajastin",
|
||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "Nouseva",
|
"LabelSortAscending": "Nouseva",
|
||||||
"LabelSortDescending": "Laskeva",
|
"LabelSortDescending": "Laskeva",
|
||||||
|
"LabelSortPubDate": "Järjestä julkaisupäivän mukaan",
|
||||||
"LabelStart": "Aloita",
|
"LabelStart": "Aloita",
|
||||||
"LabelStartTime": "Aloitusaika",
|
"LabelStartTime": "Aloitusaika",
|
||||||
"LabelStarted": "Aloitettu",
|
"LabelStarted": "Aloitettu",
|
||||||
@@ -614,7 +625,7 @@
|
|||||||
"LabelStatsOverallDays": "Päivät kokonaisuudessaan",
|
"LabelStatsOverallDays": "Päivät kokonaisuudessaan",
|
||||||
"LabelStatsOverallHours": "Tunnit kokonaisuudessaan",
|
"LabelStatsOverallHours": "Tunnit kokonaisuudessaan",
|
||||||
"LabelStatsWeekListening": "Viikon aikana kuunneltu",
|
"LabelStatsWeekListening": "Viikon aikana kuunneltu",
|
||||||
"LabelSubtitle": "Tekstitys",
|
"LabelSubtitle": "Alaotsikko",
|
||||||
"LabelSupportedFileTypes": "Tuetut tiedostotyypit",
|
"LabelSupportedFileTypes": "Tuetut tiedostotyypit",
|
||||||
"LabelTag": "Tägi",
|
"LabelTag": "Tägi",
|
||||||
"LabelTags": "Tägit",
|
"LabelTags": "Tägit",
|
||||||
@@ -713,6 +724,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Epäkelvollinen aloitusaika; on oltava lyhyempi kuin äänikirjan kesto",
|
"MessageChapterErrorStartGteDuration": "Epäkelvollinen aloitusaika; on oltava lyhyempi kuin äänikirjan kesto",
|
||||||
"MessageChapterErrorStartLtPrev": "Epäkelvollinen aloitusaika; on oltava suurempi tai yhtä suuri kuin edellisen luvun aloitusaika",
|
"MessageChapterErrorStartLtPrev": "Epäkelvollinen aloitusaika; on oltava suurempi tai yhtä suuri kuin edellisen luvun aloitusaika",
|
||||||
"MessageChapterStartIsAfter": "Luku alkaa äänikirjan lopun jälkeen",
|
"MessageChapterStartIsAfter": "Luku alkaa äänikirjan lopun jälkeen",
|
||||||
|
"MessageChaptersNotFound": "Kappaleita ei löydy",
|
||||||
"MessageCheckingCron": "Tarkistetaan cronia...",
|
"MessageCheckingCron": "Tarkistetaan cronia...",
|
||||||
"MessageConfirmCloseFeed": "Oletko varma, että haluat sulkea tämän syötteen?",
|
"MessageConfirmCloseFeed": "Oletko varma, että haluat sulkea tämän syötteen?",
|
||||||
"MessageConfirmDeleteBackup": "Oletko varma, että haluat poistaa varmuuskopion {0}:lle?",
|
"MessageConfirmDeleteBackup": "Oletko varma, että haluat poistaa varmuuskopion {0}:lle?",
|
||||||
@@ -769,6 +781,7 @@
|
|||||||
"MessageForceReScanDescription": "skannaa kaikki tiedostot uudelleen kuten uusi tarkistus. Äänitiedoston ID3-tunnisteet, OPF-tiedostot ja tekstitiedostot skannataan uusina.",
|
"MessageForceReScanDescription": "skannaa kaikki tiedostot uudelleen kuten uusi tarkistus. Äänitiedoston ID3-tunnisteet, OPF-tiedostot ja tekstitiedostot skannataan uusina.",
|
||||||
"MessageImportantNotice": "Tärkeä huomautus!",
|
"MessageImportantNotice": "Tärkeä huomautus!",
|
||||||
"MessageInsertChapterBelow": "Syötä luku alle",
|
"MessageInsertChapterBelow": "Syötä luku alle",
|
||||||
|
"MessageInvalidAsin": "Virheellinen ASIN",
|
||||||
"MessageItemsSelected": "{0} kohdetta valittu",
|
"MessageItemsSelected": "{0} kohdetta valittu",
|
||||||
"MessageItemsUpdated": "{0} kohdetta päivitetty",
|
"MessageItemsUpdated": "{0} kohdetta päivitetty",
|
||||||
"MessageJoinUsOn": "Liity meihin",
|
"MessageJoinUsOn": "Liity meihin",
|
||||||
@@ -777,70 +790,308 @@
|
|||||||
"MessageLogsDescription": "Lokitiedot tallennetaan kansioon <code>/metadata/logs</code> JSON-tiedostoina. Kaatumislokit tallennetaan kansioon <code>/metadata/logs/crash_logs.txt</code>.",
|
"MessageLogsDescription": "Lokitiedot tallennetaan kansioon <code>/metadata/logs</code> JSON-tiedostoina. Kaatumislokit tallennetaan kansioon <code>/metadata/logs/crash_logs.txt</code>.",
|
||||||
"MessageM4BFailed": "M4B epäonnistui!",
|
"MessageM4BFailed": "M4B epäonnistui!",
|
||||||
"MessageM4BFinished": "M4B valmis!",
|
"MessageM4BFinished": "M4B valmis!",
|
||||||
|
"MessageMapChapterTitles": "Kartoita lukujen otsikot olemassa oleviin äänikirjan lukuihin muuttamatta aikaleimoja",
|
||||||
|
"MessageMarkAllEpisodesFinished": "Merkitse kaikki jaksot päättyneiksi",
|
||||||
|
"MessageMarkAllEpisodesNotFinished": "Merkitse kaikki jaksot ei-päättyneiksi",
|
||||||
"MessageMarkAsFinished": "Merkitse valmiiksi",
|
"MessageMarkAsFinished": "Merkitse valmiiksi",
|
||||||
|
"MessageMarkAsNotFinished": "Merkitse Ei-päättyneiksi",
|
||||||
|
"MessageMatchBooksDescription": "yrittää yhdistää kirjaston kirjoja valitun hakupalvelun kirjaan ja täyttää tyhjät tiedot ja kansikuvan. Ei korvaa yksityiskohtia.",
|
||||||
|
"MessageNoAudioTracks": "Ei ääniraitoja",
|
||||||
|
"MessageNoAuthors": "Ei tekijöitä",
|
||||||
|
"MessageNoBackups": "Ei varmuuskopioita",
|
||||||
"MessageNoBookmarks": "Ei kirjanmerkkejä",
|
"MessageNoBookmarks": "Ei kirjanmerkkejä",
|
||||||
"MessageNoChapters": "Ei kappaleita",
|
"MessageNoChapters": "Ei kappaleita",
|
||||||
"MessageNoCollections": "Ei kokoelmia",
|
"MessageNoCollections": "Ei kokoelmia",
|
||||||
"MessageNoCoversFound": "Kansikuvia ei löydetty",
|
"MessageNoCoversFound": "Kansikuvia ei löydetty",
|
||||||
|
"MessageNoDescription": "Ei kuvausta",
|
||||||
|
"MessageNoDevices": "Ei laitteita",
|
||||||
|
"MessageNoDownloadsInProgress": "Ei latauksia tällä hetkellä meneillään",
|
||||||
|
"MessageNoDownloadsQueued": "Ei latauksia jonossa",
|
||||||
|
"MessageNoEpisodeMatchesFound": "Jaksoosumia ei löytynyt",
|
||||||
|
"MessageNoEpisodes": "Ei jaksoja",
|
||||||
|
"MessageNoFoldersAvailable": "Ei kansioita saatavilla",
|
||||||
"MessageNoGenres": "Ei lajityyppejä",
|
"MessageNoGenres": "Ei lajityyppejä",
|
||||||
|
"MessageNoIssues": "Ei vikoja",
|
||||||
"MessageNoItems": "Ei kohteita",
|
"MessageNoItems": "Ei kohteita",
|
||||||
"MessageNoItemsFound": "Kohteita ei löytynyt",
|
"MessageNoItemsFound": "Kohteita ei löytynyt",
|
||||||
"MessageNoListeningSessions": "Ei kuunteluistuntoja",
|
"MessageNoListeningSessions": "Ei kuunteluistuntoja",
|
||||||
|
"MessageNoLogs": "Ei lokeja",
|
||||||
|
"MessageNoMediaProgress": "Ei median edistymistä",
|
||||||
|
"MessageNoNotifications": "Ei ilmoituksia",
|
||||||
|
"MessageNoPodcastFeed": "Epäkelvollinen podcast: Ei syötettä",
|
||||||
"MessageNoPodcastsFound": "Podcasteja ei löytynyt",
|
"MessageNoPodcastsFound": "Podcasteja ei löytynyt",
|
||||||
|
"MessageNoResults": "Ei tuloksia",
|
||||||
|
"MessageNoSearchResultsFor": "Ei hakutuloksia \"{0}\":lle",
|
||||||
|
"MessageNoSeries": "Ei sarjaa",
|
||||||
|
"MessageNoTags": "Ei tunnisteita",
|
||||||
|
"MessageNoTasksRunning": "Ei käynnissä olevia tehtäviä",
|
||||||
"MessageNoUpdatesWereNecessary": "Päivityksiä ei tarvittu",
|
"MessageNoUpdatesWereNecessary": "Päivityksiä ei tarvittu",
|
||||||
"MessageNoUserPlaylists": "Sinulla ei ole soittolistoja",
|
"MessageNoUserPlaylists": "Sinulla ei ole soittolistoja",
|
||||||
|
"MessageNoUserPlaylistsHelp": "Soittolistat ovat yksityisiä. Vain ne luonut käyttäjä näkee ne.",
|
||||||
|
"MessageNotYetImplemented": "Ei vielä toteutettu",
|
||||||
|
"MessageOpmlPreviewNote": "Huomautus: Tämä on esikatselu jäsennetystä OPML-tiedostosta. Varsinainen podcastin nimi tullaan ottamaan RSS-syötteestä.",
|
||||||
"MessageOr": "tai",
|
"MessageOr": "tai",
|
||||||
|
"MessagePauseChapter": "Keskeytä luvun toisto",
|
||||||
|
"MessagePlayChapter": "Kuuntele luvun alku",
|
||||||
|
"MessagePlaylistCreateFromCollection": "Luo soittolista kokoelmasta",
|
||||||
|
"MessagePleaseWait": "Ole hyvä ja odota...",
|
||||||
|
"MessagePodcastHasNoRSSFeedForMatching": "Podcastilla ei ole RSS-syötteen URL-osoitetta, jota voitaisiin käyttää täsmäämiseen",
|
||||||
"MessagePodcastSearchField": "Syötä hakutermi tai RSS-syötteen URL-osoite",
|
"MessagePodcastSearchField": "Syötä hakutermi tai RSS-syötteen URL-osoite",
|
||||||
|
"MessageQuickEmbedInProgress": "Pikaupottaminen meneillään",
|
||||||
|
"MessageQuickEmbedQueue": "Jonotettu pikaupotusta varten ({0} jonossa)",
|
||||||
"MessageQuickMatchAllEpisodes": "Pikatäsmää kaikki jaksot",
|
"MessageQuickMatchAllEpisodes": "Pikatäsmää kaikki jaksot",
|
||||||
|
"MessageQuickMatchDescription": "Täytä tyhjän tuotteen tiedot ja kansi ensimmäisellä täsmäävällä tuloksella {0}:sta. Ei korvaa tietoja, ellei 'Pidä mieluummin täsmäävät metatiedot'-palvelinasetus on otettu käyttöön.",
|
||||||
|
"MessageRemoveChapter": "Poista luku",
|
||||||
|
"MessageRemoveEpisodes": "Poista {0} jakso(a)",
|
||||||
|
"MessageRemoveFromPlayerQueue": "Poista soittimen jonosta",
|
||||||
"MessageRemoveUserWarning": "Oletko varma, että haluat poistaa käyttäjän \"{0}\" pysyvästi?",
|
"MessageRemoveUserWarning": "Oletko varma, että haluat poistaa käyttäjän \"{0}\" pysyvästi?",
|
||||||
"MessageReportBugsAndContribute": "Ilmoita virheistä, toivo ominaisuuksia ja osallistu",
|
"MessageReportBugsAndContribute": "Ilmoita virheistä, toivo ominaisuuksia ja osallistu",
|
||||||
"MessageResetChaptersConfirm": "Oletko varma, että haluat nollata luvut ja kumota tekemäsi muutokset?",
|
"MessageResetChaptersConfirm": "Oletko varma, että haluat nollata luvut ja kumota tekemäsi muutokset?",
|
||||||
"MessageRestoreBackupConfirm": "Oletko varma, että haluat palauttaa varmuuskopion, joka on luotu",
|
"MessageRestoreBackupConfirm": "Oletko varma, että haluat palauttaa varmuuskopion, joka on luotu",
|
||||||
|
"MessageRestoreBackupWarning": "Varmuuskopion palauttaminen korvaa koko /config:ssa sijaitsevan tietokannan, ja kansikuvat /metadata/items & /metadata/authors:ssa.<br /><br />Varmuuskopiot eivät muuta kirjastokansioissasi olevia tiedostoja. Jos olet ottanut käyttöön palvelinasetuksissa kansikuvien ja metatietojen tallentamisen kirjaston kansioihin, niitä ei varmuuskopioida tai korvata.<br /><br />Kaikki palvelintasi käyttävät asiakkaat virkistetään automaattisesti.",
|
||||||
"MessageScheduleLibraryScanNote": "Suurimmalle osaa käyttäjistä on suositeltavaa jättää tämä ominaisuus pois päältä ja säilyttää kansiotarkkailu päällä. Kansiotarkkailu havaitsee automaattisesti tiedostomuutokset kirjaston kansioissa. Kansiotarkkailu ei toimi kaikille tiedostojärjestelmille (kuten NFS), jolloin voidaan käyttää ajastettuja kirjastoskannauksia.",
|
"MessageScheduleLibraryScanNote": "Suurimmalle osaa käyttäjistä on suositeltavaa jättää tämä ominaisuus pois päältä ja säilyttää kansiotarkkailu päällä. Kansiotarkkailu havaitsee automaattisesti tiedostomuutokset kirjaston kansioissa. Kansiotarkkailu ei toimi kaikille tiedostojärjestelmille (kuten NFS), jolloin voidaan käyttää ajastettuja kirjastoskannauksia.",
|
||||||
|
"MessageScheduleRunEveryWeekdayAtTime": "Suorita joka {0} klo {1}",
|
||||||
|
"MessageSearchResultsFor": "Hakutulokset haulle",
|
||||||
|
"MessageSelected": "{0} valittuna",
|
||||||
|
"MessageServerCouldNotBeReached": "Palvelimelle ei saatu yhteyttä",
|
||||||
|
"MessageSetChaptersFromTracksDescription": "Aseta luvut käyttämällä kutakin äänitiedostoa lukuna ja luvun otsikkoa äänitiedoston nimenä",
|
||||||
|
"MessageShareExpirationWillBe": "Umpeutuminen on <strong>{0}</strong>",
|
||||||
|
"MessageShareExpiresIn": "Umpeutuu {0}:n kuluttua",
|
||||||
|
"MessageShareURLWillBe": "Jaa URL-osoite on <strong>{0}</strong>",
|
||||||
|
"MessageStartPlaybackAtTime": "Aloitetaanko \"{0}\":n toisto klo {1}?",
|
||||||
|
"MessageTaskAudioFileNotWritable": "Äänitiedosto \"{0}\" ei ole kirjoitettava",
|
||||||
|
"MessageTaskCanceledByUser": "Tehtävä peruttu käyttäjän toimesta",
|
||||||
|
"MessageTaskDownloadingEpisodeDescription": "Ladataan jaksoa \"{0}\"",
|
||||||
|
"MessageTaskEmbeddingMetadata": "Upotetaan metatiedot",
|
||||||
|
"MessageTaskEmbeddingMetadataDescription": "Upotetaan metatiedot äänikirjaan \"{0}\"",
|
||||||
|
"MessageTaskEncodingM4b": "Koodaus M4B",
|
||||||
|
"MessageTaskEncodingM4bDescription": "Koodataan äänikirjaa \"{0}\" yhdeksi m4b-tiedostoksi",
|
||||||
"MessageTaskFailed": "Epäonnistunut",
|
"MessageTaskFailed": "Epäonnistunut",
|
||||||
|
"MessageTaskFailedToBackupAudioFile": "Äänitiedoston \"{0}\" varmuuskopiointi epäonnistui",
|
||||||
|
"MessageTaskFailedToCreateCacheDirectory": "Välimuistihakemiston luominen epäonnistui",
|
||||||
|
"MessageTaskFailedToEmbedMetadataInFile": "Metatietojen upottaminen tiedostoon \"{0}\" epäonnistui",
|
||||||
|
"MessageTaskFailedToMergeAudioFiles": "Äänitiedostojen yhdistäminen epäonnistui",
|
||||||
|
"MessageTaskFailedToMoveM4bFile": "m4b-tiedoston siirtäminen epäonnistui",
|
||||||
|
"MessageTaskFailedToWriteMetadataFile": "Metatietotiedoston kirjoittaminen epäonnistui",
|
||||||
|
"MessageTaskMatchingBooksInLibrary": "Vastaavat kirjat kirjastossa \"{0}\"",
|
||||||
|
"MessageTaskNoFilesToScan": "Ei skannattavia tiedostoja",
|
||||||
|
"MessageTaskOpmlImport": "OPML-tuonti",
|
||||||
|
"MessageTaskOpmlImportDescription": "Luodaan podcasteja {0} RSS-syötteistä",
|
||||||
|
"MessageTaskOpmlImportFeed": "OPML-tuontisyöte",
|
||||||
|
"MessageTaskOpmlImportFeedDescription": "Tuodaan RSS-syötettä \"{0}\"",
|
||||||
|
"MessageTaskOpmlImportFeedFailed": "Podcast-syötteen saaminen epäonnistui",
|
||||||
|
"MessageTaskOpmlImportFeedPodcastDescription": "Luodaan podcastia \"{0}\"",
|
||||||
|
"MessageTaskOpmlImportFeedPodcastExists": "Podcast on jo olemassa polulla",
|
||||||
|
"MessageTaskOpmlImportFeedPodcastFailed": "Podcastin luominen epäonnistui",
|
||||||
|
"MessageTaskOpmlImportFinished": "Lisätty {0} podcastia",
|
||||||
|
"MessageTaskOpmlParseFailed": "OPML-tiedoston jäsentäminen epäonnistui",
|
||||||
|
"MessageTaskOpmlParseFastFail": "Epäkelvollinen OPML-tiedoston <opml>-tunnistetta ei löytynyt tai <outline>-tunnistetta ei löytynyt",
|
||||||
|
"MessageTaskOpmlParseNoneFound": "Syötteitä ei löytynyt OPML-tiedostosta",
|
||||||
|
"MessageTaskScanItemsAdded": "{0} lisätty",
|
||||||
|
"MessageTaskScanItemsMissing": "{0} puuttuu",
|
||||||
|
"MessageTaskScanItemsUpdated": "{0} päivitetty",
|
||||||
|
"MessageTaskScanNoChangesNeeded": "Muutoksia ei tarvita",
|
||||||
|
"MessageTaskScanningFileChanges": "Tarkastetaan tiedoston muutoksia \"{0}\":sta",
|
||||||
|
"MessageTaskScanningLibrary": "Tarkastetaan kirjastoa \"{0}\"",
|
||||||
|
"MessageTaskTargetDirectoryNotWritable": "Kohdehakemisto ei ole kirjoitettava",
|
||||||
|
"MessageThinking": "Ajattellaan...",
|
||||||
|
"MessageUploaderItemFailed": "Lataaminen ulospäin epäonnistui",
|
||||||
|
"MessageUploaderItemSuccess": "Onnistuneesti ladattu! ulospäin!",
|
||||||
|
"MessageUploading": "Ladataan! ulospäin...",
|
||||||
|
"MessageValidCronExpression": "Kelvollinen cron-lauseke",
|
||||||
"MessageWatcherIsDisabledGlobally": "Kansiotarkkailu on poistettu käytöstä kaikkialla palvelimen asetuksissa",
|
"MessageWatcherIsDisabledGlobally": "Kansiotarkkailu on poistettu käytöstä kaikkialla palvelimen asetuksissa",
|
||||||
|
"MessageXLibraryIsEmpty": "{0} Kirjasto on tyhjä!",
|
||||||
|
"MessageYourAudiobookDurationIsLonger": "Äänikirjasi kesto on pidempi kuin löydetty kesto",
|
||||||
|
"MessageYourAudiobookDurationIsShorter": "Äänikirjasi kesto on lyhyempi kuin löydetty kesto",
|
||||||
|
"NoteChangeRootPassword": "Käyttäjä root on ainoa käyttäjä, jolla voi olla tyhjä salasana",
|
||||||
|
"NoteChapterEditorTimes": "Huomautus: Ensimmäisen luvun aloitusajan on oltava 0:00 ja viimeisen luvun aloitusaika ei saa ylittää tätä äänikirjan kestoa.",
|
||||||
|
"NoteFolderPicker": "Huomautus: jo kartoitettuja kansioita ei näytetä",
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Varoitus: Useimmat podcast-sovellukset edellyttävät, että RSS-syötteen URL-osoite käyttää HTTPS:a",
|
"NoteRSSFeedPodcastAppsHttps": "Varoitus: Useimmat podcast-sovellukset edellyttävät, että RSS-syötteen URL-osoite käyttää HTTPS:a",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Varoitus: yhdellä tai useammalla jaksollasi ei ole julkaisupäivämäärää. Jotkut podcast-sovellukset vaativat tämän.",
|
"NoteRSSFeedPodcastAppsPubDate": "Varoitus: yhdellä tai useammalla jaksollasi ei ole julkaisupäivämäärää. Jotkut podcast-sovellukset vaativat tämän.",
|
||||||
|
"NoteUploaderFoldersWithMediaFiles": "Mediatiedostoja sisältävät kansiot käsitellään erillisinä kirjastokohteina.",
|
||||||
|
"NoteUploaderOnlyAudioFiles": "Jos ladataan luospäin vain äänitiedostoja, silloin jokainen äänitiedosto käsitellään erillisenä äänikirjana.",
|
||||||
|
"NoteUploaderUnsupportedFiles": "Ei-tuetut tiedostot ohitetaan. Kansiota valittaessa tai pudottaessa, muut tiedostot, jotka eivät ole kohdekansiossa, ohitetaan.",
|
||||||
|
"NotificationOnBackupCompletedDescription": "Laukaistu, kun varmuuskopiointi on valmis",
|
||||||
|
"NotificationOnBackupFailedDescription": "Laukaistu, kun varmuuskopiointi epäonnistuu",
|
||||||
|
"NotificationOnEpisodeDownloadedDescription": "Laukaistu, kun podcast-jakso ladataan automaattisesti",
|
||||||
|
"NotificationOnTestDescription": "Tapahtuma ilmoitusjärjestelmän testaamista varten",
|
||||||
|
"PlaceholderNewCollection": "Uusi kokoelman nimi",
|
||||||
|
"PlaceholderNewFolderPath": "Uusi kansion polku",
|
||||||
|
"PlaceholderNewPlaylist": "Uusi soittolistan nimi",
|
||||||
|
"PlaceholderSearch": "Haku...",
|
||||||
|
"PlaceholderSearchEpisode": "Haku jaksosta..",
|
||||||
|
"StatsAuthorsAdded": "tekijät lisätty",
|
||||||
|
"StatsBooksAdded": "kirjat lisätty",
|
||||||
|
"StatsBooksAdditional": "Jotkut lisäykset sisältävät…",
|
||||||
|
"StatsBooksFinished": "kirjat päättyneet",
|
||||||
|
"StatsBooksFinishedThisYear": "Jotkut kirjat päättyneet tänä vuonna…",
|
||||||
|
"StatsBooksListenedTo": "kuunnellut kirjat",
|
||||||
|
"StatsCollectionGrewTo": "Kirjakokoelmasi kasvoi asti…",
|
||||||
"StatsSessions": "istunnot",
|
"StatsSessions": "istunnot",
|
||||||
|
"StatsSpentListening": "kuunteluun käytetty",
|
||||||
|
"StatsTopAuthor": "HUIPPUTEKIJÄ",
|
||||||
|
"StatsTopAuthors": "HUIPPUTEKIJÄT",
|
||||||
|
"StatsTopGenre": "HUIPPUTYYLILAJI",
|
||||||
|
"StatsTopGenres": "HUIPPUTYYLILAJIT",
|
||||||
|
"StatsTopMonth": "HUIPPUKUUKAUSI",
|
||||||
|
"StatsTopNarrator": "HUIPPUKERTOJA",
|
||||||
|
"StatsTopNarrators": "HUIPPUKERTOJAT",
|
||||||
|
"StatsTotalDuration": "Kokonaiskestolla…",
|
||||||
|
"StatsYearInReview": "VUOSI KATSAUKSESSA",
|
||||||
"ToastAccountUpdateSuccess": "Tili päivitetty",
|
"ToastAccountUpdateSuccess": "Tili päivitetty",
|
||||||
"ToastAppriseUrlRequired": "Arvon tulee olla Apprise URL",
|
"ToastAppriseUrlRequired": "Arvon tulee olla Apprise URL",
|
||||||
|
"ToastAsinRequired": "ASIN vaaditaan",
|
||||||
|
"ToastAuthorImageRemoveSuccess": "Tekijän kuva poistettu",
|
||||||
|
"ToastAuthorNotFound": "Tekijää \"{0}\" ei löydy",
|
||||||
|
"ToastAuthorRemoveSuccess": "Tekijä poistettu",
|
||||||
|
"ToastAuthorSearchNotFound": "Tekijää ei löydy",
|
||||||
|
"ToastAuthorUpdateMerged": "Tekijä yhdistetty",
|
||||||
|
"ToastAuthorUpdateSuccess": "Tekijä päivitetty",
|
||||||
|
"ToastAuthorUpdateSuccessNoImageFound": "Tekijä päivitetty (kuvaa ei löytynyt)",
|
||||||
|
"ToastBackupAppliedSuccess": "Varmuuskopiointi sovellettu",
|
||||||
|
"ToastBackupCreateFailed": "Varmuuskopion luominen epäonnistui",
|
||||||
|
"ToastBackupCreateSuccess": "Varmuuskopio luotu",
|
||||||
|
"ToastBackupDeleteFailed": "Varmuuskopion poistaminen epäonnistui",
|
||||||
|
"ToastBackupDeleteSuccess": "Varmuuskopio poistettu",
|
||||||
|
"ToastBackupInvalidMaxKeep": "Epäkelvollinen määrä säilytettäviä varmuuskopioita",
|
||||||
|
"ToastBackupInvalidMaxSize": "Epäkelvollinen varmuuskopion enimmäiskoko",
|
||||||
|
"ToastBackupRestoreFailed": "Varmuuskopion palauttaminen epäonnistui",
|
||||||
|
"ToastBackupUploadFailed": "Varmuuskopion lataaminen ulospäin epäonnistui",
|
||||||
|
"ToastBackupUploadSuccess": "Varmuuskopio ladattu ulospäin",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Kohteisiin sovelletut yksityiskohdat",
|
||||||
|
"ToastBatchDeleteFailed": "Eräpoisto epäonnistui",
|
||||||
|
"ToastBatchDeleteSuccess": "Eräpoisto onnistui",
|
||||||
"ToastBatchQuickMatchFailed": "Erän pikatäsmäys epäonnistui!",
|
"ToastBatchQuickMatchFailed": "Erän pikatäsmäys epäonnistui!",
|
||||||
"ToastBatchQuickMatchStarted": "{0} kirjan erän pikatäsmäys aloitettu!",
|
"ToastBatchQuickMatchStarted": "{0} kirjan erän pikatäsmäys aloitettu!",
|
||||||
|
"ToastBatchUpdateFailed": "Eräpäivitys epäonnistui",
|
||||||
|
"ToastBatchUpdateSuccess": "Eräpäivitys onnistui",
|
||||||
"ToastBookmarkCreateFailed": "Kirjanmerkin luominen epäonnistui",
|
"ToastBookmarkCreateFailed": "Kirjanmerkin luominen epäonnistui",
|
||||||
|
"ToastBookmarkCreateSuccess": "Kirjanmerkki lisätty",
|
||||||
|
"ToastBookmarkRemoveSuccess": "Kirjanmerkki poistettu",
|
||||||
|
"ToastCachePurgeFailed": "Välimuistin tyhjentäminen epäonnistui",
|
||||||
|
"ToastCachePurgeSuccess": "Välimuisti tyhjennetty onnistuneesti",
|
||||||
|
"ToastChaptersHaveErrors": "Luvuissa on virheitä",
|
||||||
|
"ToastChaptersMustHaveTitles": "Lukuilla on oltava otsikot",
|
||||||
|
"ToastChaptersRemoved": "Luvut poistettu",
|
||||||
|
"ToastChaptersUpdated": "Luvut päivitetty",
|
||||||
|
"ToastCollectionItemsAddFailed": "Kohteen/kohteiden lisääminen kokoelmaan epäonnistui",
|
||||||
|
"ToastCollectionRemoveSuccess": "Kokoelma poistettu",
|
||||||
|
"ToastCollectionUpdateSuccess": "Kokoelma päivitetty",
|
||||||
"ToastCoverUpdateFailed": "Kansikuvan päivitys epäonnistui",
|
"ToastCoverUpdateFailed": "Kansikuvan päivitys epäonnistui",
|
||||||
|
"ToastDateTimeInvalidOrIncomplete": "Päivämäärä ja aika ovat epäkelvolliset tai puutteelliset",
|
||||||
|
"ToastDeleteFileFailed": "Tiedoston poistaminen epäonnistui",
|
||||||
|
"ToastDeleteFileSuccess": "Tiedosto poistettu",
|
||||||
|
"ToastDeviceAddFailed": "Laitteen lisääminen epäonnistui",
|
||||||
|
"ToastDeviceNameAlreadyExists": "Tämän niminen sähköinen lukulaite on jo olemassa",
|
||||||
|
"ToastDeviceTestEmailFailed": "Testisähköpostin lähettäminen epäonnistui",
|
||||||
|
"ToastDeviceTestEmailSuccess": "Testisähköposti lähetetty",
|
||||||
|
"ToastEmailSettingsUpdateSuccess": "Sähköpostiasetukset päivitetty",
|
||||||
|
"ToastEncodeCancelFailed": "Koodauksen peruuttaminen epäonnistui",
|
||||||
|
"ToastEncodeCancelSucces": "Koodaaminen peruutettu",
|
||||||
|
"ToastEpisodeDownloadQueueClearFailed": "Jonon tyhjentäminen epäonnistui",
|
||||||
|
"ToastEpisodeDownloadQueueClearSuccess": "Jakson latausjono tyhjennetty",
|
||||||
|
"ToastEpisodeUpdateSuccess": "{0} jaksoa päivitetty",
|
||||||
|
"ToastErrorCannotShare": "Ei voi jakaa alkuperäisesti tällä laitteella",
|
||||||
|
"ToastFailedToLoadData": "Tietojen lataaminen epäonnistui",
|
||||||
|
"ToastFailedToMatch": "Vastaaminen epäonnistui",
|
||||||
|
"ToastFailedToShare": "Jakaminen epäonnistui",
|
||||||
|
"ToastFailedToUpdate": "Päivittäminen epäonnistui",
|
||||||
|
"ToastInvalidImageUrl": "Epäkelvollinen kuvan URL-osoite",
|
||||||
|
"ToastInvalidMaxEpisodesToDownload": "Ladattavien jaksojen enimmäismäärä on epäkelvollinen",
|
||||||
|
"ToastInvalidUrl": "Epäkelvollinen URL-osoite",
|
||||||
"ToastItemCoverUpdateSuccess": "Kohteen kansikuva päivitetty",
|
"ToastItemCoverUpdateSuccess": "Kohteen kansikuva päivitetty",
|
||||||
|
"ToastItemDeletedFailed": "Kohteen poistaminen epäonnistui",
|
||||||
|
"ToastItemDeletedSuccess": "Poistettu kohde",
|
||||||
|
"ToastItemDetailsUpdateSuccess": "Tuotteen yksityiskohdat päivitetty",
|
||||||
"ToastItemMarkedAsFinishedFailed": "Valmiiksi merkitseminen epäonnistui",
|
"ToastItemMarkedAsFinishedFailed": "Valmiiksi merkitseminen epäonnistui",
|
||||||
|
"ToastItemMarkedAsFinishedSuccess": "Kohde merkitty Päättyneeksi",
|
||||||
"ToastItemMarkedAsNotFinishedFailed": "Valmiiksi merkitsemisen poisto epäonnistui",
|
"ToastItemMarkedAsNotFinishedFailed": "Valmiiksi merkitsemisen poisto epäonnistui",
|
||||||
|
"ToastItemMarkedAsNotFinishedSuccess": "Kohde merkitty Ei-päättyneeksi",
|
||||||
"ToastItemUpdateSuccess": "Kohde päivitetty",
|
"ToastItemUpdateSuccess": "Kohde päivitetty",
|
||||||
"ToastLibraryCreateFailed": "Kirjaston luominen epäonnistui",
|
"ToastLibraryCreateFailed": "Kirjaston luominen epäonnistui",
|
||||||
"ToastLibraryCreateSuccess": "Kirjasto \"{0}\" luotu",
|
"ToastLibraryCreateSuccess": "Kirjasto \"{0}\" luotu",
|
||||||
"ToastLibraryDeleteFailed": "Kirjaston poistaminen epäonnistui",
|
"ToastLibraryDeleteFailed": "Kirjaston poistaminen epäonnistui",
|
||||||
"ToastLibraryDeleteSuccess": "Kirjasto poistettu",
|
"ToastLibraryDeleteSuccess": "Kirjasto poistettu",
|
||||||
|
"ToastLibraryScanFailedToStart": "Skannauksen käynnistäminen epäonnistui",
|
||||||
|
"ToastLibraryScanStarted": "Kirjaston skannaus käynnistetty",
|
||||||
"ToastLibraryUpdateSuccess": "Kirjasto \"{0}\" päivitetty",
|
"ToastLibraryUpdateSuccess": "Kirjasto \"{0}\" päivitetty",
|
||||||
|
"ToastMatchAllAuthorsFailed": "Kaikkia tekijöitä ei voitu vaastattaa",
|
||||||
|
"ToastMetadataFilesRemovedError": "Virhe poistettaessa metadata.{0}-tiedostot",
|
||||||
|
"ToastMetadataFilesRemovedNoneFound": "metadata.{0}-tiedostoja ei löytynyt kirjastosta",
|
||||||
|
"ToastMetadataFilesRemovedNoneRemoved": "Ei metadata.{0}-tiedostoja poistettu",
|
||||||
|
"ToastMetadataFilesRemovedSuccess": "{0} metadata.{1}-tiedostoa poistettu",
|
||||||
|
"ToastMustHaveAtLeastOnePath": "On oltava vähintään yksi polku",
|
||||||
|
"ToastNameEmailRequired": "Nimi ja sähköpostiosoite vaaditaan",
|
||||||
|
"ToastNameRequired": "Nimi vaaditaan",
|
||||||
|
"ToastNewEpisodesFound": "{0} uutta jaksoa löydetty",
|
||||||
"ToastNewUserCreatedFailed": "Tilin \"{0}\" luominen epäonnistui",
|
"ToastNewUserCreatedFailed": "Tilin \"{0}\" luominen epäonnistui",
|
||||||
"ToastNewUserCreatedSuccess": "Uusi tili luotu",
|
"ToastNewUserCreatedSuccess": "Uusi tili luotu",
|
||||||
|
"ToastNewUserLibraryError": "On valittava vähintään yksi kirjasto",
|
||||||
|
"ToastNewUserPasswordError": "On oltava salasana; vain käyttäjällä root voi olla tyhjä salasana",
|
||||||
|
"ToastNewUserTagError": "On valittava vähintään yksi tunniste",
|
||||||
|
"ToastNewUserUsernameError": "Syötä käyttäjänimi",
|
||||||
|
"ToastNoNewEpisodesFound": "Uusia jaksoja ei löytynyt",
|
||||||
|
"ToastNoRSSFeed": "Podcastilla ei ole RSS-syötettä",
|
||||||
|
"ToastNoUpdatesNecessary": "Päivityksiä ei tarvita",
|
||||||
|
"ToastNotificationCreateFailed": "Ilmoituksen luominen epäonnistui",
|
||||||
|
"ToastNotificationDeleteFailed": "Ilmoituksen poistaminen epäonnistui",
|
||||||
|
"ToastNotificationFailedMaximum": "Epäonnistuneiden yritysten enimmäismäärän on oltava >= 0",
|
||||||
|
"ToastNotificationQueueMaximum": "Ilmoitusjonon enimmäismäärä on oltava >= 0",
|
||||||
|
"ToastNotificationSettingsUpdateSuccess": "Ilmoitusasetukset päivitetty",
|
||||||
|
"ToastNotificationTestTriggerFailed": "Testiilmoituksen laukaiseminen epäonnistui",
|
||||||
|
"ToastNotificationTestTriggerSuccess": "Laukaistiin testiilmoitus",
|
||||||
|
"ToastNotificationUpdateSuccess": "Ilmoitus päivitetty",
|
||||||
"ToastPlaylistCreateFailed": "Soittolistan luominen epäonnistui",
|
"ToastPlaylistCreateFailed": "Soittolistan luominen epäonnistui",
|
||||||
"ToastPlaylistCreateSuccess": "Soittolista luotu",
|
"ToastPlaylistCreateSuccess": "Soittolista luotu",
|
||||||
"ToastPlaylistRemoveSuccess": "Soittolista poistettu",
|
"ToastPlaylistRemoveSuccess": "Soittolista poistettu",
|
||||||
"ToastPlaylistUpdateSuccess": "Soittolista päivitetty",
|
"ToastPlaylistUpdateSuccess": "Soittolista päivitetty",
|
||||||
"ToastPodcastCreateFailed": "Podcastin luominen epäonnistui",
|
"ToastPodcastCreateFailed": "Podcastin luominen epäonnistui",
|
||||||
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui",
|
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui",
|
||||||
|
"ToastPodcastGetFeedFailed": "Podcast-syötteen saaminen epäonnistui",
|
||||||
|
"ToastPodcastNoEpisodesInFeed": "RSS-syötteestä ei löytynyt jaksoja",
|
||||||
|
"ToastPodcastNoRssFeed": "Podcastilla ei ole RSS-syötettä",
|
||||||
|
"ToastProgressIsNotBeingSynced": "Edistystä ei synkronoida, aloita toisto uudelleen",
|
||||||
|
"ToastProviderCreatedFailed": "Palveluntarjoajan lisääminen epäonnistui",
|
||||||
|
"ToastProviderCreatedSuccess": "Uusi palveluntarjoaja lisätty",
|
||||||
|
"ToastProviderNameAndUrlRequired": "Nimi ja URL-osoite vaaditaan",
|
||||||
|
"ToastProviderRemoveSuccess": "Palveluntarjoaja poistettu",
|
||||||
"ToastRSSFeedCloseFailed": "RSS syötteen sulkeminen epäonnistui",
|
"ToastRSSFeedCloseFailed": "RSS syötteen sulkeminen epäonnistui",
|
||||||
"ToastRSSFeedCloseSuccess": "RSS syöte suljettu",
|
"ToastRSSFeedCloseSuccess": "RSS syöte suljettu",
|
||||||
"ToastRemoveFailed": "Poistaminen epäonnistui",
|
"ToastRemoveFailed": "Poistaminen epäonnistui",
|
||||||
"ToastRemoveItemFromCollectionFailed": "Kohteen poistaminen kokoelmasta epäonnistui",
|
"ToastRemoveItemFromCollectionFailed": "Kohteen poistaminen kokoelmasta epäonnistui",
|
||||||
"ToastRemoveItemFromCollectionSuccess": "Kohde poistettu kokoelmasta",
|
"ToastRemoveItemFromCollectionSuccess": "Kohde poistettu kokoelmasta",
|
||||||
|
"ToastRemoveItemsWithIssuesFailed": "Vikoja sisältävien kirjastokohteiden poistaminen epäonnistui",
|
||||||
|
"ToastRemoveItemsWithIssuesSuccess": "Vikoja sisältäviä kirjastokohteita poistettu",
|
||||||
"ToastRenameFailed": "Uudelleennimeäminen epäonnistui",
|
"ToastRenameFailed": "Uudelleennimeäminen epäonnistui",
|
||||||
|
"ToastRescanFailed": "Uudelleenskannaus {0}n kohdalla epäonnistui",
|
||||||
|
"ToastRescanRemoved": "Uudelleenskannauksen täydellinen kohde poistettiin",
|
||||||
|
"ToastRescanUpToDate": "Uudelleenskannauksen täydellinen kohde oli ajan tasalla",
|
||||||
|
"ToastRescanUpdated": "Uudelleenskannauksen täydellinen kohde päivitettiin",
|
||||||
|
"ToastScanFailed": "Kirjastokohteen skannaaminen epäonnistui",
|
||||||
"ToastSelectAtLeastOneUser": "Valitse ainakin yksi käyttäjä",
|
"ToastSelectAtLeastOneUser": "Valitse ainakin yksi käyttäjä",
|
||||||
|
"ToastSendEbookToDeviceFailed": "S-kirjan lähettäminen laitteeseen epäonnistui",
|
||||||
|
"ToastSendEbookToDeviceSuccess": "S-kirja lähetetty laitteeseen \"{0}\"",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Ei voi lisätä kahta samannimistä sarjaa",
|
||||||
|
"ToastSeriesUpdateFailed": "Sarjan päivittäminen epäonnistui",
|
||||||
|
"ToastSeriesUpdateSuccess": "Sarjan päivittäminen onnistui",
|
||||||
"ToastServerSettingsUpdateSuccess": "Palvelimen asetukset päivitetty",
|
"ToastServerSettingsUpdateSuccess": "Palvelimen asetukset päivitetty",
|
||||||
"ToastSessionCloseFailed": "Istunnon sulkeminen epäonnistui",
|
"ToastSessionCloseFailed": "Istunnon sulkeminen epäonnistui",
|
||||||
"ToastSessionDeleteFailed": "Istunnon poistaminen epäonnistui",
|
"ToastSessionDeleteFailed": "Istunnon poistaminen epäonnistui",
|
||||||
"ToastSessionDeleteSuccess": "Istunto poistettu",
|
"ToastSessionDeleteSuccess": "Istunto poistettu",
|
||||||
|
"ToastSleepTimerDone": "Uniajastin tehty... zZzzZz",
|
||||||
|
"ToastSlugMustChange": "Slug sisältää epäkelvollisia merkkejä",
|
||||||
|
"ToastSlugRequired": "Slug vaaditaan",
|
||||||
"ToastSocketConnected": "Yhteys saatu",
|
"ToastSocketConnected": "Yhteys saatu",
|
||||||
"ToastSocketDisconnected": "Yhteys katkaistu",
|
"ToastSocketDisconnected": "Yhteys katkaistu",
|
||||||
"ToastSocketFailedToConnect": "Yhteyden muodostus epäonnistui",
|
"ToastSocketFailedToConnect": "Yhteyden muodostus epäonnistui",
|
||||||
|
"ToastSortingPrefixesEmptyError": "On oltava vähintään yksi lajitteluetuliite",
|
||||||
|
"ToastSortingPrefixesUpdateSuccess": "Lajitteluetuliitteet päivitetty ({0} kohdetta)",
|
||||||
"ToastTitleRequired": "Otsikko on pakollinen",
|
"ToastTitleRequired": "Otsikko on pakollinen",
|
||||||
"ToastUnknownError": "Tuntematon virhe",
|
"ToastUnknownError": "Tuntematon virhe",
|
||||||
|
"ToastUnlinkOpenIdFailed": "Käyttäjän linkityksen poistaminen OpenID:sta epäonnistui",
|
||||||
|
"ToastUnlinkOpenIdSuccess": "Käyttäjän linkitys poistettu OpenID:sta",
|
||||||
|
"ToastUploaderFilepathExistsError": "Tiedostopolku \"{0}\" on jo olemassa palvelimella",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Kohde {0} käyttää ulospäinlatauspolun alihakemistoa.",
|
||||||
"ToastUserDeleteFailed": "Käyttäjän poisto epäonnistui",
|
"ToastUserDeleteFailed": "Käyttäjän poisto epäonnistui",
|
||||||
"ToastUserDeleteSuccess": "Käyttäjä poistettu",
|
"ToastUserDeleteSuccess": "Käyttäjä poistettu",
|
||||||
"ToastUserPasswordChangeSuccess": "Salasana vaihdettu onnistuneesti",
|
"ToastUserPasswordChangeSuccess": "Salasana vaihdettu onnistuneesti",
|
||||||
|
|||||||
+14
-5
@@ -229,6 +229,7 @@
|
|||||||
"LabelAddedDate": "Ajouté le {0}",
|
"LabelAddedDate": "Ajouté le {0}",
|
||||||
"LabelAdminUsersOnly": "Administrateurs uniquement",
|
"LabelAdminUsersOnly": "Administrateurs uniquement",
|
||||||
"LabelAll": "Tout",
|
"LabelAll": "Tout",
|
||||||
|
"LabelAllEpisodesDownloaded": "Tous les épisodes ont été téléchargés",
|
||||||
"LabelAllUsers": "Tous les utilisateurs",
|
"LabelAllUsers": "Tous les utilisateurs",
|
||||||
"LabelAllUsersExcludingGuests": "Tous les utilisateurs à l’exception des invités",
|
"LabelAllUsersExcludingGuests": "Tous les utilisateurs à l’exception des invités",
|
||||||
"LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités",
|
"LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités",
|
||||||
@@ -252,7 +253,7 @@
|
|||||||
"LabelBackToUser": "Retour à l’utilisateur",
|
"LabelBackToUser": "Retour à l’utilisateur",
|
||||||
"LabelBackupAudioFiles": "Sauvegarder les fichiers audio",
|
"LabelBackupAudioFiles": "Sauvegarder les fichiers audio",
|
||||||
"LabelBackupLocation": "Emplacement de la sauvegarde",
|
"LabelBackupLocation": "Emplacement de la sauvegarde",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Activer les sauvegardes automatiques",
|
"LabelBackupsEnableAutomaticBackups": "Sauvegardes automatiques",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Sauvegardes enregistrées dans /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Sauvegardes enregistrées dans /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Taille maximale de la sauvegarde (en Go) (0 pour illimité)",
|
"LabelBackupsMaxBackupSize": "Taille maximale de la sauvegarde (en Go) (0 pour illimité)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Afin de prévenir les mauvaises configuration, la sauvegarde échouera si elle excède la taille limite.",
|
"LabelBackupsMaxBackupSizeHelp": "Afin de prévenir les mauvaises configuration, la sauvegarde échouera si elle excède la taille limite.",
|
||||||
@@ -279,7 +280,7 @@
|
|||||||
"LabelCollections": "Collections",
|
"LabelCollections": "Collections",
|
||||||
"LabelComplete": "Complet",
|
"LabelComplete": "Complet",
|
||||||
"LabelConfirmPassword": "Confirmer le mot de passe",
|
"LabelConfirmPassword": "Confirmer le mot de passe",
|
||||||
"LabelContinueListening": "Continuer la lecture",
|
"LabelContinueListening": "Continuer l'écoute",
|
||||||
"LabelContinueReading": "Continuer la lecture",
|
"LabelContinueReading": "Continuer la lecture",
|
||||||
"LabelContinueSeries": "Continuer les séries",
|
"LabelContinueSeries": "Continuer les séries",
|
||||||
"LabelCover": "Couverture",
|
"LabelCover": "Couverture",
|
||||||
@@ -302,7 +303,7 @@
|
|||||||
"LabelDiscFromFilename": "Depuis le fichier",
|
"LabelDiscFromFilename": "Depuis le fichier",
|
||||||
"LabelDiscFromMetadata": "Depuis les métadonnées",
|
"LabelDiscFromMetadata": "Depuis les métadonnées",
|
||||||
"LabelDiscover": "Découvrir",
|
"LabelDiscover": "Découvrir",
|
||||||
"LabelDownload": "Téléchargement",
|
"LabelDownload": "Télécharger",
|
||||||
"LabelDownloadNEpisodes": "Télécharger {0} épisode(s)",
|
"LabelDownloadNEpisodes": "Télécharger {0} épisode(s)",
|
||||||
"LabelDownloadable": "Téléchargeable",
|
"LabelDownloadable": "Téléchargeable",
|
||||||
"LabelDuration": "Durée",
|
"LabelDuration": "Durée",
|
||||||
@@ -558,6 +559,8 @@
|
|||||||
"LabelSettingsBookshelfViewHelp": "Interface skeumorphique avec étagères en bois",
|
"LabelSettingsBookshelfViewHelp": "Interface skeumorphique avec étagères en bois",
|
||||||
"LabelSettingsChromecastSupport": "Support du Chromecast",
|
"LabelSettingsChromecastSupport": "Support du Chromecast",
|
||||||
"LabelSettingsDateFormat": "Format de date",
|
"LabelSettingsDateFormat": "Format de date",
|
||||||
|
"LabelSettingsEnableWatcher": "Analyser automatiquement les bibliothèques pour détecter les modifications",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Analyser automatiquement la bibliothèque pour détecter les modifications",
|
||||||
"LabelSettingsEnableWatcherHelp": "Active la mise à jour automatique d'éléments lorsque des modifications de fichiers sont détectées. * Nécessite le redémarrage du serveur",
|
"LabelSettingsEnableWatcherHelp": "Active la mise à jour automatique d'éléments lorsque des modifications de fichiers sont détectées. * Nécessite le redémarrage du serveur",
|
||||||
"LabelSettingsEpubsAllowScriptedContent": "Autoriser le contenu scénarisé pour les fichiers EPUB",
|
"LabelSettingsEpubsAllowScriptedContent": "Autoriser le contenu scénarisé pour les fichiers EPUB",
|
||||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Autoriser les fichiers EPUB à exécuter des scripts. Il est recommandé de laisser ce paramètre désactivé, sauf si vous faites confiance à la source des fichiers EPUB.",
|
"LabelSettingsEpubsAllowScriptedContentHelp": "Autoriser les fichiers EPUB à exécuter des scripts. Il est recommandé de laisser ce paramètre désactivé, sauf si vous faites confiance à la source des fichiers EPUB.",
|
||||||
@@ -575,7 +578,7 @@
|
|||||||
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Sauter les livres précédents dans « Continuer la série »",
|
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Sauter les livres précédents dans « Continuer la série »",
|
||||||
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "L’étagère de la page d’accueil « Continuer la série » affiche le premier livre non commencé dans les séries dont au moins un livre est terminé et aucun livre n’est en cours. L’activation de ce paramètre permet de poursuivre la série à partir du dernier livre terminé au lieu du premier livre non commencé.",
|
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "L’étagère de la page d’accueil « Continuer la série » affiche le premier livre non commencé dans les séries dont au moins un livre est terminé et aucun livre n’est en cours. L’activation de ce paramètre permet de poursuivre la série à partir du dernier livre terminé au lieu du premier livre non commencé.",
|
||||||
"LabelSettingsParseSubtitles": "Analyser les sous-titres",
|
"LabelSettingsParseSubtitles": "Analyser les sous-titres",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extrait les sous-titres depuis le dossier du livre audio.<br>Les sous-titres doivent être séparés par des « - »<br>c’est-à-dire : « Titre du livre - Ceci est un sous-titre » aura le sous-titre « Ceci est un sous-titre »",
|
"LabelSettingsParseSubtitlesHelp": "Extraire les sous-titres des noms de dossiers de livres audio.<br>Les sous-titres doivent être séparés par « - »<br>Par exemple, « Titre du livre - Un sous-titre » a pour sous-titre « Un sous-titre »",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Préférer les métadonnées par correspondance",
|
"LabelSettingsPreferMatchedMetadata": "Préférer les métadonnées par correspondance",
|
||||||
"LabelSettingsPreferMatchedMetadataHelp": "Les métadonnées mises en correspondance remplaceront les détails de l’élément lors de l’utilisation de la correspondance rapide. Par défaut, la correspondance rapide ne remplira que les détails manquants.",
|
"LabelSettingsPreferMatchedMetadataHelp": "Les métadonnées mises en correspondance remplaceront les détails de l’élément lors de l’utilisation de la correspondance rapide. Par défaut, la correspondance rapide ne remplira que les détails manquants.",
|
||||||
"LabelSettingsSkipMatchingBooksWithASIN": "Ignorer la recherche par correspondance pour les livres ayant déjà un ASIN",
|
"LabelSettingsSkipMatchingBooksWithASIN": "Ignorer la recherche par correspondance pour les livres ayant déjà un ASIN",
|
||||||
@@ -601,6 +604,7 @@
|
|||||||
"LabelSlug": "Identifiant d’URL",
|
"LabelSlug": "Identifiant d’URL",
|
||||||
"LabelSortAscending": "Croissant",
|
"LabelSortAscending": "Croissant",
|
||||||
"LabelSortDescending": "Décroissant",
|
"LabelSortDescending": "Décroissant",
|
||||||
|
"LabelSortPubDate": "Trier par date de publication",
|
||||||
"LabelStart": "Démarrer",
|
"LabelStart": "Démarrer",
|
||||||
"LabelStartTime": "Heure de démarrage",
|
"LabelStartTime": "Heure de démarrage",
|
||||||
"LabelStarted": "Démarré",
|
"LabelStarted": "Démarré",
|
||||||
@@ -706,6 +710,7 @@
|
|||||||
"MessageBackupsLocationNoEditNote": "Remarque : l’emplacement de sauvegarde est défini via une variable d’environnement et ne peut pas être modifié ici.",
|
"MessageBackupsLocationNoEditNote": "Remarque : l’emplacement de sauvegarde est défini via une variable d’environnement et ne peut pas être modifié ici.",
|
||||||
"MessageBackupsLocationPathEmpty": "L'emplacement de secours ne peut pas être vide",
|
"MessageBackupsLocationPathEmpty": "L'emplacement de secours ne peut pas être vide",
|
||||||
"MessageBatchEditPopulateMapDetailsAllHelp": "Remplir les champs disponibles avec les données de tous les éléments. Les champs avec des valeurs multiples seront fusionnés.",
|
"MessageBatchEditPopulateMapDetailsAllHelp": "Remplir les champs disponibles avec les données de tous les éléments. Les champs avec des valeurs multiples seront fusionnés.",
|
||||||
|
"MessageBatchEditPopulateMapDetailsItemHelp": "Renseigner les champs de la carte active avec les informations de cet élément",
|
||||||
"MessageBatchQuickMatchDescription": "La recherche par correspondance rapide tentera d’ajouter les couvertures et métadonnées manquantes pour les éléments sélectionnés. Activez les options ci-dessous pour permettre la Recherche par correspondance d’écraser les couvertures et/ou métadonnées existantes.",
|
"MessageBatchQuickMatchDescription": "La recherche par correspondance rapide tentera d’ajouter les couvertures et métadonnées manquantes pour les éléments sélectionnés. Activez les options ci-dessous pour permettre la Recherche par correspondance d’écraser les couvertures et/ou métadonnées existantes.",
|
||||||
"MessageBookshelfNoCollections": "Vous n’avez pas encore de collections",
|
"MessageBookshelfNoCollections": "Vous n’avez pas encore de collections",
|
||||||
"MessageBookshelfNoCollectionsHelp": "Les collections sont publiques. Tous les utilisateurs ayant accès à la bibliothèque pourront les voir.",
|
"MessageBookshelfNoCollectionsHelp": "Les collections sont publiques. Tous les utilisateurs ayant accès à la bibliothèque pourront les voir.",
|
||||||
@@ -819,7 +824,7 @@
|
|||||||
"MessageNoTasksRunning": "Aucune tâche en cours",
|
"MessageNoTasksRunning": "Aucune tâche en cours",
|
||||||
"MessageNoUpdatesWereNecessary": "Aucune mise à jour n’était nécessaire",
|
"MessageNoUpdatesWereNecessary": "Aucune mise à jour n’était nécessaire",
|
||||||
"MessageNoUserPlaylists": "Vous n’avez aucune liste de lecture",
|
"MessageNoUserPlaylists": "Vous n’avez aucune liste de lecture",
|
||||||
"MessageNoUserPlaylistsHelp": "Les playlists sont privées. Seul l'utilisateur qui les a créées peut les voir.",
|
"MessageNoUserPlaylistsHelp": "Les playlists sont privées. Seul l’utilisateur qui les crée peut les voir.",
|
||||||
"MessageNotYetImplemented": "Non implémenté",
|
"MessageNotYetImplemented": "Non implémenté",
|
||||||
"MessageOpmlPreviewNote": "Remarque : Il s’agit d’un aperçu du fichier OPML analysé. Le titre réel du podcast provient du flux RSS.",
|
"MessageOpmlPreviewNote": "Remarque : Il s’agit d’un aperçu du fichier OPML analysé. Le titre réel du podcast provient du flux RSS.",
|
||||||
"MessageOr": "ou",
|
"MessageOr": "ou",
|
||||||
@@ -950,6 +955,7 @@
|
|||||||
"ToastBackupRestoreFailed": "Échec de la restauration de sauvegarde",
|
"ToastBackupRestoreFailed": "Échec de la restauration de sauvegarde",
|
||||||
"ToastBackupUploadFailed": "Échec du téléversement de sauvegarde",
|
"ToastBackupUploadFailed": "Échec du téléversement de sauvegarde",
|
||||||
"ToastBackupUploadSuccess": "Sauvegarde téléversée",
|
"ToastBackupUploadSuccess": "Sauvegarde téléversée",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Détails appliqués aux articles",
|
||||||
"ToastBatchDeleteFailed": "Échec de la suppression par lot",
|
"ToastBatchDeleteFailed": "Échec de la suppression par lot",
|
||||||
"ToastBatchDeleteSuccess": "Suppression par lot réussie",
|
"ToastBatchDeleteSuccess": "Suppression par lot réussie",
|
||||||
"ToastBatchQuickMatchFailed": "Échec de la correspondance rapide par lot !",
|
"ToastBatchQuickMatchFailed": "Échec de la correspondance rapide par lot !",
|
||||||
@@ -1062,6 +1068,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Sélectionnez au moins un utilisateur",
|
"ToastSelectAtLeastOneUser": "Sélectionnez au moins un utilisateur",
|
||||||
"ToastSendEbookToDeviceFailed": "Échec de l’envoi du livre numérique à l’appareil",
|
"ToastSendEbookToDeviceFailed": "Échec de l’envoi du livre numérique à l’appareil",
|
||||||
"ToastSendEbookToDeviceSuccess": "Livre numérique envoyé à l’appareil : {0}",
|
"ToastSendEbookToDeviceSuccess": "Livre numérique envoyé à l’appareil : {0}",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Impossible d’ajouter deux séries ayant le même nom",
|
||||||
"ToastSeriesUpdateFailed": "Échec de la mise à jour de la série",
|
"ToastSeriesUpdateFailed": "Échec de la mise à jour de la série",
|
||||||
"ToastSeriesUpdateSuccess": "Mise à jour de la série réussie",
|
"ToastSeriesUpdateSuccess": "Mise à jour de la série réussie",
|
||||||
"ToastServerSettingsUpdateSuccess": "Mise à jour des paramètres du serveur",
|
"ToastServerSettingsUpdateSuccess": "Mise à jour des paramètres du serveur",
|
||||||
@@ -1080,6 +1087,8 @@
|
|||||||
"ToastUnknownError": "Erreur inconnue",
|
"ToastUnknownError": "Erreur inconnue",
|
||||||
"ToastUnlinkOpenIdFailed": "Échec de la dissociation de l’utilisateur l’OpenID",
|
"ToastUnlinkOpenIdFailed": "Échec de la dissociation de l’utilisateur l’OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Utilisateur dissocié de OpenID",
|
"ToastUnlinkOpenIdSuccess": "Utilisateur dissocié de OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Le chemin de fichier « {0} » existe déjà sur le serveur",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "L’élément « {0} » utilise un sous-répertoire du chemin de téléchargement.",
|
||||||
"ToastUserDeleteFailed": "Échec de la suppression de l’utilisateur",
|
"ToastUserDeleteFailed": "Échec de la suppression de l’utilisateur",
|
||||||
"ToastUserDeleteSuccess": "Utilisateur supprimé",
|
"ToastUserDeleteSuccess": "Utilisateur supprimé",
|
||||||
"ToastUserPasswordChangeSuccess": "Mot de passe modifié avec succès",
|
"ToastUserPasswordChangeSuccess": "Mot de passe modifié avec succès",
|
||||||
|
|||||||
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Popis za izvođenje",
|
"HeaderPlaylist": "Popis za izvođenje",
|
||||||
"HeaderPlaylistItems": "Stavke popisa za izvođenje",
|
"HeaderPlaylistItems": "Stavke popisa za izvođenje",
|
||||||
"HeaderPodcastsToAdd": "Podcasti za dodavanje",
|
"HeaderPodcastsToAdd": "Podcasti za dodavanje",
|
||||||
|
"HeaderPresets": "Predlošci postavki",
|
||||||
"HeaderPreviewCover": "Pretpregled naslovnice",
|
"HeaderPreviewCover": "Pretpregled naslovnice",
|
||||||
"HeaderRSSFeedGeneral": "RSS pojedinosti",
|
"HeaderRSSFeedGeneral": "RSS pojedinosti",
|
||||||
"HeaderRSSFeedIsOpen": "RSS izvor je otvoren",
|
"HeaderRSSFeedIsOpen": "RSS izvor je otvoren",
|
||||||
@@ -530,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Datum izlaska",
|
"LabelReleaseDate": "Datum izlaska",
|
||||||
"LabelRemoveAllMetadataAbs": "Ukloni sve datoteke metadata.abs",
|
"LabelRemoveAllMetadataAbs": "Ukloni sve datoteke metadata.abs",
|
||||||
"LabelRemoveAllMetadataJson": "Ukloni sve datoteke metadata.json",
|
"LabelRemoveAllMetadataJson": "Ukloni sve datoteke metadata.json",
|
||||||
|
"LabelRemoveAudibleBranding": "Ukloni Audibleove najave i odjave iz poglavlja",
|
||||||
"LabelRemoveCover": "Ukloni naslovnicu",
|
"LabelRemoveCover": "Ukloni naslovnicu",
|
||||||
"LabelRemoveMetadataFile": "Ukloni datoteke s meta-podatcima iz mapa knjižničkih stavki",
|
"LabelRemoveMetadataFile": "Ukloni datoteke s meta-podatcima iz mapa knjižničkih stavki",
|
||||||
"LabelRemoveMetadataFileHelp": "Uklanjanje svih datoteka metadata.json i metadata.abs u vaših {0} mapa.",
|
"LabelRemoveMetadataFileHelp": "Uklanjanje svih datoteka metadata.json i metadata.abs u vaših {0} mapa.",
|
||||||
@@ -604,6 +606,7 @@
|
|||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "Uzlazno",
|
"LabelSortAscending": "Uzlazno",
|
||||||
"LabelSortDescending": "Silazno",
|
"LabelSortDescending": "Silazno",
|
||||||
|
"LabelSortPubDate": "Sortiranje po datumu objave",
|
||||||
"LabelStart": "Početak",
|
"LabelStart": "Početak",
|
||||||
"LabelStartTime": "Vrijeme početka",
|
"LabelStartTime": "Vrijeme početka",
|
||||||
"LabelStarted": "Započeto",
|
"LabelStarted": "Započeto",
|
||||||
@@ -704,6 +707,8 @@
|
|||||||
"LabelYourProgress": "Vaš napredak",
|
"LabelYourProgress": "Vaš napredak",
|
||||||
"MessageAddToPlayerQueue": "Dodaj u redoslijed izvođenja",
|
"MessageAddToPlayerQueue": "Dodaj u redoslijed izvođenja",
|
||||||
"MessageAppriseDescription": "Da biste se koristili ovom značajkom, treba vam instanca <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API-ja</a> ili API koji može rukovati istom vrstom zahtjeva.<br />The Adresa Apprise API-ja treba biti puna URL putanja za slanje obavijesti, npr. ako vam se API instanca poslužuje na adresi <code>http://192.168.1.1:8337</code> trebate upisati <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Da biste se koristili ovom značajkom, treba vam instanca <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API-ja</a> ili API koji može rukovati istom vrstom zahtjeva.<br />The Adresa Apprise API-ja treba biti puna URL putanja za slanje obavijesti, npr. ako vam se API instanca poslužuje na adresi <code>http://192.168.1.1:8337</code> trebate upisati <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Upišite ASIN iz odgovarajuće Audibleove regije, ne s Amazonov.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Ponovno pokrenite poslužitelj da biste primijenili OIDC promjene.",
|
||||||
"MessageBackupsDescription": "Sigurnosne kopije sadrže korisnike, korisnikov napredak medija, pojedinosti knjižničke građe, postavke poslužitelja i slike koje se spremaju u <code>/metadata/items</code> & <code>/metadata/authors</code>. Sigurnosne kopije ne sadrže niti jednu datoteku iz mapa knjižnice.",
|
"MessageBackupsDescription": "Sigurnosne kopije sadrže korisnike, korisnikov napredak medija, pojedinosti knjižničke građe, postavke poslužitelja i slike koje se spremaju u <code>/metadata/items</code> & <code>/metadata/authors</code>. Sigurnosne kopije ne sadrže niti jednu datoteku iz mapa knjižnice.",
|
||||||
"MessageBackupsLocationEditNote": "Napomena: Uređivanje lokacije za sigurnosne kopije ne premješta ili mijenja postojeće sigurnosne kopije",
|
"MessageBackupsLocationEditNote": "Napomena: Uređivanje lokacije za sigurnosne kopije ne premješta ili mijenja postojeće sigurnosne kopije",
|
||||||
"MessageBackupsLocationNoEditNote": "Napomena: Lokacija za sigurnosne kopije zadana je kroz varijablu okoline i ovdje se ne može izmijeniti.",
|
"MessageBackupsLocationNoEditNote": "Napomena: Lokacija za sigurnosne kopije zadana je kroz varijablu okoline i ovdje se ne može izmijeniti.",
|
||||||
@@ -722,6 +727,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Netočno vrijeme početka, mora biti manje od trajanja zvučne knjige",
|
"MessageChapterErrorStartGteDuration": "Netočno vrijeme početka, mora biti manje od trajanja zvučne knjige",
|
||||||
"MessageChapterErrorStartLtPrev": "Netočno vrijeme početka, mora biti veće ili jednako vremenu početka prethodnog poglavlja",
|
"MessageChapterErrorStartLtPrev": "Netočno vrijeme početka, mora biti veće ili jednako vremenu početka prethodnog poglavlja",
|
||||||
"MessageChapterStartIsAfter": "Početak poglavlja je nakon kraja zvučne knjige",
|
"MessageChapterStartIsAfter": "Početak poglavlja je nakon kraja zvučne knjige",
|
||||||
|
"MessageChaptersNotFound": "Poglavlja nisu pronađena",
|
||||||
"MessageCheckingCron": "Provjeravam cron...",
|
"MessageCheckingCron": "Provjeravam cron...",
|
||||||
"MessageConfirmCloseFeed": "Sigurno želite zatvoriti ovaj izvor?",
|
"MessageConfirmCloseFeed": "Sigurno želite zatvoriti ovaj izvor?",
|
||||||
"MessageConfirmDeleteBackup": "Sigurno želite izbrisati sigurnosnu kopiju za {0}?",
|
"MessageConfirmDeleteBackup": "Sigurno želite izbrisati sigurnosnu kopiju za {0}?",
|
||||||
@@ -778,6 +784,7 @@
|
|||||||
"MessageForceReScanDescription": "će ponovno skenirati sve datoteke kao nove datoteke. ID3 tagovi zvučnih datoteka, OPF datoteke i tekstualne datoteke skenirat će se kao da su nove.",
|
"MessageForceReScanDescription": "će ponovno skenirati sve datoteke kao nove datoteke. ID3 tagovi zvučnih datoteka, OPF datoteke i tekstualne datoteke skenirat će se kao da su nove.",
|
||||||
"MessageImportantNotice": "Važna obavijest!",
|
"MessageImportantNotice": "Važna obavijest!",
|
||||||
"MessageInsertChapterBelow": "Unesi poglavlje ispod",
|
"MessageInsertChapterBelow": "Unesi poglavlje ispod",
|
||||||
|
"MessageInvalidAsin": "Nevažeći ASIN",
|
||||||
"MessageItemsSelected": "{0} odabranih stavki",
|
"MessageItemsSelected": "{0} odabranih stavki",
|
||||||
"MessageItemsUpdated": "{0} stavki ažurirano",
|
"MessageItemsUpdated": "{0} stavki ažurirano",
|
||||||
"MessageJoinUsOn": "Pridruži nam se na",
|
"MessageJoinUsOn": "Pridruži nam se na",
|
||||||
@@ -967,6 +974,8 @@
|
|||||||
"ToastCachePurgeFailed": "Čišćenje predmemorije nije uspjelo",
|
"ToastCachePurgeFailed": "Čišćenje predmemorije nije uspjelo",
|
||||||
"ToastCachePurgeSuccess": "Predmemorija uspješno očišćena",
|
"ToastCachePurgeSuccess": "Predmemorija uspješno očišćena",
|
||||||
"ToastChaptersHaveErrors": "Poglavlja imaju pogreške",
|
"ToastChaptersHaveErrors": "Poglavlja imaju pogreške",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "Neispravna vrijednost pomaka. Početak zadnjeg poglavlja bio bi nakon duljine trajanja ove zvučne knjige.",
|
||||||
|
"ToastChaptersInvalidShiftAmountStart": "Neispravna vrijednost pomaka. Trajanje prvog poglavlja bilo bi nula ili negativno i drugo poglavlje bi ga prepisalo. Povećajte vrijeme početka drugog poglavlja.",
|
||||||
"ToastChaptersMustHaveTitles": "Poglavlja moraju imati naslove",
|
"ToastChaptersMustHaveTitles": "Poglavlja moraju imati naslove",
|
||||||
"ToastChaptersRemoved": "Poglavlja uklonjena",
|
"ToastChaptersRemoved": "Poglavlja uklonjena",
|
||||||
"ToastChaptersUpdated": "Poglavlja su ažurirana",
|
"ToastChaptersUpdated": "Poglavlja su ažurirana",
|
||||||
@@ -1086,6 +1095,8 @@
|
|||||||
"ToastUnknownError": "Nepoznata pogreška",
|
"ToastUnknownError": "Nepoznata pogreška",
|
||||||
"ToastUnlinkOpenIdFailed": "Uklanjanje OpenID veze korisnika nije uspjelo",
|
"ToastUnlinkOpenIdFailed": "Uklanjanje OpenID veze korisnika nije uspjelo",
|
||||||
"ToastUnlinkOpenIdSuccess": "Korisnik odspojen od OpenID-ja",
|
"ToastUnlinkOpenIdSuccess": "Korisnik odspojen od OpenID-ja",
|
||||||
|
"ToastUploaderFilepathExistsError": "Putanja \"{0}\" već postoji na poslužitelju",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Stavka \"{0}\" koristi se podmapom u putanje za učitavanje.",
|
||||||
"ToastUserDeleteFailed": "Brisanje korisnika nije uspjelo",
|
"ToastUserDeleteFailed": "Brisanje korisnika nije uspjelo",
|
||||||
"ToastUserDeleteSuccess": "Korisnik izbrisan",
|
"ToastUserDeleteSuccess": "Korisnik izbrisan",
|
||||||
"ToastUserPasswordChangeSuccess": "Zaporka je uspješno promijenjena",
|
"ToastUserPasswordChangeSuccess": "Zaporka je uspješno promijenjena",
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
"ButtonApplyChapters": "Fejezetek alkalmazása",
|
"ButtonApplyChapters": "Fejezetek alkalmazása",
|
||||||
"ButtonAuthors": "Szerzők",
|
"ButtonAuthors": "Szerzők",
|
||||||
"ButtonBack": "Vissza",
|
"ButtonBack": "Vissza",
|
||||||
|
"ButtonBatchEditPopulateFromExisting": "Létezőből feltöltés",
|
||||||
|
"ButtonBatchEditPopulateMapDetails": "",
|
||||||
"ButtonBrowseForFolder": "Mappa keresése",
|
"ButtonBrowseForFolder": "Mappa keresése",
|
||||||
"ButtonCancel": "Mégse",
|
"ButtonCancel": "Mégse",
|
||||||
"ButtonCancelEncode": "Kódolás megszakítása",
|
"ButtonCancelEncode": "Kódolás megszakítása",
|
||||||
@@ -432,7 +434,7 @@
|
|||||||
"LabelMetadataProvider": "Metaadat-szolgáltató",
|
"LabelMetadataProvider": "Metaadat-szolgáltató",
|
||||||
"LabelMinute": "Perc",
|
"LabelMinute": "Perc",
|
||||||
"LabelMinutes": "Perc",
|
"LabelMinutes": "Perc",
|
||||||
"LabelMissing": "Hiányzó",
|
"LabelMissing": "Hiányzik",
|
||||||
"LabelMissingEbook": "Nincs e-könyve",
|
"LabelMissingEbook": "Nincs e-könyve",
|
||||||
"LabelMissingSupplementaryEbook": "Nincs kiegészítő e-könyve",
|
"LabelMissingSupplementaryEbook": "Nincs kiegészítő e-könyve",
|
||||||
"LabelMobileRedirectURIs": "Engedélyezett mobil átirányítási URI-k",
|
"LabelMobileRedirectURIs": "Engedélyezett mobil átirányítási URI-k",
|
||||||
@@ -604,7 +606,7 @@
|
|||||||
"LabelStatsBestDay": "Legjobb nap",
|
"LabelStatsBestDay": "Legjobb nap",
|
||||||
"LabelStatsDailyAverage": "Napi átlag",
|
"LabelStatsDailyAverage": "Napi átlag",
|
||||||
"LabelStatsDays": "Napok",
|
"LabelStatsDays": "Napok",
|
||||||
"LabelStatsDaysListened": "Napon hallgatva",
|
"LabelStatsDaysListened": "Hallgatással töltött napok",
|
||||||
"LabelStatsHours": "Órák",
|
"LabelStatsHours": "Órák",
|
||||||
"LabelStatsInARow": "egymás után",
|
"LabelStatsInARow": "egymás után",
|
||||||
"LabelStatsItemsFinished": "Befejezett elem",
|
"LabelStatsItemsFinished": "Befejezett elem",
|
||||||
|
|||||||
+25
-9
@@ -13,7 +13,7 @@
|
|||||||
"ButtonBatchEditPopulateFromExisting": "Popola da esistente",
|
"ButtonBatchEditPopulateFromExisting": "Popola da esistente",
|
||||||
"ButtonBatchEditPopulateMapDetails": "Inserisci i dettagli della mappa",
|
"ButtonBatchEditPopulateMapDetails": "Inserisci i dettagli della mappa",
|
||||||
"ButtonBrowseForFolder": "Per Cartella",
|
"ButtonBrowseForFolder": "Per Cartella",
|
||||||
"ButtonCancel": "Cancella",
|
"ButtonCancel": "Annulla",
|
||||||
"ButtonCancelEncode": "Ferma la codifica",
|
"ButtonCancelEncode": "Ferma la codifica",
|
||||||
"ButtonChangeRootPassword": "Cambia la Password di root",
|
"ButtonChangeRootPassword": "Cambia la Password di root",
|
||||||
"ButtonCheckAndDownloadNewEpisodes": "Controlla & scarica i nuovi episodi",
|
"ButtonCheckAndDownloadNewEpisodes": "Controlla & scarica i nuovi episodi",
|
||||||
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Playlist",
|
"HeaderPlaylist": "Playlist",
|
||||||
"HeaderPlaylistItems": "Elementi della playlist",
|
"HeaderPlaylistItems": "Elementi della playlist",
|
||||||
"HeaderPodcastsToAdd": "Podcasts da Aggiungere",
|
"HeaderPodcastsToAdd": "Podcasts da Aggiungere",
|
||||||
|
"HeaderPresets": "Presets",
|
||||||
"HeaderPreviewCover": "Anteprima Cover",
|
"HeaderPreviewCover": "Anteprima Cover",
|
||||||
"HeaderRSSFeedGeneral": "Dettagli RSS",
|
"HeaderRSSFeedGeneral": "Dettagli RSS",
|
||||||
"HeaderRSSFeedIsOpen": "RSS Feed è aperto",
|
"HeaderRSSFeedIsOpen": "RSS Feed è aperto",
|
||||||
@@ -229,6 +230,7 @@
|
|||||||
"LabelAddedDate": "Aggiunti {0}",
|
"LabelAddedDate": "Aggiunti {0}",
|
||||||
"LabelAdminUsersOnly": "Solo utenti Amministratori",
|
"LabelAdminUsersOnly": "Solo utenti Amministratori",
|
||||||
"LabelAll": "Tutti",
|
"LabelAll": "Tutti",
|
||||||
|
"LabelAllEpisodesDownloaded": "Tutti gli Episodi Scaricati",
|
||||||
"LabelAllUsers": "Tutti gli Utenti",
|
"LabelAllUsers": "Tutti gli Utenti",
|
||||||
"LabelAllUsersExcludingGuests": "Tutti gli Utenti Esclusi gli ospiti",
|
"LabelAllUsersExcludingGuests": "Tutti gli Utenti Esclusi gli ospiti",
|
||||||
"LabelAllUsersIncludingGuests": "Tutti gli Utenti Inclusi gli ospiti",
|
"LabelAllUsersIncludingGuests": "Tutti gli Utenti Inclusi gli ospiti",
|
||||||
@@ -252,7 +254,7 @@
|
|||||||
"LabelBackToUser": "Torna a Utenti",
|
"LabelBackToUser": "Torna a Utenti",
|
||||||
"LabelBackupAudioFiles": "Backup file Audio",
|
"LabelBackupAudioFiles": "Backup file Audio",
|
||||||
"LabelBackupLocation": "Percorso del Backup",
|
"LabelBackupLocation": "Percorso del Backup",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Abilita backup Automatico",
|
"LabelBackupsEnableAutomaticBackups": "Backup Automatico",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "I Backup saranno salvati in /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "I Backup saranno salvati in /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Dimensione massima backup (in GB) (0 Illimitato)",
|
"LabelBackupsMaxBackupSize": "Dimensione massima backup (in GB) (0 Illimitato)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Come protezione contro gli errori di config, i backup falliranno se superano la dimensione configurata.",
|
"LabelBackupsMaxBackupSizeHelp": "Come protezione contro gli errori di config, i backup falliranno se superano la dimensione configurata.",
|
||||||
@@ -279,12 +281,12 @@
|
|||||||
"LabelCollections": "Raccolte",
|
"LabelCollections": "Raccolte",
|
||||||
"LabelComplete": "Completo",
|
"LabelComplete": "Completo",
|
||||||
"LabelConfirmPassword": "Conferma Password",
|
"LabelConfirmPassword": "Conferma Password",
|
||||||
"LabelContinueListening": "Continua ad Ascoltare",
|
"LabelContinueListening": "Continua l'ascolto",
|
||||||
"LabelContinueReading": "Continua la Lettura",
|
"LabelContinueReading": "Continua la lettura",
|
||||||
"LabelContinueSeries": "Continua serie",
|
"LabelContinueSeries": "Continua serie",
|
||||||
"LabelCover": "Copertina",
|
"LabelCover": "Copertina",
|
||||||
"LabelCoverImageURL": "Indirizzo della cover URL",
|
"LabelCoverImageURL": "Indirizzo della cover URL",
|
||||||
"LabelCoverProvider": "Cover Provider",
|
"LabelCoverProvider": "Cover Sorgente",
|
||||||
"LabelCreatedAt": "Creato A",
|
"LabelCreatedAt": "Creato A",
|
||||||
"LabelCronExpression": "Espressione Cron",
|
"LabelCronExpression": "Espressione Cron",
|
||||||
"LabelCurrent": "Attuale",
|
"LabelCurrent": "Attuale",
|
||||||
@@ -529,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Data Release",
|
"LabelReleaseDate": "Data Release",
|
||||||
"LabelRemoveAllMetadataAbs": "Remuovi tutti i metadata.abs files",
|
"LabelRemoveAllMetadataAbs": "Remuovi tutti i metadata.abs files",
|
||||||
"LabelRemoveAllMetadataJson": "Rimuovi tutti i metadata.json files",
|
"LabelRemoveAllMetadataJson": "Rimuovi tutti i metadata.json files",
|
||||||
|
"LabelRemoveAudibleBranding": "Rimuovi l'intro e il termine Audible dai capitoli",
|
||||||
"LabelRemoveCover": "Rimuovi cover",
|
"LabelRemoveCover": "Rimuovi cover",
|
||||||
"LabelRemoveMetadataFile": "Rimuovi i file metadata nella cartella della libreria",
|
"LabelRemoveMetadataFile": "Rimuovi i file metadata nella cartella della libreria",
|
||||||
"LabelRemoveMetadataFileHelp": "Rimuovi tutti i file metadata.json e i file metadata.abs nelle tue {0} cartelle.",
|
"LabelRemoveMetadataFileHelp": "Rimuovi tutti i file metadata.json e i file metadata.abs nelle tue {0} cartelle.",
|
||||||
@@ -558,6 +561,8 @@
|
|||||||
"LabelSettingsBookshelfViewHelp": "Design con scaffali in legno",
|
"LabelSettingsBookshelfViewHelp": "Design con scaffali in legno",
|
||||||
"LabelSettingsChromecastSupport": "Supporto a Chromecast",
|
"LabelSettingsChromecastSupport": "Supporto a Chromecast",
|
||||||
"LabelSettingsDateFormat": "Formato Data",
|
"LabelSettingsDateFormat": "Formato Data",
|
||||||
|
"LabelSettingsEnableWatcher": "Scansiona le librerie Automaticamente per trovare modifiche",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Scansiona la libreria Automaticamente per trovare modifiche",
|
||||||
"LabelSettingsEnableWatcherHelp": "Abilita l'aggiunta/aggiornamento automatico degli elementi quando vengono rilevate modifiche ai file. *Richiede il riavvio del Server",
|
"LabelSettingsEnableWatcherHelp": "Abilita l'aggiunta/aggiornamento automatico degli elementi quando vengono rilevate modifiche ai file. *Richiede il riavvio del Server",
|
||||||
"LabelSettingsEpubsAllowScriptedContent": "Consenti contenuti con script negli epub",
|
"LabelSettingsEpubsAllowScriptedContent": "Consenti contenuti con script negli epub",
|
||||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Consenti ai file epub di eseguire script. Si consiglia di mantenere questa impostazione disabilitata a meno che non si ritenga attendibile l'origine dei file epub.",
|
"LabelSettingsEpubsAllowScriptedContentHelp": "Consenti ai file epub di eseguire script. Si consiglia di mantenere questa impostazione disabilitata a meno che non si ritenga attendibile l'origine dei file epub.",
|
||||||
@@ -601,6 +606,7 @@
|
|||||||
"LabelSlug": "Lento",
|
"LabelSlug": "Lento",
|
||||||
"LabelSortAscending": "Crescente",
|
"LabelSortAscending": "Crescente",
|
||||||
"LabelSortDescending": "Discendente",
|
"LabelSortDescending": "Discendente",
|
||||||
|
"LabelSortPubDate": "Ordina per data di pubblicazione",
|
||||||
"LabelStart": "Inizo",
|
"LabelStart": "Inizo",
|
||||||
"LabelStartTime": "Tempo di inizio",
|
"LabelStartTime": "Tempo di inizio",
|
||||||
"LabelStarted": "Iniziato",
|
"LabelStarted": "Iniziato",
|
||||||
@@ -672,7 +678,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Consenti la sovrascrittura dei dettagli esistenti per i libri selezionati quando viene individuata una corrispondenza",
|
"LabelUpdateDetailsHelp": "Consenti la sovrascrittura dei dettagli esistenti per i libri selezionati quando viene individuata una corrispondenza",
|
||||||
"LabelUpdatedAt": "Aggiornato alle",
|
"LabelUpdatedAt": "Aggiornato alle",
|
||||||
"LabelUploaderDragAndDrop": "Drag & drop file o Cartelle",
|
"LabelUploaderDragAndDrop": "Drag & drop file o Cartelle",
|
||||||
"LabelUploaderDragAndDropFilesOnly": "Drag & drop files",
|
"LabelUploaderDragAndDropFilesOnly": "Drag & drop file",
|
||||||
"LabelUploaderDropFiles": "Elimina file",
|
"LabelUploaderDropFiles": "Elimina file",
|
||||||
"LabelUploaderItemFetchMetadataHelp": "Recupera automaticamente titolo, autore e serie",
|
"LabelUploaderItemFetchMetadataHelp": "Recupera automaticamente titolo, autore e serie",
|
||||||
"LabelUseAdvancedOptions": "Usa le opzioni avanzate",
|
"LabelUseAdvancedOptions": "Usa le opzioni avanzate",
|
||||||
@@ -701,6 +707,7 @@
|
|||||||
"LabelYourProgress": "Completato al",
|
"LabelYourProgress": "Completato al",
|
||||||
"MessageAddToPlayerQueue": "Aggiungi alla coda di riproduzione",
|
"MessageAddToPlayerQueue": "Aggiungi alla coda di riproduzione",
|
||||||
"MessageAppriseDescription": "Per utilizzare questa funzione è necessario disporre di un'istanza di <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> in esecuzione o un'API che gestirà quelle stesse richieste. <br />L'API Url dovrebbe essere il percorso URL completo per inviare la notifica, ad esempio se la tua istanza API è servita cosi .<code>http://192.168.1.1:8337</code> Allora dovrai mettere <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Per utilizzare questa funzione è necessario disporre di un'istanza di <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> in esecuzione o un'API che gestirà quelle stesse richieste. <br />L'API Url dovrebbe essere il percorso URL completo per inviare la notifica, ad esempio se la tua istanza API è servita cosi .<code>http://192.168.1.1:8337</code> Allora dovrai mettere <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Assicurati di utilizzare l'ASIN della regione Audible corretta, non di Amazon.",
|
||||||
"MessageBackupsDescription": "I backup includono utenti, progressi degli utenti, dettagli sugli elementi della libreria, impostazioni del server e immagini archiviate in <code>/metadata/items</code> & <code>/metadata/authors</code>. I backup non includono i file archiviati nelle cartelle della libreria.",
|
"MessageBackupsDescription": "I backup includono utenti, progressi degli utenti, dettagli sugli elementi della libreria, impostazioni del server e immagini archiviate in <code>/metadata/items</code> & <code>/metadata/authors</code>. I backup non includono i file archiviati nelle cartelle della libreria.",
|
||||||
"MessageBackupsLocationEditNote": "Nota: l'aggiornamento della posizione di backup non sposterà o modificherà i backup esistenti",
|
"MessageBackupsLocationEditNote": "Nota: l'aggiornamento della posizione di backup non sposterà o modificherà i backup esistenti",
|
||||||
"MessageBackupsLocationNoEditNote": "Nota: la posizione del backup viene impostata tramite una variabile di ambiente e non può essere modificata qui.",
|
"MessageBackupsLocationNoEditNote": "Nota: la posizione del backup viene impostata tramite una variabile di ambiente e non può essere modificata qui.",
|
||||||
@@ -719,6 +726,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "L'ora di inizio non valida deve essere inferiore alla durata dell'audiolibro",
|
"MessageChapterErrorStartGteDuration": "L'ora di inizio non valida deve essere inferiore alla durata dell'audiolibro",
|
||||||
"MessageChapterErrorStartLtPrev": "L'ora di inizio non valida deve essere maggiore o uguale all'ora di inizio del capitolo precedente",
|
"MessageChapterErrorStartLtPrev": "L'ora di inizio non valida deve essere maggiore o uguale all'ora di inizio del capitolo precedente",
|
||||||
"MessageChapterStartIsAfter": "L'inizio del capitolo è dopo la fine del tuo audiolibro",
|
"MessageChapterStartIsAfter": "L'inizio del capitolo è dopo la fine del tuo audiolibro",
|
||||||
|
"MessageChaptersNotFound": "Capitoli non trovati",
|
||||||
"MessageCheckingCron": "Controllo cron...",
|
"MessageCheckingCron": "Controllo cron...",
|
||||||
"MessageConfirmCloseFeed": "Sei sicuro di voler chiudere questo feed?",
|
"MessageConfirmCloseFeed": "Sei sicuro di voler chiudere questo feed?",
|
||||||
"MessageConfirmDeleteBackup": "Sei sicuro di voler eliminare il backup {0}?",
|
"MessageConfirmDeleteBackup": "Sei sicuro di voler eliminare il backup {0}?",
|
||||||
@@ -775,8 +783,9 @@
|
|||||||
"MessageForceReScanDescription": "eseguirà nuovamente la scansione di tutti i file come una nuova scansione. I tag ID3 dei file audio, i file OPF e i file di testo verranno scansionati come nuovi.",
|
"MessageForceReScanDescription": "eseguirà nuovamente la scansione di tutti i file come una nuova scansione. I tag ID3 dei file audio, i file OPF e i file di testo verranno scansionati come nuovi.",
|
||||||
"MessageImportantNotice": "Avviso Importante!",
|
"MessageImportantNotice": "Avviso Importante!",
|
||||||
"MessageInsertChapterBelow": "Inserisci capitolo sotto",
|
"MessageInsertChapterBelow": "Inserisci capitolo sotto",
|
||||||
"MessageItemsSelected": "{0} oggetti Selezionati",
|
"MessageInvalidAsin": "ASIN non Valido",
|
||||||
"MessageItemsUpdated": "{0} Oggetti aggiornati",
|
"MessageItemsSelected": "{0} oggetti selezionati",
|
||||||
|
"MessageItemsUpdated": "{0} oggetti aggiornati",
|
||||||
"MessageJoinUsOn": "Unisciti a noi su",
|
"MessageJoinUsOn": "Unisciti a noi su",
|
||||||
"MessageLoading": "Caricamento…",
|
"MessageLoading": "Caricamento…",
|
||||||
"MessageLoadingFolders": "Caricamento Cartelle...",
|
"MessageLoadingFolders": "Caricamento Cartelle...",
|
||||||
@@ -808,7 +817,7 @@
|
|||||||
"MessageNoItems": "Nessun oggetto",
|
"MessageNoItems": "Nessun oggetto",
|
||||||
"MessageNoItemsFound": "Nessun oggetto trovato",
|
"MessageNoItemsFound": "Nessun oggetto trovato",
|
||||||
"MessageNoListeningSessions": "Nessuna sessione di ascolto",
|
"MessageNoListeningSessions": "Nessuna sessione di ascolto",
|
||||||
"MessageNoLogs": "Nessun Logs",
|
"MessageNoLogs": "Nessun Log",
|
||||||
"MessageNoMediaProgress": "Nessun progresso multimediale",
|
"MessageNoMediaProgress": "Nessun progresso multimediale",
|
||||||
"MessageNoNotifications": "Nessuna notifica",
|
"MessageNoNotifications": "Nessuna notifica",
|
||||||
"MessageNoPodcastFeed": "Podcast non valido: nessun feed",
|
"MessageNoPodcastFeed": "Podcast non valido: nessun feed",
|
||||||
@@ -843,6 +852,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Sei sicuro di voler ripristinare il backup creato su",
|
"MessageRestoreBackupConfirm": "Sei sicuro di voler ripristinare il backup creato su",
|
||||||
"MessageRestoreBackupWarning": "Il ripristino di un backup sovrascriverà l'intero database situato in /config e sovrascrive le immagini in /metadata/items & /metadata/authors.<br /><br />I backup non modificano alcun file nelle cartelle della libreria. Se hai abilitato le impostazioni del server per archiviare copertine e metadati nelle cartelle della libreria, questi non vengono sottoposti a backup o sovrascritti.<br /><br />Tutti i client che utilizzano il tuo server verranno aggiornati automaticamente.",
|
"MessageRestoreBackupWarning": "Il ripristino di un backup sovrascriverà l'intero database situato in /config e sovrascrive le immagini in /metadata/items & /metadata/authors.<br /><br />I backup non modificano alcun file nelle cartelle della libreria. Se hai abilitato le impostazioni del server per archiviare copertine e metadati nelle cartelle della libreria, questi non vengono sottoposti a backup o sovrascritti.<br /><br />Tutti i client che utilizzano il tuo server verranno aggiornati automaticamente.",
|
||||||
"MessageScheduleLibraryScanNote": "Per la maggior parte degli utenti, si consiglia di lasciare questa funzionalità disabilitata e di mantenere abilitata l'impostazione di folder watcher. Il folder watcher rileverà automaticamente le modifiche nelle cartelle della libreria. Il folder watcher non funziona per ogni file system (come NFS), quindi è possibile utilizzare le scansioni pianificate della libreria.",
|
"MessageScheduleLibraryScanNote": "Per la maggior parte degli utenti, si consiglia di lasciare questa funzionalità disabilitata e di mantenere abilitata l'impostazione di folder watcher. Il folder watcher rileverà automaticamente le modifiche nelle cartelle della libreria. Il folder watcher non funziona per ogni file system (come NFS), quindi è possibile utilizzare le scansioni pianificate della libreria.",
|
||||||
|
"MessageScheduleRunEveryWeekdayAtTime": "Esegui ogni {0} alle {1}",
|
||||||
"MessageSearchResultsFor": "cerca risultati per",
|
"MessageSearchResultsFor": "cerca risultati per",
|
||||||
"MessageSelected": "{0} selezionati",
|
"MessageSelected": "{0} selezionati",
|
||||||
"MessageServerCouldNotBeReached": "Impossibile raggiungere il server",
|
"MessageServerCouldNotBeReached": "Impossibile raggiungere il server",
|
||||||
@@ -950,6 +960,7 @@
|
|||||||
"ToastBackupRestoreFailed": "Ripristino fallito",
|
"ToastBackupRestoreFailed": "Ripristino fallito",
|
||||||
"ToastBackupUploadFailed": "Caricamento backup fallito",
|
"ToastBackupUploadFailed": "Caricamento backup fallito",
|
||||||
"ToastBackupUploadSuccess": "Backup caricato",
|
"ToastBackupUploadSuccess": "Backup caricato",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Dettagli applicati agli articoli",
|
||||||
"ToastBatchDeleteFailed": "Eliminazione batch non riuscita",
|
"ToastBatchDeleteFailed": "Eliminazione batch non riuscita",
|
||||||
"ToastBatchDeleteSuccess": "Eliminazione batch riuscita",
|
"ToastBatchDeleteSuccess": "Eliminazione batch riuscita",
|
||||||
"ToastBatchQuickMatchFailed": "Batch Quick Match non riuscito!",
|
"ToastBatchQuickMatchFailed": "Batch Quick Match non riuscito!",
|
||||||
@@ -962,6 +973,8 @@
|
|||||||
"ToastCachePurgeFailed": "Impossibile eliminare la cache",
|
"ToastCachePurgeFailed": "Impossibile eliminare la cache",
|
||||||
"ToastCachePurgeSuccess": "Cache eliminata correttamente",
|
"ToastCachePurgeSuccess": "Cache eliminata correttamente",
|
||||||
"ToastChaptersHaveErrors": "I capitoli contengono errori",
|
"ToastChaptersHaveErrors": "I capitoli contengono errori",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "Quantità di spostamento non valida. L'orario di inizio dell'ultimo capitolo si estenderebbe oltre la durata di questo audiolibro.",
|
||||||
|
"ToastChaptersInvalidShiftAmountStart": "Quantità di spostamento non valida. Il primo capitolo avrebbe una lunghezza pari a zero o negativa e verrebbe sovrascritto dal secondo capitolo. Aumentare la durata iniziale del secondo capitolo.",
|
||||||
"ToastChaptersMustHaveTitles": "I capitoli devono avere titoli",
|
"ToastChaptersMustHaveTitles": "I capitoli devono avere titoli",
|
||||||
"ToastChaptersRemoved": "Capitoli rimossi",
|
"ToastChaptersRemoved": "Capitoli rimossi",
|
||||||
"ToastChaptersUpdated": "Capitoli aggiornati",
|
"ToastChaptersUpdated": "Capitoli aggiornati",
|
||||||
@@ -1062,6 +1075,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Seleziona almeno un utente",
|
"ToastSelectAtLeastOneUser": "Seleziona almeno un utente",
|
||||||
"ToastSendEbookToDeviceFailed": "Impossibile inviare il libro al dispositivo",
|
"ToastSendEbookToDeviceFailed": "Impossibile inviare il libro al dispositivo",
|
||||||
"ToastSendEbookToDeviceSuccess": "Libro inviato al dispositivo «{0}»",
|
"ToastSendEbookToDeviceSuccess": "Libro inviato al dispositivo «{0}»",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Non è possibile aggiungere due serie con lo stesso nome",
|
||||||
"ToastSeriesUpdateFailed": "Aggiornamento Serie Fallito",
|
"ToastSeriesUpdateFailed": "Aggiornamento Serie Fallito",
|
||||||
"ToastSeriesUpdateSuccess": "Serie Aggiornate",
|
"ToastSeriesUpdateSuccess": "Serie Aggiornate",
|
||||||
"ToastServerSettingsUpdateSuccess": "Impostazioni del server aggiornate",
|
"ToastServerSettingsUpdateSuccess": "Impostazioni del server aggiornate",
|
||||||
@@ -1080,6 +1094,8 @@
|
|||||||
"ToastUnknownError": "Errore sconosciuto",
|
"ToastUnknownError": "Errore sconosciuto",
|
||||||
"ToastUnlinkOpenIdFailed": "Impossibile scollegare l'utente da OpenID",
|
"ToastUnlinkOpenIdFailed": "Impossibile scollegare l'utente da OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Utente scollegato da OpenID",
|
"ToastUnlinkOpenIdSuccess": "Utente scollegato da OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Il percorso file \"{0}\" esiste già sul server",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "L'elemento \"{0}\" utilizza una sottodirectory del percorso di caricamento.",
|
||||||
"ToastUserDeleteFailed": "Errore eliminazione utente",
|
"ToastUserDeleteFailed": "Errore eliminazione utente",
|
||||||
"ToastUserDeleteSuccess": "Utente eliminato",
|
"ToastUserDeleteSuccess": "Utente eliminato",
|
||||||
"ToastUserPasswordChangeSuccess": "Password modificata con successo",
|
"ToastUserPasswordChangeSuccess": "Password modificata con successo",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"ButtonApplyChapters": "Hoofdstukken toepassen",
|
"ButtonApplyChapters": "Hoofdstukken toepassen",
|
||||||
"ButtonAuthors": "Auteurs",
|
"ButtonAuthors": "Auteurs",
|
||||||
"ButtonBack": "Terug",
|
"ButtonBack": "Terug",
|
||||||
|
"ButtonBatchEditPopulateFromExisting": "Vullen vanuit bestaande",
|
||||||
"ButtonBatchEditPopulateMapDetails": "Kaartgegevens invullen",
|
"ButtonBatchEditPopulateMapDetails": "Kaartgegevens invullen",
|
||||||
"ButtonBrowseForFolder": "Bladeren naar map",
|
"ButtonBrowseForFolder": "Bladeren naar map",
|
||||||
"ButtonCancel": "Annuleren",
|
"ButtonCancel": "Annuleren",
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
"ButtonNext": "Volgende",
|
"ButtonNext": "Volgende",
|
||||||
"ButtonNextChapter": "Volgend hoofdstuk",
|
"ButtonNextChapter": "Volgend hoofdstuk",
|
||||||
"ButtonNextItemInQueue": "Volgend Item in Wachtrij",
|
"ButtonNextItemInQueue": "Volgend Item in Wachtrij",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Akkoord",
|
||||||
"ButtonOpenFeed": "Feed openen",
|
"ButtonOpenFeed": "Feed openen",
|
||||||
"ButtonOpenManager": "Manager openen",
|
"ButtonOpenManager": "Manager openen",
|
||||||
"ButtonPause": "Pauze",
|
"ButtonPause": "Pauze",
|
||||||
@@ -218,6 +219,7 @@
|
|||||||
"LabelAccountTypeAdmin": "Beheerder",
|
"LabelAccountTypeAdmin": "Beheerder",
|
||||||
"LabelAccountTypeGuest": "Gast",
|
"LabelAccountTypeGuest": "Gast",
|
||||||
"LabelAccountTypeUser": "Gebruiker",
|
"LabelAccountTypeUser": "Gebruiker",
|
||||||
|
"LabelActivities": "Activiteiten",
|
||||||
"LabelActivity": "Activiteit",
|
"LabelActivity": "Activiteit",
|
||||||
"LabelAddToCollection": "Toevoegen aan collectie",
|
"LabelAddToCollection": "Toevoegen aan collectie",
|
||||||
"LabelAddToCollectionBatch": "{0} boeken toevoegen aan collectie",
|
"LabelAddToCollectionBatch": "{0} boeken toevoegen aan collectie",
|
||||||
@@ -227,6 +229,7 @@
|
|||||||
"LabelAddedDate": "Toegevoegd {0}",
|
"LabelAddedDate": "Toegevoegd {0}",
|
||||||
"LabelAdminUsersOnly": "Enkel Admin gebruikers",
|
"LabelAdminUsersOnly": "Enkel Admin gebruikers",
|
||||||
"LabelAll": "Alle",
|
"LabelAll": "Alle",
|
||||||
|
"LabelAllEpisodesDownloaded": "Alle afleveringen gedownload",
|
||||||
"LabelAllUsers": "Alle gebruikers",
|
"LabelAllUsers": "Alle gebruikers",
|
||||||
"LabelAllUsersExcludingGuests": "Alle gebruikers exclusief gasten",
|
"LabelAllUsersExcludingGuests": "Alle gebruikers exclusief gasten",
|
||||||
"LabelAllUsersIncludingGuests": "Alle gebruikers inclusief gasten",
|
"LabelAllUsersIncludingGuests": "Alle gebruikers inclusief gasten",
|
||||||
@@ -282,6 +285,7 @@
|
|||||||
"LabelContinueSeries": "Doorgaan met Serie",
|
"LabelContinueSeries": "Doorgaan met Serie",
|
||||||
"LabelCover": "Omslag",
|
"LabelCover": "Omslag",
|
||||||
"LabelCoverImageURL": "Coverafbeelding URL",
|
"LabelCoverImageURL": "Coverafbeelding URL",
|
||||||
|
"LabelCoverProvider": "Omslag bron",
|
||||||
"LabelCreatedAt": "Gecreëerd op",
|
"LabelCreatedAt": "Gecreëerd op",
|
||||||
"LabelCronExpression": "Cron-uitdrukking",
|
"LabelCronExpression": "Cron-uitdrukking",
|
||||||
"LabelCurrent": "Huidig",
|
"LabelCurrent": "Huidig",
|
||||||
@@ -433,7 +437,7 @@
|
|||||||
"LabelMetadataProvider": "Metadatabron",
|
"LabelMetadataProvider": "Metadatabron",
|
||||||
"LabelMinute": "Minuut",
|
"LabelMinute": "Minuut",
|
||||||
"LabelMinutes": "Minuten",
|
"LabelMinutes": "Minuten",
|
||||||
"LabelMissing": "Ontbrekend",
|
"LabelMissing": "Missende",
|
||||||
"LabelMissingEbook": "Heeft geen ebook",
|
"LabelMissingEbook": "Heeft geen ebook",
|
||||||
"LabelMissingSupplementaryEbook": "Heeft geen supplementair ebook",
|
"LabelMissingSupplementaryEbook": "Heeft geen supplementair ebook",
|
||||||
"LabelMobileRedirectURIs": "Toegestane mobiele omleidings-URL's",
|
"LabelMobileRedirectURIs": "Toegestane mobiele omleidings-URL's",
|
||||||
@@ -464,7 +468,7 @@
|
|||||||
"LabelNotificationsMaxQueueSize": "Max rijgrootte voor notificatie gebeurtenissen",
|
"LabelNotificationsMaxQueueSize": "Max rijgrootte voor notificatie gebeurtenissen",
|
||||||
"LabelNotificationsMaxQueueSizeHelp": "Gebeurtenissen zijn beperkt tot 1 aftrap per seconde. Gebeurtenissen zullen genegeerd worden als de rij aan de maximale grootte zit. Dit voorkomt notificatie-spamming.",
|
"LabelNotificationsMaxQueueSizeHelp": "Gebeurtenissen zijn beperkt tot 1 aftrap per seconde. Gebeurtenissen zullen genegeerd worden als de rij aan de maximale grootte zit. Dit voorkomt notificatie-spamming.",
|
||||||
"LabelNumberOfBooks": "Aantal Boeken",
|
"LabelNumberOfBooks": "Aantal Boeken",
|
||||||
"LabelNumberOfEpisodes": "# afleveringen",
|
"LabelNumberOfEpisodes": "# Afleveringen",
|
||||||
"LabelOpenIDAdvancedPermsClaimDescription": "Naam van de OpenID-claim die geavanceerde machtigingen bevat voor gebruikersacties binnen de applicatie die van toepassing zijn op niet-beheerdersrollen (<b>indien geconfigureerd</b>). Als de claim ontbreekt in het antwoord, wordt toegang tot ABS geweigerd. Als er één optie ontbreekt, wordt deze behandeld als <code>false</code>. Zorg ervoor dat de claim van de identiteitsprovider overeenkomt met de verwachte structuur:",
|
"LabelOpenIDAdvancedPermsClaimDescription": "Naam van de OpenID-claim die geavanceerde machtigingen bevat voor gebruikersacties binnen de applicatie die van toepassing zijn op niet-beheerdersrollen (<b>indien geconfigureerd</b>). Als de claim ontbreekt in het antwoord, wordt toegang tot ABS geweigerd. Als er één optie ontbreekt, wordt deze behandeld als <code>false</code>. Zorg ervoor dat de claim van de identiteitsprovider overeenkomt met de verwachte structuur:",
|
||||||
"LabelOpenIDClaims": "Laat de volgende opties leeg om geavanceerde groeps- en machtigingstoewijzing uit te schakelen en de groep 'Gebruiker' automatisch toe te wijzen.",
|
"LabelOpenIDClaims": "Laat de volgende opties leeg om geavanceerde groeps- en machtigingstoewijzing uit te schakelen en de groep 'Gebruiker' automatisch toe te wijzen.",
|
||||||
"LabelOpenIDGroupClaimDescription": "Naam van de OpenID-claim die een lijst met de groepen van de gebruiker bevat. Vaak aangeduid als <code>groepen</code>. <b>Indien geconfigureerd</b>, zal de applicatie automatisch rollen toewijzen op basis van de groepslidmaatschappen van de gebruiker, op voorwaarde dat deze groepen hoofdlettergevoelig 'admin', 'gebruiker' of 'gast' worden genoemd in de claim. De claim moet een lijst bevatten en als een gebruiker tot meerdere groepen behoort, zal de applicatie de rol toewijzen die overeenkomt met het hoogste toegangsniveau. Als er geen groep overeenkomt, wordt de toegang geweigerd.",
|
"LabelOpenIDGroupClaimDescription": "Naam van de OpenID-claim die een lijst met de groepen van de gebruiker bevat. Vaak aangeduid als <code>groepen</code>. <b>Indien geconfigureerd</b>, zal de applicatie automatisch rollen toewijzen op basis van de groepslidmaatschappen van de gebruiker, op voorwaarde dat deze groepen hoofdlettergevoelig 'admin', 'gebruiker' of 'gast' worden genoemd in de claim. De claim moet een lijst bevatten en als een gebruiker tot meerdere groepen behoort, zal de applicatie de rol toewijzen die overeenkomt met het hoogste toegangsniveau. Als er geen groep overeenkomt, wordt de toegang geweigerd.",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"ButtonCheckAndDownloadNewEpisodes": "Sjekk og last ned nye episoder",
|
"ButtonCheckAndDownloadNewEpisodes": "Sjekk og last ned nye episoder",
|
||||||
"ButtonChooseAFolder": "Velg mappe",
|
"ButtonChooseAFolder": "Velg mappe",
|
||||||
"ButtonChooseFiles": "Velg filer",
|
"ButtonChooseFiles": "Velg filer",
|
||||||
"ButtonClearFilter": "Bytt filter",
|
"ButtonClearFilter": "Fjern filter",
|
||||||
"ButtonCloseFeed": "Lukk Feed",
|
"ButtonCloseFeed": "Lukk Feed",
|
||||||
"ButtonCloseSession": "Lukk åpen økt",
|
"ButtonCloseSession": "Lukk åpen økt",
|
||||||
"ButtonCollections": "Samlinger",
|
"ButtonCollections": "Samlinger",
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
"ButtonApplyChapters": "Aplicar Capítulos",
|
"ButtonApplyChapters": "Aplicar Capítulos",
|
||||||
"ButtonAuthors": "Autores",
|
"ButtonAuthors": "Autores",
|
||||||
"ButtonBack": "Voltar",
|
"ButtonBack": "Voltar",
|
||||||
|
"ButtonBatchEditPopulateFromExisting": "Popular de um existente",
|
||||||
|
"ButtonBatchEditPopulateMapDetails": "Popular mapeamento de detalhes",
|
||||||
"ButtonBrowseForFolder": "Procurar por Pasta",
|
"ButtonBrowseForFolder": "Procurar por Pasta",
|
||||||
"ButtonCancel": "Cancelar",
|
"ButtonCancel": "Cancelar",
|
||||||
"ButtonCancelEncode": "Cancelar Codificação",
|
"ButtonCancelEncode": "Cancelar Codificação",
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
"ButtonChooseFiles": "Escolha arquivos",
|
"ButtonChooseFiles": "Escolha arquivos",
|
||||||
"ButtonClearFilter": "Limpar Filtro",
|
"ButtonClearFilter": "Limpar Filtro",
|
||||||
"ButtonCloseFeed": "Fechar Feed",
|
"ButtonCloseFeed": "Fechar Feed",
|
||||||
|
"ButtonCloseSession": "Fechar Sessão Aberta",
|
||||||
"ButtonCollections": "Coleções",
|
"ButtonCollections": "Coleções",
|
||||||
"ButtonConfigureScanner": "Configurar Verificador",
|
"ButtonConfigureScanner": "Configurar Verificador",
|
||||||
"ButtonCreate": "Criar",
|
"ButtonCreate": "Criar",
|
||||||
@@ -28,6 +31,9 @@
|
|||||||
"ButtonEdit": "Editar",
|
"ButtonEdit": "Editar",
|
||||||
"ButtonEditChapters": "Editar Capítulos",
|
"ButtonEditChapters": "Editar Capítulos",
|
||||||
"ButtonEditPodcast": "Editar Podcast",
|
"ButtonEditPodcast": "Editar Podcast",
|
||||||
|
"ButtonEnable": "Ativar",
|
||||||
|
"ButtonFireAndFail": "Disparar e Falhar",
|
||||||
|
"ButtonFireOnTest": "Disparar evento onTest",
|
||||||
"ButtonForceReScan": "Forcar Nova Verificação",
|
"ButtonForceReScan": "Forcar Nova Verificação",
|
||||||
"ButtonFullPath": "Caminho Completo",
|
"ButtonFullPath": "Caminho Completo",
|
||||||
"ButtonHide": "Ocultar",
|
"ButtonHide": "Ocultar",
|
||||||
@@ -37,6 +43,7 @@
|
|||||||
"ButtonJumpForward": "Adiantar",
|
"ButtonJumpForward": "Adiantar",
|
||||||
"ButtonLatest": "Mais Recentes",
|
"ButtonLatest": "Mais Recentes",
|
||||||
"ButtonLibrary": "Biblioteca",
|
"ButtonLibrary": "Biblioteca",
|
||||||
|
"ButtonLogout": "Logout",
|
||||||
"ButtonLookup": "Procurar",
|
"ButtonLookup": "Procurar",
|
||||||
"ButtonManageTracks": "Gerenciar Faixas",
|
"ButtonManageTracks": "Gerenciar Faixas",
|
||||||
"ButtonMapChapterTitles": "Designar Títulos de Capítulos",
|
"ButtonMapChapterTitles": "Designar Títulos de Capítulos",
|
||||||
@@ -45,18 +52,24 @@
|
|||||||
"ButtonNevermind": "Cancelar",
|
"ButtonNevermind": "Cancelar",
|
||||||
"ButtonNext": "Próximo",
|
"ButtonNext": "Próximo",
|
||||||
"ButtonNextChapter": "Próximo Capítulo",
|
"ButtonNextChapter": "Próximo Capítulo",
|
||||||
|
"ButtonNextItemInQueue": "Próximo Item da Fila",
|
||||||
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Abrir Feed",
|
"ButtonOpenFeed": "Abrir Feed",
|
||||||
"ButtonOpenManager": "Abrir Gerenciador",
|
"ButtonOpenManager": "Abrir Gerenciador",
|
||||||
"ButtonPause": "Pausar",
|
"ButtonPause": "Pausar",
|
||||||
"ButtonPlay": "Reproduzir",
|
"ButtonPlay": "Reproduzir",
|
||||||
|
"ButtonPlayAll": "Reproduzir Tudo",
|
||||||
"ButtonPlaying": "Reproduzindo",
|
"ButtonPlaying": "Reproduzindo",
|
||||||
"ButtonPlaylists": "Lista de Reprodução",
|
"ButtonPlaylists": "Lista de Reprodução",
|
||||||
"ButtonPrevious": "Anterior",
|
"ButtonPrevious": "Anterior",
|
||||||
"ButtonPreviousChapter": "Capítulo Anterior",
|
"ButtonPreviousChapter": "Capítulo Anterior",
|
||||||
|
"ButtonProbeAudioFile": "Sondar Arquivo de Áudio",
|
||||||
"ButtonPurgeAllCache": "Apagar Todo o Cache",
|
"ButtonPurgeAllCache": "Apagar Todo o Cache",
|
||||||
"ButtonPurgeItemsCache": "Apagar o Cache de Itens",
|
"ButtonPurgeItemsCache": "Apagar o Cache de Itens",
|
||||||
"ButtonQueueAddItem": "Adicionar à Lista",
|
"ButtonQueueAddItem": "Adicionar à Lista",
|
||||||
"ButtonQueueRemoveItem": "Remover da Lista",
|
"ButtonQueueRemoveItem": "Remover da Lista",
|
||||||
|
"ButtonQuickEmbed": "Incorporação Rápida",
|
||||||
|
"ButtonQuickEmbedMetadata": "Incorporação Rápida de Metadata",
|
||||||
"ButtonQuickMatch": "Consulta rápida",
|
"ButtonQuickMatch": "Consulta rápida",
|
||||||
"ButtonReScan": "Nova Verificação",
|
"ButtonReScan": "Nova Verificação",
|
||||||
"ButtonRead": "Ler",
|
"ButtonRead": "Ler",
|
||||||
@@ -77,6 +90,8 @@
|
|||||||
"ButtonSaveTracklist": "Salvar Lista de Faixas",
|
"ButtonSaveTracklist": "Salvar Lista de Faixas",
|
||||||
"ButtonScan": "Verificar",
|
"ButtonScan": "Verificar",
|
||||||
"ButtonScanLibrary": "Verificar Biblioteca",
|
"ButtonScanLibrary": "Verificar Biblioteca",
|
||||||
|
"ButtonScrollLeft": "Arrastar para Esquerda",
|
||||||
|
"ButtonScrollRight": "Arrastar para Direita",
|
||||||
"ButtonSearch": "Pesquisar",
|
"ButtonSearch": "Pesquisar",
|
||||||
"ButtonSelectFolderPath": "Selecionar Caminho da Pasta",
|
"ButtonSelectFolderPath": "Selecionar Caminho da Pasta",
|
||||||
"ButtonSeries": "Séries",
|
"ButtonSeries": "Séries",
|
||||||
@@ -86,8 +101,11 @@
|
|||||||
"ButtonShow": "Exibir",
|
"ButtonShow": "Exibir",
|
||||||
"ButtonStartM4BEncode": "Iniciar Codificação M4B",
|
"ButtonStartM4BEncode": "Iniciar Codificação M4B",
|
||||||
"ButtonStartMetadataEmbed": "Iniciar Inclusão de Metadados",
|
"ButtonStartMetadataEmbed": "Iniciar Inclusão de Metadados",
|
||||||
|
"ButtonStats": "Estatísticas",
|
||||||
"ButtonSubmit": "Enviar",
|
"ButtonSubmit": "Enviar",
|
||||||
"ButtonTest": "Testar",
|
"ButtonTest": "Testar",
|
||||||
|
"ButtonUnlinkOpenId": "Desvincular OpenID",
|
||||||
|
"ButtonUpload": "Fazer Upload",
|
||||||
"ButtonUploadBackup": "Upload de Backup",
|
"ButtonUploadBackup": "Upload de Backup",
|
||||||
"ButtonUploadCover": "Upload de Capa",
|
"ButtonUploadCover": "Upload de Capa",
|
||||||
"ButtonUploadOPMLFile": "Upload Arquivo OPML",
|
"ButtonUploadOPMLFile": "Upload Arquivo OPML",
|
||||||
@@ -99,6 +117,7 @@
|
|||||||
"ErrorUploadFetchMetadataNoResults": "Não foi possível buscar metadados - tente atualizar o título e/ou autor",
|
"ErrorUploadFetchMetadataNoResults": "Não foi possível buscar metadados - tente atualizar o título e/ou autor",
|
||||||
"ErrorUploadLacksTitle": "É preciso ter um título",
|
"ErrorUploadLacksTitle": "É preciso ter um título",
|
||||||
"HeaderAccount": "Conta",
|
"HeaderAccount": "Conta",
|
||||||
|
"HeaderAddCustomMetadataProvider": "Adicionar Provedor de Metadados Personalizado",
|
||||||
"HeaderAdvanced": "Avançado",
|
"HeaderAdvanced": "Avançado",
|
||||||
"HeaderAppriseNotificationSettings": "Configuração de notificações Apprise",
|
"HeaderAppriseNotificationSettings": "Configuração de notificações Apprise",
|
||||||
"HeaderAudioTracks": "Trilhas de áudio",
|
"HeaderAudioTracks": "Trilhas de áudio",
|
||||||
@@ -196,6 +215,7 @@
|
|||||||
"LabelAddToPlaylist": "Adicionar à Lista de Reprodução",
|
"LabelAddToPlaylist": "Adicionar à Lista de Reprodução",
|
||||||
"LabelAddToPlaylistBatch": "Adicionar {0} itens à Lista de Reprodução",
|
"LabelAddToPlaylistBatch": "Adicionar {0} itens à Lista de Reprodução",
|
||||||
"LabelAddedAt": "Acrescentado em",
|
"LabelAddedAt": "Acrescentado em",
|
||||||
|
"LabelAddedDate": "Adicionado {0}",
|
||||||
"LabelAdminUsersOnly": "Apenas usuários administradores",
|
"LabelAdminUsersOnly": "Apenas usuários administradores",
|
||||||
"LabelAll": "Todos",
|
"LabelAll": "Todos",
|
||||||
"LabelAllUsers": "Todos Usuários",
|
"LabelAllUsers": "Todos Usuários",
|
||||||
|
|||||||
+19
-5
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Плейлист",
|
"HeaderPlaylist": "Плейлист",
|
||||||
"HeaderPlaylistItems": "Элементы списка воспроизведения",
|
"HeaderPlaylistItems": "Элементы списка воспроизведения",
|
||||||
"HeaderPodcastsToAdd": "Подкасты для добавления",
|
"HeaderPodcastsToAdd": "Подкасты для добавления",
|
||||||
|
"HeaderPresets": "Пресеты",
|
||||||
"HeaderPreviewCover": "Предпросмотр обложки",
|
"HeaderPreviewCover": "Предпросмотр обложки",
|
||||||
"HeaderRSSFeedGeneral": "Сведения о RSS",
|
"HeaderRSSFeedGeneral": "Сведения о RSS",
|
||||||
"HeaderRSSFeedIsOpen": "RSS-канал открыт",
|
"HeaderRSSFeedIsOpen": "RSS-канал открыт",
|
||||||
@@ -219,7 +220,7 @@
|
|||||||
"LabelAccountTypeAdmin": "Администратор",
|
"LabelAccountTypeAdmin": "Администратор",
|
||||||
"LabelAccountTypeGuest": "Гость",
|
"LabelAccountTypeGuest": "Гость",
|
||||||
"LabelAccountTypeUser": "Пользователь",
|
"LabelAccountTypeUser": "Пользователь",
|
||||||
"LabelActivities": "Мероприятия",
|
"LabelActivities": "События",
|
||||||
"LabelActivity": "Активность",
|
"LabelActivity": "Активность",
|
||||||
"LabelAddToCollection": "Добавить в коллекцию",
|
"LabelAddToCollection": "Добавить в коллекцию",
|
||||||
"LabelAddToCollectionBatch": "Добавить {0} книг в коллекцию",
|
"LabelAddToCollectionBatch": "Добавить {0} книг в коллекцию",
|
||||||
@@ -229,6 +230,7 @@
|
|||||||
"LabelAddedDate": "Добавлено {0}",
|
"LabelAddedDate": "Добавлено {0}",
|
||||||
"LabelAdminUsersOnly": "Только для пользователей с правами администратора",
|
"LabelAdminUsersOnly": "Только для пользователей с правами администратора",
|
||||||
"LabelAll": "Все",
|
"LabelAll": "Все",
|
||||||
|
"LabelAllEpisodesDownloaded": "Все эпизоды загружены",
|
||||||
"LabelAllUsers": "Все пользователи",
|
"LabelAllUsers": "Все пользователи",
|
||||||
"LabelAllUsersExcludingGuests": "Все пользователи, кроме гостей",
|
"LabelAllUsersExcludingGuests": "Все пользователи, кроме гостей",
|
||||||
"LabelAllUsersIncludingGuests": "Все пользователи, включая гостей",
|
"LabelAllUsersIncludingGuests": "Все пользователи, включая гостей",
|
||||||
@@ -305,7 +307,7 @@
|
|||||||
"LabelDownload": "Скачать",
|
"LabelDownload": "Скачать",
|
||||||
"LabelDownloadNEpisodes": "Скачать {0} эпизодов",
|
"LabelDownloadNEpisodes": "Скачать {0} эпизодов",
|
||||||
"LabelDownloadable": "Загружаемый",
|
"LabelDownloadable": "Загружаемый",
|
||||||
"LabelDuration": "Продолжительность",
|
"LabelDuration": "Длительность",
|
||||||
"LabelDurationComparisonExactMatch": "(точное совпадение)",
|
"LabelDurationComparisonExactMatch": "(точное совпадение)",
|
||||||
"LabelDurationComparisonLonger": "({0} дольше)",
|
"LabelDurationComparisonLonger": "({0} дольше)",
|
||||||
"LabelDurationComparisonShorter": "({0} короче)",
|
"LabelDurationComparisonShorter": "({0} короче)",
|
||||||
@@ -529,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Дата выхода",
|
"LabelReleaseDate": "Дата выхода",
|
||||||
"LabelRemoveAllMetadataAbs": "Удалите все файлы metadata.abs",
|
"LabelRemoveAllMetadataAbs": "Удалите все файлы metadata.abs",
|
||||||
"LabelRemoveAllMetadataJson": "Удалите все файлы metadata.json",
|
"LabelRemoveAllMetadataJson": "Удалите все файлы metadata.json",
|
||||||
|
"LabelRemoveAudibleBranding": "Удалить вступление и концовку Audible из глав",
|
||||||
"LabelRemoveCover": "Удалить обложку",
|
"LabelRemoveCover": "Удалить обложку",
|
||||||
"LabelRemoveMetadataFile": "Удаление файлов метаданных в папках элементов библиотеки",
|
"LabelRemoveMetadataFile": "Удаление файлов метаданных в папках элементов библиотеки",
|
||||||
"LabelRemoveMetadataFileHelp": "Удалите все файлы metadata.json и metadata.abs из ваших папок {0}.",
|
"LabelRemoveMetadataFileHelp": "Удалите все файлы metadata.json и metadata.abs из ваших папок {0}.",
|
||||||
@@ -603,6 +606,7 @@
|
|||||||
"LabelSlug": "Слизень",
|
"LabelSlug": "Слизень",
|
||||||
"LabelSortAscending": "По возрастанию",
|
"LabelSortAscending": "По возрастанию",
|
||||||
"LabelSortDescending": "По убыванию",
|
"LabelSortDescending": "По убыванию",
|
||||||
|
"LabelSortPubDate": "Отсортировать по дате публикации",
|
||||||
"LabelStart": "Начало",
|
"LabelStart": "Начало",
|
||||||
"LabelStartTime": "Время начала",
|
"LabelStartTime": "Время начала",
|
||||||
"LabelStarted": "Начат",
|
"LabelStarted": "Начат",
|
||||||
@@ -703,6 +707,8 @@
|
|||||||
"LabelYourProgress": "Ваш прогресс",
|
"LabelYourProgress": "Ваш прогресс",
|
||||||
"MessageAddToPlayerQueue": "Добавить в очередь проигрывателя",
|
"MessageAddToPlayerQueue": "Добавить в очередь проигрывателя",
|
||||||
"MessageAppriseDescription": "Для использования этой функции необходимо иметь запущенный экземпляр <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> или api которое обрабатывает те же самые запросы. <br />URL-адрес API Apprise должен быть полным 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> или api которое обрабатывает те же самые запросы. <br />URL-адрес API Apprise должен быть полным URL-адресом для отправки уведомления, т.е., если API запущено по адресу <code>http://192.168.1.1:8337</code> тогда нужно указать <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Убедитесь, что вы используете ASIN из правильной региональной зоны Audible, а не из Amazon.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Перезапустите ваш сервер после сохранения для применения изменений в OIDC.",
|
||||||
"MessageBackupsDescription": "Бэкап включает пользователей, прогресс пользователей, данные элементов библиотеки, настройки сервера и изображения хранящиеся в <code>/metadata/items</code> и <code>/metadata/authors</code>. Бэкапы <strong>НЕ</strong> сохраняют файлы из папок библиотек.",
|
"MessageBackupsDescription": "Бэкап включает пользователей, прогресс пользователей, данные элементов библиотеки, настройки сервера и изображения хранящиеся в <code>/metadata/items</code> и <code>/metadata/authors</code>. Бэкапы <strong>НЕ</strong> сохраняют файлы из папок библиотек.",
|
||||||
"MessageBackupsLocationEditNote": "Примечание: Обновление местоположения резервной копии не приведет к перемещению или изменению существующих резервных копий",
|
"MessageBackupsLocationEditNote": "Примечание: Обновление местоположения резервной копии не приведет к перемещению или изменению существующих резервных копий",
|
||||||
"MessageBackupsLocationNoEditNote": "Примечание: Местоположение резервного копирования задается с помощью переменной среды и не может быть изменено здесь.",
|
"MessageBackupsLocationNoEditNote": "Примечание: Местоположение резервного копирования задается с помощью переменной среды и не может быть изменено здесь.",
|
||||||
@@ -721,6 +727,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Неверное время начала, должно быть меньше продолжительности аудиокниги",
|
"MessageChapterErrorStartGteDuration": "Неверное время начала, должно быть меньше продолжительности аудиокниги",
|
||||||
"MessageChapterErrorStartLtPrev": "Неверное время начала, должно быть больше или равно времени начала предыдущей главы",
|
"MessageChapterErrorStartLtPrev": "Неверное время начала, должно быть больше или равно времени начала предыдущей главы",
|
||||||
"MessageChapterStartIsAfter": "Глава начинается после окончания аудиокниги",
|
"MessageChapterStartIsAfter": "Глава начинается после окончания аудиокниги",
|
||||||
|
"MessageChaptersNotFound": "Главы не найденны",
|
||||||
"MessageCheckingCron": "Проверка cron...",
|
"MessageCheckingCron": "Проверка cron...",
|
||||||
"MessageConfirmCloseFeed": "Вы уверены, что хотите закрыть этот канал?",
|
"MessageConfirmCloseFeed": "Вы уверены, что хотите закрыть этот канал?",
|
||||||
"MessageConfirmDeleteBackup": "Вы уверены, что хотите удалить бэкап для {0}?",
|
"MessageConfirmDeleteBackup": "Вы уверены, что хотите удалить бэкап для {0}?",
|
||||||
@@ -777,8 +784,9 @@
|
|||||||
"MessageForceReScanDescription": "будет сканировать все файлы снова, как свежее сканирование. Теги ID3 аудиофайлов, OPF-файлы и текстовые файлы будут сканироваться как новые.",
|
"MessageForceReScanDescription": "будет сканировать все файлы снова, как свежее сканирование. Теги ID3 аудиофайлов, OPF-файлы и текстовые файлы будут сканироваться как новые.",
|
||||||
"MessageImportantNotice": "Важное замечание!",
|
"MessageImportantNotice": "Важное замечание!",
|
||||||
"MessageInsertChapterBelow": "Вставить главу ниже",
|
"MessageInsertChapterBelow": "Вставить главу ниже",
|
||||||
"MessageItemsSelected": "{0} Элементов выделено",
|
"MessageInvalidAsin": "Неправильный ASIN",
|
||||||
"MessageItemsUpdated": "{0} Элементов обновлено",
|
"MessageItemsSelected": "{0} элементов выделено",
|
||||||
|
"MessageItemsUpdated": "{0} элементов обновлено",
|
||||||
"MessageJoinUsOn": "Присоединяйтесь к нам в",
|
"MessageJoinUsOn": "Присоединяйтесь к нам в",
|
||||||
"MessageLoading": "Загрузка...",
|
"MessageLoading": "Загрузка...",
|
||||||
"MessageLoadingFolders": "Загрузка каталогов...",
|
"MessageLoadingFolders": "Загрузка каталогов...",
|
||||||
@@ -810,7 +818,7 @@
|
|||||||
"MessageNoItems": "Нет элементов",
|
"MessageNoItems": "Нет элементов",
|
||||||
"MessageNoItemsFound": "Элементы не найдены",
|
"MessageNoItemsFound": "Элементы не найдены",
|
||||||
"MessageNoListeningSessions": "Нет сеансов прослушивания",
|
"MessageNoListeningSessions": "Нет сеансов прослушивания",
|
||||||
"MessageNoLogs": "Нет логов",
|
"MessageNoLogs": "Нет записей",
|
||||||
"MessageNoMediaProgress": "Нет прогресса медиа",
|
"MessageNoMediaProgress": "Нет прогресса медиа",
|
||||||
"MessageNoNotifications": "Нет уведомлений",
|
"MessageNoNotifications": "Нет уведомлений",
|
||||||
"MessageNoPodcastFeed": "Недопустимый подкаст: Нет канала",
|
"MessageNoPodcastFeed": "Недопустимый подкаст: Нет канала",
|
||||||
@@ -953,6 +961,7 @@
|
|||||||
"ToastBackupRestoreFailed": "Не удалось восстановить из бэкапа",
|
"ToastBackupRestoreFailed": "Не удалось восстановить из бэкапа",
|
||||||
"ToastBackupUploadFailed": "Не удалось загрузить бэкап",
|
"ToastBackupUploadFailed": "Не удалось загрузить бэкап",
|
||||||
"ToastBackupUploadSuccess": "Бэкап загружен",
|
"ToastBackupUploadSuccess": "Бэкап загружен",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Подробности, применяемые к элементам",
|
||||||
"ToastBatchDeleteFailed": "Не удалось выполнить пакетное удаление",
|
"ToastBatchDeleteFailed": "Не удалось выполнить пакетное удаление",
|
||||||
"ToastBatchDeleteSuccess": "Успешное пакетное удаление",
|
"ToastBatchDeleteSuccess": "Успешное пакетное удаление",
|
||||||
"ToastBatchQuickMatchFailed": "Не удалось выполнить пакетное быстрое сопоставление!",
|
"ToastBatchQuickMatchFailed": "Не удалось выполнить пакетное быстрое сопоставление!",
|
||||||
@@ -965,6 +974,8 @@
|
|||||||
"ToastCachePurgeFailed": "Не удалось очистить кэш",
|
"ToastCachePurgeFailed": "Не удалось очистить кэш",
|
||||||
"ToastCachePurgeSuccess": "Кэш успешно очищен",
|
"ToastCachePurgeSuccess": "Кэш успешно очищен",
|
||||||
"ToastChaptersHaveErrors": "Главы имеют ошибки",
|
"ToastChaptersHaveErrors": "Главы имеют ошибки",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "Некорректное значение сдвига. Начало последней главы будет превышать продолжительность этой аудиокниги.",
|
||||||
|
"ToastChaptersInvalidShiftAmountStart": "Некорректное значение сдвига. Первая глава будет иметь нулевую или отрицательную длину и будет перезаписана второй главой. Увеличьте начальную продолжительность второй главы.",
|
||||||
"ToastChaptersMustHaveTitles": "Главы должны содержать названия",
|
"ToastChaptersMustHaveTitles": "Главы должны содержать названия",
|
||||||
"ToastChaptersRemoved": "Удалены главы",
|
"ToastChaptersRemoved": "Удалены главы",
|
||||||
"ToastChaptersUpdated": "Обновленные главы",
|
"ToastChaptersUpdated": "Обновленные главы",
|
||||||
@@ -1065,6 +1076,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "Выберите хотя бы одного пользователя",
|
"ToastSelectAtLeastOneUser": "Выберите хотя бы одного пользователя",
|
||||||
"ToastSendEbookToDeviceFailed": "Не удалось отправить e-книгу на устройство",
|
"ToastSendEbookToDeviceFailed": "Не удалось отправить e-книгу на устройство",
|
||||||
"ToastSendEbookToDeviceSuccess": "E-книга отправлена на устройство \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "E-книга отправлена на устройство \"{0}\"",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "Невозможно добавить две серии с одинаковым названием",
|
||||||
"ToastSeriesUpdateFailed": "Не удалось обновить серию",
|
"ToastSeriesUpdateFailed": "Не удалось обновить серию",
|
||||||
"ToastSeriesUpdateSuccess": "Успешное обновление серии",
|
"ToastSeriesUpdateSuccess": "Успешное обновление серии",
|
||||||
"ToastServerSettingsUpdateSuccess": "Обновлены настройки сервера",
|
"ToastServerSettingsUpdateSuccess": "Обновлены настройки сервера",
|
||||||
@@ -1083,6 +1095,8 @@
|
|||||||
"ToastUnknownError": "Неизвестная ошибка",
|
"ToastUnknownError": "Неизвестная ошибка",
|
||||||
"ToastUnlinkOpenIdFailed": "Не удалось отвязать пользователя от OpenID",
|
"ToastUnlinkOpenIdFailed": "Не удалось отвязать пользователя от OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Пользователь отвязан от OpenID",
|
"ToastUnlinkOpenIdSuccess": "Пользователь отвязан от OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Путь к файлу \"{0}\" уже существует на сервере",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Элемент «{0}» использует подкаталог пути загрузки.",
|
||||||
"ToastUserDeleteFailed": "Не удалось удалить пользователя",
|
"ToastUserDeleteFailed": "Не удалось удалить пользователя",
|
||||||
"ToastUserDeleteSuccess": "Пользователь удален",
|
"ToastUserDeleteSuccess": "Пользователь удален",
|
||||||
"ToastUserPasswordChangeSuccess": "Пароль успешно изменен",
|
"ToastUserPasswordChangeSuccess": "Пароль успешно изменен",
|
||||||
|
|||||||
+928
-12
File diff suppressed because it is too large
Load Diff
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Seznam predvajanja",
|
"HeaderPlaylist": "Seznam predvajanja",
|
||||||
"HeaderPlaylistItems": "Elementi seznama predvajanja",
|
"HeaderPlaylistItems": "Elementi seznama predvajanja",
|
||||||
"HeaderPodcastsToAdd": "Podcasti za dodajanje",
|
"HeaderPodcastsToAdd": "Podcasti za dodajanje",
|
||||||
|
"HeaderPresets": "Prednastavitve",
|
||||||
"HeaderPreviewCover": "Naslovnica za predogled",
|
"HeaderPreviewCover": "Naslovnica za predogled",
|
||||||
"HeaderRSSFeedGeneral": "RSS podrobnosti",
|
"HeaderRSSFeedGeneral": "RSS podrobnosti",
|
||||||
"HeaderRSSFeedIsOpen": "Vir RSS je odprt",
|
"HeaderRSSFeedIsOpen": "Vir RSS je odprt",
|
||||||
@@ -530,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Datum izdaje",
|
"LabelReleaseDate": "Datum izdaje",
|
||||||
"LabelRemoveAllMetadataAbs": "Odstrani vse datoteke metadata.abs",
|
"LabelRemoveAllMetadataAbs": "Odstrani vse datoteke metadata.abs",
|
||||||
"LabelRemoveAllMetadataJson": "Odstrani vse datoteke metadata.json",
|
"LabelRemoveAllMetadataJson": "Odstrani vse datoteke metadata.json",
|
||||||
|
"LabelRemoveAudibleBranding": "Odstrani Audible uvod in zaključek iz poglavij",
|
||||||
"LabelRemoveCover": "Odstrani naslovnico",
|
"LabelRemoveCover": "Odstrani naslovnico",
|
||||||
"LabelRemoveMetadataFile": "Odstrani datoteke z metapodatki v mapah elementov knjižnice",
|
"LabelRemoveMetadataFile": "Odstrani datoteke z metapodatki v mapah elementov knjižnice",
|
||||||
"LabelRemoveMetadataFileHelp": "Odstrani vse datoteke metadata.json in metadata.abs v svojih mapah {0}.",
|
"LabelRemoveMetadataFileHelp": "Odstrani vse datoteke metadata.json in metadata.abs v svojih mapah {0}.",
|
||||||
@@ -604,6 +606,7 @@
|
|||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "Naraščajoče",
|
"LabelSortAscending": "Naraščajoče",
|
||||||
"LabelSortDescending": "Padajoče",
|
"LabelSortDescending": "Padajoče",
|
||||||
|
"LabelSortPubDate": "Razvrsti po datumu objave",
|
||||||
"LabelStart": "Začetek",
|
"LabelStart": "Začetek",
|
||||||
"LabelStartTime": "Čas začetka",
|
"LabelStartTime": "Čas začetka",
|
||||||
"LabelStarted": "Začeto",
|
"LabelStarted": "Začeto",
|
||||||
@@ -704,6 +707,8 @@
|
|||||||
"LabelYourProgress": "Tvoj napredek",
|
"LabelYourProgress": "Tvoj napredek",
|
||||||
"MessageAddToPlayerQueue": "Dodaj v čakalno vrsto predvajalnika",
|
"MessageAddToPlayerQueue": "Dodaj v čakalno vrsto predvajalnika",
|
||||||
"MessageAppriseDescription": "Če želite uporabljati to funkcijo, morate imeti zagnano namestitev <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API Apprise</a> ali API, ki bo obravnavala te iste zahteve. <br />Url API-ja Apprise mora biti celotna pot URL-ja za pošiljanje obvestila, npr. če je vaša namestitev API-ja postrežena na <code>http://192.168.1.1:8337</code>, bi morali vnesti <code >http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Če želite uporabljati to funkcijo, morate imeti zagnano namestitev <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API Apprise</a> ali API, ki bo obravnavala te iste zahteve. <br />Url API-ja Apprise mora biti celotna pot URL-ja za pošiljanje obvestila, npr. če je vaša namestitev API-ja postrežena na <code>http://192.168.1.1:8337</code>, bi morali vnesti <code >http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Prepričajte se, da uporabljate ASIN iz pravilne zvočne regije, ne iz Amazona.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Za uveljavitev OIDC sprememb, po shranjevanju znova zaženite strežnik.",
|
||||||
"MessageBackupsDescription": "Varnostne kopije vključujejo uporabnike, napredek uporabnikov, podrobnosti elementov knjižnice, nastavitve strežnika in slike, shranjene v <code>/metadata/items</code> & <code>/metadata/authors</code>. Varnostne kopije <strong>ne</strong> vključujejo datotek, shranjenih v mapah vaše knjižnice.",
|
"MessageBackupsDescription": "Varnostne kopije vključujejo uporabnike, napredek uporabnikov, podrobnosti elementov knjižnice, nastavitve strežnika in slike, shranjene v <code>/metadata/items</code> & <code>/metadata/authors</code>. Varnostne kopije <strong>ne</strong> vključujejo datotek, shranjenih v mapah vaše knjižnice.",
|
||||||
"MessageBackupsLocationEditNote": "Opomba: Posodabljanje lokacije varnostne kopije ne bo premaknilo ali spremenilo obstoječih varnostnih kopij",
|
"MessageBackupsLocationEditNote": "Opomba: Posodabljanje lokacije varnostne kopije ne bo premaknilo ali spremenilo obstoječih varnostnih kopij",
|
||||||
"MessageBackupsLocationNoEditNote": "Opomba: Lokacija varnostne kopije je nastavljena s spremenljivko okolja in je tu ni mogoče spremeniti.",
|
"MessageBackupsLocationNoEditNote": "Opomba: Lokacija varnostne kopije je nastavljena s spremenljivko okolja in je tu ni mogoče spremeniti.",
|
||||||
@@ -722,6 +727,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Neveljaven začetni čas, mora biti krajši od trajanja zvočne knjige",
|
"MessageChapterErrorStartGteDuration": "Neveljaven začetni čas, mora biti krajši od trajanja zvočne knjige",
|
||||||
"MessageChapterErrorStartLtPrev": "Neveljaven začetni čas mora biti večji od ali enak začetnemu času prejšnjega poglavja",
|
"MessageChapterErrorStartLtPrev": "Neveljaven začetni čas mora biti večji od ali enak začetnemu času prejšnjega poglavja",
|
||||||
"MessageChapterStartIsAfter": "Začetek poglavja je po koncu vaše zvočne knjige",
|
"MessageChapterStartIsAfter": "Začetek poglavja je po koncu vaše zvočne knjige",
|
||||||
|
"MessageChaptersNotFound": "Poglavij ni bilo najdenih",
|
||||||
"MessageCheckingCron": "Preverjam cron...",
|
"MessageCheckingCron": "Preverjam cron...",
|
||||||
"MessageConfirmCloseFeed": "Ali ste prepričani, da želite zapreti ta vir?",
|
"MessageConfirmCloseFeed": "Ali ste prepričani, da želite zapreti ta vir?",
|
||||||
"MessageConfirmDeleteBackup": "Ali ste prepričani, da želite izbrisati varnostno kopijo za {0}?",
|
"MessageConfirmDeleteBackup": "Ali ste prepričani, da želite izbrisati varnostno kopijo za {0}?",
|
||||||
@@ -778,6 +784,7 @@
|
|||||||
"MessageForceReScanDescription": "bo znova pregledal vse datoteke kot pregled od začetka. Oznake ID3 zvočnih datotek, datoteke OPF in besedilne datoteke bodo pregledane kot nove.",
|
"MessageForceReScanDescription": "bo znova pregledal vse datoteke kot pregled od začetka. Oznake ID3 zvočnih datotek, datoteke OPF in besedilne datoteke bodo pregledane kot nove.",
|
||||||
"MessageImportantNotice": "Pomembno obvestilo!",
|
"MessageImportantNotice": "Pomembno obvestilo!",
|
||||||
"MessageInsertChapterBelow": "Spodaj vstavite poglavje",
|
"MessageInsertChapterBelow": "Spodaj vstavite poglavje",
|
||||||
|
"MessageInvalidAsin": "Neveljaven ASIN",
|
||||||
"MessageItemsSelected": "{0} izbranih elementov",
|
"MessageItemsSelected": "{0} izbranih elementov",
|
||||||
"MessageItemsUpdated": "Št. posodobljenih elementov: {0}",
|
"MessageItemsUpdated": "Št. posodobljenih elementov: {0}",
|
||||||
"MessageJoinUsOn": "Pridružite se nam",
|
"MessageJoinUsOn": "Pridružite se nam",
|
||||||
@@ -967,6 +974,8 @@
|
|||||||
"ToastCachePurgeFailed": "Čiščenje predpomnilnika ni uspelo",
|
"ToastCachePurgeFailed": "Čiščenje predpomnilnika ni uspelo",
|
||||||
"ToastCachePurgeSuccess": "Predpomnilnik je bil uspešno očiščen",
|
"ToastCachePurgeSuccess": "Predpomnilnik je bil uspešno očiščen",
|
||||||
"ToastChaptersHaveErrors": "Poglavja imajo napake",
|
"ToastChaptersHaveErrors": "Poglavja imajo napake",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "Neveljavna vrednost zamika. Začetni čas zadnjega poglavja bi presegel trajanje te zvočne knjige.",
|
||||||
|
"ToastChaptersInvalidShiftAmountStart": "Neveljavna vrednost zamika. Prvo poglavje bi imelo ničelno ali negativno dolžino in bi ga prepisalo drugo poglavje. Povečajte začetno trajanje drugega poglavja.",
|
||||||
"ToastChaptersMustHaveTitles": "Poglavja morajo imeti naslove",
|
"ToastChaptersMustHaveTitles": "Poglavja morajo imeti naslove",
|
||||||
"ToastChaptersRemoved": "Poglavja so odstranjena",
|
"ToastChaptersRemoved": "Poglavja so odstranjena",
|
||||||
"ToastChaptersUpdated": "Poglavja so posodobljena",
|
"ToastChaptersUpdated": "Poglavja so posodobljena",
|
||||||
@@ -1086,6 +1095,8 @@
|
|||||||
"ToastUnknownError": "Neznana napaka",
|
"ToastUnknownError": "Neznana napaka",
|
||||||
"ToastUnlinkOpenIdFailed": "Prekinitev povezave uporabnika z OpenID ni uspela",
|
"ToastUnlinkOpenIdFailed": "Prekinitev povezave uporabnika z OpenID ni uspela",
|
||||||
"ToastUnlinkOpenIdSuccess": "Uporabnik je prekinil povezavo z OpenID",
|
"ToastUnlinkOpenIdSuccess": "Uporabnik je prekinil povezavo z OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Datoteka s potjo \"{0}\" že obstaja na strežniku",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Element \"{0}\" uporablja podmapo v poti za nalaganje.",
|
||||||
"ToastUserDeleteFailed": "Brisanje uporabnika ni uspelo",
|
"ToastUserDeleteFailed": "Brisanje uporabnika ni uspelo",
|
||||||
"ToastUserDeleteSuccess": "Uporabnik je bil izbrisan",
|
"ToastUserDeleteSuccess": "Uporabnik je bil izbrisan",
|
||||||
"ToastUserPasswordChangeSuccess": "Geslo je bilo uspešno spremenjeno",
|
"ToastUserPasswordChangeSuccess": "Geslo je bilo uspešno spremenjeno",
|
||||||
|
|||||||
+19
-3
@@ -32,6 +32,8 @@
|
|||||||
"ButtonEditChapters": "Redigera kapitel",
|
"ButtonEditChapters": "Redigera kapitel",
|
||||||
"ButtonEditPodcast": "Redigera podcast",
|
"ButtonEditPodcast": "Redigera podcast",
|
||||||
"ButtonEnable": "Aktivera",
|
"ButtonEnable": "Aktivera",
|
||||||
|
"ButtonFireAndFail": "Starta och Misslyckas",
|
||||||
|
"ButtonFireOnTest": "Starta onTest händelse",
|
||||||
"ButtonForceReScan": "Starta ny skanning",
|
"ButtonForceReScan": "Starta ny skanning",
|
||||||
"ButtonFullPath": "Fullständig sökväg",
|
"ButtonFullPath": "Fullständig sökväg",
|
||||||
"ButtonHide": "Dölj",
|
"ButtonHide": "Dölj",
|
||||||
@@ -102,6 +104,7 @@
|
|||||||
"ButtonStats": "Statistik",
|
"ButtonStats": "Statistik",
|
||||||
"ButtonSubmit": "Skicka",
|
"ButtonSubmit": "Skicka",
|
||||||
"ButtonTest": "Testa",
|
"ButtonTest": "Testa",
|
||||||
|
"ButtonUnlinkOpenId": "Koppla från OpenID",
|
||||||
"ButtonUpload": "Ladda upp",
|
"ButtonUpload": "Ladda upp",
|
||||||
"ButtonUploadBackup": "Läs in säkerhetskopia",
|
"ButtonUploadBackup": "Läs in säkerhetskopia",
|
||||||
"ButtonUploadCover": "Ladda upp omslag",
|
"ButtonUploadCover": "Ladda upp omslag",
|
||||||
@@ -209,6 +212,7 @@
|
|||||||
"HeaderYearReview": "Sammanställning av {0}",
|
"HeaderYearReview": "Sammanställning av {0}",
|
||||||
"HeaderYourStats": "Din statistik",
|
"HeaderYourStats": "Din statistik",
|
||||||
"LabelAbridged": "Förkortad version",
|
"LabelAbridged": "Förkortad version",
|
||||||
|
"LabelAbridgedChecked": "Förkortad (kontrollerad)",
|
||||||
"LabelAbridgedUnchecked": "Oavkortad (okontrollerad)",
|
"LabelAbridgedUnchecked": "Oavkortad (okontrollerad)",
|
||||||
"LabelAccessibleBy": "Tillgänglig för",
|
"LabelAccessibleBy": "Tillgänglig för",
|
||||||
"LabelAccountType": "Kontotyp",
|
"LabelAccountType": "Kontotyp",
|
||||||
@@ -249,7 +253,7 @@
|
|||||||
"LabelBackToUser": "Tillbaka till användaren",
|
"LabelBackToUser": "Tillbaka till användaren",
|
||||||
"LabelBackupAudioFiles": "Säkerhetskopiera ljudfiler",
|
"LabelBackupAudioFiles": "Säkerhetskopiera ljudfiler",
|
||||||
"LabelBackupLocation": "Plats för säkerhetskopia",
|
"LabelBackupLocation": "Plats för säkerhetskopia",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Aktivera automatisk säkerhetskopiering",
|
"LabelBackupsEnableAutomaticBackups": "Automatisk säkerhetskopiering",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Säkerhetskopior sparas i \"/metadata/backups\"",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Säkerhetskopior sparas i \"/metadata/backups\"",
|
||||||
"LabelBackupsMaxBackupSize": "Maximal storlek på säkerhetskopia i GigaByte (0 = obegränsad)",
|
"LabelBackupsMaxBackupSize": "Maximal storlek på säkerhetskopia i GigaByte (0 = obegränsad)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Som ett skydd mot en felaktig konfiguration kommer säkerhetskopior inte att genomföras om de överskrider den konfigurerade storleken.",
|
"LabelBackupsMaxBackupSizeHelp": "Som ett skydd mot en felaktig konfiguration kommer säkerhetskopior inte att genomföras om de överskrider den konfigurerade storleken.",
|
||||||
@@ -271,6 +275,7 @@
|
|||||||
"LabelClosePlayer": "Stäng spelaren",
|
"LabelClosePlayer": "Stäng spelaren",
|
||||||
"LabelCodec": "Codec",
|
"LabelCodec": "Codec",
|
||||||
"LabelCollapseSeries": "Komprimera serier",
|
"LabelCollapseSeries": "Komprimera serier",
|
||||||
|
"LabelCollapseSubSeries": "Komprimera underserier",
|
||||||
"LabelCollection": "Samling",
|
"LabelCollection": "Samling",
|
||||||
"LabelCollections": "Samlingar",
|
"LabelCollections": "Samlingar",
|
||||||
"LabelComplete": "Komplett",
|
"LabelComplete": "Komplett",
|
||||||
@@ -339,6 +344,7 @@
|
|||||||
"LabelEpisodic": "Uppdelad i avsnitt",
|
"LabelEpisodic": "Uppdelad i avsnitt",
|
||||||
"LabelExample": "Exempel",
|
"LabelExample": "Exempel",
|
||||||
"LabelExpandSeries": "Expandera serier",
|
"LabelExpandSeries": "Expandera serier",
|
||||||
|
"LabelExpandSubSeries": "Expandera Underserier",
|
||||||
"LabelExplicit": "Explicit version",
|
"LabelExplicit": "Explicit version",
|
||||||
"LabelExplicitChecked": "Explicit version (markerad)",
|
"LabelExplicitChecked": "Explicit version (markerad)",
|
||||||
"LabelExplicitUnchecked": "Ej Explicit version (ej markerad)",
|
"LabelExplicitUnchecked": "Ej Explicit version (ej markerad)",
|
||||||
@@ -362,6 +368,7 @@
|
|||||||
"LabelFontItalic": "Kursiv",
|
"LabelFontItalic": "Kursiv",
|
||||||
"LabelFontScale": "Skala på typsnitt",
|
"LabelFontScale": "Skala på typsnitt",
|
||||||
"LabelFontStrikethrough": "Genomstruken",
|
"LabelFontStrikethrough": "Genomstruken",
|
||||||
|
"LabelFormat": "Format",
|
||||||
"LabelFull": "Komplett",
|
"LabelFull": "Komplett",
|
||||||
"LabelGenre": "Kategori",
|
"LabelGenre": "Kategori",
|
||||||
"LabelGenres": "Kategorier",
|
"LabelGenres": "Kategorier",
|
||||||
@@ -434,6 +441,8 @@
|
|||||||
"LabelMissing": "Saknar",
|
"LabelMissing": "Saknar",
|
||||||
"LabelMissingEbook": "Saknar e-bok",
|
"LabelMissingEbook": "Saknar e-bok",
|
||||||
"LabelMissingSupplementaryEbook": "Saknar kompletterande e-bok",
|
"LabelMissingSupplementaryEbook": "Saknar kompletterande e-bok",
|
||||||
|
"LabelMobileRedirectURIs": "Tillåtna mobila omdirigerings-URI:er",
|
||||||
|
"LabelMobileRedirectURIsDescription": "Detta är en vitlista över giltiga omdirigerings-URI:er för mobila appar. Standard är <code>audiobookshelf://oauth</code>, som du kan radera eller komplettera med ytterligare URI:er för integrering av tredje-parts appar. Används ett asterisk (<code>*</code>) som enda inmatning tillåts alla URI:er.",
|
||||||
"LabelMore": "Mer",
|
"LabelMore": "Mer",
|
||||||
"LabelMoreInfo": "Mer information",
|
"LabelMoreInfo": "Mer information",
|
||||||
"LabelName": "Namn",
|
"LabelName": "Namn",
|
||||||
@@ -544,7 +553,8 @@
|
|||||||
"LabelSettingsBookshelfViewHelp": "Bakgrund med ett utseende liknande en bokhylla i trä",
|
"LabelSettingsBookshelfViewHelp": "Bakgrund med ett utseende liknande en bokhylla i trä",
|
||||||
"LabelSettingsChromecastSupport": "Stöd för Chromecast",
|
"LabelSettingsChromecastSupport": "Stöd för Chromecast",
|
||||||
"LabelSettingsDateFormat": "Datumformat",
|
"LabelSettingsDateFormat": "Datumformat",
|
||||||
"LabelSettingsEnableWatcher": "Automatiskt upptäcka förändringar i biblioteket",
|
"LabelSettingsEnableWatcher": "Upptäck automatiskt förändringar i biblioteket",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Upptäck automatiskt förändringar i biblioteket",
|
||||||
"LabelSettingsEnableWatcherHelp": "Aktiverar automatik att upptäcka när objekt<br>adderas, uppdateras eller raderas.<br>OBS: Kräver en omstart av servern",
|
"LabelSettingsEnableWatcherHelp": "Aktiverar automatik att upptäcka när objekt<br>adderas, uppdateras eller raderas.<br>OBS: Kräver en omstart av servern",
|
||||||
"LabelSettingsEpubsAllowScriptedContent": "Tillåt e-böcker i epubs-format som innehåller script",
|
"LabelSettingsEpubsAllowScriptedContent": "Tillåt e-böcker i epubs-format som innehåller script",
|
||||||
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillåt att epub-filer får innehålla script.<br>Det rekommenderas att denna inställning är<br>avstängd när du inte litar på källan för epub-filerna.",
|
"LabelSettingsEpubsAllowScriptedContentHelp": "Tillåt att epub-filer får innehålla script.<br>Det rekommenderas att denna inställning är<br>avstängd när du inte litar på källan för epub-filerna.",
|
||||||
@@ -819,12 +829,13 @@
|
|||||||
"MessageResetChaptersConfirm": "Är du säker på att du vill återställa alla kapitel och ångra de ändringarna du gjort?",
|
"MessageResetChaptersConfirm": "Är du säker på att du vill återställa alla kapitel och ångra de ändringarna du gjort?",
|
||||||
"MessageRestoreBackupConfirm": "Är du säker på att du vill läsa in säkerhetskopian som skapades den",
|
"MessageRestoreBackupConfirm": "Är du säker på att du vill läsa in säkerhetskopian som skapades den",
|
||||||
"MessageRestoreBackupWarning": "Att återställa en säkerhetskopia kommer att skriva över hela databasen som finns i /config och omslagsbilder i /metadata/items & /metadata/authors.<br /><br />Säkerhetskopior ändrar inte några filer i dina biblioteksmappar. Om du har aktiverat serverinställningar för att lagra omslagskonst och metadata i dina biblioteksmappar säkerhetskopieras eller skrivs de inte över.<br /><br />Alla klienter som använder din server kommer att uppdateras automatiskt.",
|
"MessageRestoreBackupWarning": "Att återställa en säkerhetskopia kommer att skriva över hela databasen som finns i /config och omslagsbilder i /metadata/items & /metadata/authors.<br /><br />Säkerhetskopior ändrar inte några filer i dina biblioteksmappar. Om du har aktiverat serverinställningar för att lagra omslagskonst och metadata i dina biblioteksmappar säkerhetskopieras eller skrivs de inte över.<br /><br />Alla klienter som använder din server kommer att uppdateras automatiskt.",
|
||||||
"MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas att denna funktion ej aktiveras. Istället bör funktionen som automatisk upptäcker ändringar av filerna vara aktiverad. För vissa filsystem (som t.ex. NFS) fungerar inte denna funktion. Då kan schemalagda skanningar av biblioteken användas istället.",
|
"MessageScheduleLibraryScanNote": "För de flesta användare rekommenderas att denna funktion ej aktiveras. Istället bör funktionen som automatisk upptäcker ändringar i biblioteket vara aktiverad. För vissa filsystem (som t.ex. NFS) fungerar inte denna funktion. Då kan schemalagda skanningar av biblioteken användas istället.",
|
||||||
"MessageScheduleRunEveryWeekdayAtTime": "Startar varje {0} klockan {1}",
|
"MessageScheduleRunEveryWeekdayAtTime": "Startar varje {0} klockan {1}",
|
||||||
"MessageSearchResultsFor": "Sökresultat för",
|
"MessageSearchResultsFor": "Sökresultat för",
|
||||||
"MessageSelected": "{0} valda",
|
"MessageSelected": "{0} valda",
|
||||||
"MessageServerCouldNotBeReached": "Servern kunde inte nås",
|
"MessageServerCouldNotBeReached": "Servern kunde inte nås",
|
||||||
"MessageSetChaptersFromTracksDescription": "Använd varje ljudfil som ett kapitel och ljudfilens namn som kapitlets rubrik",
|
"MessageSetChaptersFromTracksDescription": "Använd varje ljudfil som ett kapitel och ljudfilens namn som kapitlets rubrik",
|
||||||
|
"MessageShareExpiresIn": "Upphör om {0}",
|
||||||
"MessageStartPlaybackAtTime": "Starta uppspelning av \"{0}\" vid tidpunkt {1}?",
|
"MessageStartPlaybackAtTime": "Starta uppspelning av \"{0}\" vid tidpunkt {1}?",
|
||||||
"MessageTaskAudioFileNotWritable": "Det går inte att skriva till ljudfilen \"{0}\"",
|
"MessageTaskAudioFileNotWritable": "Det går inte att skriva till ljudfilen \"{0}\"",
|
||||||
"MessageTaskCanceledByUser": "Uppgiften avslutades av användaren",
|
"MessageTaskCanceledByUser": "Uppgiften avslutades av användaren",
|
||||||
@@ -909,6 +920,7 @@
|
|||||||
"ToastAuthorUpdateMerged": "Författaren sammanslagen",
|
"ToastAuthorUpdateMerged": "Författaren sammanslagen",
|
||||||
"ToastAuthorUpdateSuccess": "Författaren uppdaterad",
|
"ToastAuthorUpdateSuccess": "Författaren uppdaterad",
|
||||||
"ToastAuthorUpdateSuccessNoImageFound": "Författaren uppdaterad (ingen bild hittad)",
|
"ToastAuthorUpdateSuccessNoImageFound": "Författaren uppdaterad (ingen bild hittad)",
|
||||||
|
"ToastBackupAppliedSuccess": "Säkerhetskopian är importerad",
|
||||||
"ToastBackupCreateFailed": "Det gick inte att skapa en säkerhetskopia",
|
"ToastBackupCreateFailed": "Det gick inte att skapa en säkerhetskopia",
|
||||||
"ToastBackupCreateSuccess": "Säkerhetskopian har skapats",
|
"ToastBackupCreateSuccess": "Säkerhetskopian har skapats",
|
||||||
"ToastBackupDeleteFailed": "Det gick inte att radera säkerhetskopian",
|
"ToastBackupDeleteFailed": "Det gick inte att radera säkerhetskopian",
|
||||||
@@ -918,6 +930,7 @@
|
|||||||
"ToastBackupRestoreFailed": "Det gick inte att återställa säkerhetskopian",
|
"ToastBackupRestoreFailed": "Det gick inte att återställa säkerhetskopian",
|
||||||
"ToastBackupUploadFailed": "Det gick inte att ladda upp säkerhetskopian",
|
"ToastBackupUploadFailed": "Det gick inte att ladda upp säkerhetskopian",
|
||||||
"ToastBackupUploadSuccess": "Säkerhetskopian uppladdad",
|
"ToastBackupUploadSuccess": "Säkerhetskopian uppladdad",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "Informationen har adderats till alla objekt",
|
||||||
"ToastBatchQuickMatchStarted": "Snabbmatchning av {0} böcker har påbörjats!",
|
"ToastBatchQuickMatchStarted": "Snabbmatchning av {0} böcker har påbörjats!",
|
||||||
"ToastBatchUpdateFailed": "Batchuppdateringen misslyckades",
|
"ToastBatchUpdateFailed": "Batchuppdateringen misslyckades",
|
||||||
"ToastBatchUpdateSuccess": "Batchuppdateringen lyckades",
|
"ToastBatchUpdateSuccess": "Batchuppdateringen lyckades",
|
||||||
@@ -948,6 +961,8 @@
|
|||||||
"ToastEpisodeDownloadQueueClearSuccess": "Kö för nedladdning av avsnitt har tömts",
|
"ToastEpisodeDownloadQueueClearSuccess": "Kö för nedladdning av avsnitt har tömts",
|
||||||
"ToastEpisodeUpdateSuccess": "{0} avsnitt uppdaterades",
|
"ToastEpisodeUpdateSuccess": "{0} avsnitt uppdaterades",
|
||||||
"ToastFailedToLoadData": "Misslyckades med att ladda data",
|
"ToastFailedToLoadData": "Misslyckades med att ladda data",
|
||||||
|
"ToastFailedToMatch": "Misslyckades med att matcha",
|
||||||
|
"ToastFailedToShare": "Misslyckades med att dela",
|
||||||
"ToastFailedToUpdate": "Misslyckades med att uppdatera",
|
"ToastFailedToUpdate": "Misslyckades med att uppdatera",
|
||||||
"ToastInvalidImageUrl": "Felaktig URL-adress till omslagsbilden",
|
"ToastInvalidImageUrl": "Felaktig URL-adress till omslagsbilden",
|
||||||
"ToastInvalidMaxEpisodesToDownload": "Ogiltigt maximalt antal avsnitt att ladda ner",
|
"ToastInvalidMaxEpisodesToDownload": "Ogiltigt maximalt antal avsnitt att ladda ner",
|
||||||
@@ -1030,6 +1045,7 @@
|
|||||||
"ToastSortingPrefixesUpdateSuccess": "{0} begrepp för sortering har uppdateras",
|
"ToastSortingPrefixesUpdateSuccess": "{0} begrepp för sortering har uppdateras",
|
||||||
"ToastTitleRequired": "En titel måste anges",
|
"ToastTitleRequired": "En titel måste anges",
|
||||||
"ToastUnknownError": "Ett okänt fel inträffade",
|
"ToastUnknownError": "Ett okänt fel inträffade",
|
||||||
|
"ToastUploaderFilepathExistsError": "En fil med namnet \"{0}\" finns redan på servern",
|
||||||
"ToastUserDeleteFailed": "Misslyckades med att ta bort användaren",
|
"ToastUserDeleteFailed": "Misslyckades med att ta bort användaren",
|
||||||
"ToastUserDeleteSuccess": "Användaren borttagen",
|
"ToastUserDeleteSuccess": "Användaren borttagen",
|
||||||
"ToastUserPasswordChangeSuccess": "Lösenordet har ändrats",
|
"ToastUserPasswordChangeSuccess": "Lösenordet har ändrats",
|
||||||
|
|||||||
+96
-2
@@ -47,6 +47,8 @@
|
|||||||
"ButtonLookup": "Sorgula",
|
"ButtonLookup": "Sorgula",
|
||||||
"ButtonManageTracks": "Parçaları Yönet",
|
"ButtonManageTracks": "Parçaları Yönet",
|
||||||
"ButtonMapChapterTitles": "Bölüm Başlıklarını Haritalandır",
|
"ButtonMapChapterTitles": "Bölüm Başlıklarını Haritalandır",
|
||||||
|
"ButtonMatchAllAuthors": "Bütün Yazarlarla Eşleştir",
|
||||||
|
"ButtonMatchBooks": "Kitapları Eşleştir",
|
||||||
"ButtonNevermind": "Vazgeç",
|
"ButtonNevermind": "Vazgeç",
|
||||||
"ButtonNext": "Sonraki",
|
"ButtonNext": "Sonraki",
|
||||||
"ButtonNextChapter": "Sonraki Bölüm",
|
"ButtonNextChapter": "Sonraki Bölüm",
|
||||||
@@ -66,6 +68,9 @@
|
|||||||
"ButtonPurgeItemsCache": "Öğenin Önbelleğini Temizle",
|
"ButtonPurgeItemsCache": "Öğenin Önbelleğini Temizle",
|
||||||
"ButtonQueueAddItem": "Sıraya ekle",
|
"ButtonQueueAddItem": "Sıraya ekle",
|
||||||
"ButtonQueueRemoveItem": "Sıradan çıkar",
|
"ButtonQueueRemoveItem": "Sıradan çıkar",
|
||||||
|
"ButtonQuickEmbed": "Hızlı Embed",
|
||||||
|
"ButtonQuickEmbedMetadata": "Hızlı Embed Üstverisi",
|
||||||
|
"ButtonQuickMatch": "Hızlı Eşleştirme",
|
||||||
"ButtonReScan": "Yeniden Tara",
|
"ButtonReScan": "Yeniden Tara",
|
||||||
"ButtonRead": "Oku",
|
"ButtonRead": "Oku",
|
||||||
"ButtonReadLess": "Daha az göster",
|
"ButtonReadLess": "Daha az göster",
|
||||||
@@ -74,14 +79,37 @@
|
|||||||
"ButtonRemove": "Kaldır",
|
"ButtonRemove": "Kaldır",
|
||||||
"ButtonRemoveAll": "Hepsini Sil",
|
"ButtonRemoveAll": "Hepsini Sil",
|
||||||
"ButtonRemoveAllLibraryItems": "Bütün Kütüphane Öğelerini Sil",
|
"ButtonRemoveAllLibraryItems": "Bütün Kütüphane Öğelerini Sil",
|
||||||
|
"ButtonRemoveFromContinueListening": "Dinlemeye Devam Etmekten Çıkar",
|
||||||
|
"ButtonRemoveFromContinueReading": "Okumaya Devam Etmekten Çıkar",
|
||||||
|
"ButtonRemoveSeriesFromContinueSeries": "Seriye Devam Et'ten Seriyi Çıkar",
|
||||||
|
"ButtonReset": "Sıfırla",
|
||||||
|
"ButtonResetToDefault": "Varsayılana sıfırla",
|
||||||
|
"ButtonRestore": "Geri getir",
|
||||||
"ButtonSave": "Kaydet",
|
"ButtonSave": "Kaydet",
|
||||||
|
"ButtonSaveAndClose": "Kaydet & Kapat",
|
||||||
|
"ButtonSaveTracklist": "Parça Listesini Kaydet",
|
||||||
|
"ButtonScan": "Tara",
|
||||||
|
"ButtonScanLibrary": "Kütüphaneyi Tara",
|
||||||
|
"ButtonScrollLeft": "Sola Kaydır",
|
||||||
|
"ButtonScrollRight": "Sağa Kaydır",
|
||||||
"ButtonSearch": "Ara",
|
"ButtonSearch": "Ara",
|
||||||
"ButtonSeries": "Dizi",
|
"ButtonSelectFolderPath": "Klasör Yolunu Seç",
|
||||||
|
"ButtonSeries": "Seriler",
|
||||||
"ButtonSubmit": "Gönder",
|
"ButtonSubmit": "Gönder",
|
||||||
|
"ButtonViewAll": "Tümünü Görüntüle",
|
||||||
"ButtonYes": "Evet",
|
"ButtonYes": "Evet",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Üst veriyi almakta hata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Üstveri alınamadı - başlık ve(ya) yazarı güncellemeyi deneyin",
|
||||||
|
"ErrorUploadLacksTitle": "Başlığa sahip olmalı",
|
||||||
"HeaderAccount": "Hesap",
|
"HeaderAccount": "Hesap",
|
||||||
|
"HeaderAddCustomMetadataProvider": "Özel Üstveri Sağlayıcısı Ekle",
|
||||||
"HeaderAdvanced": "Gelişmiş",
|
"HeaderAdvanced": "Gelişmiş",
|
||||||
|
"HeaderAppriseNotificationSettings": "Bildirim Ayarlarının Haberini Ver",
|
||||||
"HeaderAudioTracks": "Ses Kanalları",
|
"HeaderAudioTracks": "Ses Kanalları",
|
||||||
|
"HeaderAudiobookTools": "Sesli Kitap Dosya Yönetim Araçları",
|
||||||
|
"HeaderAuthentication": "Kimlik Doğrulama",
|
||||||
|
"HeaderBackups": "Yedeklemeler",
|
||||||
|
"HeaderChangePassword": "Parolayı Değiştir",
|
||||||
"HeaderChapters": "Bölümler",
|
"HeaderChapters": "Bölümler",
|
||||||
"HeaderCollection": "Koleksiyon",
|
"HeaderCollection": "Koleksiyon",
|
||||||
"HeaderCollectionItems": "Koleksiyon Öğeleri",
|
"HeaderCollectionItems": "Koleksiyon Öğeleri",
|
||||||
@@ -89,37 +117,99 @@
|
|||||||
"HeaderEbookFiles": "Ebook Dosyaları",
|
"HeaderEbookFiles": "Ebook Dosyaları",
|
||||||
"HeaderEpisodes": "Bölümler",
|
"HeaderEpisodes": "Bölümler",
|
||||||
"HeaderEreaderSettings": "Ereader Ayarları",
|
"HeaderEreaderSettings": "Ereader Ayarları",
|
||||||
|
"HeaderFiles": "Dosyalar",
|
||||||
|
"HeaderIgnoredFiles": "Görmezden Gelinen Dosyalar",
|
||||||
|
"HeaderItemFiles": "Öğe Dosyaları",
|
||||||
|
"HeaderItemMetadataUtils": "Öğe Üstveri Araçları",
|
||||||
|
"HeaderLastListeningSession": "Son Dinleme Oturumu",
|
||||||
"HeaderLatestEpisodes": "En son bölümler",
|
"HeaderLatestEpisodes": "En son bölümler",
|
||||||
"HeaderLibraries": "Kütüphaneler",
|
"HeaderLibraries": "Kütüphaneler",
|
||||||
|
"HeaderLibraryFiles": "Kütüphane Dosyaları",
|
||||||
|
"HeaderLibraryStats": "Kütüphane İstatistikleri",
|
||||||
|
"HeaderListeningSessions": "Dinleme Oturumları",
|
||||||
|
"HeaderListeningStats": "Dinleme İstatistikleri",
|
||||||
|
"HeaderLogin": "Giriş Yap",
|
||||||
|
"HeaderLogs": "Günlükler",
|
||||||
|
"HeaderManageGenres": "Türleri Yönet",
|
||||||
|
"HeaderManageTags": "Etiketleri Yönet",
|
||||||
|
"HeaderMapDetails": "Detayları haritalandır",
|
||||||
|
"HeaderMatch": "Eşleştir",
|
||||||
|
"HeaderMetadataOrderOfPrecedence": "Üstveri öncelik sırası",
|
||||||
|
"HeaderMetadataToEmbed": "Gömülecek üstveri",
|
||||||
|
"HeaderNewAccount": "Yeni Hesap",
|
||||||
|
"HeaderNewLibrary": "Yeni Kütüphane",
|
||||||
|
"HeaderNotificationCreate": "Bildirim Oluştur",
|
||||||
|
"HeaderNotificationUpdate": "Güncelleme Bildirimi",
|
||||||
|
"HeaderNotifications": "Bildirimler",
|
||||||
"HeaderOpenRSSFeed": "RSS Akışını Aç",
|
"HeaderOpenRSSFeed": "RSS Akışını Aç",
|
||||||
|
"HeaderOtherFiles": "Diğer Dosyalar",
|
||||||
|
"HeaderPasswordAuthentication": "Parola Doğrulaması",
|
||||||
|
"HeaderPermissions": "İzinler",
|
||||||
|
"HeaderPlayerQueue": "Oynatıcı Sırası",
|
||||||
|
"HeaderPlayerSettings": "Oynatıcı Ayarları",
|
||||||
"HeaderPlaylist": "Oynatma listesi",
|
"HeaderPlaylist": "Oynatma listesi",
|
||||||
"HeaderPlaylistItems": "Oynatma Listesi Öğeleri",
|
"HeaderPlaylistItems": "Oynatma Listesi Öğeleri",
|
||||||
|
"HeaderPodcastsToAdd": "Eklenecek Podcastler",
|
||||||
|
"HeaderPreviewCover": "Kapak Önizlemesi",
|
||||||
"HeaderRSSFeedGeneral": "RSS Detayları",
|
"HeaderRSSFeedGeneral": "RSS Detayları",
|
||||||
"HeaderRSSFeedIsOpen": "RSS Akışı Açık",
|
"HeaderRSSFeedIsOpen": "RSS Akışı Açık",
|
||||||
|
"HeaderRSSFeeds": "RSS Bültenleri",
|
||||||
|
"HeaderRemoveEpisode": "Bölümü Kaldır",
|
||||||
|
"HeaderRemoveEpisodes": "{0} Bölümü Kaldır",
|
||||||
|
"HeaderSavedMediaProgress": "Kaydedilen Medya İlerleyişi",
|
||||||
|
"HeaderSchedule": "Program",
|
||||||
"HeaderSettings": "Ayarlar",
|
"HeaderSettings": "Ayarlar",
|
||||||
"HeaderSleepTimer": "Uyku Zamanlayıcısı",
|
"HeaderSleepTimer": "Uyku Zamanlayıcısı",
|
||||||
"HeaderStatsMinutesListeningChart": "Dinlenilen Dakika (son 7 gün)",
|
"HeaderStatsMinutesListeningChart": "Dinlenilen Dakika (son 7 gün)",
|
||||||
"HeaderStatsRecentSessions": "Geçmiş Oturumlar",
|
"HeaderStatsRecentSessions": "Geçmiş Oturumlar",
|
||||||
"HeaderTableOfContents": "İçindekiler",
|
"HeaderTableOfContents": "İçindekiler",
|
||||||
|
"HeaderUpdateAuthor": "Yazarı Güncelle",
|
||||||
|
"HeaderUpdateDetails": "Detayları Güncelle",
|
||||||
|
"HeaderUpdateLibrary": "Kütüphaneyi Güncelle",
|
||||||
|
"HeaderUsers": "Kullanıcılar",
|
||||||
|
"HeaderYearReview": "{0} Yılına Bir Bakış",
|
||||||
"HeaderYourStats": "İstatistiklerin",
|
"HeaderYourStats": "İstatistiklerin",
|
||||||
|
"LabelAccountType": "Hesap Türü",
|
||||||
|
"LabelAccountTypeAdmin": "Yönetici",
|
||||||
|
"LabelAccountTypeGuest": "Ziyaretçi",
|
||||||
|
"LabelAccountTypeUser": "Kullanıcı",
|
||||||
|
"LabelActivities": "Etkinlikler",
|
||||||
|
"LabelActivity": "Etkinlik",
|
||||||
|
"LabelAddToCollection": "Koleksiyona Ekle",
|
||||||
|
"LabelAddToCollectionBatch": "Koleksiyona {0} Kitap Ekle",
|
||||||
"LabelAddToPlaylist": "Oynatma Listesine Ekle",
|
"LabelAddToPlaylist": "Oynatma Listesine Ekle",
|
||||||
|
"LabelAddToPlaylistBatch": "Çalma Listesine {0} Öğe Ekle",
|
||||||
"LabelAddedAt": "Eklenme Zamanı",
|
"LabelAddedAt": "Eklenme Zamanı",
|
||||||
"LabelAddedDate": "Eklendi {0}",
|
"LabelAddedDate": "Eklendi {0}",
|
||||||
|
"LabelAdminUsersOnly": "Yalnızca yönetici kullanıcılar",
|
||||||
"LabelAll": "Hepsi",
|
"LabelAll": "Hepsi",
|
||||||
|
"LabelAllEpisodesDownloaded": "Tüm bölümler indirildi",
|
||||||
|
"LabelAllUsers": "Tüm Kullanıcılar",
|
||||||
|
"LabelAllUsersExcludingGuests": "Ziyaretçiler dışında tüm kullanıcılar",
|
||||||
|
"LabelAllUsersIncludingGuests": "Ziyaretçiler dahil tüm kullanıcılar",
|
||||||
|
"LabelAlreadyInYourLibrary": "Zaten kütüphanenizde var",
|
||||||
|
"LabelApiToken": "API Token",
|
||||||
"LabelAuthor": "Yazar",
|
"LabelAuthor": "Yazar",
|
||||||
"LabelAuthorFirstLast": "Yazar (İlk Son)",
|
"LabelAuthorFirstLast": "Yazar (İlk Son)",
|
||||||
"LabelAuthorLastFirst": "Yazar (Son, İlk)",
|
"LabelAuthorLastFirst": "Yazar (Son, İlk)",
|
||||||
"LabelAuthors": "Yazarlar",
|
"LabelAuthors": "Yazarlar",
|
||||||
"LabelAutoDownloadEpisodes": "Bölümleri Otomatik Olarak İndir",
|
"LabelAutoDownloadEpisodes": "Bölümleri Otomatik Olarak İndir",
|
||||||
"LabelBooks": "Kitaplar",
|
"LabelBooks": "Kitaplar",
|
||||||
|
"LabelChangePassword": "Şifreyi Değiştir",
|
||||||
"LabelChapters": "Bölümler",
|
"LabelChapters": "Bölümler",
|
||||||
|
"LabelClickForMoreInfo": "Daha fazla bilgi için tıklayın",
|
||||||
"LabelClosePlayer": "Oynatıcıyı kapat",
|
"LabelClosePlayer": "Oynatıcıyı kapat",
|
||||||
"LabelCollapseSeries": "Seriyi Daralt",
|
"LabelCollapseSeries": "Seriyi Daralt",
|
||||||
"LabelComplete": "Tamamlandı",
|
"LabelComplete": "Tamamlandı",
|
||||||
"LabelContinueListening": "Dinlemeye Devam Et",
|
"LabelContinueListening": "Dinlemeye Devam Et",
|
||||||
"LabelContinueReading": "Okumaya Devam Et",
|
"LabelContinueReading": "Okumaya Devam Et",
|
||||||
"LabelContinueSeries": "Seriye Devam Et",
|
"LabelContinueSeries": "Seriye Devam Et",
|
||||||
|
"LabelDeleteFromFileSystemCheckbox": "Dosya sisteminden sil (sadece veritabanından silmek için işareti kaldır)",
|
||||||
"LabelDescription": "Açıklama",
|
"LabelDescription": "Açıklama",
|
||||||
|
"LabelDeselectAll": "Tümünün Seçimini Kaldır",
|
||||||
|
"LabelDevice": "Cihaz",
|
||||||
|
"LabelDeviceInfo": "Cihaz Bilgisi",
|
||||||
|
"LabelDirectory": "Dizin",
|
||||||
"LabelDiscover": "Keşfet",
|
"LabelDiscover": "Keşfet",
|
||||||
"LabelDownload": "İndir",
|
"LabelDownload": "İndir",
|
||||||
"LabelDuration": "Süre",
|
"LabelDuration": "Süre",
|
||||||
@@ -179,6 +269,7 @@
|
|||||||
"LabelReadAgain": "Tekrar Oku",
|
"LabelReadAgain": "Tekrar Oku",
|
||||||
"LabelRecentlyAdded": "Yakınlarda Eklenmiş",
|
"LabelRecentlyAdded": "Yakınlarda Eklenmiş",
|
||||||
"LabelSeason": "Sezon",
|
"LabelSeason": "Sezon",
|
||||||
|
"LabelSeries": "Seriler",
|
||||||
"LabelSetEbookAsPrimary": "Birincil olarak ayarla",
|
"LabelSetEbookAsPrimary": "Birincil olarak ayarla",
|
||||||
"LabelSetEbookAsSupplementary": "Yedek olarak ayarla",
|
"LabelSetEbookAsSupplementary": "Yedek olarak ayarla",
|
||||||
"LabelShowAll": "Hepsini Göster",
|
"LabelShowAll": "Hepsini Göster",
|
||||||
@@ -205,5 +296,8 @@
|
|||||||
"LabelUnknown": "Bilinmeyen",
|
"LabelUnknown": "Bilinmeyen",
|
||||||
"LabelUser": "Kullanıcı",
|
"LabelUser": "Kullanıcı",
|
||||||
"LabelUsername": "Kullanıcı Adı",
|
"LabelUsername": "Kullanıcı Adı",
|
||||||
"LabelYourBookmarks": "Yer İşaretleriniz"
|
"LabelYearReviewHide": "Yıla Bakışı Sakla",
|
||||||
|
"LabelYearReviewShow": "Yıla Bakışı Göster",
|
||||||
|
"LabelYourBookmarks": "Yer İşaretleriniz",
|
||||||
|
"LabelYourProgress": "Gelişiminiz"
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-3
@@ -177,6 +177,7 @@
|
|||||||
"HeaderPlaylist": "Список відтворення",
|
"HeaderPlaylist": "Список відтворення",
|
||||||
"HeaderPlaylistItems": "Елементи списку відтворення",
|
"HeaderPlaylistItems": "Елементи списку відтворення",
|
||||||
"HeaderPodcastsToAdd": "Додати подкасти",
|
"HeaderPodcastsToAdd": "Додати подкасти",
|
||||||
|
"HeaderPresets": "Пресети",
|
||||||
"HeaderPreviewCover": "Попередній перегляд",
|
"HeaderPreviewCover": "Попередній перегляд",
|
||||||
"HeaderRSSFeedGeneral": "Подробиці RSS",
|
"HeaderRSSFeedGeneral": "Подробиці RSS",
|
||||||
"HeaderRSSFeedIsOpen": "RSS-канал відкрито",
|
"HeaderRSSFeedIsOpen": "RSS-канал відкрито",
|
||||||
@@ -530,6 +531,7 @@
|
|||||||
"LabelReleaseDate": "Дата публікації",
|
"LabelReleaseDate": "Дата публікації",
|
||||||
"LabelRemoveAllMetadataAbs": "Видалити всі файли metadata.abs",
|
"LabelRemoveAllMetadataAbs": "Видалити всі файли metadata.abs",
|
||||||
"LabelRemoveAllMetadataJson": "Видалити всі файли metadata.json",
|
"LabelRemoveAllMetadataJson": "Видалити всі файли metadata.json",
|
||||||
|
"LabelRemoveAudibleBranding": "Видалити звуковий вступ та завершення з розділів",
|
||||||
"LabelRemoveCover": "Видалити обкладинку",
|
"LabelRemoveCover": "Видалити обкладинку",
|
||||||
"LabelRemoveMetadataFile": "Видалити файли метаданих у папках елементів бібліотеки",
|
"LabelRemoveMetadataFile": "Видалити файли метаданих у папках елементів бібліотеки",
|
||||||
"LabelRemoveMetadataFileHelp": "Видалити всі файли metadata.json та metadata.abs у ваших папках {0}.",
|
"LabelRemoveMetadataFileHelp": "Видалити всі файли metadata.json та metadata.abs у ваших папках {0}.",
|
||||||
@@ -604,10 +606,11 @@
|
|||||||
"LabelSlug": "Назва",
|
"LabelSlug": "Назва",
|
||||||
"LabelSortAscending": "По зростанню",
|
"LabelSortAscending": "По зростанню",
|
||||||
"LabelSortDescending": "По спаданню",
|
"LabelSortDescending": "По спаданню",
|
||||||
|
"LabelSortPubDate": "Сортувати дату публікації",
|
||||||
"LabelStart": "Початок",
|
"LabelStart": "Початок",
|
||||||
"LabelStartTime": "Час початку",
|
"LabelStartTime": "Час початку",
|
||||||
"LabelStarted": "Почато",
|
"LabelStarted": "Стартував",
|
||||||
"LabelStartedAt": "Почато",
|
"LabelStartedAt": "Почато з",
|
||||||
"LabelStatsAudioTracks": "Аудіодоріжки",
|
"LabelStatsAudioTracks": "Аудіодоріжки",
|
||||||
"LabelStatsAuthors": "Автори",
|
"LabelStatsAuthors": "Автори",
|
||||||
"LabelStatsBestDay": "Найкращий день",
|
"LabelStatsBestDay": "Найкращий день",
|
||||||
@@ -704,6 +707,8 @@
|
|||||||
"LabelYourProgress": "Ваш прогрес",
|
"LabelYourProgress": "Ваш прогрес",
|
||||||
"MessageAddToPlayerQueue": "Додати до черги відтворення",
|
"MessageAddToPlayerQueue": "Додати до черги відтворення",
|
||||||
"MessageAppriseDescription": "Щоб скористатися цією функцією, вам потрібно мати запущену <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> або API, що оброблятиме ті ж запити. <br />Аби надсилати сповіщення, URL-адреса API Apprise мусить бути повною, наприклад, якщо ваш 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> або API, що оброблятиме ті ж запити. <br />Аби надсилати сповіщення, URL-адреса API Apprise мусить бути повною, наприклад, якщо ваш API розміщено за адресою <code>http://192.168.1.1:8337</code>, то необхідно вказати адресу <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "Переконайтесь, що ви використовуєте ASIN з правильної регіональної Audible зони, а не з Amazon.",
|
||||||
|
"MessageAuthenticationOIDCChangesRestart": "Перезавантажте сервер після збереження, щоб застосувати зміни OIDC.",
|
||||||
"MessageBackupsDescription": "Резервні копії містять користувачів, прогрес, подробиці елементів бібліотеки, налаштування сервера та зображення з <code>/metadata/items</code> та <code>/metadata/authors</code>. Резервні копії <strong>не</strong> містять жодних файлів з тек бібліотеки.",
|
"MessageBackupsDescription": "Резервні копії містять користувачів, прогрес, подробиці елементів бібліотеки, налаштування сервера та зображення з <code>/metadata/items</code> та <code>/metadata/authors</code>. Резервні копії <strong>не</strong> містять жодних файлів з тек бібліотеки.",
|
||||||
"MessageBackupsLocationEditNote": "Примітка: оновлення розташування резервної копії не переносить та не змінює існуючих копій",
|
"MessageBackupsLocationEditNote": "Примітка: оновлення розташування резервної копії не переносить та не змінює існуючих копій",
|
||||||
"MessageBackupsLocationNoEditNote": "Примітка: розташування резервної копії встановлюється за допомогою змінної середовища та не може бути змінене тут.",
|
"MessageBackupsLocationNoEditNote": "Примітка: розташування резервної копії встановлюється за допомогою змінної середовища та не може бути змінене тут.",
|
||||||
@@ -722,6 +727,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "Час початку мусить бути меншим за тривалість аудіокниги",
|
"MessageChapterErrorStartGteDuration": "Час початку мусить бути меншим за тривалість аудіокниги",
|
||||||
"MessageChapterErrorStartLtPrev": "Неприпустимий час початку, має бути більшим за час початку попередньої глави",
|
"MessageChapterErrorStartLtPrev": "Неприпустимий час початку, має бути більшим за час початку попередньої глави",
|
||||||
"MessageChapterStartIsAfter": "Початок глави знаходиться після закінчення книги",
|
"MessageChapterStartIsAfter": "Початок глави знаходиться після закінчення книги",
|
||||||
|
"MessageChaptersNotFound": "Розділи не знайдені",
|
||||||
"MessageCheckingCron": "Перевірка планувальника...",
|
"MessageCheckingCron": "Перевірка планувальника...",
|
||||||
"MessageConfirmCloseFeed": "Ви дійсно бажаєте закрити цей канал?",
|
"MessageConfirmCloseFeed": "Ви дійсно бажаєте закрити цей канал?",
|
||||||
"MessageConfirmDeleteBackup": "Ви дійсно бажаєте видалити резервну копію за {0}?",
|
"MessageConfirmDeleteBackup": "Ви дійсно бажаєте видалити резервну копію за {0}?",
|
||||||
@@ -778,6 +784,7 @@
|
|||||||
"MessageForceReScanDescription": "Просканує усі файли заново, неначе вперше. ID3-мітки, файли OPF та текстові файли будуть проскановані як нові.",
|
"MessageForceReScanDescription": "Просканує усі файли заново, неначе вперше. ID3-мітки, файли OPF та текстові файли будуть проскановані як нові.",
|
||||||
"MessageImportantNotice": "Важливе повідомлення!",
|
"MessageImportantNotice": "Важливе повідомлення!",
|
||||||
"MessageInsertChapterBelow": "Введіть главу нижче",
|
"MessageInsertChapterBelow": "Введіть главу нижче",
|
||||||
|
"MessageInvalidAsin": "Невірний ASIN",
|
||||||
"MessageItemsSelected": "Вибрано елементів: {0}",
|
"MessageItemsSelected": "Вибрано елементів: {0}",
|
||||||
"MessageItemsUpdated": "Оновлено елементів: {0}",
|
"MessageItemsUpdated": "Оновлено елементів: {0}",
|
||||||
"MessageJoinUsOn": "Приєднуйтесь до",
|
"MessageJoinUsOn": "Приєднуйтесь до",
|
||||||
@@ -811,7 +818,7 @@
|
|||||||
"MessageNoItems": "Елементи відсутні",
|
"MessageNoItems": "Елементи відсутні",
|
||||||
"MessageNoItemsFound": "Елементів не знайдено",
|
"MessageNoItemsFound": "Елементів не знайдено",
|
||||||
"MessageNoListeningSessions": "Сеанси прослуховування відсутні",
|
"MessageNoListeningSessions": "Сеанси прослуховування відсутні",
|
||||||
"MessageNoLogs": "Журнал порожній",
|
"MessageNoLogs": "Немає журнали",
|
||||||
"MessageNoMediaProgress": "Прогрес відсутній",
|
"MessageNoMediaProgress": "Прогрес відсутній",
|
||||||
"MessageNoNotifications": "Сповіщення відсутні",
|
"MessageNoNotifications": "Сповіщення відсутні",
|
||||||
"MessageNoPodcastFeed": "Невірний подкаст: Немає каналу",
|
"MessageNoPodcastFeed": "Невірний подкаст: Немає каналу",
|
||||||
@@ -967,6 +974,8 @@
|
|||||||
"ToastCachePurgeFailed": "Не вдалося очистити кеш",
|
"ToastCachePurgeFailed": "Не вдалося очистити кеш",
|
||||||
"ToastCachePurgeSuccess": "Кеш очищено",
|
"ToastCachePurgeSuccess": "Кеш очищено",
|
||||||
"ToastChaptersHaveErrors": "Глави містять помилки",
|
"ToastChaptersHaveErrors": "Глави містять помилки",
|
||||||
|
"ToastChaptersInvalidShiftAmountLast": "Недійсна тривалість зсуву. Час початку останнього розділу перевищує тривалість цієї аудіокниги.",
|
||||||
|
"ToastChaptersInvalidShiftAmountStart": "Недійсна величина зсуву. Перший розділ матиме нульову або від’ємну тривалість і буде перезаписаний другим розділом. Збільште початкову тривалість другого розділу.",
|
||||||
"ToastChaptersMustHaveTitles": "Глави повинні мати назви",
|
"ToastChaptersMustHaveTitles": "Глави повинні мати назви",
|
||||||
"ToastChaptersRemoved": "Розділи видалені",
|
"ToastChaptersRemoved": "Розділи видалені",
|
||||||
"ToastChaptersUpdated": "Розділи оновлені",
|
"ToastChaptersUpdated": "Розділи оновлені",
|
||||||
@@ -1086,6 +1095,8 @@
|
|||||||
"ToastUnknownError": "Невідома помилка",
|
"ToastUnknownError": "Невідома помилка",
|
||||||
"ToastUnlinkOpenIdFailed": "Не вдалося відв'язати користувача від OpenID",
|
"ToastUnlinkOpenIdFailed": "Не вдалося відв'язати користувача від OpenID",
|
||||||
"ToastUnlinkOpenIdSuccess": "Користувача відв'язано від OpenID",
|
"ToastUnlinkOpenIdSuccess": "Користувача відв'язано від OpenID",
|
||||||
|
"ToastUploaderFilepathExistsError": "Шлях до файлу \"{0}\" уже існує на сервері",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "Елемент \"{0}\" використовує підкаталог шляху завантаження.",
|
||||||
"ToastUserDeleteFailed": "Не вдалося видалити користувача",
|
"ToastUserDeleteFailed": "Не вдалося видалити користувача",
|
||||||
"ToastUserDeleteSuccess": "Користувача видалено",
|
"ToastUserDeleteSuccess": "Користувача видалено",
|
||||||
"ToastUserPasswordChangeSuccess": "Пароль успішно змінено",
|
"ToastUserPasswordChangeSuccess": "Пароль успішно змінено",
|
||||||
|
|||||||
@@ -229,6 +229,7 @@
|
|||||||
"LabelAddedDate": "已添加 {0}",
|
"LabelAddedDate": "已添加 {0}",
|
||||||
"LabelAdminUsersOnly": "仅限管理员用户",
|
"LabelAdminUsersOnly": "仅限管理员用户",
|
||||||
"LabelAll": "全部",
|
"LabelAll": "全部",
|
||||||
|
"LabelAllEpisodesDownloaded": "所有剧集已下载",
|
||||||
"LabelAllUsers": "所有用户",
|
"LabelAllUsers": "所有用户",
|
||||||
"LabelAllUsersExcludingGuests": "除访客外的所有用户",
|
"LabelAllUsersExcludingGuests": "除访客外的所有用户",
|
||||||
"LabelAllUsersIncludingGuests": "包括访客的所有用户",
|
"LabelAllUsersIncludingGuests": "包括访客的所有用户",
|
||||||
@@ -603,6 +604,7 @@
|
|||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "升序",
|
"LabelSortAscending": "升序",
|
||||||
"LabelSortDescending": "降序",
|
"LabelSortDescending": "降序",
|
||||||
|
"LabelSortPubDate": "按出版日期排序",
|
||||||
"LabelStart": "开始",
|
"LabelStart": "开始",
|
||||||
"LabelStartTime": "开始时间",
|
"LabelStartTime": "开始时间",
|
||||||
"LabelStarted": "开始于",
|
"LabelStarted": "开始于",
|
||||||
@@ -703,6 +705,7 @@
|
|||||||
"LabelYourProgress": "你的进度",
|
"LabelYourProgress": "你的进度",
|
||||||
"MessageAddToPlayerQueue": "添加到播放队列",
|
"MessageAddToPlayerQueue": "添加到播放队列",
|
||||||
"MessageAppriseDescription": "要使用此功能,你需要运行一个 <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> 实例或一个可以处理这些相同请求的 API. <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> 实例或一个可以处理这些相同请求的 API. <br />Apprise API Url 应该是发送通知的完整 URL 路径, 例如: 如果你的 API 实例运行在 <code>http://192.168.1.1:8337</code>, 那么你可以输入 <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageAsinCheck": "确保你使用的 ASIN 来自正确的 Audible 地区, 而不是亚马逊.",
|
||||||
"MessageBackupsDescription": "备份包括用户, 用户进度, 媒体库项目详细信息, 服务器设置和图像, 存储在 <code>/metadata/items</code> & <code>/metadata/authors</code>. 备份不包括存储在你的媒体库文件夹中的任何文件.",
|
"MessageBackupsDescription": "备份包括用户, 用户进度, 媒体库项目详细信息, 服务器设置和图像, 存储在 <code>/metadata/items</code> & <code>/metadata/authors</code>. 备份不包括存储在你的媒体库文件夹中的任何文件.",
|
||||||
"MessageBackupsLocationEditNote": "注意: 更新备份位置不会移动或修改现有备份",
|
"MessageBackupsLocationEditNote": "注意: 更新备份位置不会移动或修改现有备份",
|
||||||
"MessageBackupsLocationNoEditNote": "注意: 备份位置是通过环境变量设置的, 不能在此处更改.",
|
"MessageBackupsLocationNoEditNote": "注意: 备份位置是通过环境变量设置的, 不能在此处更改.",
|
||||||
@@ -721,6 +724,7 @@
|
|||||||
"MessageChapterErrorStartGteDuration": "无效的开始时间, 必须小于有声读物持续时间",
|
"MessageChapterErrorStartGteDuration": "无效的开始时间, 必须小于有声读物持续时间",
|
||||||
"MessageChapterErrorStartLtPrev": "无效的开始时间, 必须大于或等于上一章节的开始时间",
|
"MessageChapterErrorStartLtPrev": "无效的开始时间, 必须大于或等于上一章节的开始时间",
|
||||||
"MessageChapterStartIsAfter": "章节开始是在有声读物结束之后",
|
"MessageChapterStartIsAfter": "章节开始是在有声读物结束之后",
|
||||||
|
"MessageChaptersNotFound": "未找到章节",
|
||||||
"MessageCheckingCron": "检查计划任务...",
|
"MessageCheckingCron": "检查计划任务...",
|
||||||
"MessageConfirmCloseFeed": "你确定要关闭此订阅源吗?",
|
"MessageConfirmCloseFeed": "你确定要关闭此订阅源吗?",
|
||||||
"MessageConfirmDeleteBackup": "你确定要删除备份 {0}?",
|
"MessageConfirmDeleteBackup": "你确定要删除备份 {0}?",
|
||||||
@@ -777,6 +781,7 @@
|
|||||||
"MessageForceReScanDescription": "将像重新扫描一样再次扫描所有文件. 音频文件 ID3 标签, OPF 文件和文本文件将被扫描为新文件.",
|
"MessageForceReScanDescription": "将像重新扫描一样再次扫描所有文件. 音频文件 ID3 标签, OPF 文件和文本文件将被扫描为新文件.",
|
||||||
"MessageImportantNotice": "重要通知!",
|
"MessageImportantNotice": "重要通知!",
|
||||||
"MessageInsertChapterBelow": "在下面插入章节",
|
"MessageInsertChapterBelow": "在下面插入章节",
|
||||||
|
"MessageInvalidAsin": "无效的 ASIN",
|
||||||
"MessageItemsSelected": "已选定 {0} 个项目",
|
"MessageItemsSelected": "已选定 {0} 个项目",
|
||||||
"MessageItemsUpdated": "已更新 {0} 个项目",
|
"MessageItemsUpdated": "已更新 {0} 个项目",
|
||||||
"MessageJoinUsOn": "加入我们",
|
"MessageJoinUsOn": "加入我们",
|
||||||
@@ -953,6 +958,7 @@
|
|||||||
"ToastBackupRestoreFailed": "备份还原失败",
|
"ToastBackupRestoreFailed": "备份还原失败",
|
||||||
"ToastBackupUploadFailed": "上传备份失败",
|
"ToastBackupUploadFailed": "上传备份失败",
|
||||||
"ToastBackupUploadSuccess": "备份已上传",
|
"ToastBackupUploadSuccess": "备份已上传",
|
||||||
|
"ToastBatchApplyDetailsToItemsSuccess": "应用于项目的详细信息",
|
||||||
"ToastBatchDeleteFailed": "批量删除失败",
|
"ToastBatchDeleteFailed": "批量删除失败",
|
||||||
"ToastBatchDeleteSuccess": "批量删除成功",
|
"ToastBatchDeleteSuccess": "批量删除成功",
|
||||||
"ToastBatchQuickMatchFailed": "批量快速匹配失败!",
|
"ToastBatchQuickMatchFailed": "批量快速匹配失败!",
|
||||||
@@ -1065,6 +1071,7 @@
|
|||||||
"ToastSelectAtLeastOneUser": "至少选择一位用户",
|
"ToastSelectAtLeastOneUser": "至少选择一位用户",
|
||||||
"ToastSendEbookToDeviceFailed": "发送电子书到设备失败",
|
"ToastSendEbookToDeviceFailed": "发送电子书到设备失败",
|
||||||
"ToastSendEbookToDeviceSuccess": "电子书已经发送到设备 \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "电子书已经发送到设备 \"{0}\"",
|
||||||
|
"ToastSeriesSubmitFailedSameName": "无法添加两个同名系列",
|
||||||
"ToastSeriesUpdateFailed": "更新系列失败",
|
"ToastSeriesUpdateFailed": "更新系列失败",
|
||||||
"ToastSeriesUpdateSuccess": "系列已更新",
|
"ToastSeriesUpdateSuccess": "系列已更新",
|
||||||
"ToastServerSettingsUpdateSuccess": "服务器设置已更新",
|
"ToastServerSettingsUpdateSuccess": "服务器设置已更新",
|
||||||
@@ -1083,6 +1090,8 @@
|
|||||||
"ToastUnknownError": "未知错误",
|
"ToastUnknownError": "未知错误",
|
||||||
"ToastUnlinkOpenIdFailed": "无法取消用户与 OpenID 的关联",
|
"ToastUnlinkOpenIdFailed": "无法取消用户与 OpenID 的关联",
|
||||||
"ToastUnlinkOpenIdSuccess": "用户已取消与 OpenID 的关联",
|
"ToastUnlinkOpenIdSuccess": "用户已取消与 OpenID 的关联",
|
||||||
|
"ToastUploaderFilepathExistsError": "文件路径 \"{0}\" 在服务器上已存在",
|
||||||
|
"ToastUploaderItemExistsInSubdirectoryError": "项目 \"{0}\" 正在使用上传路径的子目录。",
|
||||||
"ToastUserDeleteFailed": "删除用户失败",
|
"ToastUserDeleteFailed": "删除用户失败",
|
||||||
"ToastUserDeleteSuccess": "用户已删除",
|
"ToastUserDeleteSuccess": "用户已删除",
|
||||||
"ToastUserPasswordChangeSuccess": "密码修改成功",
|
"ToastUserPasswordChangeSuccess": "密码修改成功",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.20.0",
|
"version": "2.23.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.20.0",
|
"version": "2.23.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.20.0",
|
"version": "2.23.0",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast server",
|
"description": "Self-hosted audiobook and podcast server",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
+1
-4
@@ -20,10 +20,7 @@ class Auth {
|
|||||||
// Map of openId sessions indexed by oauth2 state-variable
|
// Map of openId sessions indexed by oauth2 state-variable
|
||||||
this.openIdAuthSession = new Map()
|
this.openIdAuthSession = new Map()
|
||||||
const escapedRouterBasePath = escapeRegExp(global.RouterBasePath)
|
const escapedRouterBasePath = escapeRegExp(global.RouterBasePath)
|
||||||
this.ignorePatterns = [
|
this.ignorePatterns = [new RegExp(`^(${escapedRouterBasePath}/api)?/items/[^/]+/cover$`), new RegExp(`^(${escapedRouterBasePath}/api)?/authors/[^/]+/image$`)]
|
||||||
new RegExp(`^(${escapedRouterBasePath}/api)?/items/[^/]+/cover$`),
|
|
||||||
new RegExp(`^(${escapedRouterBasePath}/api)?/authors/[^/]+/image$`)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+15
-6
@@ -310,10 +310,10 @@ class Server {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
router.use(express.urlencoded({ extended: true, limit: '5mb' }))
|
router.use(express.urlencoded({ extended: true, limit: '5mb' }))
|
||||||
router.use(express.json({ limit: '5mb' }))
|
router.use(express.json({ limit: '10mb' }))
|
||||||
|
|
||||||
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
||||||
router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router)
|
router.use('/hls', this.hlsRouter.router)
|
||||||
router.use('/public', this.publicRouter.router)
|
router.use('/public', this.publicRouter.router)
|
||||||
|
|
||||||
// Static path to generated nuxt
|
// Static path to generated nuxt
|
||||||
@@ -395,10 +395,19 @@ class Server {
|
|||||||
})
|
})
|
||||||
router.get('/healthcheck', (req, res) => res.sendStatus(200))
|
router.get('/healthcheck', (req, res) => res.sendStatus(200))
|
||||||
|
|
||||||
this.server.listen(this.Port, this.Host, () => {
|
const unixSocketPrefix = 'unix/'
|
||||||
if (this.Host) Logger.info(`Listening on http://${this.Host}:${this.Port}`)
|
if (this.Host?.startsWith(unixSocketPrefix)) {
|
||||||
else Logger.info(`Listening on port :${this.Port}`)
|
const sockPath = this.Host.slice(unixSocketPrefix.length)
|
||||||
})
|
this.server.listen(sockPath, async () => {
|
||||||
|
await fs.chmod(sockPath, 0o666)
|
||||||
|
Logger.info(`Listening on unix socket ${sockPath}`)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.server.listen(this.Port, this.Host, () => {
|
||||||
|
if (this.Host) Logger.info(`Listening on http://${this.Host}:${this.Port}`)
|
||||||
|
else Logger.info(`Listening on port :${this.Port}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Start listening for socket connections
|
// Start listening for socket connections
|
||||||
SocketAuthority.initialize(this)
|
SocketAuthority.initialize(this)
|
||||||
|
|||||||
@@ -84,6 +84,42 @@ class SocketAuthority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits event with library item to all clients that can access the library item
|
||||||
|
* Note: Emits toOldJSONExpanded()
|
||||||
|
*
|
||||||
|
* @param {string} evt
|
||||||
|
* @param {import('./models/LibraryItem')} libraryItem
|
||||||
|
*/
|
||||||
|
libraryItemEmitter(evt, libraryItem) {
|
||||||
|
for (const socketId in this.clients) {
|
||||||
|
if (this.clients[socketId].user?.checkCanAccessLibraryItem(libraryItem)) {
|
||||||
|
this.clients[socketId].socket.emit(evt, libraryItem.toOldJSONExpanded())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits event with library items to all clients that can access the library items
|
||||||
|
* Note: Emits toOldJSONExpanded()
|
||||||
|
*
|
||||||
|
* @param {string} evt
|
||||||
|
* @param {import('./models/LibraryItem')[]} libraryItems
|
||||||
|
*/
|
||||||
|
libraryItemsEmitter(evt, libraryItems) {
|
||||||
|
for (const socketId in this.clients) {
|
||||||
|
if (this.clients[socketId].user) {
|
||||||
|
const libraryItemsAccessibleToUser = libraryItems.filter((li) => this.clients[socketId].user.checkCanAccessLibraryItem(li))
|
||||||
|
if (libraryItemsAccessibleToUser.length) {
|
||||||
|
this.clients[socketId].socket.emit(
|
||||||
|
evt,
|
||||||
|
libraryItemsAccessibleToUser.map((li) => li.toOldJSONExpanded())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the Socket.IO server and disconnect all clients
|
* Closes the Socket.IO server and disconnect all clients
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -152,10 +152,7 @@ class AuthorController {
|
|||||||
for (const libraryItem of libraryItems) {
|
for (const libraryItem of libraryItems) {
|
||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
}
|
}
|
||||||
SocketAuthority.emitter(
|
SocketAuthority.libraryItemsEmitter('items_updated', libraryItems)
|
||||||
'items_updated',
|
|
||||||
libraryItems.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old author
|
// Remove old author
|
||||||
@@ -210,10 +207,7 @@ class AuthorController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (libraryItems.length) {
|
if (libraryItems.length) {
|
||||||
SocketAuthority.emitter(
|
SocketAuthority.libraryItemsEmitter('items_updated', libraryItems)
|
||||||
'items_updated',
|
|
||||||
libraryItems.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
numBooksForAuthor = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
numBooksForAuthor = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
||||||
|
|||||||
@@ -84,49 +84,67 @@ class FileSystemController {
|
|||||||
*/
|
*/
|
||||||
async checkPathExists(req, res) {
|
async checkPathExists(req, res) {
|
||||||
if (!req.user.canUpload) {
|
if (!req.user.canUpload) {
|
||||||
Logger.error(`[FileSystemController] Non-admin user "${req.user.username}" attempting to check path exists`)
|
Logger.error(`[FileSystemController] User "${req.user.username}" without upload permissions attempting to check path exists`)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { filepath, directory, folderPath } = req.body
|
const { directory, folderPath } = req.body
|
||||||
|
|
||||||
if (!filepath?.length || typeof filepath !== 'string') {
|
if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') {
|
||||||
|
Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`)
|
||||||
|
return res.status(400).json({
|
||||||
|
error: 'Invalid request body'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that library folder exists
|
||||||
|
const libraryFolder = await Database.libraryFolderModel.findOne({
|
||||||
|
where: {
|
||||||
|
path: folderPath
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!libraryFolder) {
|
||||||
|
Logger.error(`[FileSystemController] Library folder not found: ${folderPath}`)
|
||||||
|
return res.sendStatus(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filepath = Path.posix.join(libraryFolder.path, directory)
|
||||||
|
// Ensure filepath is inside library folder (prevents directory traversal)
|
||||||
|
if (!filepath.startsWith(libraryFolder.path)) {
|
||||||
|
Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`)
|
||||||
return res.sendStatus(400)
|
return res.sendStatus(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
const exists = await fs.pathExists(filepath)
|
if (await fs.pathExists(filepath)) {
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
return res.json({
|
return res.json({
|
||||||
exists: true
|
exists: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// If directory and folderPath are passed in, check if a library item exists in a subdirectory
|
// Check if a library item exists in a subdirectory
|
||||||
// See: https://github.com/advplyr/audiobookshelf/issues/4146
|
// See: https://github.com/advplyr/audiobookshelf/issues/4146
|
||||||
if (typeof directory === 'string' && typeof folderPath === 'string' && directory.length > 0 && folderPath.length > 0) {
|
const cleanedDirectory = directory.split('/').filter(Boolean).join('/')
|
||||||
const cleanedDirectory = directory.split('/').filter(Boolean).join('/')
|
if (cleanedDirectory.includes('/')) {
|
||||||
if (cleanedDirectory.includes('/')) {
|
// Can only be 2 levels deep
|
||||||
// Can only be 2 levels deep
|
const possiblePaths = []
|
||||||
const possiblePaths = []
|
const subdir = Path.dirname(directory)
|
||||||
const subdir = Path.dirname(directory)
|
possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir)))
|
||||||
possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir)))
|
if (subdir.includes('/')) {
|
||||||
if (subdir.includes('/')) {
|
possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir))))
|
||||||
possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir))))
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const libraryItem = await Database.libraryItemModel.findOne({
|
const libraryItem = await Database.libraryItemModel.findOne({
|
||||||
where: {
|
where: {
|
||||||
path: possiblePaths
|
path: possiblePaths
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (libraryItem) {
|
||||||
|
return res.json({
|
||||||
|
exists: true,
|
||||||
|
libraryItemTitle: libraryItem.title
|
||||||
})
|
})
|
||||||
|
|
||||||
if (libraryItem) {
|
|
||||||
return res.json({
|
|
||||||
exists: true,
|
|
||||||
libraryItemTitle: libraryItem.title
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1188,10 +1188,7 @@ class LibraryController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (itemsUpdated.length) {
|
if (itemsUpdated.length) {
|
||||||
SocketAuthority.emitter(
|
SocketAuthority.libraryItemsEmitter('items_updated', itemsUpdated)
|
||||||
'items_updated',
|
|
||||||
itemsUpdated.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@@ -1232,10 +1229,7 @@ class LibraryController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (itemsUpdated.length) {
|
if (itemsUpdated.length) {
|
||||||
SocketAuthority.emitter(
|
SocketAuthority.libraryItemsEmitter('items_updated', itemsUpdated)
|
||||||
'items_updated',
|
|
||||||
itemsUpdated.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ class LibraryItemController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug(`[LibraryItemController] Updated library item media ${req.libraryItem.media.title}`)
|
Logger.debug(`[LibraryItemController] Updated library item media ${req.libraryItem.media.title}`)
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
}
|
}
|
||||||
res.json({
|
res.json({
|
||||||
updated: hasUpdates,
|
updated: hasUpdates,
|
||||||
@@ -300,7 +300,7 @@ class LibraryItemController {
|
|||||||
req.libraryItem.changed('updatedAt', true)
|
req.libraryItem.changed('updatedAt', true)
|
||||||
await req.libraryItem.save()
|
await req.libraryItem.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
cover: result.cover
|
cover: result.cover
|
||||||
@@ -332,7 +332,7 @@ class LibraryItemController {
|
|||||||
req.libraryItem.changed('updatedAt', true)
|
req.libraryItem.changed('updatedAt', true)
|
||||||
await req.libraryItem.save()
|
await req.libraryItem.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
}
|
}
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -358,7 +358,7 @@ class LibraryItemController {
|
|||||||
|
|
||||||
await CacheManager.purgeCoverCache(req.libraryItem.id)
|
await CacheManager.purgeCoverCache(req.libraryItem.id)
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
@@ -485,7 +485,7 @@ class LibraryItemController {
|
|||||||
req.libraryItem.media.changed('audioFiles', true)
|
req.libraryItem.media.changed('audioFiles', true)
|
||||||
await req.libraryItem.media.save()
|
await req.libraryItem.media.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
res.json(req.libraryItem.toOldJSON())
|
res.json(req.libraryItem.toOldJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +663,7 @@ class LibraryItemController {
|
|||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
Logger.debug(`[LibraryItemController] Updated library item media "${libraryItem.media.title}"`)
|
Logger.debug(`[LibraryItemController] Updated library item media "${libraryItem.media.title}"`)
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
itemsUpdated++
|
itemsUpdated++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -834,8 +834,8 @@ class LibraryItemController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.libraryItem.isMissing || !req.libraryItem.isBook || !req.libraryItem.media.includedAudioFiles.length) {
|
if (req.libraryItem.isMissing || !req.libraryItem.isBook || !req.libraryItem.media.includedAudioFiles.length) {
|
||||||
Logger.error(`[LibraryItemController] Invalid library item`)
|
Logger.error(`[LibraryItemController] getMetadataObject: Invalid library item "${req.libraryItem.media.title}"`)
|
||||||
return res.sendStatus(500)
|
return res.sendStatus(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(this.audioMetadataManager.getMetadataObjectForApi(req.libraryItem))
|
res.json(this.audioMetadataManager.getMetadataObjectForApi(req.libraryItem))
|
||||||
@@ -894,7 +894,7 @@ class LibraryItemController {
|
|||||||
|
|
||||||
await req.libraryItem.saveMetadataFile()
|
await req.libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@@ -1005,7 +1005,7 @@ class LibraryItemController {
|
|||||||
|
|
||||||
await req.libraryItem.save()
|
await req.libraryItem.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1153,7 +1153,7 @@ class LibraryItemController {
|
|||||||
|
|
||||||
await req.libraryItem.save()
|
await req.libraryItem.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,25 +37,31 @@ class MiscController {
|
|||||||
Logger.warn(`User "${req.user.username}" attempted to upload without permission`)
|
Logger.warn(`User "${req.user.username}" attempted to upload without permission`)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
if (!req.files) {
|
if (!req.files || !Object.values(req.files).length) {
|
||||||
Logger.error('Invalid request, no files')
|
Logger.error('Invalid request, no files')
|
||||||
return res.sendStatus(400)
|
return res.sendStatus(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = Object.values(req.files)
|
const files = Object.values(req.files)
|
||||||
const { title, author, series, folder: folderId, library: libraryId } = req.body
|
let { title, author, series, folder: folderId, library: libraryId } = req.body
|
||||||
|
// Validate request body
|
||||||
|
if (!libraryId || !folderId || typeof libraryId !== 'string' || typeof folderId !== 'string' || !title || typeof title !== 'string') {
|
||||||
|
return res.status(400).send('Invalid request body')
|
||||||
|
}
|
||||||
|
if (!series || typeof series !== 'string') {
|
||||||
|
series = null
|
||||||
|
}
|
||||||
|
if (!author || typeof author !== 'string') {
|
||||||
|
author = null
|
||||||
|
}
|
||||||
|
|
||||||
const library = await Database.libraryModel.findByIdWithFolders(libraryId)
|
const library = await Database.libraryModel.findByIdWithFolders(libraryId)
|
||||||
if (!library) {
|
if (!library) {
|
||||||
return res.status(404).send(`Library not found with id ${libraryId}`)
|
return res.status(404).send('Library not found')
|
||||||
}
|
}
|
||||||
const folder = library.libraryFolders.find((fold) => fold.id === folderId)
|
const folder = library.libraryFolders.find((fold) => fold.id === folderId)
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
return res.status(404).send(`Folder not found with id ${folderId} in library ${library.name}`)
|
return res.status(404).send('Folder not found')
|
||||||
}
|
|
||||||
|
|
||||||
if (!files.length || !title) {
|
|
||||||
return res.status(500).send(`Invalid post data`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Podcasts should only be one folder deep
|
// Podcasts should only be one folder deep
|
||||||
@@ -216,7 +222,7 @@ class MiscController {
|
|||||||
|
|
||||||
// Update nameIgnorePrefix column on series
|
// Update nameIgnorePrefix column on series
|
||||||
const allSeries = await Database.seriesModel.findAll({
|
const allSeries = await Database.seriesModel.findAll({
|
||||||
attributes: ['id', 'name', 'nameIgnorePrefix']
|
attributes: ['id', 'name', 'nameIgnorePrefix', 'libraryId']
|
||||||
})
|
})
|
||||||
const bulkUpdateSeries = []
|
const bulkUpdateSeries = []
|
||||||
allSeries.forEach((series) => {
|
allSeries.forEach((series) => {
|
||||||
@@ -224,6 +230,8 @@ class MiscController {
|
|||||||
if (nameIgnorePrefix !== series.nameIgnorePrefix) {
|
if (nameIgnorePrefix !== series.nameIgnorePrefix) {
|
||||||
bulkUpdateSeries.push({
|
bulkUpdateSeries.push({
|
||||||
id: series.id,
|
id: series.id,
|
||||||
|
name: series.name,
|
||||||
|
libraryId: series.libraryId,
|
||||||
nameIgnorePrefix
|
nameIgnorePrefix
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -343,7 +351,7 @@ class MiscController {
|
|||||||
})
|
})
|
||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
numItemsUpdated++
|
numItemsUpdated++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +394,7 @@ class MiscController {
|
|||||||
})
|
})
|
||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
numItemsUpdated++
|
numItemsUpdated++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +489,7 @@ class MiscController {
|
|||||||
})
|
})
|
||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
numItemsUpdated++
|
numItemsUpdated++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,7 +532,7 @@ class MiscController {
|
|||||||
})
|
})
|
||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
numItemsUpdated++
|
numItemsUpdated++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ class PodcastController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_added', newLibraryItem)
|
||||||
|
|
||||||
res.json(newLibraryItem.toOldJSONExpanded())
|
res.json(newLibraryItem.toOldJSONExpanded())
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ class PodcastController {
|
|||||||
const overrideDetails = req.query.override === '1'
|
const overrideDetails = req.query.override === '1'
|
||||||
const episodesUpdated = await Scanner.quickMatchPodcastEpisodes(req.libraryItem, { overrideDetails })
|
const episodesUpdated = await Scanner.quickMatchPodcastEpisodes(req.libraryItem, { overrideDetails })
|
||||||
if (episodesUpdated) {
|
if (episodesUpdated) {
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@@ -418,7 +418,7 @@ class PodcastController {
|
|||||||
Logger.info(`[PodcastController] Updated episode "${episode.title}" keys`, episode.changed())
|
Logger.info(`[PodcastController] Updated episode "${episode.title}" keys`, episode.changed())
|
||||||
await episode.save()
|
await episode.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
} else {
|
} else {
|
||||||
Logger.info(`[PodcastController] No changes to episode "${episode.title}"`)
|
Logger.info(`[PodcastController] No changes to episode "${episode.title}"`)
|
||||||
}
|
}
|
||||||
@@ -504,7 +504,7 @@ class PodcastController {
|
|||||||
req.libraryItem.media.numEpisodes = req.libraryItem.media.podcastEpisodes.length
|
req.libraryItem.media.numEpisodes = req.libraryItem.media.podcastEpisodes.length
|
||||||
await req.libraryItem.media.save()
|
await req.libraryItem.media.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', req.libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', req.libraryItem)
|
||||||
res.json(req.libraryItem.toOldJSON())
|
res.json(req.libraryItem.toOldJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,12 +108,12 @@ class SearchController {
|
|||||||
async findChapters(req, res) {
|
async findChapters(req, res) {
|
||||||
const asin = req.query.asin
|
const asin = req.query.asin
|
||||||
if (!isValidASIN(asin.toUpperCase())) {
|
if (!isValidASIN(asin.toUpperCase())) {
|
||||||
return res.json({ error: 'Invalid ASIN' })
|
return res.json({ error: 'Invalid ASIN', stringKey: 'MessageInvalidAsin' })
|
||||||
}
|
}
|
||||||
const region = (req.query.region || 'us').toLowerCase()
|
const region = (req.query.region || 'us').toLowerCase()
|
||||||
const chapterData = await BookFinder.findChapters(asin, region)
|
const chapterData = await BookFinder.findChapters(asin, region)
|
||||||
if (!chapterData) {
|
if (!chapterData) {
|
||||||
return res.json({ error: 'Chapters not found' })
|
return res.json({ error: 'Chapters not found', stringKey: 'MessageChaptersNotFound' })
|
||||||
}
|
}
|
||||||
res.json(chapterData)
|
res.json(chapterData)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
const Path = require('path')
|
||||||
const { Request, Response, NextFunction } = require('express')
|
const { Request, Response, NextFunction } = require('express')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const Database = require('../Database')
|
const Database = require('../Database')
|
||||||
const { toNumber, isUUID } = require('../utils/index')
|
const { toNumber, isUUID } = require('../utils/index')
|
||||||
|
const { getAudioMimeTypeFromExtname, encodeUriPath } = require('../utils/fileUtils')
|
||||||
|
|
||||||
const ShareManager = require('../managers/ShareManager')
|
const ShareManager = require('../managers/ShareManager')
|
||||||
|
|
||||||
@@ -266,6 +268,51 @@ class SessionController {
|
|||||||
this.playbackSessionManager.syncLocalSessionsRequest(req, res)
|
this.playbackSessionManager.syncLocalSessionsRequest(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET: /public/session/:id/track/:index
|
||||||
|
* While a session is open, this endpoint can be used to stream the audio track
|
||||||
|
*
|
||||||
|
* @this {import('../routers/PublicRouter')}
|
||||||
|
*
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
*/
|
||||||
|
async getTrack(req, res) {
|
||||||
|
const audioTrackIndex = toNumber(req.params.index, null)
|
||||||
|
if (audioTrackIndex === null) {
|
||||||
|
Logger.error(`[SessionController] Invalid audio track index "${req.params.index}"`)
|
||||||
|
return res.sendStatus(400)
|
||||||
|
}
|
||||||
|
|
||||||
|
const playbackSession = this.playbackSessionManager.getSession(req.params.id)
|
||||||
|
if (!playbackSession) {
|
||||||
|
Logger.error(`[SessionController] Unable to find playback session with id=${req.params.id}`)
|
||||||
|
return res.sendStatus(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioTrack = playbackSession.audioTracks.find((t) => t.index === audioTrackIndex)
|
||||||
|
if (!audioTrack) {
|
||||||
|
Logger.error(`[SessionController] Unable to find audio track with index=${audioTrackIndex}`)
|
||||||
|
return res.sendStatus(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await Database.userModel.getUserById(playbackSession.userId)
|
||||||
|
Logger.debug(`[SessionController] Serving audio track ${audioTrack.index} for session "${req.params.id}" belonging to user "${user.username}"`)
|
||||||
|
|
||||||
|
if (global.XAccel) {
|
||||||
|
const encodedURI = encodeUriPath(global.XAccel + audioTrack.metadata.path)
|
||||||
|
Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
|
||||||
|
return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
|
||||||
|
const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(audioTrack.metadata.path))
|
||||||
|
if (audioMimeType) {
|
||||||
|
res.setHeader('Content-Type', audioMimeType)
|
||||||
|
}
|
||||||
|
res.sendFile(audioTrack.metadata.path)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {RequestWithUser} req
|
* @param {RequestWithUser} req
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class ToolsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options = req.query || {}
|
const options = req.query || {}
|
||||||
|
Logger.info(`[ToolsController] encodeM4b: Starting audiobook merge for "${req.libraryItem.media.title}" with options: ${JSON.stringify(options)}`)
|
||||||
this.abMergeManager.startAudiobookMerge(req.user.id, req.libraryItem, options)
|
this.abMergeManager.startAudiobookMerge(req.user.id, req.libraryItem, options)
|
||||||
|
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ class PlaybackSessionManager {
|
|||||||
this.sessions = []
|
this.sessions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get open session by id
|
||||||
|
*
|
||||||
|
* @param {string} sessionId
|
||||||
|
* @returns {PlaybackSession}
|
||||||
|
*/
|
||||||
getSession(sessionId) {
|
getSession(sessionId) {
|
||||||
return this.sessions.find((s) => s.id === sessionId)
|
return this.sessions.find((s) => s.id === sessionId)
|
||||||
}
|
}
|
||||||
@@ -175,6 +181,25 @@ class PlaybackSessionManager {
|
|||||||
// New session from local
|
// New session from local
|
||||||
session = new PlaybackSession(sessionJson)
|
session = new PlaybackSession(sessionJson)
|
||||||
session.deviceInfo = deviceInfo
|
session.deviceInfo = deviceInfo
|
||||||
|
|
||||||
|
if (session.mediaMetadata == null) {
|
||||||
|
session.mediaMetadata = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate mediaMetadata with the current library items metadata for any keys not set by client
|
||||||
|
const libraryItemMediaMetadata = libraryItem.media.oldMetadataToJSON()
|
||||||
|
for (const key in libraryItemMediaMetadata) {
|
||||||
|
if (session.mediaMetadata[key] === undefined) {
|
||||||
|
session.mediaMetadata[key] = libraryItemMediaMetadata[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.displayTitle == null || session.displayTitle === '') {
|
||||||
|
session.displayTitle = libraryItem.title
|
||||||
|
}
|
||||||
|
if (session.displayAuthor == null || session.displayAuthor === '') {
|
||||||
|
session.displayAuthor = libraryItem.authorNamesFirstLast
|
||||||
|
}
|
||||||
session.duration = libraryItem.media.getPlaybackDuration(sessionJson.episodeId)
|
session.duration = libraryItem.media.getPlaybackDuration(sessionJson.episodeId)
|
||||||
|
|
||||||
Logger.debug(`[PlaybackSessionManager] Inserting new session for "${session.displayTitle}" (${session.id})`)
|
Logger.debug(`[PlaybackSessionManager] Inserting new session for "${session.displayTitle}" (${session.id})`)
|
||||||
|
|||||||
@@ -211,6 +211,14 @@ class PodcastManager {
|
|||||||
const podcastEpisode = await Database.podcastEpisodeModel.createFromRssPodcastEpisode(this.currentDownload.rssPodcastEpisode, libraryItem.media.id, audioFile)
|
const podcastEpisode = await Database.podcastEpisodeModel.createFromRssPodcastEpisode(this.currentDownload.rssPodcastEpisode, libraryItem.media.id, audioFile)
|
||||||
|
|
||||||
libraryItem.libraryFiles.push(libraryFile.toJSON())
|
libraryItem.libraryFiles.push(libraryFile.toJSON())
|
||||||
|
// Re-calculating library item size because this wasnt being updated properly for podcasts in v2.20.0 and below
|
||||||
|
let libraryItemSize = 0
|
||||||
|
libraryItem.libraryFiles.forEach((lf) => {
|
||||||
|
if (lf.metadata.size && !isNaN(lf.metadata.size)) {
|
||||||
|
libraryItemSize += Number(lf.metadata.size)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
libraryItem.size = libraryItemSize
|
||||||
libraryItem.changed('libraryFiles', true)
|
libraryItem.changed('libraryFiles', true)
|
||||||
|
|
||||||
libraryItem.media.podcastEpisodes.push(podcastEpisode)
|
libraryItem.media.podcastEpisodes.push(podcastEpisode)
|
||||||
@@ -246,7 +254,7 @@ class PodcastManager {
|
|||||||
await libraryItem.media.save()
|
await libraryItem.media.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
const podcastEpisodeExpanded = podcastEpisode.toOldJSONExpanded(libraryItem.id)
|
const podcastEpisodeExpanded = podcastEpisode.toOldJSONExpanded(libraryItem.id)
|
||||||
podcastEpisodeExpanded.libraryItem = libraryItem.toOldJSONExpanded()
|
podcastEpisodeExpanded.libraryItem = libraryItem.toOldJSONExpanded()
|
||||||
SocketAuthority.emitter('episode_added', podcastEpisodeExpanded)
|
SocketAuthority.emitter('episode_added', podcastEpisodeExpanded)
|
||||||
@@ -359,7 +367,7 @@ class PodcastManager {
|
|||||||
libraryItem.changed('updatedAt', true)
|
libraryItem.changed('updatedAt', true)
|
||||||
await libraryItem.save()
|
await libraryItem.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
|
|
||||||
return libraryItem.media.autoDownloadEpisodes
|
return libraryItem.media.autoDownloadEpisodes
|
||||||
}
|
}
|
||||||
@@ -417,7 +425,7 @@ class PodcastManager {
|
|||||||
libraryItem.changed('updatedAt', true)
|
libraryItem.changed('updatedAt', true)
|
||||||
await libraryItem.save()
|
await libraryItem.save()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
|
|
||||||
return newEpisodes || []
|
return newEpisodes || []
|
||||||
}
|
}
|
||||||
@@ -704,7 +712,7 @@ class PodcastManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_added', newLibraryItem)
|
||||||
|
|
||||||
// Turn on podcast auto download cron if not already on
|
// Turn on podcast auto download cron if not already on
|
||||||
if (newLibraryItem.media.autoDownloadEpisodes) {
|
if (newLibraryItem.media.autoDownloadEpisodes) {
|
||||||
|
|||||||
@@ -215,9 +215,13 @@ class FeedEpisode extends Model {
|
|||||||
* @returns {Promise<FeedEpisode[]>}
|
* @returns {Promise<FeedEpisode[]>}
|
||||||
*/
|
*/
|
||||||
static async createFromBooks(books, feed, slug, transaction) {
|
static async createFromBooks(books, feed, slug, transaction) {
|
||||||
const earliestLibraryItemCreatedAt = books.reduce((earliest, book) => {
|
// This is never null unless the books array is empty, as this method is not invoked when no books. Reduce needs an initial item
|
||||||
return book.libraryItem.createdAt < earliest.libraryItem.createdAt ? book : earliest
|
const earliestLibraryItemCreatedAt =
|
||||||
}).libraryItem.createdAt
|
books.length > 0
|
||||||
|
? books.reduce((earliest, book) => {
|
||||||
|
return book.libraryItem.createdAt < earliest.libraryItem.createdAt ? book : earliest
|
||||||
|
}).libraryItem.createdAt
|
||||||
|
: null
|
||||||
|
|
||||||
const feedEpisodeObjs = []
|
const feedEpisodeObjs = []
|
||||||
let numExisting = 0
|
let numExisting = 0
|
||||||
|
|||||||
@@ -246,7 +246,6 @@ class LibraryItem extends Model {
|
|||||||
include
|
include
|
||||||
})
|
})
|
||||||
if (!libraryItem) {
|
if (!libraryItem) {
|
||||||
Logger.error(`[LibraryItem] Library item not found`)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,9 +246,10 @@ class MediaProgress extends Model {
|
|||||||
// For local sync
|
// For local sync
|
||||||
if (progressPayload.lastUpdate) {
|
if (progressPayload.lastUpdate) {
|
||||||
this.updatedAt = progressPayload.lastUpdate
|
this.updatedAt = progressPayload.lastUpdate
|
||||||
|
this.changed('updatedAt', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.save()
|
return this.save({ silent: !!progressPayload.lastUpdate })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,9 +80,13 @@ class PodcastEpisode extends Model {
|
|||||||
if (rssPodcastEpisode.guid) {
|
if (rssPodcastEpisode.guid) {
|
||||||
podcastEpisode.extraData.guid = rssPodcastEpisode.guid
|
podcastEpisode.extraData.guid = rssPodcastEpisode.guid
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioFile.chapters?.length) {
|
if (audioFile.chapters?.length) {
|
||||||
podcastEpisode.chapters = audioFile.chapters.map((ch) => ({ ...ch }))
|
podcastEpisode.chapters = audioFile.chapters.map((ch) => ({ ...ch }))
|
||||||
|
} else if (rssPodcastEpisode.chapters?.length) {
|
||||||
|
podcastEpisode.chapters = rssPodcastEpisode.chapters.map((ch) => ({ ...ch }))
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.create(podcastEpisode)
|
return this.create(podcastEpisode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const ShareController = require('../controllers/ShareController')
|
const ShareController = require('../controllers/ShareController')
|
||||||
|
const SessionController = require('../controllers/SessionController')
|
||||||
|
|
||||||
class PublicRouter {
|
class PublicRouter {
|
||||||
constructor(playbackSessionManager) {
|
constructor(playbackSessionManager) {
|
||||||
@@ -17,6 +18,7 @@ class PublicRouter {
|
|||||||
this.router.get('/share/:slug/cover', ShareController.getMediaItemShareCoverImage.bind(this))
|
this.router.get('/share/:slug/cover', ShareController.getMediaItemShareCoverImage.bind(this))
|
||||||
this.router.get('/share/:slug/download', ShareController.downloadMediaItemShare.bind(this))
|
this.router.get('/share/:slug/download', ShareController.downloadMediaItemShare.bind(this))
|
||||||
this.router.patch('/share/:slug/progress', ShareController.updateMediaItemShareProgress.bind(this))
|
this.router.patch('/share/:slug/progress', ShareController.updateMediaItemShareProgress.bind(this))
|
||||||
|
this.router.get('/session/:id/track/:index', SessionController.getTrack.bind(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports = PublicRouter
|
module.exports = PublicRouter
|
||||||
|
|||||||
@@ -475,6 +475,8 @@ class BookScanner {
|
|||||||
bookAuthors: [],
|
bookAuthors: [],
|
||||||
bookSeries: []
|
bookSeries: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createdAtTimestamp = new Date().getTime()
|
||||||
if (bookMetadata.authors.length) {
|
if (bookMetadata.authors.length) {
|
||||||
for (const authorName of bookMetadata.authors) {
|
for (const authorName of bookMetadata.authors) {
|
||||||
const matchingAuthorId = await Database.getAuthorIdByName(libraryItemData.libraryId, authorName)
|
const matchingAuthorId = await Database.getAuthorIdByName(libraryItemData.libraryId, authorName)
|
||||||
@@ -485,6 +487,8 @@ class BookScanner {
|
|||||||
} else {
|
} else {
|
||||||
// New author
|
// New author
|
||||||
bookObject.bookAuthors.push({
|
bookObject.bookAuthors.push({
|
||||||
|
// Ensures authors are in a set order
|
||||||
|
createdAt: createdAtTimestamp + bookObject.bookAuthors.length,
|
||||||
author: {
|
author: {
|
||||||
libraryId: libraryItemData.libraryId,
|
libraryId: libraryItemData.libraryId,
|
||||||
name: authorName,
|
name: authorName,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class LibraryItemScanner {
|
|||||||
|
|
||||||
const { libraryItem: expandedLibraryItem, wasUpdated } = await this.rescanLibraryItemMedia(libraryItem, libraryItemScanData, library.settings, scanLogger)
|
const { libraryItem: expandedLibraryItem, wasUpdated } = await this.rescanLibraryItemMedia(libraryItem, libraryItemScanData, library.settings, scanLogger)
|
||||||
if (libraryItemDataUpdated || wasUpdated) {
|
if (libraryItemDataUpdated || wasUpdated) {
|
||||||
SocketAuthority.emitter('item_updated', expandedLibraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', expandedLibraryItem)
|
||||||
|
|
||||||
await this.checkAuthorsAndSeriesRemovedFromBooks(library.id, scanLogger)
|
await this.checkAuthorsAndSeriesRemovedFromBooks(library.id, scanLogger)
|
||||||
|
|
||||||
|
|||||||
@@ -223,11 +223,7 @@ class LibraryScanner {
|
|||||||
|
|
||||||
// Emit item updates in chunks of 10 to client
|
// Emit item updates in chunks of 10 to client
|
||||||
if (libraryItemsUpdated.length === 10) {
|
if (libraryItemsUpdated.length === 10) {
|
||||||
// TODO: Should only emit to clients where library item is accessible
|
SocketAuthority.libraryItemsEmitter('items_updated', libraryItemsUpdated)
|
||||||
SocketAuthority.emitter(
|
|
||||||
'items_updated',
|
|
||||||
libraryItemsUpdated.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
libraryItemsUpdated = []
|
libraryItemsUpdated = []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,11 +231,7 @@ class LibraryScanner {
|
|||||||
}
|
}
|
||||||
// Emit item updates to client
|
// Emit item updates to client
|
||||||
if (libraryItemsUpdated.length) {
|
if (libraryItemsUpdated.length) {
|
||||||
// TODO: Should only emit to clients where library item is accessible
|
SocketAuthority.libraryItemsEmitter('items_updated', libraryItemsUpdated)
|
||||||
SocketAuthority.emitter(
|
|
||||||
'items_updated',
|
|
||||||
libraryItemsUpdated.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authors and series that were removed from books should be removed if they are now empty
|
// Authors and series that were removed from books should be removed if they are now empty
|
||||||
@@ -277,11 +269,7 @@ class LibraryScanner {
|
|||||||
|
|
||||||
// Emit new items in chunks of 10 to client
|
// Emit new items in chunks of 10 to client
|
||||||
if (newLibraryItems.length === 10) {
|
if (newLibraryItems.length === 10) {
|
||||||
// TODO: Should only emit to clients where library item is accessible
|
SocketAuthority.libraryItemsEmitter('items_added', newLibraryItems)
|
||||||
SocketAuthority.emitter(
|
|
||||||
'items_added',
|
|
||||||
newLibraryItems.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
newLibraryItems = []
|
newLibraryItems = []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,11 +277,7 @@ class LibraryScanner {
|
|||||||
}
|
}
|
||||||
// Emit new items to client
|
// Emit new items to client
|
||||||
if (newLibraryItems.length) {
|
if (newLibraryItems.length) {
|
||||||
// TODO: Should only emit to clients where library item is accessible
|
SocketAuthority.libraryItemsEmitter('items_added', newLibraryItems)
|
||||||
SocketAuthority.emitter(
|
|
||||||
'items_added',
|
|
||||||
newLibraryItems.map((li) => li.toOldJSONExpanded())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,7 +407,7 @@ class LibraryScanner {
|
|||||||
const folder = library.libraryFolders[0]
|
const folder = library.libraryFolders[0]
|
||||||
|
|
||||||
const filePathItems = folderGroups[folderId].fileUpdates.map((fileUpdate) => fileUtils.getFilePathItemFromFileUpdate(fileUpdate))
|
const filePathItems = folderGroups[folderId].fileUpdates.map((fileUpdate) => fileUtils.getFilePathItemFromFileUpdate(fileUpdate))
|
||||||
const fileUpdateGroup = scanUtils.groupFileItemsIntoLibraryItemDirs(library.mediaType, filePathItems, !!library.settings?.audiobooksOnly)
|
const fileUpdateGroup = scanUtils.groupFileItemsIntoLibraryItemDirs(library.mediaType, filePathItems, !!library.settings?.audiobooksOnly, true)
|
||||||
|
|
||||||
if (!Object.keys(fileUpdateGroup).length) {
|
if (!Object.keys(fileUpdateGroup).length) {
|
||||||
Logger.info(`[LibraryScanner] No important changes to scan for in folder "${folderId}"`)
|
Logger.info(`[LibraryScanner] No important changes to scan for in folder "${folderId}"`)
|
||||||
@@ -609,7 +593,7 @@ class LibraryScanner {
|
|||||||
Logger.info(`[LibraryScanner] Scanning file update group and library item was deleted "${existingLibraryItem.media.title}" - marking as missing`)
|
Logger.info(`[LibraryScanner] Scanning file update group and library item was deleted "${existingLibraryItem.media.title}" - marking as missing`)
|
||||||
existingLibraryItem.isMissing = true
|
existingLibraryItem.isMissing = true
|
||||||
await existingLibraryItem.save()
|
await existingLibraryItem.save()
|
||||||
SocketAuthority.emitter('item_updated', existingLibraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', existingLibraryItem)
|
||||||
|
|
||||||
itemGroupingResults[itemDir] = ScanResult.REMOVED
|
itemGroupingResults[itemDir] = ScanResult.REMOVED
|
||||||
continue
|
continue
|
||||||
@@ -643,7 +627,7 @@ class LibraryScanner {
|
|||||||
const isSingleMediaItem = isSingleMediaFile(fileUpdateGroup, itemDir)
|
const isSingleMediaItem = isSingleMediaFile(fileUpdateGroup, itemDir)
|
||||||
const newLibraryItem = await LibraryItemScanner.scanPotentialNewLibraryItem(fullPath, library, folder, isSingleMediaItem)
|
const newLibraryItem = await LibraryItemScanner.scanPotentialNewLibraryItem(fullPath, library, folder, isSingleMediaItem)
|
||||||
if (newLibraryItem) {
|
if (newLibraryItem) {
|
||||||
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_added', newLibraryItem)
|
||||||
}
|
}
|
||||||
itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING
|
itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,17 +59,36 @@ class PodcastScanner {
|
|||||||
|
|
||||||
if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) {
|
if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) {
|
||||||
// Filter out and destroy episodes that were removed
|
// Filter out and destroy episodes that were removed
|
||||||
existingPodcastEpisodes = await Promise.all(
|
const episodesToRemove = []
|
||||||
existingPodcastEpisodes.filter(async (ep) => {
|
existingPodcastEpisodes = existingPodcastEpisodes.filter((ep) => {
|
||||||
if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) {
|
if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) {
|
||||||
libraryScan.addLog(LogLevel.INFO, `Podcast episode "${ep.title}" audio file was removed`)
|
episodesToRemove.push(ep)
|
||||||
// TODO: Should clean up other data linked to this episode
|
return false
|
||||||
await ep.destroy()
|
}
|
||||||
return false
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (episodesToRemove.length) {
|
||||||
|
// Remove episodes from playlists and media progress
|
||||||
|
const episodeIds = episodesToRemove.map((ep) => ep.id)
|
||||||
|
await Database.playlistModel.removeMediaItemsFromPlaylists(episodeIds)
|
||||||
|
const mediaProgressRemoved = await Database.mediaProgressModel.destroy({
|
||||||
|
where: {
|
||||||
|
mediaItemId: episodeIds
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
})
|
})
|
||||||
)
|
if (mediaProgressRemoved) {
|
||||||
|
libraryScan.addLog(LogLevel.INFO, `Removed ${mediaProgressRemoved} media progress for episodes`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove episodes
|
||||||
|
await Promise.all(
|
||||||
|
episodesToRemove.map(async (ep) => {
|
||||||
|
await ep.destroy()
|
||||||
|
libraryScan.addLog(LogLevel.INFO, `Podcast episode "${ep.title}" audio file was removed`)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Update audio files that were modified
|
// Update audio files that were modified
|
||||||
if (libraryItemData.audioLibraryFilesModified.length) {
|
if (libraryItemData.audioLibraryFilesModified.length) {
|
||||||
@@ -113,6 +132,9 @@ class PodcastScanner {
|
|||||||
|
|
||||||
// Create new podcast episodes from new found audio files
|
// Create new podcast episodes from new found audio files
|
||||||
for (const newAudioFile of newAudioFiles) {
|
for (const newAudioFile of newAudioFiles) {
|
||||||
|
// Podcast episode audio files always have index 1
|
||||||
|
newAudioFile.index = 1
|
||||||
|
|
||||||
const newEpisode = {
|
const newEpisode = {
|
||||||
title: newAudioFile.metaTags.tagTitle || newAudioFile.metadata.filenameNoExt,
|
title: newAudioFile.metaTags.tagTitle || newAudioFile.metadata.filenameNoExt,
|
||||||
subtitle: null,
|
subtitle: null,
|
||||||
@@ -136,7 +158,6 @@ class PodcastScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hasMediaChanges = false
|
let hasMediaChanges = false
|
||||||
|
|
||||||
if (existingPodcastEpisodes.length !== media.numEpisodes) {
|
if (existingPodcastEpisodes.length !== media.numEpisodes) {
|
||||||
media.numEpisodes = existingPodcastEpisodes.length
|
media.numEpisodes = existingPodcastEpisodes.length
|
||||||
hasMediaChanges = true
|
hasMediaChanges = true
|
||||||
@@ -253,6 +274,9 @@ class PodcastScanner {
|
|||||||
|
|
||||||
// Create podcast episodes from audio files
|
// Create podcast episodes from audio files
|
||||||
for (const audioFile of scannedAudioFiles) {
|
for (const audioFile of scannedAudioFiles) {
|
||||||
|
// Podcast episode audio files always have index 1
|
||||||
|
audioFile.index = 1
|
||||||
|
|
||||||
const newEpisode = {
|
const newEpisode = {
|
||||||
title: audioFile.metaTags.tagTitle || audioFile.metadata.filenameNoExt,
|
title: audioFile.metaTags.tagTitle || audioFile.metadata.filenameNoExt,
|
||||||
subtitle: null,
|
subtitle: null,
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class Scanner {
|
|||||||
|
|
||||||
await libraryItem.saveMetadataFile()
|
await libraryItem.saveMetadataFile()
|
||||||
|
|
||||||
SocketAuthority.emitter('item_updated', libraryItem.toOldJSONExpanded())
|
SocketAuthority.libraryItemEmitter('item_updated', libraryItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ module.exports.AudioMimeType = {
|
|||||||
FLAC: 'audio/flac',
|
FLAC: 'audio/flac',
|
||||||
WMA: 'audio/x-ms-wma',
|
WMA: 'audio/x-ms-wma',
|
||||||
AIFF: 'audio/x-aiff',
|
AIFF: 'audio/x-aiff',
|
||||||
|
AIF: 'audio/x-aiff',
|
||||||
WEBM: 'audio/webm',
|
WEBM: 'audio/webm',
|
||||||
WEBMA: 'audio/webm',
|
WEBMA: 'audio/webm',
|
||||||
MKA: 'audio/x-matroska',
|
MKA: 'audio/x-matroska',
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ module.exports.recurseFiles = async (path, relPathToReplace = null) => {
|
|||||||
})
|
})
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
// Filter out items in ignore directories
|
// Filter out items in ignore directories
|
||||||
if (directoriesToIgnore.some((dir) => item.fullname.startsWith(dir))) {
|
if (directoriesToIgnore.some((dir) => item.fullname.startsWith(dir + '/'))) {
|
||||||
Logger.debug(`[fileUtils] Ignoring path in dir with .ignore "${item.fullname}"`)
|
Logger.debug(`[fileUtils] Ignoring path in dir with .ignore "${item.fullname}"`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const globals = {
|
const globals = {
|
||||||
SupportedImageTypes: ['png', 'jpg', 'jpeg', 'webp'],
|
SupportedImageTypes: ['png', 'jpg', 'jpeg', 'webp'],
|
||||||
SupportedAudioTypes: ['m4b', 'mp3', 'm4a', 'flac', 'opus', 'ogg', 'oga', 'mp4', 'aac', 'wma', 'aiff', 'wav', 'webm', 'webma', 'mka', 'awb', 'caf', 'mpg', 'mpeg'],
|
SupportedAudioTypes: ['m4b', 'mp3', 'm4a', 'flac', 'opus', 'ogg', 'oga', 'mp4', 'aac', 'wma', 'aiff', 'aif', 'wav', 'webm', 'webma', 'mka', 'awb', 'caf', 'mpg', 'mpeg'],
|
||||||
SupportedEbookTypes: ['epub', 'pdf', 'mobi', 'azw3', 'cbr', 'cbz'],
|
SupportedEbookTypes: ['epub', 'pdf', 'mobi', 'azw3', 'cbr', 'cbz'],
|
||||||
TextFileTypes: ['txt', 'nfo'],
|
TextFileTypes: ['txt', 'nfo'],
|
||||||
MetadataFileTypes: ['opf', 'abs', 'xml', 'json']
|
MetadataFileTypes: ['opf', 'abs', 'xml', 'json']
|
||||||
|
|||||||
@@ -243,3 +243,29 @@ module.exports.isValidASIN = (str) => {
|
|||||||
if (!str || typeof str !== 'string') return false
|
if (!str || typeof str !== 'string') return false
|
||||||
return /^[A-Z0-9]{10}$/.test(str)
|
return /^[A-Z0-9]{10}$/.test(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert timestamp to seconds
|
||||||
|
* @example "01:00:00" => 3600
|
||||||
|
* @example "01:00" => 60
|
||||||
|
* @example "01" => 1
|
||||||
|
*
|
||||||
|
* @param {string} timestamp
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
module.exports.timestampToSeconds = (timestamp) => {
|
||||||
|
if (typeof timestamp !== 'string') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const parts = timestamp.split(':').map(Number)
|
||||||
|
if (parts.some(isNaN)) {
|
||||||
|
return null
|
||||||
|
} else if (parts.length === 1) {
|
||||||
|
return parts[0]
|
||||||
|
} else if (parts.length === 2) {
|
||||||
|
return parts[0] * 60 + parts[1]
|
||||||
|
} else if (parts.length === 3) {
|
||||||
|
return parts[0] * 3600 + parts[1] * 60 + parts[2]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const ssrfFilter = require('ssrf-req-filter')
|
const ssrfFilter = require('ssrf-req-filter')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { xmlToJSON, levenshteinDistance } = require('./index')
|
const { xmlToJSON, levenshteinDistance, timestampToSeconds } = require('./index')
|
||||||
const htmlSanitizer = require('../utils/htmlSanitizer')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef RssPodcastChapter
|
||||||
|
* @property {number} id
|
||||||
|
* @property {string} title
|
||||||
|
* @property {number} start
|
||||||
|
* @property {number} end
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef RssPodcastEpisode
|
* @typedef RssPodcastEpisode
|
||||||
* @property {string} title
|
* @property {string} title
|
||||||
@@ -22,6 +30,7 @@ const htmlSanitizer = require('../utils/htmlSanitizer')
|
|||||||
* @property {string} guid
|
* @property {string} guid
|
||||||
* @property {string} chaptersUrl
|
* @property {string} chaptersUrl
|
||||||
* @property {string} chaptersType
|
* @property {string} chaptersType
|
||||||
|
* @property {RssPodcastChapter[]} chapters
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,7 +205,7 @@ function extractEpisodeData(item) {
|
|||||||
} else if (typeof guidItem?._ === 'string') {
|
} else if (typeof guidItem?._ === 'string') {
|
||||||
episode.guid = guidItem._
|
episode.guid = guidItem._
|
||||||
} else {
|
} else {
|
||||||
Logger.error(`[podcastUtils] Invalid guid ${item['guid']} for ${episode.enclosure.url}`)
|
Logger.error(`[podcastUtils] Invalid guid for ${episode.enclosure.url}`, item['guid'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,12 +214,53 @@ function extractEpisodeData(item) {
|
|||||||
const cleanKey = key.split(':').pop()
|
const cleanKey = key.split(':').pop()
|
||||||
episode[cleanKey] = extractFirstArrayItemString(item, key)
|
episode[cleanKey] = extractFirstArrayItemString(item, key)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Extract psc:chapters if duration is set
|
||||||
|
let episodeDuration = !isNaN(episode.duration) ? timestampToSeconds(episode.duration) : null
|
||||||
|
if (item['psc:chapters']?.[0]?.['psc:chapter']?.length && episodeDuration) {
|
||||||
|
// Example chapter:
|
||||||
|
// {"id":0,"start":0,"end":43.004286,"title":"chapter 1"}
|
||||||
|
|
||||||
|
const cleanedChapters = item['psc:chapters'][0]['psc:chapter'].map((chapter, index) => {
|
||||||
|
if (!chapter['$']?.title || !chapter['$']?.start || typeof chapter['$']?.start !== 'string' || typeof chapter['$']?.title !== 'string') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = timestampToSeconds(chapter['$'].start)
|
||||||
|
if (start === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: index,
|
||||||
|
title: chapter['$'].title,
|
||||||
|
start
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (cleanedChapters.some((chapter) => !chapter)) {
|
||||||
|
Logger.warn(`[podcastUtils] Invalid chapter data for ${episode.enclosure.url}`)
|
||||||
|
} else {
|
||||||
|
episode.chapters = cleanedChapters.map((chapter, index) => {
|
||||||
|
const nextChapter = cleanedChapters[index + 1]
|
||||||
|
const end = nextChapter ? nextChapter.start : episodeDuration
|
||||||
|
return {
|
||||||
|
id: chapter.id,
|
||||||
|
title: chapter.title,
|
||||||
|
start: chapter.start,
|
||||||
|
end
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return episode
|
return episode
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanEpisodeData(data) {
|
function cleanEpisodeData(data) {
|
||||||
const pubJsDate = data.pubDate ? new Date(data.pubDate) : null
|
const pubJsDate = data.pubDate ? new Date(data.pubDate) : null
|
||||||
const publishedAt = pubJsDate && !isNaN(pubJsDate) ? pubJsDate.valueOf() : null
|
const publishedAt = pubJsDate && !isNaN(pubJsDate) ? pubJsDate.valueOf() : null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
subtitle: data.subtitle || '',
|
subtitle: data.subtitle || '',
|
||||||
@@ -227,7 +277,8 @@ function cleanEpisodeData(data) {
|
|||||||
enclosure: data.enclosure,
|
enclosure: data.enclosure,
|
||||||
guid: data.guid || null,
|
guid: data.guid || null,
|
||||||
chaptersUrl: data.chaptersUrl || null,
|
chaptersUrl: data.chaptersUrl || null,
|
||||||
chaptersType: data.chaptersType || null
|
chaptersType: data.chaptersType || null,
|
||||||
|
chapters: data.chapters || []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1195,10 +1195,12 @@ module.exports = {
|
|||||||
libraryItem.media.series = []
|
libraryItem.media.series = []
|
||||||
return libraryItem.toOldJSON()
|
return libraryItem.toOldJSON()
|
||||||
})
|
})
|
||||||
seriesMatches.push({
|
if (books.length) {
|
||||||
series: series.toOldJSON(),
|
seriesMatches.push({
|
||||||
books
|
series: series.toOldJSON(),
|
||||||
})
|
books
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search authors
|
// Search authors
|
||||||
@@ -1247,7 +1249,12 @@ module.exports = {
|
|||||||
libraryId
|
libraryId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return statResults[0]
|
return {
|
||||||
|
totalSize: statResults?.[0]?.totalSize || 0,
|
||||||
|
totalDuration: statResults?.[0]?.totalDuration || 0,
|
||||||
|
numAudioFiles: statResults?.[0]?.numAudioFiles || 0,
|
||||||
|
totalItems: statResults?.[0]?.totalItems || 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -411,6 +411,43 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search podcast episode title
|
||||||
|
const podcastEpisodes = await Database.podcastEpisodeModel.findAll({
|
||||||
|
where: [
|
||||||
|
Sequelize.literal(textSearchQuery.matchExpression('podcastEpisode.title')),
|
||||||
|
{
|
||||||
|
'$podcast.libraryItem.libraryId$': library.id
|
||||||
|
}
|
||||||
|
],
|
||||||
|
replacements: userPermissionPodcastWhere.replacements,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: Database.podcastModel,
|
||||||
|
where: [...userPermissionPodcastWhere.podcastWhere],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: Database.libraryItemModel
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
offset,
|
||||||
|
limit
|
||||||
|
})
|
||||||
|
const episodeMatches = []
|
||||||
|
for (const episode of podcastEpisodes) {
|
||||||
|
const libraryItem = episode.podcast.libraryItem
|
||||||
|
libraryItem.media = episode.podcast
|
||||||
|
libraryItem.media.podcastEpisodes = []
|
||||||
|
const oldPodcastEpisodeJson = episode.toOldJSONExpanded(libraryItem.id)
|
||||||
|
const libraryItemJson = libraryItem.toOldJSONExpanded()
|
||||||
|
libraryItemJson.recentEpisode = oldPodcastEpisodeJson
|
||||||
|
episodeMatches.push({
|
||||||
|
libraryItem: libraryItemJson
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const matchJsonValue = textSearchQuery.matchExpression('json_each.value')
|
const matchJsonValue = textSearchQuery.matchExpression('json_each.value')
|
||||||
|
|
||||||
// Search tags
|
// Search tags
|
||||||
@@ -450,7 +487,8 @@ module.exports = {
|
|||||||
return {
|
return {
|
||||||
podcast: itemMatches,
|
podcast: itemMatches,
|
||||||
tags: tagMatches,
|
tags: tagMatches,
|
||||||
genres: genreMatches
|
genres: genreMatches,
|
||||||
|
episodes: episodeMatches
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -533,8 +571,10 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
...statResults[0],
|
totalDuration: statResults?.[0]?.totalDuration || 0,
|
||||||
totalSize: sizeResults[0].totalSize || 0
|
numAudioFiles: statResults?.[0]?.numAudioFiles || 0,
|
||||||
|
totalItems: statResults?.[0]?.totalItems || 0,
|
||||||
|
totalSize: sizeResults?.[0]?.totalSize || 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+16
-6
@@ -24,6 +24,12 @@ function isMediaFile(mediaType, ext, audiobooksOnly = false) {
|
|||||||
return globals.SupportedAudioTypes.includes(extclean) || globals.SupportedEbookTypes.includes(extclean)
|
return globals.SupportedAudioTypes.includes(extclean) || globals.SupportedEbookTypes.includes(extclean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isScannableNonMediaFile(ext) {
|
||||||
|
if (!ext) return false
|
||||||
|
const extclean = ext.slice(1).toLowerCase()
|
||||||
|
return globals.TextFileTypes.includes(extclean) || globals.MetadataFileTypes.includes(extclean) || globals.SupportedImageTypes.includes(extclean)
|
||||||
|
}
|
||||||
|
|
||||||
function checkFilepathIsAudioFile(filepath) {
|
function checkFilepathIsAudioFile(filepath) {
|
||||||
const ext = Path.extname(filepath)
|
const ext = Path.extname(filepath)
|
||||||
if (!ext) return false
|
if (!ext) return false
|
||||||
@@ -35,27 +41,31 @@ module.exports.checkFilepathIsAudioFile = checkFilepathIsAudioFile
|
|||||||
/**
|
/**
|
||||||
* @param {string} mediaType
|
* @param {string} mediaType
|
||||||
* @param {import('./fileUtils').FilePathItem[]} fileItems
|
* @param {import('./fileUtils').FilePathItem[]} fileItems
|
||||||
* @param {boolean} [audiobooksOnly=false]
|
* @param {boolean} audiobooksOnly
|
||||||
|
* @param {boolean} [includeNonMediaFiles=false] - Used by the watcher to re-scan when covers/metadata files are added/removed
|
||||||
* @returns {Record<string,string[]>} map of files grouped into potential libarary item dirs
|
* @returns {Record<string,string[]>} map of files grouped into potential libarary item dirs
|
||||||
*/
|
*/
|
||||||
function groupFileItemsIntoLibraryItemDirs(mediaType, fileItems, audiobooksOnly = false) {
|
function groupFileItemsIntoLibraryItemDirs(mediaType, fileItems, audiobooksOnly, includeNonMediaFiles = false) {
|
||||||
// Step 1: Filter out non-book-media files in root dir (with depth of 0)
|
// Step 1: Filter out non-book-media files in root dir (with depth of 0)
|
||||||
const itemsFiltered = fileItems.filter((i) => {
|
const itemsFiltered = fileItems.filter((i) => {
|
||||||
return i.deep > 0 || (mediaType === 'book' && isMediaFile(mediaType, i.extension, audiobooksOnly))
|
return i.deep > 0 || (mediaType === 'book' && isMediaFile(mediaType, i.extension, audiobooksOnly))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Step 2: Separate media files and other files
|
// Step 2: Separate media files and other files
|
||||||
// - Directories without a media file will not be included
|
// - Directories without a media file will not be included (unless includeNonMediaFiles is true)
|
||||||
/** @type {import('./fileUtils').FilePathItem[]} */
|
/** @type {import('./fileUtils').FilePathItem[]} */
|
||||||
const mediaFileItems = []
|
const mediaFileItems = []
|
||||||
/** @type {import('./fileUtils').FilePathItem[]} */
|
/** @type {import('./fileUtils').FilePathItem[]} */
|
||||||
const otherFileItems = []
|
const otherFileItems = []
|
||||||
itemsFiltered.forEach((item) => {
|
itemsFiltered.forEach((item) => {
|
||||||
if (isMediaFile(mediaType, item.extension, audiobooksOnly)) mediaFileItems.push(item)
|
if (isMediaFile(mediaType, item.extension, audiobooksOnly) || (includeNonMediaFiles && isScannableNonMediaFile(item.extension))) {
|
||||||
else otherFileItems.push(item)
|
mediaFileItems.push(item)
|
||||||
|
} else {
|
||||||
|
otherFileItems.push(item)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Step 3: Group audio files in library items
|
// Step 3: Group media files (or non-media files if includeNonMediaFiles is true) in library items
|
||||||
const libraryItemGroup = {}
|
const libraryItemGroup = {}
|
||||||
mediaFileItems.forEach((item) => {
|
mediaFileItems.forEach((item) => {
|
||||||
const dirparts = item.reldirpath.split('/').filter((p) => !!p)
|
const dirparts = item.reldirpath.split('/').filter((p) => !!p)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ describe('fileUtils', () => {
|
|||||||
|
|
||||||
// Mock file structure with normalized paths
|
// Mock file structure with normalized paths
|
||||||
const mockDirContents = new Map([
|
const mockDirContents = new Map([
|
||||||
['/test', ['file1.mp3', 'subfolder', 'ignoreme', 'temp.mp3.tmp']],
|
['/test', ['file1.mp3', 'subfolder', 'ignoreme', 'ignoremenot.mp3', 'temp.mp3.tmp']],
|
||||||
['/test/subfolder', ['file2.m4b']],
|
['/test/subfolder', ['file2.m4b']],
|
||||||
['/test/ignoreme', ['.ignore', 'ignored.mp3']]
|
['/test/ignoreme', ['.ignore', 'ignored.mp3']]
|
||||||
])
|
])
|
||||||
@@ -59,7 +59,8 @@ describe('fileUtils', () => {
|
|||||||
['/test/ignoreme', { isDirectory: () => true, size: 0, mtimeMs: Date.now(), ino: '4' }],
|
['/test/ignoreme', { isDirectory: () => true, size: 0, mtimeMs: Date.now(), ino: '4' }],
|
||||||
['/test/ignoreme/.ignore', { isDirectory: () => false, size: 0, mtimeMs: Date.now(), ino: '5' }],
|
['/test/ignoreme/.ignore', { isDirectory: () => false, size: 0, mtimeMs: Date.now(), ino: '5' }],
|
||||||
['/test/ignoreme/ignored.mp3', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '6' }],
|
['/test/ignoreme/ignored.mp3', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '6' }],
|
||||||
['/test/temp.mp3.tmp', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '7' }]
|
['/test/ignoremenot.mp3', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '7' }],
|
||||||
|
['/test/temp.mp3.tmp', { isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '8' }]
|
||||||
])
|
])
|
||||||
|
|
||||||
// Stub fs.readdir
|
// Stub fs.readdir
|
||||||
@@ -103,7 +104,7 @@ describe('fileUtils', () => {
|
|||||||
it('should return filtered file list', async () => {
|
it('should return filtered file list', async () => {
|
||||||
const files = await fileUtils.recurseFiles('/test')
|
const files = await fileUtils.recurseFiles('/test')
|
||||||
expect(files).to.be.an('array')
|
expect(files).to.be.an('array')
|
||||||
expect(files).to.have.lengthOf(2)
|
expect(files).to.have.lengthOf(3)
|
||||||
|
|
||||||
expect(files[0]).to.deep.equal({
|
expect(files[0]).to.deep.equal({
|
||||||
name: 'file1.mp3',
|
name: 'file1.mp3',
|
||||||
@@ -115,6 +116,15 @@ describe('fileUtils', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(files[1]).to.deep.equal({
|
expect(files[1]).to.deep.equal({
|
||||||
|
name: 'ignoremenot.mp3',
|
||||||
|
path: 'ignoremenot.mp3',
|
||||||
|
reldirpath: '',
|
||||||
|
fullpath: '/test/ignoremenot.mp3',
|
||||||
|
extension: '.mp3',
|
||||||
|
deep: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(files[2]).to.deep.equal({
|
||||||
name: 'file2.m4b',
|
name: 'file2.m4b',
|
||||||
path: 'subfolder/file2.m4b',
|
path: 'subfolder/file2.m4b',
|
||||||
reldirpath: 'subfolder',
|
reldirpath: 'subfolder',
|
||||||
|
|||||||
Reference in New Issue
Block a user