Merge branch 'master' into Fuzzy-Matching-Continued

This commit is contained in:
advplyr 2023-10-07 11:52:04 -05:00
commit 786df450e5
34 changed files with 489 additions and 346 deletions

View File

@ -15,8 +15,8 @@
</div> </div>
<p v-if="book.author" class="text-gray-300 text-xs md:text-sm">by {{ book.author }}</p> <p v-if="book.author" class="text-gray-300 text-xs md:text-sm">by {{ book.author }}</p>
<p v-if="book.narrator" class="text-gray-400 text-xs">Narrated by {{ book.narrator }}</p> <p v-if="book.narrator" class="text-gray-400 text-xs">Narrated by {{ book.narrator }}</p>
<p v-if="book.duration" class="text-gray-400 text-xs">Runtime: {{ $elapsedPrettyExtended(book.duration * 60) }}</p> <p v-if="book.duration" class="text-gray-400 text-xs">Runtime: {{ $elapsedPrettyExtended(bookDuration, false) }} {{ bookDurationComparison }}</p>
<div v-if="book.series && book.series.length" class="flex py-1 -mx-1"> <div v-if="book.series?.length" class="flex py-1 -mx-1">
<div v-for="(series, index) in book.series" :key="index" class="bg-white bg-opacity-10 rounded-full px-1 py-0.5 mx-1"> <div v-for="(series, index) in book.series" :key="index" class="bg-white bg-opacity-10 rounded-full px-1 py-0.5 mx-1">
<p class="leading-3 text-xs text-gray-400"> <p class="leading-3 text-xs text-gray-400">
{{ series.series }}<span v-if="series.sequence">&nbsp;#{{ series.sequence }}</span> {{ series.series }}<span v-if="series.sequence">&nbsp;#{{ series.sequence }}</span>
@ -29,9 +29,7 @@
</div> </div>
<div v-else class="px-4 flex-grow"> <div v-else class="px-4 flex-grow">
<h1> <h1>
<div class="flex items-center"> <div class="flex items-center">{{ book.title }}<widgets-explicit-indicator :explicit="book.explicit" /></div>
{{ book.title }}<widgets-explicit-indicator :explicit="book.explicit" />
</div>
</h1> </h1>
<p class="text-base text-gray-300 whitespace-nowrap truncate">by {{ book.author }}</p> <p class="text-base text-gray-300 whitespace-nowrap truncate">by {{ book.author }}</p>
<p v-if="book.genres" class="text-xs text-gray-400 leading-5">{{ book.genres.join(', ') }}</p> <p v-if="book.genres" class="text-xs text-gray-400 leading-5">{{ book.genres.join(', ') }}</p>
@ -56,7 +54,8 @@ export default {
default: () => {} default: () => {}
}, },
isPodcast: Boolean, isPodcast: Boolean,
bookCoverAspectRatio: Number bookCoverAspectRatio: Number,
currentBookDuration: Number
}, },
data() { data() {
return { return {
@ -65,12 +64,27 @@ export default {
}, },
computed: { computed: {
bookCovers() { bookCovers() {
return this.book.covers ? this.book.covers || [] : [] return this.book.covers || []
},
bookDuration() {
return (this.book.duration || 0) * 60
},
bookDurationComparison() {
if (!this.bookDuration || !this.currentBookDuration) return ''
let differenceInSeconds = this.currentBookDuration - this.bookDuration
// Only show seconds on difference if difference is less than an hour
if (differenceInSeconds < 0) {
differenceInSeconds = Math.abs(differenceInSeconds)
return `(${this.$elapsedPrettyExtended(differenceInSeconds, false, differenceInSeconds < 3600)} shorter)`
} else if (differenceInSeconds > 0) {
return `(${this.$elapsedPrettyExtended(differenceInSeconds, false, differenceInSeconds < 3600)} longer)`
}
return '(exact match)'
} }
}, },
methods: { methods: {
selectMatch() { selectMatch() {
var book = { ...this.book } const book = { ...this.book }
book.cover = this.selectedCover book.cover = this.selectedCover
this.$emit('select', book) this.$emit('select', book)
}, },

View File

@ -14,13 +14,17 @@
</div> </div>
<div class="w-1/2 px-2"> <div class="w-1/2 px-2">
<ui-text-input-with-label v-if="!isEditingRoot" v-model="newUser.password" :label="isNew ? $strings.LabelPassword : $strings.LabelChangePassword" type="password" /> <ui-text-input-with-label v-if="!isEditingRoot" v-model="newUser.password" :label="isNew ? $strings.LabelPassword : $strings.LabelChangePassword" type="password" />
<ui-text-input-with-label v-else v-model="newUser.email" :label="$strings.LabelEmail" />
</div> </div>
</div> </div>
<div v-show="!isEditingRoot" class="flex py-2"> <div v-show="!isEditingRoot" class="flex py-2">
<div class="px-2 w-52"> <div class="w-1/2 px-2">
<ui-dropdown v-model="newUser.type" :label="$strings.LabelAccountType" :disabled="isEditingRoot" :items="accountTypes" @input="userTypeUpdated" /> <ui-text-input-with-label v-model="newUser.email" :label="$strings.LabelEmail" />
</div> </div>
<div class="flex-grow" /> <div class="px-2 w-52">
<ui-dropdown v-model="newUser.type" :label="$strings.LabelAccountType" :disabled="isEditingRoot" :items="accountTypes" small @input="userTypeUpdated" />
</div>
<!-- <div class="flex-grow" /> -->
<div class="flex items-center pt-4 px-2"> <div class="flex items-center pt-4 px-2">
<p class="px-3 font-semibold" id="user-enabled-toggle" :class="isEditingRoot ? 'text-gray-300' : ''">{{ $strings.LabelEnable }}</p> <p class="px-3 font-semibold" id="user-enabled-toggle" :class="isEditingRoot ? 'text-gray-300' : ''">{{ $strings.LabelEnable }}</p>
<ui-toggle-switch labeledBy="user-enabled-toggle" v-model="newUser.isActive" :disabled="isEditingRoot" /> <ui-toggle-switch labeledBy="user-enabled-toggle" v-model="newUser.isActive" :disabled="isEditingRoot" />
@ -257,7 +261,6 @@ export default {
if (account.type === 'root' && !account.isActive) return if (account.type === 'root' && !account.isActive) return
this.processing = true this.processing = true
console.log('Calling update', account)
this.$axios this.$axios
.$patch(`/api/users/${this.account.id}`, account) .$patch(`/api/users/${this.account.id}`, account)
.then((data) => { .then((data) => {
@ -329,6 +332,7 @@ export default {
if (this.account) { if (this.account) {
this.newUser = { this.newUser = {
username: this.account.username, username: this.account.username,
email: this.account.email,
password: this.account.password, password: this.account.password,
type: this.account.type, type: this.account.type,
isActive: this.account.isActive, isActive: this.account.isActive,
@ -337,9 +341,9 @@ export default {
itemTagsSelected: [...(this.account.itemTagsSelected || [])] itemTagsSelected: [...(this.account.itemTagsSelected || [])]
} }
} else { } else {
this.fetchAllTags()
this.newUser = { this.newUser = {
username: null, username: null,
email: null,
password: null, password: null,
type: 'user', type: 'user',
isActive: true, isActive: true,

View File

@ -22,7 +22,7 @@
</div> </div>
<div v-show="!processing" class="w-full max-h-full overflow-y-auto overflow-x-hidden matchListWrapper mt-4"> <div v-show="!processing" class="w-full max-h-full overflow-y-auto overflow-x-hidden matchListWrapper mt-4">
<template v-for="(res, index) in searchResults"> <template v-for="(res, index) in searchResults">
<cards-book-match-card :key="index" :book="res" :is-podcast="isPodcast" :book-cover-aspect-ratio="bookCoverAspectRatio" @select="selectMatch" /> <cards-book-match-card :key="index" :book="res" :current-book-duration="currentBookDuration" :is-podcast="isPodcast" :book-cover-aspect-ratio="bookCoverAspectRatio" @select="selectMatch" />
</template> </template>
</div> </div>
<div v-if="selectedMatchOrig" class="absolute top-0 left-0 w-full bg-bg h-full px-2 py-6 md:p-8 max-h-full overflow-y-auto overflow-x-hidden"> <div v-if="selectedMatchOrig" class="absolute top-0 left-0 w-full bg-bg h-full px-2 py-6 md:p-8 max-h-full overflow-y-auto overflow-x-hidden">
@ -205,7 +205,7 @@ export default {
processing: Boolean, processing: Boolean,
libraryItem: { libraryItem: {
type: Object, type: Object,
default: () => { } default: () => {}
} }
}, },
data() { data() {
@ -290,13 +290,17 @@ export default {
return this.$strings.LabelSearchTitle return this.$strings.LabelSearchTitle
}, },
media() { media() {
return this.libraryItem ? this.libraryItem.media || {} : {} return this.libraryItem?.media || {}
}, },
mediaMetadata() { mediaMetadata() {
return this.media.metadata || {} return this.media.metadata || {}
}, },
currentBookDuration() {
if (this.isPodcast) return 0
return this.media.duration || 0
},
mediaType() { mediaType() {
return this.libraryItem ? this.libraryItem.mediaType : null return this.libraryItem?.mediaType || null
}, },
isPodcast() { isPodcast() {
return this.mediaType == 'podcast' return this.mediaType == 'podcast'

View File

@ -129,7 +129,6 @@ export default {
this.users = res.users.sort((a, b) => { this.users = res.users.sort((a, b) => {
return a.createdAt - b.createdAt return a.createdAt - b.createdAt
}) })
console.log('Loaded users', this.users)
}) })
.catch((error) => { .catch((error) => {
console.error('Failed', error) console.error('Failed', error)

View File

@ -191,7 +191,7 @@ export default {
} }
}, },
methods: { methods: {
search() {}, submit() {},
inputUpdate() { inputUpdate() {
clearTimeout(this.searchTimeout) clearTimeout(this.searchTimeout)
this.searchTimeout = setTimeout(() => { this.searchTimeout = setTimeout(() => {

View File

@ -11,7 +11,7 @@
</div> </div>
{{ item }} {{ item }}
</div> </div>
<input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" /> <input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" />
</div> </div>
</form> </form>
@ -145,6 +145,31 @@ export default {
this.menu.style.left = boundingBox.x + 'px' this.menu.style.left = boundingBox.x + 'px'
this.menu.style.width = boundingBox.width + 'px' this.menu.style.width = boundingBox.width + 'px'
}, },
inputPaste(evt) {
setTimeout(() => {
const pastedText = evt.target?.value || ''
console.log('Pasted text=', pastedText)
const pastedItems = [
...new Set(
pastedText
.split(';')
.map((i) => i.trim())
.filter((i) => i)
)
]
// Filter out items already selected
const itemsToAdd = pastedItems.filter((i) => !this.selected.some((_i) => _i.toLowerCase() === i.toLowerCase()))
if (pastedItems.length && !itemsToAdd.length) {
this.textInput = null
this.currentSearch = null
} else {
for (const itemToAdd of itemsToAdd) {
this.insertNewItem(itemToAdd)
}
}
}, 10)
},
inputFocus() { inputFocus() {
if (!this.menu) { if (!this.menu) {
this.unmountMountMenu() this.unmountMountMenu()

View File

@ -14,7 +14,7 @@
<div v-if="showEdit && !disabled" class="rounded-full cursor-pointer w-6 h-6 mx-0.5 bg-bg flex items-center justify-center"> <div v-if="showEdit && !disabled" class="rounded-full cursor-pointer w-6 h-6 mx-0.5 bg-bg flex items-center justify-center">
<span class="material-icons text-white hover:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem">add</span> <span class="material-icons text-white hover:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem">add</span>
</div> </div>
<input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" /> <input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" />
</div> </div>
</form> </form>
@ -112,6 +112,7 @@ export default {
return !!this.selected.find((i) => i.id === itemValue) return !!this.selected.find((i) => i.id === itemValue)
}, },
search() { search() {
if (!this.textInput) return
this.currentSearch = this.textInput this.currentSearch = this.textInput
const dataToSearch = this.filterData[this.filterKey] || [] const dataToSearch = this.filterData[this.filterKey] || []
@ -165,6 +166,34 @@ export default {
this.menu.style.left = boundingBox.x + 'px' this.menu.style.left = boundingBox.x + 'px'
this.menu.style.width = boundingBox.width + 'px' this.menu.style.width = boundingBox.width + 'px'
}, },
inputPaste(evt) {
setTimeout(() => {
const pastedText = evt.target?.value || ''
console.log('Pasted text=', pastedText)
const pastedItems = [
...new Set(
pastedText
.split(';')
.map((i) => i.trim())
.filter((i) => i)
)
]
// Filter out items already selected
const itemsToAdd = pastedItems.filter((i) => !this.selected.some((_i) => _i[this.textKey].toLowerCase() === i.toLowerCase()))
if (pastedItems.length && !itemsToAdd.length) {
this.textInput = null
this.currentSearch = null
} else {
for (const [index, itemToAdd] of itemsToAdd.entries()) {
this.insertNewItem({
id: `new-${Date.now()}-${index}`,
name: itemToAdd
})
}
}
}, 10)
},
inputFocus() { inputFocus() {
if (!this.menu) { if (!this.menu) {
this.unmountMountMenu() this.unmountMountMenu()

View File

@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.4.3", "version": "2.4.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.4.3", "version": "2.4.4",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.13.6", "@nuxtjs/axios": "^5.13.6",

View File

@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.4.3", "version": "2.4.4",
"description": "Self-hosted audiobook and podcast client", "description": "Self-hosted audiobook and podcast client",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -54,7 +54,7 @@ Vue.prototype.$secondsToTimestamp = (seconds, includeMs = false, alwaysIncludeHo
return `${_hours}:${_minutes.toString().padStart(2, '0')}:${_seconds.toString().padStart(2, '0')}${msString}` return `${_hours}:${_minutes.toString().padStart(2, '0')}:${_seconds.toString().padStart(2, '0')}${msString}`
} }
Vue.prototype.$elapsedPrettyExtended = (seconds, useDays = true) => { Vue.prototype.$elapsedPrettyExtended = (seconds, useDays = true, showSeconds = true) => {
if (isNaN(seconds) || seconds === null) return '' if (isNaN(seconds) || seconds === null) return ''
seconds = Math.round(seconds) seconds = Math.round(seconds)
@ -69,11 +69,16 @@ Vue.prototype.$elapsedPrettyExtended = (seconds, useDays = true) => {
hours -= days * 24 hours -= days * 24
} }
// If not showing seconds then round minutes up
if (minutes && seconds && !showSeconds) {
if (seconds >= 30) minutes++
}
const strs = [] const strs = []
if (days) strs.push(`${days}d`) if (days) strs.push(`${days}d`)
if (hours) strs.push(`${hours}h`) if (hours) strs.push(`${hours}h`)
if (minutes) strs.push(`${minutes}m`) if (minutes) strs.push(`${minutes}m`)
if (seconds) strs.push(`${seconds}s`) if (seconds && showSeconds) strs.push(`${seconds}s`)
return strs.join(' ') return strs.join(' ')
} }

View File

@ -417,7 +417,7 @@
"LabelSettingsPreferAudioMetadata": "Prefer audio metadata", "LabelSettingsPreferAudioMetadata": "Prefer audio metadata",
"LabelSettingsPreferAudioMetadataHelp": "Audio file ID3 meta tags will be used for book details over folder names", "LabelSettingsPreferAudioMetadataHelp": "Audio file ID3 meta tags will be used for book details over folder names",
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata", "LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
"LabelSettingsPreferMatchedMetadataHelp": "Matched data will overide item details when using Quick Match. By default Quick Match will only fill in missing details.", "LabelSettingsPreferMatchedMetadataHelp": "Matched data will override item details when using Quick Match. By default Quick Match will only fill in missing details.",
"LabelSettingsPreferOPFMetadata": "Prefer OPF metadata", "LabelSettingsPreferOPFMetadata": "Prefer OPF metadata",
"LabelSettingsPreferOPFMetadataHelp": "OPF file metadata will be used for book details over folder names", "LabelSettingsPreferOPFMetadataHelp": "OPF file metadata will be used for book details over folder names",
"LabelSettingsSkipMatchingBooksWithASIN": "Skip matching books that already have an ASIN", "LabelSettingsSkipMatchingBooksWithASIN": "Skip matching books that already have an ASIN",

View File

@ -20,11 +20,11 @@
"ButtonCreate": "Crear", "ButtonCreate": "Crear",
"ButtonCreateBackup": "Crear Respaldo", "ButtonCreateBackup": "Crear Respaldo",
"ButtonDelete": "Eliminar", "ButtonDelete": "Eliminar",
"ButtonDownloadQueue": "Queue", "ButtonDownloadQueue": "Fila",
"ButtonEdit": "Editar", "ButtonEdit": "Editar",
"ButtonEditChapters": "Editar Capitulo", "ButtonEditChapters": "Editar Capítulo",
"ButtonEditPodcast": "Editar Podcast", "ButtonEditPodcast": "Editar Podcast",
"ButtonForceReScan": "Forzar Re-Escanear", "ButtonForceReScan": "Forzar Re-Escaneo",
"ButtonFullPath": "Ruta de Acceso Completa", "ButtonFullPath": "Ruta de Acceso Completa",
"ButtonHide": "Esconder", "ButtonHide": "Esconder",
"ButtonHome": "Inicio", "ButtonHome": "Inicio",
@ -34,13 +34,13 @@
"ButtonLogout": "Cerrar Sesión", "ButtonLogout": "Cerrar Sesión",
"ButtonLookup": "Buscar", "ButtonLookup": "Buscar",
"ButtonManageTracks": "Administrar Pistas de Audio", "ButtonManageTracks": "Administrar Pistas de Audio",
"ButtonMapChapterTitles": "Map Chapter Titles", "ButtonMapChapterTitles": "Asignar Títulos a Capítulos",
"ButtonMatchAllAuthors": "Encontrar Todos los Autores", "ButtonMatchAllAuthors": "Encontrar Todos los Autores",
"ButtonMatchBooks": "Encontrar Libros", "ButtonMatchBooks": "Encontrar Libros",
"ButtonNevermind": "Olvidar", "ButtonNevermind": "Olvidar",
"ButtonOk": "Ok", "ButtonOk": "Ok",
"ButtonOpenFeed": "Abrir Fuente", "ButtonOpenFeed": "Abrir Fuente",
"ButtonOpenManager": "Open Manager", "ButtonOpenManager": "Abrir Editor",
"ButtonPlay": "Reproducir", "ButtonPlay": "Reproducir",
"ButtonPlaying": "Reproduciendo", "ButtonPlaying": "Reproduciendo",
"ButtonPlaylists": "Listas de Reproducción", "ButtonPlaylists": "Listas de Reproducción",
@ -55,7 +55,7 @@
"ButtonRemoveAll": "Remover Todos", "ButtonRemoveAll": "Remover Todos",
"ButtonRemoveAllLibraryItems": "Remover Todos los Elementos de la Biblioteca", "ButtonRemoveAllLibraryItems": "Remover Todos los Elementos de la Biblioteca",
"ButtonRemoveFromContinueListening": "Remover de Continuar Escuchando", "ButtonRemoveFromContinueListening": "Remover de Continuar Escuchando",
"ButtonRemoveFromContinueReading": "Remove from Continue Reading", "ButtonRemoveFromContinueReading": "Remover de Continuar Leyendo",
"ButtonRemoveSeriesFromContinueSeries": "Remover Serie de Continuar Series", "ButtonRemoveSeriesFromContinueSeries": "Remover Serie de Continuar Series",
"ButtonReScan": "Re-Escanear", "ButtonReScan": "Re-Escanear",
"ButtonReset": "Reiniciar", "ButtonReset": "Reiniciar",
@ -74,7 +74,7 @@
"ButtonStartM4BEncode": "Iniciar Codificación M4B", "ButtonStartM4BEncode": "Iniciar Codificación M4B",
"ButtonStartMetadataEmbed": "Iniciar la Inserción de Metadata", "ButtonStartMetadataEmbed": "Iniciar la Inserción de Metadata",
"ButtonSubmit": "Enviar", "ButtonSubmit": "Enviar",
"ButtonTest": "Test", "ButtonTest": "Prueba",
"ButtonUpload": "Subir", "ButtonUpload": "Subir",
"ButtonUploadBackup": "Subir Respaldo", "ButtonUploadBackup": "Subir Respaldo",
"ButtonUploadCover": "Subir Portada", "ButtonUploadCover": "Subir Portada",
@ -98,12 +98,12 @@
"HeaderCurrentDownloads": "Descargando Actualmente", "HeaderCurrentDownloads": "Descargando Actualmente",
"HeaderDetails": "Detalles", "HeaderDetails": "Detalles",
"HeaderDownloadQueue": "Lista de Descarga", "HeaderDownloadQueue": "Lista de Descarga",
"HeaderEbookFiles": "Ebook Files", "HeaderEbookFiles": "Archivos de Ebook",
"HeaderEmail": "Email", "HeaderEmail": "Email",
"HeaderEmailSettings": "Email Settings", "HeaderEmailSettings": "Opciones de Email",
"HeaderEpisodes": "Episodios", "HeaderEpisodes": "Episodios",
"HeaderEreaderDevices": "Ereader Devices", "HeaderEreaderDevices": "Dispositivos Ereader",
"HeaderEreaderSettings": "Ereader Settings", "HeaderEreaderSettings": "Opciones de Ereader",
"HeaderFiles": "Elemento", "HeaderFiles": "Elemento",
"HeaderFindChapters": "Buscar Capitulo", "HeaderFindChapters": "Buscar Capitulo",
"HeaderIgnoredFiles": "Ignorar Elemento", "HeaderIgnoredFiles": "Ignorar Elemento",
@ -120,7 +120,7 @@
"HeaderLogs": "Logs", "HeaderLogs": "Logs",
"HeaderManageGenres": "Administrar Géneros", "HeaderManageGenres": "Administrar Géneros",
"HeaderManageTags": "Administrar Etiquetas", "HeaderManageTags": "Administrar Etiquetas",
"HeaderMapDetails": "Map details", "HeaderMapDetails": "Asignar Detalles",
"HeaderMatch": "Encontrar", "HeaderMatch": "Encontrar",
"HeaderMetadataToEmbed": "Metadatos para Insertar", "HeaderMetadataToEmbed": "Metadatos para Insertar",
"HeaderNewAccount": "Nueva Cuenta", "HeaderNewAccount": "Nueva Cuenta",
@ -129,7 +129,7 @@
"HeaderOpenRSSFeed": "Abrir fuente RSS", "HeaderOpenRSSFeed": "Abrir fuente RSS",
"HeaderOtherFiles": "Otros Archivos", "HeaderOtherFiles": "Otros Archivos",
"HeaderPermissions": "Permisos", "HeaderPermissions": "Permisos",
"HeaderPlayerQueue": "Player Queue", "HeaderPlayerQueue": "Fila del Reproductor",
"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": "Podcasts para agregar", "HeaderPodcastsToAdd": "Podcasts para agregar",
@ -139,13 +139,13 @@
"HeaderRSSFeedGeneral": "Detalles RSS", "HeaderRSSFeedGeneral": "Detalles RSS",
"HeaderRSSFeedIsOpen": "Fuente RSS esta abierta", "HeaderRSSFeedIsOpen": "Fuente RSS esta abierta",
"HeaderRSSFeeds": "RSS Feeds", "HeaderRSSFeeds": "RSS Feeds",
"HeaderSavedMediaProgress": "Guardar Progreso de multimedia", "HeaderSavedMediaProgress": "Guardar Progreso de Multimedia",
"HeaderSchedule": "Horario", "HeaderSchedule": "Horario",
"HeaderScheduleLibraryScans": "Programar Escaneo Automático de Biblioteca", "HeaderScheduleLibraryScans": "Programar Escaneo Automático de Biblioteca",
"HeaderSession": "Session", "HeaderSession": "Session",
"HeaderSetBackupSchedule": "Programar Respaldo", "HeaderSetBackupSchedule": "Programar Respaldo",
"HeaderSettings": "Configuraciones", "HeaderSettings": "Configuraciones",
"HeaderSettingsDisplay": "Display", "HeaderSettingsDisplay": "Interfaz",
"HeaderSettingsExperimental": "Funciones Experimentales", "HeaderSettingsExperimental": "Funciones Experimentales",
"HeaderSettingsGeneral": "General", "HeaderSettingsGeneral": "General",
"HeaderSettingsScanner": "Escáner", "HeaderSettingsScanner": "Escáner",
@ -156,21 +156,21 @@
"HeaderStatsRecentSessions": "Sesiones Recientes", "HeaderStatsRecentSessions": "Sesiones Recientes",
"HeaderStatsTop10Authors": "Top 10 Autores", "HeaderStatsTop10Authors": "Top 10 Autores",
"HeaderStatsTop5Genres": "Top 5 Géneros", "HeaderStatsTop5Genres": "Top 5 Géneros",
"HeaderTableOfContents": "Table of Contents", "HeaderTableOfContents": "Tabla de Contenidos",
"HeaderTools": "Herramientas", "HeaderTools": "Herramientas",
"HeaderUpdateAccount": "Actualizar Cuenta", "HeaderUpdateAccount": "Actualizar Cuenta",
"HeaderUpdateAuthor": "Actualizar Autor", "HeaderUpdateAuthor": "Actualizar Autor",
"HeaderUpdateDetails": "Actualizar Detalles", "HeaderUpdateDetails": "Actualizar Detalles",
"HeaderUpdateLibrary": "Actualizar Biblioteca", "HeaderUpdateLibrary": "Actualizar Biblioteca",
"HeaderUsers": "Usuarios", "HeaderUsers": "Usuarios",
"HeaderYourStats": "Tus Estáticas", "HeaderYourStats": "Tus Estadísticas",
"LabelAbridged": "Abridged", "LabelAbridged": "Abreviado",
"LabelAccountType": "Tipo de Cuenta", "LabelAccountType": "Tipo de Cuenta",
"LabelAccountTypeAdmin": "Administrador", "LabelAccountTypeAdmin": "Administrador",
"LabelAccountTypeGuest": "Invitado", "LabelAccountTypeGuest": "Invitado",
"LabelAccountTypeUser": "Usuario", "LabelAccountTypeUser": "Usuario",
"LabelActivity": "Actividad", "LabelActivity": "Actividad",
"LabelAdded": "Added", "LabelAdded": "Añadido",
"LabelAddedAt": "Añadido", "LabelAddedAt": "Añadido",
"LabelAddToCollection": "Añadido a la Colección", "LabelAddToCollection": "Añadido a la Colección",
"LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección", "LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección",
@ -186,38 +186,38 @@
"LabelAuthors": "Autores", "LabelAuthors": "Autores",
"LabelAutoDownloadEpisodes": "Descargar Episodios Automáticamente", "LabelAutoDownloadEpisodes": "Descargar Episodios Automáticamente",
"LabelBackToUser": "Regresar a Usuario", "LabelBackToUser": "Regresar a Usuario",
"LabelBackupLocation": "Backup Location", "LabelBackupLocation": "Ubicación del Respaldo",
"LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático", "LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático",
"LabelBackupsEnableAutomaticBackupsHelp": "Respaldo Guardado en /metadata/backups", "LabelBackupsEnableAutomaticBackupsHelp": "Respaldo Guardado en /metadata/backups",
"LabelBackupsMaxBackupSize": "Tamaño Máximo de Respaldos (en GB)", "LabelBackupsMaxBackupSize": "Tamaño Máximo de Respaldos (en GB)",
"LabelBackupsMaxBackupSizeHelp": "Como protección contra una configuración errónea, los respaldos fallaran si se excede el tamaño configurado.", "LabelBackupsMaxBackupSizeHelp": "Como protección contra una configuración errónea, los respaldos fallarán si se excede el tamaño configurado.",
"LabelBackupsNumberToKeep": "Numero de respaldos para conservar", "LabelBackupsNumberToKeep": "Numero de respaldos para conservar",
"LabelBackupsNumberToKeepHelp": "Solamente 1 respaldo se removerá a la vez. Si tiene mas respaldos guardados, necesita removerlos manualmente.", "LabelBackupsNumberToKeepHelp": "Solamente 1 respaldo se removerá a la vez. Si tiene mas respaldos guardados, debe removerlos manualmente.",
"LabelBitrate": "Bitrate", "LabelBitrate": "Bitrate",
"LabelBooks": "Libros", "LabelBooks": "Libros",
"LabelChangePassword": "Cambiar Contraseña", "LabelChangePassword": "Cambiar Contraseña",
"LabelChannels": "Canales", "LabelChannels": "Canales",
"LabelChapters": "Capitulos", "LabelChapters": "Capítulos",
"LabelChaptersFound": "Capitulo Encontrado", "LabelChaptersFound": "Capítulo Encontrado",
"LabelChapterTitle": "Titulo del Capitulo", "LabelChapterTitle": "Titulo del Capítulo",
"LabelClosePlayer": "Close player", "LabelClosePlayer": "Cerrar Reproductor",
"LabelCodec": "Codec", "LabelCodec": "Codec",
"LabelCollapseSeries": "Colapsar Series", "LabelCollapseSeries": "Colapsar Serie",
"LabelCollection": "Collection", "LabelCollection": "Colección",
"LabelCollections": "Colecciones", "LabelCollections": "Colecciones",
"LabelComplete": "Completo", "LabelComplete": "Completo",
"LabelConfirmPassword": "Confirmar Contraseña", "LabelConfirmPassword": "Confirmar Contraseña",
"LabelContinueListening": "Continuar Escuchando", "LabelContinueListening": "Continuar Escuchando",
"LabelContinueReading": "Continue Reading", "LabelContinueReading": "Continuar Leyendo",
"LabelContinueSeries": "Continuar Series", "LabelContinueSeries": "Continuar Serie",
"LabelCover": "Portada", "LabelCover": "Portada",
"LabelCoverImageURL": "URL de Imagen de Portada", "LabelCoverImageURL": "URL de Imagen de Portada",
"LabelCreatedAt": "Creado", "LabelCreatedAt": "Creado",
"LabelCronExpression": "Cron Expression", "LabelCronExpression": "Expresión de Cron",
"LabelCurrent": "Actual", "LabelCurrent": "Actual",
"LabelCurrently": "En este momento:", "LabelCurrently": "En este momento:",
"LabelCustomCronExpression": "Custom Cron Expression:", "LabelCustomCronExpression": "Expresión de Cron Personalizada:",
"LabelDatetime": "Datetime", "LabelDatetime": "Hora y Fecha",
"LabelDescription": "Descripción", "LabelDescription": "Descripción",
"LabelDeselectAll": "Deseleccionar Todos", "LabelDeselectAll": "Deseleccionar Todos",
"LabelDevice": "Dispositivo", "LabelDevice": "Dispositivo",
@ -225,19 +225,19 @@
"LabelDirectory": "Directorio", "LabelDirectory": "Directorio",
"LabelDiscFromFilename": "Disco a partir del Nombre del Archivo", "LabelDiscFromFilename": "Disco a partir del Nombre del Archivo",
"LabelDiscFromMetadata": "Disco a partir de Metadata", "LabelDiscFromMetadata": "Disco a partir de Metadata",
"LabelDiscover": "Discover", "LabelDiscover": "Descubrir",
"LabelDownload": "Descargar", "LabelDownload": "Descargar",
"LabelDownloadNEpisodes": "Download {0} episodes", "LabelDownloadNEpisodes": "Descargar {0} episodios",
"LabelDuration": "Duración", "LabelDuration": "Duración",
"LabelDurationFound": "Duración Comprobada:", "LabelDurationFound": "Duración Comprobada:",
"LabelEbook": "Ebook", "LabelEbook": "Ebook",
"LabelEbooks": "Ebooks", "LabelEbooks": "Ebooks",
"LabelEdit": "Editar", "LabelEdit": "Editar",
"LabelEmail": "Email", "LabelEmail": "Email",
"LabelEmailSettingsFromAddress": "From Address", "LabelEmailSettingsFromAddress": "Remitente",
"LabelEmailSettingsSecure": "Secure", "LabelEmailSettingsSecure": "Seguridad",
"LabelEmailSettingsSecureHelp": "If true the connection will use TLS when connecting to server. If false then TLS is used if server supports the STARTTLS extension. In most cases set this value to true if you are connecting to port 465. For port 587 or 25 keep it false. (from nodemailer.com/smtp/#authentication)", "LabelEmailSettingsSecureHelp": "Si está activado, se usará TLS para conectarse al servidor. Si está apagado, se usará TLS si su servidor tiene soporte para la extensión STARTTLS. En la mayoría de los casos, puede dejar esta opción activada si se está conectando al puerto 465. Apáguela en el caso de usar los puertos 587 o 25. (de nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "Test Address", "LabelEmailSettingsTestAddress": "Probar Dirección",
"LabelEmbeddedCover": "Portada Integrada", "LabelEmbeddedCover": "Portada Integrada",
"LabelEnable": "Habilitar", "LabelEnable": "Habilitar",
"LabelEnd": "Fin", "LabelEnd": "Fin",
@ -256,13 +256,13 @@
"LabelFinished": "Terminado", "LabelFinished": "Terminado",
"LabelFolder": "Carpeta", "LabelFolder": "Carpeta",
"LabelFolders": "Carpetas", "LabelFolders": "Carpetas",
"LabelFontScale": "Font scale", "LabelFontScale": "Tamaño de Fuente",
"LabelFormat": "Formato", "LabelFormat": "Formato",
"LabelGenre": "Genero", "LabelGenre": "Genero",
"LabelGenres": "Géneros", "LabelGenres": "Géneros",
"LabelHardDeleteFile": "Eliminar Definitivamente", "LabelHardDeleteFile": "Eliminar Definitivamente",
"LabelHasEbook": "Has ebook", "LabelHasEbook": "Tiene Ebook",
"LabelHasSupplementaryEbook": "Has supplementary ebook", "LabelHasSupplementaryEbook": "Tiene Ebook Suplementario",
"LabelHost": "Host", "LabelHost": "Host",
"LabelHour": "Hora", "LabelHour": "Hora",
"LabelIcon": "Icono", "LabelIcon": "Icono",
@ -276,34 +276,34 @@
"LabelIntervalEvery2Hours": "Cada 2 Horas", "LabelIntervalEvery2Hours": "Cada 2 Horas",
"LabelIntervalEvery30Minutes": "Cada 30 minutos", "LabelIntervalEvery30Minutes": "Cada 30 minutos",
"LabelIntervalEvery6Hours": "Cada 6 Horas", "LabelIntervalEvery6Hours": "Cada 6 Horas",
"LabelIntervalEveryDay": "Cada Dia", "LabelIntervalEveryDay": "Cada Día",
"LabelIntervalEveryHour": "Cada Hora", "LabelIntervalEveryHour": "Cada Hora",
"LabelInvalidParts": "Partes Invalidas", "LabelInvalidParts": "Partes Inválidas",
"LabelInvert": "Invert", "LabelInvert": "Invertir",
"LabelItem": "Elemento", "LabelItem": "Elemento",
"LabelLanguage": "Lenguaje", "LabelLanguage": "Lenguaje",
"LabelLanguageDefaultServer": "Lenguaje Predeterminado del Servidor", "LabelLanguageDefaultServer": "Lenguaje Predeterminado del Servidor",
"LabelLastBookAdded": "Last Book Added", "LabelLastBookAdded": "Último Libro Agregado",
"LabelLastBookUpdated": "Last Book Updated", "LabelLastBookUpdated": "Último Libro Actualizado",
"LabelLastSeen": "Ultima Vez Visto", "LabelLastSeen": "Última Vez Visto",
"LabelLastTime": "Ultima Vez", "LabelLastTime": "Última Vez",
"LabelLastUpdate": "Ultima Actualización", "LabelLastUpdate": "Última Actualización",
"LabelLayout": "Layout", "LabelLayout": "Distribución",
"LabelLayoutSinglePage": "Single page", "LabelLayoutSinglePage": "Una Página",
"LabelLayoutSplitPage": "Split page", "LabelLayoutSplitPage": "Dos Páginas",
"LabelLess": "Menos", "LabelLess": "Menos",
"LabelLibrariesAccessibleToUser": "Bibliotecas Disponibles para el Usuario", "LabelLibrariesAccessibleToUser": "Bibliotecas Disponibles para el Usuario",
"LabelLibrary": "Biblioteca", "LabelLibrary": "Biblioteca",
"LabelLibraryItem": "Elemento de Biblioteca", "LabelLibraryItem": "Elemento de Biblioteca",
"LabelLibraryName": "Nombre de Biblioteca", "LabelLibraryName": "Nombre de Biblioteca",
"LabelLimit": "Limites", "LabelLimit": "Limites",
"LabelLineSpacing": "Line spacing", "LabelLineSpacing": "Interlineado",
"LabelListenAgain": "Escuchar Otra Vez", "LabelListenAgain": "Escuchar Otra Vez",
"LabelLogLevelDebug": "Debug", "LabelLogLevelDebug": "Debug",
"LabelLogLevelInfo": "Info", "LabelLogLevelInfo": "Información",
"LabelLogLevelWarn": "Advertencia", "LabelLogLevelWarn": "Advertencia",
"LabelLookForNewEpisodesAfterDate": "Buscar Nuevos Episodios a partir de esta Fecha", "LabelLookForNewEpisodesAfterDate": "Buscar Nuevos Episodios a partir de esta Fecha",
"LabelMediaPlayer": "Media Player", "LabelMediaPlayer": "Reproductor de Medios",
"LabelMediaType": "Tipo de Multimedia", "LabelMediaType": "Tipo de Multimedia",
"LabelMetadataProvider": "Proveedor de Metadata", "LabelMetadataProvider": "Proveedor de Metadata",
"LabelMetaTag": "Meta Tag", "LabelMetaTag": "Meta Tag",
@ -311,8 +311,8 @@
"LabelMinute": "Minuto", "LabelMinute": "Minuto",
"LabelMissing": "Ausente", "LabelMissing": "Ausente",
"LabelMissingParts": "Partes Ausentes", "LabelMissingParts": "Partes Ausentes",
"LabelMore": "Mas", "LabelMore": "Más",
"LabelMoreInfo": "Mas Información", "LabelMoreInfo": "Más Información",
"LabelName": "Nombre", "LabelName": "Nombre",
"LabelNarrator": "Narrador", "LabelNarrator": "Narrador",
"LabelNarrators": "Narradores", "LabelNarrators": "Narradores",
@ -322,17 +322,17 @@
"LabelNewPassword": "Nueva Contraseña", "LabelNewPassword": "Nueva Contraseña",
"LabelNextBackupDate": "Fecha del Siguiente Respaldo", "LabelNextBackupDate": "Fecha del Siguiente Respaldo",
"LabelNextScheduledRun": "Próxima Ejecución Programada", "LabelNextScheduledRun": "Próxima Ejecución Programada",
"LabelNoEpisodesSelected": "No episodes selected", "LabelNoEpisodesSelected": "Ningún Episodio Seleccionado",
"LabelNotes": "Notas", "LabelNotes": "Notas",
"LabelNotFinished": "No Terminado", "LabelNotFinished": "No Terminado",
"LabelNotificationAppriseURL": "Apprise URL(s)", "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",
"LabelNotificationsMaxFailedAttempts": "Máximo de Intentos Fallidos", "LabelNotificationsMaxFailedAttempts": "Máximo de Intentos Fallidos",
"LabelNotificationsMaxFailedAttemptsHelp": "Las notificaciones se desactivan después de fallar este numero 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 notificación", "LabelNotificationsMaxQueueSize": "Tamaño máximo de la cola de notificaciones",
"LabelNotificationsMaxQueueSizeHelp": "Las notificaciones están limitadas a 1 por segundo. Las notificaciones serán ignorados 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.",
"LabelNotificationTitleTemplate": "Plantilla de Titulo", "LabelNotificationTitleTemplate": "Plantilla de Titulo",
"LabelNotStarted": "Sin Iniciar", "LabelNotStarted": "Sin Iniciar",
"LabelNumberOfBooks": "Numero de Libros", "LabelNumberOfBooks": "Numero de Libros",
@ -354,97 +354,97 @@
"LabelPodcast": "Podcast", "LabelPodcast": "Podcast",
"LabelPodcasts": "Podcasts", "LabelPodcasts": "Podcasts",
"LabelPodcastType": "Tipo Podcast", "LabelPodcastType": "Tipo Podcast",
"LabelPort": "Port", "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 su fuente sea indexado por iTunes y Google podcast directories", "LabelPreventIndexing": "Evite que su fuente sea indexada por los directorios de podcasts de iTunes y Google",
"LabelPrimaryEbook": "Primary ebook", "LabelPrimaryEbook": "Ebook principal",
"LabelProgress": "Progreso", "LabelProgress": "Progreso",
"LabelProvider": "Proveedor", "LabelProvider": "Proveedor",
"LabelPubDate": "Fecha de Publicación", "LabelPubDate": "Fecha de Publicación",
"LabelPublisher": "Editor", "LabelPublisher": "Editor",
"LabelPublishYear": "Año de Publicación", "LabelPublishYear": "Año de Publicación",
"LabelRead": "Read", "LabelRead": "Leído",
"LabelReadAgain": "Read Again", "LabelReadAgain": "Volver a leer",
"LabelReadEbookWithoutProgress": "Read ebook without keeping progress", "LabelReadEbookWithoutProgress": "Leer Ebook sin guardar progreso",
"LabelRecentlyAdded": "Agregado Reciente", "LabelRecentlyAdded": "Agregado Recientemente",
"LabelRecentSeries": "Series Recientes", "LabelRecentSeries": "Series Recientes",
"LabelRecommended": "Recomendados", "LabelRecommended": "Recomendados",
"LabelRegion": "Region", "LabelRegion": "Región",
"LabelReleaseDate": "Fecha de Estreno", "LabelReleaseDate": "Fecha de Estreno",
"LabelRemoveCover": "Remover Portada", "LabelRemoveCover": "Remover Portada",
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email", "LabelRSSFeedCustomOwnerEmail": "Email de dueño personalizado",
"LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
"LabelRSSFeedOpen": "Fuente RSS Abierta", "LabelRSSFeedOpen": "Fuente RSS Abierta",
"LabelRSSFeedPreventIndexing": "Prevenir Indaxación", "LabelRSSFeedPreventIndexing": "Prevenir Indexado",
"LabelRSSFeedSlug": "Fuente RSS Slug", "LabelRSSFeedSlug": "Fuente RSS Slug",
"LabelRSSFeedURL": "URL de Fuente RSS", "LabelRSSFeedURL": "URL de Fuente RSS",
"LabelSearchTerm": "Buscar Termino", "LabelSearchTerm": "Buscar Termino",
"LabelSearchTitle": "Buscar Titulo", "LabelSearchTitle": "Buscar Titulo",
"LabelSearchTitleOrASIN": "Buscar Titulo o ASIN", "LabelSearchTitleOrASIN": "Buscar Título o ASIN",
"LabelSeason": "Temporada", "LabelSeason": "Temporada",
"LabelSelectAllEpisodes": "Select all episodes", "LabelSelectAllEpisodes": "Seleccionar todos los episodios",
"LabelSelectEpisodesShowing": "Select {0} episodes showing", "LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
"LabelSendEbookToDevice": "Send Ebook to...", "LabelSendEbookToDevice": "Enviar Ebook a...",
"LabelSequence": "Secuencia", "LabelSequence": "Secuencia",
"LabelSeries": "Series", "LabelSeries": "Series",
"LabelSeriesName": "Nombre de la Serie", "LabelSeriesName": "Nombre de la Serie",
"LabelSeriesProgress": "Progreso de la Serie", "LabelSeriesProgress": "Progreso de la Serie",
"LabelSetEbookAsPrimary": "Set as primary", "LabelSetEbookAsPrimary": "Establecer como primario",
"LabelSetEbookAsSupplementary": "Set as supplementary", "LabelSetEbookAsSupplementary": "Establecer como suplementario",
"LabelSettingsAudiobooksOnly": "Audiobooks only", "LabelSettingsAudiobooksOnly": "Sólo Audiolibros",
"LabelSettingsAudiobooksOnlyHelp": "Enabling this setting will ignore ebook files unless they are inside an audiobook folder in which case they will be set as supplementary ebooks", "LabelSettingsAudiobooksOnlyHelp": "Al activar esta opción se ignorarán los archivos de ebook a menos de que estén dentro de la carpeta de un audiolibro, en cuyo caso se marcarán como ebooks suplementarios",
"LabelSettingsBookshelfViewHelp": "Diseño Skeumorphic con Estantes de Madera", "LabelSettingsBookshelfViewHelp": "Diseño Esqueuomorfo con Estantes de Madera",
"LabelSettingsChromecastSupport": "Soporte para Chromecast", "LabelSettingsChromecastSupport": "Soporte para Chromecast",
"LabelSettingsDateFormat": "Formato de Fecha", "LabelSettingsDateFormat": "Formato de Fecha",
"LabelSettingsDisableWatcher": "Deshabilitar Watcher", "LabelSettingsDisableWatcher": "Deshabilitar Watcher",
"LabelSettingsDisableWatcherForLibrary": "Deshabilitar Watcher de Carpetas para esta biblioteca", "LabelSettingsDisableWatcherForLibrary": "Deshabilitar Watcher de Carpetas para esta biblioteca",
"LabelSettingsDisableWatcherHelp": "Deshabilitar la función automática de agregar/actualizar los elementos, cuando se detecta cambio en los archivos. *Require Reiniciar el Servidor", "LabelSettingsDisableWatcherHelp": "Deshabilitar la función de agregar/actualizar elementos automáticamente cuando se detectan cambios en los archivos. *Require Reiniciar el Servidor",
"LabelSettingsEnableWatcher": "Enable Watcher", "LabelSettingsEnableWatcher": "Habilitar Watcher",
"LabelSettingsEnableWatcherForLibrary": "Enable folder watcher for library", "LabelSettingsEnableWatcherForLibrary": "Habilitar Watcher para la carpeta de esta biblioteca",
"LabelSettingsEnableWatcherHelp": "Enables the automatic adding/updating of items when file changes are detected. *Requires server restart", "LabelSettingsEnableWatcherHelp": "Permite agregar/actualizar elementos automáticamente cuando se detectan cambios en los archivos. *Requires server restart",
"LabelSettingsExperimentalFeatures": "Funciones Experimentales", "LabelSettingsExperimentalFeatures": "Funciones Experimentales",
"LabelSettingsExperimentalFeaturesHelp": "Funciones en desarrollo sobre las que esperamos sus comentarios y experiencia. Haga click aquí para abrir una conversación en Github.", "LabelSettingsExperimentalFeaturesHelp": "Funciones en desarrollo que se beneficiarían de sus comentarios y experiencias de prueba. Haga click aquí para abrir una conversación en Github.",
"LabelSettingsFindCovers": "Buscar Portadas", "LabelSettingsFindCovers": "Buscar Portadas",
"LabelSettingsFindCoversHelp": "Si tu audiolibro no tiene una portada incluida o la portada no esta dentro de la carpeta, el escaneador tratara de encontrar una portada.<br>Nota: Esto extenderá el tiempo de escaneo", "LabelSettingsFindCoversHelp": "Si tu audiolibro no tiene una portada incluída, o la portada no esta dentro de la carpeta, el escaneador tratará de encontrar una portada.<br>Nota: Esto extenderá el tiempo de escaneo",
"LabelSettingsHideSingleBookSeries": "Hide single book series", "LabelSettingsHideSingleBookSeries": "Esconder series con un solo libro",
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.", "LabelSettingsHideSingleBookSeriesHelp": "Las series con un solo libro no aparecerán en la página de series ni la repisa para series de la página principal.",
"LabelSettingsHomePageBookshelfView": "La pagina de inicio usa la vista de librero", "LabelSettingsHomePageBookshelfView": "Usar la vista de librero en la página principal",
"LabelSettingsLibraryBookshelfView": "La biblioteca usa la vista de librero", "LabelSettingsLibraryBookshelfView": "Usar la vista de librero en la biblioteca",
"LabelSettingsOverdriveMediaMarkers": "Usar Markers de multimedia en Overdrive para estos capítulos", "LabelSettingsOverdriveMediaMarkers": "Usar Overdrive Media Markers para estos capítulos",
"LabelSettingsOverdriveMediaMarkersHelp": "Archivos MP3 de Overdrive vienen con capítulos con tiempos incrustados como metadata personalizada. Habilitar esto utilizará estas etiquetas para los tiempos de los capítulos automáticamente.", "LabelSettingsOverdriveMediaMarkersHelp": "Los archivos MP3 de Overdrive vienen con capítulos con tiempos incrustados como metadatos personalizados. Habilitar esta opción utilizará automáticamente estas etiquetas para los tiempos de los capítulos.",
"LabelSettingsParseSubtitles": "Parse subtitles", "LabelSettingsParseSubtitles": "Extraer Subtítulos",
"LabelSettingsParseSubtitlesHelp": "Extraiga subtítulos de los nombres de las carpetas de los audiolibros.<br>Los subtítulos deben estar separados por \" - \"<br>ejemplo. \"Titulo Libro - Este Subtitulo\" tiene el subtitulo \"Este Subtitulo\"", "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í\"",
"LabelSettingsPreferAudioMetadata": "Preferir metadata del audio", "LabelSettingsPreferAudioMetadata": "Preferir metadatos del archivo de audio",
"LabelSettingsPreferAudioMetadataHelp": "Archivos de Audio ID3 meta tags se utilizarán para detalles de libros en vez de los nombres de carpetas", "LabelSettingsPreferAudioMetadataHelp": "Preferir los metadatos ID3 del archivo de audio en vez de los nombres de carpetas para los detalles de libros",
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata", "LabelSettingsPreferMatchedMetadata": "Preferir metadatos encontrados",
"LabelSettingsPreferMatchedMetadataHelp": "Los datos coincidentes anularán los detalles del elemento cuando se utilice Quick Match. Por defecto, Quick Match solo 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.",
"LabelSettingsPreferOPFMetadata": "Preferir OPF metadata", "LabelSettingsPreferOPFMetadata": "Preferir Metadatos OPF",
"LabelSettingsPreferOPFMetadataHelp": "Los archivos de metadata OPF serán usados para los detalles del libro en vez de el nombre de las carpetas", "LabelSettingsPreferOPFMetadataHelp": "Preferir los archivos de metadatos OPF en vez de los nombres de carpetas para los detalles de los libros.",
"LabelSettingsSkipMatchingBooksWithASIN": "Omitir libros coincidentes que ya tengan un ASIN", "LabelSettingsSkipMatchingBooksWithASIN": "Omitir libros coincidentes que ya tengan un ASIN",
"LabelSettingsSkipMatchingBooksWithISBN": "Omitir libros coincidentes que ya tengan un ISBN", "LabelSettingsSkipMatchingBooksWithISBN": "Omitir libros coincidentes que ya tengan un ISBN",
"LabelSettingsSortingIgnorePrefixes": "Ignorar prefijos al ordenando", "LabelSettingsSortingIgnorePrefixes": "Ignorar prefijos al ordenar",
"LabelSettingsSortingIgnorePrefixesHelp": "ejemplo. el prefijo \"el\" del titulo \"El titulo del libro\" sera ordenado como \"Titulo del Libro, el\"", "LabelSettingsSortingIgnorePrefixesHelp": "Por ejemplo: El prefijo \"el\" del titulo \"El titulo del libro\" se ordenará como \"Titulo del Libro, el\".",
"LabelSettingsSquareBookCovers": "Usar portadas cuadradas", "LabelSettingsSquareBookCovers": "Usar portadas cuadradas",
"LabelSettingsSquareBookCoversHelp": "Prefiere usar portadas cuadradas sobre las portadas estándar 1.6:1", "LabelSettingsSquareBookCoversHelp": "Prefierir usar portadas cuadradas sobre las portadas estándar 1.6:1",
"LabelSettingsStoreCoversWithItem": "Guardar portada con elemento", "LabelSettingsStoreCoversWithItem": "Guardar portadas con elementos",
"LabelSettingsStoreCoversWithItemHelp": "Por defecto, las portadas se almacenan en /metadata/items, si habilita esta configuración, las portadas se almacenará en la carpeta de elementos de su biblioteca. Solamente un archivo llamado \"cover\" sera guardado.", "LabelSettingsStoreCoversWithItemHelp": "Por defecto, las portadas se almacenan en /metadata/items. Si habilita esta opción, las portadas se almacenarán en la carpeta de elementos de su biblioteca. Se guardará un solo archivo llamado \"cover\".",
"LabelSettingsStoreMetadataWithItem": "Guardar metadata con elemento", "LabelSettingsStoreMetadataWithItem": "Guardar metadatos con elementos",
"LabelSettingsStoreMetadataWithItemHelp": "Por defecto, los archivos de metadatos se almacenan en /metadata/items, si habilita esta configuración, los archivos de metadata se guardaran en la carpeta de elementos de tu biblioteca. Usa la extension .abs", "LabelSettingsStoreMetadataWithItemHelp": "Por defecto, los archivos de metadatos se almacenan en /metadata/items. Si habilita esta opción, los archivos de metadatos se guardarán en la carpeta de elementos de su biblioteca. Usa la extensión .abs",
"LabelSettingsTimeFormat": "Format de Tiempo", "LabelSettingsTimeFormat": "Formato de Tiempo",
"LabelShowAll": "Mostrar Todos", "LabelShowAll": "Mostrar Todos",
"LabelSize": "Tamaño", "LabelSize": "Tamaño",
"LabelSleepTimer": "Temporizador para Dormir", "LabelSleepTimer": "Temporizador para Dormir",
"LabelSlug": "Slug", "LabelSlug": "Slug",
"LabelStart": "Iniciar", "LabelStart": "Iniciar",
"LabelStarted": "Indiciado", "LabelStarted": "Iniciado",
"LabelStartedAt": "Iniciado En", "LabelStartedAt": "Iniciado En",
"LabelStartTime": "Tiempo de Inicio", "LabelStartTime": "Tiempo de Inicio",
"LabelStatsAudioTracks": "Pistas de Audio", "LabelStatsAudioTracks": "Pistas de Audio",
"LabelStatsAuthors": "Autores", "LabelStatsAuthors": "Autores",
"LabelStatsBestDay": "Mejor Dia", "LabelStatsBestDay": "Mejor Día",
"LabelStatsDailyAverage": "Promedio Diario", "LabelStatsDailyAverage": "Promedio Diario",
"LabelStatsDays": "Dias", "LabelStatsDays": "Días",
"LabelStatsDaysListened": "Dias Escuchando", "LabelStatsDaysListened": "Días Escuchando",
"LabelStatsHours": "Horas", "LabelStatsHours": "Horas",
"LabelStatsInARow": "seguidos", "LabelStatsInARow": "seguidos",
"LabelStatsItemsFinished": "Elementos Terminados", "LabelStatsItemsFinished": "Elementos Terminados",
@ -453,42 +453,42 @@
"LabelStatsMinutesListening": "Minutos Escuchando", "LabelStatsMinutesListening": "Minutos Escuchando",
"LabelStatsOverallDays": "Total de Dias", "LabelStatsOverallDays": "Total de Dias",
"LabelStatsOverallHours": "Total de Horas", "LabelStatsOverallHours": "Total de Horas",
"LabelStatsWeekListening": "Escuchando en la Semana", "LabelStatsWeekListening": "Tiempo escuchando en la Semana",
"LabelSubtitle": "Subtitulo", "LabelSubtitle": "Subtítulo",
"LabelSupportedFileTypes": "Tipo de Archivos Soportados", "LabelSupportedFileTypes": "Tipos de Archivos Soportados",
"LabelTag": "Etiqueta", "LabelTag": "Etiqueta",
"LabelTags": "Etiquetas", "LabelTags": "Etiquetas",
"LabelTagsAccessibleToUser": "Etiquetas Accessible para el Usuario", "LabelTagsAccessibleToUser": "Etiquetas Accessibles al Usuario",
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User", "LabelTagsNotAccessibleToUser": "Etiquetas no Accesibles al Usuario",
"LabelTasks": "Tareas Corriendo", "LabelTasks": "Tareas Corriendo",
"LabelTheme": "Theme", "LabelTheme": "Tema",
"LabelThemeDark": "Dark", "LabelThemeDark": "Oscuro",
"LabelThemeLight": "Light", "LabelThemeLight": "Claro",
"LabelTimeBase": "Time Base", "LabelTimeBase": "Time Base",
"LabelTimeListened": "Tiempo Escuchando", "LabelTimeListened": "Tiempo Escuchando",
"LabelTimeListenedToday": "Tiempo Escuchando Hoy", "LabelTimeListenedToday": "Tiempo Escuchando Hoy",
"LabelTimeRemaining": "{0} restante", "LabelTimeRemaining": "{0} restante",
"LabelTimeToShift": "Tiempo para Cambiar en Segundos", "LabelTimeToShift": "Tiempo para Cambiar en Segundos",
"LabelTitle": "Titulo", "LabelTitle": "Título",
"LabelToolsEmbedMetadata": "Incorporar Metadata", "LabelToolsEmbedMetadata": "Incrustar Metadatos",
"LabelToolsEmbedMetadataDescription": "Incorpora metadata en archivos de audio incluyendo la portada y capítulos.", "LabelToolsEmbedMetadataDescription": "Incrusta metadatos en los archivos de audio, incluyendo la portada y capítulos.",
"LabelToolsMakeM4b": "Hacer Archivo M4B de Audiolibro", "LabelToolsMakeM4b": "Hacer Archivo de Audiolibro M4B",
"LabelToolsMakeM4bDescription": "Generar archivo .M4B de audiolibro con metadata, imágenes de portada y capítulos incorporados.", "LabelToolsMakeM4bDescription": "Generar archivo de audiolibro .M4B con metadatos, imágenes de portada y capítulos incorporados.",
"LabelToolsSplitM4b": "Dividir M4B en Archivos MP3", "LabelToolsSplitM4b": "Dividir M4B en Archivos MP3",
"LabelToolsSplitM4bDescription": "Dividir M4B en Archivos MP3 y incorporar metadata, images de portada y capítulos.", "LabelToolsSplitM4bDescription": "Dividir M4B en Archivos MP3 e incorporar metadatos, imágenes de portada y capítulos.",
"LabelTotalDuration": "Duración Total", "LabelTotalDuration": "Duración Total",
"LabelTotalTimeListened": "Tiempo Total Escuchado", "LabelTotalTimeListened": "Tiempo Total Escuchado",
"LabelTrackFromFilename": "Pista desde el Nombre del Archivo", "LabelTrackFromFilename": "Pista desde el Nombre del Archivo",
"LabelTrackFromMetadata": "Pista desde Metadata", "LabelTrackFromMetadata": "Pista desde Metadatos",
"LabelTracks": "Pistas", "LabelTracks": "Pistas",
"LabelTracksMultiTrack": "Multi-track", "LabelTracksMultiTrack": "Varias pistas",
"LabelTracksNone": "No tracks", "LabelTracksNone": "Ninguna pista",
"LabelTracksSingleTrack": "Single-track", "LabelTracksSingleTrack": "Una pista",
"LabelType": "Tipo", "LabelType": "Tipo",
"LabelUnabridged": "Unabridged", "LabelUnabridged": "No Abreviado",
"LabelUnknown": "Desconocido", "LabelUnknown": "Desconocido",
"LabelUpdateCover": "Actualizar Portada", "LabelUpdateCover": "Actualizar Portada",
"LabelUpdateCoverHelp": "Permitir sobrescribir portadas existentes de los libros seleccionados cuando sean encontrados.", "LabelUpdateCoverHelp": "Permitir sobrescribir las portadas existentes de los libros seleccionados cuando sean encontradas.",
"LabelUpdatedAt": "Actualizado En", "LabelUpdatedAt": "Actualizado En",
"LabelUpdateDetails": "Actualizar Detalles", "LabelUpdateDetails": "Actualizar Detalles",
"LabelUpdateDetailsHelp": "Permitir sobrescribir detalles existentes de los libros seleccionados cuando sean encontrados", "LabelUpdateDetailsHelp": "Permitir sobrescribir detalles existentes de los libros seleccionados cuando sean encontrados",
@ -497,79 +497,79 @@
"LabelUseChapterTrack": "Usar pista por capitulo", "LabelUseChapterTrack": "Usar pista por capitulo",
"LabelUseFullTrack": "Usar pista completa", "LabelUseFullTrack": "Usar pista completa",
"LabelUser": "Usuario", "LabelUser": "Usuario",
"LabelUsername": "Nombré de Usuario", "LabelUsername": "Nombre de Usuario",
"LabelValue": "Valor", "LabelValue": "Valor",
"LabelVersion": "Versión", "LabelVersion": "Versión",
"LabelViewBookmarks": "Ver Marcadores", "LabelViewBookmarks": "Ver Marcadores",
"LabelViewChapters": "Ver Capítulos", "LabelViewChapters": "Ver Capítulos",
"LabelViewQueue": "Ver player queue", "LabelViewQueue": "Ver Fila del Reproductor",
"LabelVolume": "Volumen", "LabelVolume": "Volumen",
"LabelWeekdaysToRun": "Correr en Dias de la Semana", "LabelWeekdaysToRun": "Correr en Días de la Semana",
"LabelYourAudiobookDuration": "Duración de tu Audiolibro", "LabelYourAudiobookDuration": "Duración de tu Audiolibro",
"LabelYourBookmarks": "Tus Marcadores Bookmarks", "LabelYourBookmarks": "Tus Marcadores",
"LabelYourPlaylists": "Tus Listas", "LabelYourPlaylists": "Tus Listas",
"LabelYourProgress": "Tu Progreso", "LabelYourProgress": "Tu Progreso",
"MessageAddToPlayerQueue": "Agregar a player queue", "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\">Apprise API</a> corriendo o un API que maneje los mismos resultados. <br />El Apprise API URL debe tener la misma ruta de archivos que donde se envina las notificaciones, 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>.",
"MessageBackupsDescription": "Los respaldos incluyen, usuarios, el progreso del los usuarios, detalles de los elementos de la biblioteca, configuración del servidor y las imágenes en <code>/metadata/items</code> & <code>/metadata/authors</code>. Los Respaldo <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.",
"MessageBatchQuickMatchDescription": "Quick Match tratara de agregar porta y metadata faltantes de los elementos seleccionados. Habilite la opción de abajo para que Quick Match pueda sobrescribir portadas y/o metadata 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": "No tienes ninguna colección.", "MessageBookshelfNoCollections": "No tienes ninguna colección.",
"MessageBookshelfNoResultsForFilter": "Ningún Resultado para el filtro \"{0}: {1}\"", "MessageBookshelfNoResultsForFilter": "Ningún Resultado para el filtro \"{0}: {1}\"",
"MessageBookshelfNoRSSFeeds": "Ninguna Fuente RSS esta abierta", "MessageBookshelfNoRSSFeeds": "Ninguna Fuente RSS esta abierta",
"MessageBookshelfNoSeries": "No tienes ninguna series", "MessageBookshelfNoSeries": "No tienes ninguna serie",
"MessageChapterEndIsAfter": "El final del capítulo es después del final de su audiolibro.", "MessageChapterEndIsAfter": "El final del capítulo es después del final de su audiolibro.",
"MessageChapterErrorFirstNotZero": "El primer capitulo debe iniciar en 0", "MessageChapterErrorFirstNotZero": "El primer capitulo debe iniciar en 0",
"MessageChapterErrorStartGteDuration": "El tiempo de inicio no es válida 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álida debe ser mayor o igual que la hora de inicio del capítulo anterior", "MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válido: debe ser mayor o igual que el tiempo de inicio del capítulo anterior",
"MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro", "MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro",
"MessageCheckingCron": "Checking cron...", "MessageCheckingCron": "Revisando cron...",
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?", "MessageConfirmCloseFeed": "Está seguro de que desea cerrar esta fuente?",
"MessageConfirmDeleteBackup": "Esta seguro que desea eliminar el respaldo {0}?", "MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?",
"MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", "MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?",
"MessageConfirmDeleteLibrary": "Esta seguro que desea eliminar permanentemente la biblioteca \"{0}\"?", "MessageConfirmDeleteLibrary": "¿Está seguro de que desea eliminar permanentemente la biblioteca \"{0}\"?",
"MessageConfirmDeleteSession": "Esta seguro que desea eliminar esta session?", "MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
"MessageConfirmForceReScan": "Esta seguro que desea forzar re-escanear?", "MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?", "MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?", "MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
"MessageConfirmMarkSeriesFinished": "Esta seguro que desea marcar todos los libros en esta serie como terminados?", "MessageConfirmMarkSeriesFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como terminados?",
"MessageConfirmMarkSeriesNotFinished": "Esta seguro que desea marcar todos los libros en esta serie como no terminados?", "MessageConfirmMarkSeriesNotFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como no terminados?",
"MessageConfirmRemoveAllChapters": "Esta seguro que desea remover todos los capitulos?", "MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?",
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?", "MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?",
"MessageConfirmRemoveCollection": "Esta seguro que desea remover la colección \"{0}\"?", "MessageConfirmRemoveCollection": "¿Está seguro de que desea remover la colección \"{0}\"?",
"MessageConfirmRemoveEpisode": "Esta seguro que desea remover el episodio \"{0}\"?", "MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?",
"MessageConfirmRemoveEpisodes": "Esta seguro que desea remover {0} episodios?", "MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?",
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?", "MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?",
"MessageConfirmRemovePlaylist": "Esta seguro que desea remover su lista de reproducción \"{0}\"?", "MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{0}\"?",
"MessageConfirmRenameGenre": "Esta seguro que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?", "MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
"MessageConfirmRenameGenreMergeNote": "Nota: Este genero ya existe por lo que se fusionarán.", "MessageConfirmRenameGenreMergeNote": "Nota: Este género ya existe, por lo que se fusionarán.",
"MessageConfirmRenameGenreWarning": "Advertencia! un genero similar ya existe \"{0}\".", "MessageConfirmRenameGenreWarning": "Advertencia! Un genero similar ya existe \"{0}\".",
"MessageConfirmRenameTag": "Esta seguro que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?", "MessageConfirmRenameTag": "¿Está seguro de que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?",
"MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe por lo que se fusionarán.", "MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe, por lo que se fusionarán.",
"MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".", "MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".",
"MessageConfirmSendEbookToDevice": "Are you sure you want to send {0} ebook \"{1}\" to device \"{2}\"?", "MessageConfirmSendEbookToDevice": "¿Está seguro de que enviar {0} ebook(s) \"{1}\" al dispositivo \"{2}\"?",
"MessageDownloadingEpisode": "Descargando Capitulo", "MessageDownloadingEpisode": "Descargando Capitulo",
"MessageDragFilesIntoTrackOrder": "Arrastras los archivos en el orden correcto de la pista.", "MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas.",
"MessageEmbedFinished": "Incorporación Terminada!", "MessageEmbedFinished": "Incrustación Terminada!",
"MessageEpisodesQueuedForDownload": "{0} Episodio(s) en cola para descargar", "MessageEpisodesQueuedForDownload": "{0} Episodio(s) en cola para descargar",
"MessageFeedURLWillBe": "Fuente URL sera {0}", "MessageFeedURLWillBe": "URL de la fuente será {0}",
"MessageFetching": "Buscando...", "MessageFetching": "Buscando...",
"MessageForceReScanDescription": "Escaneara todos los archivos como un nuevo escaneo. Archivos de audio con etiqueta 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": "Noticia importante!", "MessageImportantNotice": "¡Notificación importante!",
"MessageInsertChapterBelow": "Insertar Capítulo Abajo", "MessageInsertChapterBelow": "Insertar Capítulo Abajo",
"MessageItemsSelected": "{0} Elementos Seleccionados", "MessageItemsSelected": "{0} Elementos Seleccionados",
"MessageItemsUpdated": "{0} Elementos Actualizados", "MessageItemsUpdated": "{0} Elementos Actualizados",
"MessageJoinUsOn": "Únete en", "MessageJoinUsOn": "Únetenos en",
"MessageListeningSessionsInTheLastYear": "{0} sesiones de escuchadas en el último año", "MessageListeningSessionsInTheLastYear": "{0} sesiones de escucha en el último año",
"MessageLoading": "Cargando...", "MessageLoading": "Cargando...",
"MessageLoadingFolders": "Cargando archivos...", "MessageLoadingFolders": "Cargando archivos...",
"MessageM4BFailed": "M4B Fallo!", "MessageM4BFailed": "¡Fallo de M4B!",
"MessageM4BFinished": "M4B Terminado!", "MessageM4BFinished": "¡M4B Terminado!",
"MessageMapChapterTitles": "Map chapter titles to your existing audiobook chapters without adjusting timestamps", "MessageMapChapterTitles": "Asignar los nombres de capítulos a los capítulos existentes en tu audiolibro sin ajustar sus tiempos",
"MessageMarkAllEpisodesFinished": "Mark all episodes finished", "MessageMarkAllEpisodesFinished": "Marcar todos los episodios como terminados",
"MessageMarkAllEpisodesNotFinished": "Mark all episodes not finished", "MessageMarkAllEpisodesNotFinished": "Marcar todos los episodios como no terminados",
"MessageMarkAsFinished": "Marcar como Terminado", "MessageMarkAsFinished": "Marcar como Terminado",
"MessageMarkAsNotFinished": "Marcar como No Terminado", "MessageMarkAsNotFinished": "Marcar como No Terminado",
"MessageMatchBooksDescription": "intentará hacer coincidir los libros de la biblioteca con un libro del proveedor de búsqueda seleccionado y rellenará los detalles vacíos y la portada. No sobrescribe los detalles.", "MessageMatchBooksDescription": "Se intentará hacer coincidir los libros de la biblioteca con un libro del proveedor de búsqueda seleccionado, y se rellenarán los detalles vacíos y la portada. No sobrescribe los detalles.",
"MessageNoAudioTracks": "Sin Pista de Audio", "MessageNoAudioTracks": "Sin Pista de Audio",
"MessageNoAuthors": "Sin Autores", "MessageNoAuthors": "Sin Autores",
"MessageNoBackups": "Sin Respaldos", "MessageNoBackups": "Sin Respaldos",
@ -582,18 +582,18 @@
"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": "Sin Episodios",
"MessageNoFoldersAvailable": "No Carpetas Disponibles", "MessageNoFoldersAvailable": "No Hay Carpetas Disponibles",
"MessageNoGenres": "Sin Géneros", "MessageNoGenres": "Sin Géneros",
"MessageNoIssues": "Sin Problemas", "MessageNoIssues": "Sin Problemas",
"MessageNoItems": "Sin Elementos", "MessageNoItems": "Sin Elementos",
"MessageNoItemsFound": "Ningún Elemento Encontrado", "MessageNoItemsFound": "Ningún Elemento Encontrado",
"MessageNoListeningSessions": "Ninguna Session Escuchada", "MessageNoListeningSessions": "Ninguna Session Escuchada",
"MessageNoLogs": "No Logs", "MessageNoLogs": "No hay logs",
"MessageNoMediaProgress": "Multimedia sin Progreso ", "MessageNoMediaProgress": "Multimedia sin Progreso",
"MessageNoNotifications": "Ninguna Notificación", "MessageNoNotifications": "Ninguna Notificación",
"MessageNoPodcastsFound": "Ningún podcasts encontrado", "MessageNoPodcastsFound": "Ningún podcast encontrado",
"MessageNoResults": "Sin Resultados", "MessageNoResults": "Sin Resultados",
"MessageNoSearchResultsFor": "No hay resultados de la búsqueda para \"{0}\"", "MessageNoSearchResultsFor": "No hay resultados para la búsqueda \"{0}\"",
"MessageNoSeries": "Sin Series", "MessageNoSeries": "Sin Series",
"MessageNoTags": "Sin Etiquetas", "MessageNoTags": "Sin Etiquetas",
"MessageNoTasksRunning": "Ninguna Tarea Corriendo", "MessageNoTasksRunning": "Ninguna Tarea Corriendo",
@ -603,45 +603,45 @@
"MessageNoUserPlaylists": "No tienes lista de reproducciones", "MessageNoUserPlaylists": "No tienes lista de reproducciones",
"MessageOr": "o", "MessageOr": "o",
"MessagePauseChapter": "Pausar la reproducción del capítulo", "MessagePauseChapter": "Pausar la reproducción del capítulo",
"MessagePlayChapter": "Escuche para comenzar el capítulo", "MessagePlayChapter": "Escuchar el comienzo del capítulo",
"MessagePlaylistCreateFromCollection": "Crear lista de reproducción a partir de colección", "MessagePlaylistCreateFromCollection": "Crear una lista de reproducción a partir de una colección",
"MessagePodcastHasNoRSSFeedForMatching": "El podcast no tiene una URL de fuente RSS que pueda usar que coincida", "MessagePodcastHasNoRSSFeedForMatching": "El podcast no tiene una URL de fuente RSS que pueda usar",
"MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la configuración 'Prefer matched metadata' del servidor este habilita.", "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": "Remover capítulos",
"MessageRemoveEpisodes": "Remover {0} episodio(s)", "MessageRemoveEpisodes": "Remover {0} episodio(s)",
"MessageRemoveFromPlayerQueue": "Romover la cola de reporduccion", "MessageRemoveFromPlayerQueue": "Romover la cola de reproducción",
"MessageRemoveUserWarning": "Esta seguro que desea eliminar el usuario \"{0}\"?", "MessageRemoveUserWarning": "¿Está seguro de que desea eliminar el usuario \"{0}\"?",
"MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuye en", "MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuya en",
"MessageResetChaptersConfirm": "Esta seguro que desea reiniciar el capitulo y deshacer los cambios que hiciste?", "MessageResetChaptersConfirm": "¿Está seguro de que desea deshacer los cambios y revertir los capítulos a su estado original?",
"MessageRestoreBackupConfirm": "Esta seguro que desea para restaurar del respaldo creado en", "MessageRestoreBackupConfirm": "¿Está seguro de que desea para restaurar del respaldo creado en",
"MessageRestoreBackupWarning": "Restaurar sobrescribirá toda la base de datos localizada en /config y las imágenes de portadas en /metadata/items & /metadata/authors.<br /><br />El respaldo no modifica ningún archivo en las carpetas de tu biblioteca. Si ha habilitado la configuración del servidor para almacenar portadas y metadata en las carpetas de su biblioteca, entonces esos no se respaldan o sobrescribe.<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.",
"MessageSearchResultsFor": "Resultados de la búsqueda de", "MessageSearchResultsFor": "Resultados de la búsqueda de",
"MessageServerCouldNotBeReached": "No se pude establecer la conexión con el servidor", "MessageServerCouldNotBeReached": "No se pudo establecer la conexión con el servidor",
"MessageSetChaptersFromTracksDescription": "Establecer capítulos usando cada archivo de audio como un capítulo y el título del capítulo como el nombre del archivo de audio", "MessageSetChaptersFromTracksDescription": "Establecer capítulos usando cada archivo de audio como un capítulo y el título del capítulo como el nombre del archivo de audio",
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?", "MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
"MessageThinking": "Pensando...", "MessageThinking": "Pensando...",
"MessageUploaderItemFailed": "Error al Subir", "MessageUploaderItemFailed": "Error al Subir",
"MessageUploaderItemSuccess": "Éxito al Subir!", "MessageUploaderItemSuccess": "¡Éxito al Subir!",
"MessageUploading": "Subiendo...", "MessageUploading": "Subiendo...",
"MessageValidCronExpression": "Valid cron expression", "MessageValidCronExpression": "Expresión de Cron bálida",
"MessageWatcherIsDisabledGlobally": "Watcher es desactivado globalmente en la configuración del servidor", "MessageWatcherIsDisabledGlobally": "El watcher está desactivado globalmente en la configuración del servidor",
"MessageXLibraryIsEmpty": "{0} La biblioteca esta vacía!", "MessageXLibraryIsEmpty": "La biblioteca {0} está vacía!",
"MessageYourAudiobookDurationIsLonger": "La duración de tu audiolibro es más larga que la duración encontrada", "MessageYourAudiobookDurationIsLonger": "La duración de su audiolibro es más larga que la duración encontrada",
"MessageYourAudiobookDurationIsShorter": "La duración de su audiolibro es más corta que la duración encontrada", "MessageYourAudiobookDurationIsShorter": "La duración de su audiolibro es más corta que la duración encontrada",
"NoteChangeRootPassword": "El usuario Root es el único usuario que puede no tener una contraseña", "NoteChangeRootPassword": "El usuario Root es el único usuario que puede no tener una contraseña",
"NoteChapterEditorTimes": "Nota: La hora de inicio del primer capítulo debe permanecer en 0:00 y la hora de inicio del último capítulo no puede exceder la duración de este audiolibro.", "NoteChapterEditorTimes": "Nota: El tiempo de inicio del primer capítulo debe permanecer en 0:00, y el tiempo de inicio del último capítulo no puede exceder la duración del audiolibro.",
"NoteFolderPicker": "Nota: las carpetas ya asignadas no se mostrarán", "NoteFolderPicker": "Nota: Las carpetas ya asignadas no se mostrarán",
"NoteFolderPickerDebian": "Nota: Folder picker for the debian install is not fully implemented. You should enter the path to your library directly.", "NoteFolderPickerDebian": "Nota: El selector de archivos no está completamente implementado para instalaciones en Debian. Deberá ingresar la ruta de la carpeta de su biblioteca directamente.",
"NoteRSSFeedPodcastAppsHttps": "Advertencia: La mayoría de las aplicaciones de podcast requieren que URL de la fuente RSS use HTTPS", "NoteRSSFeedPodcastAppsHttps": "Advertencia: La mayoría de las aplicaciones de podcast requieren que la URL de la fuente RSS use HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "Advertencia: 1 o más de tus episodios no tienen fecha de publicación. Algunas aplicaciones de podcast lo requieren.", "NoteRSSFeedPodcastAppsPubDate": "Advertencia: 1 o más de sus episodios no tienen fecha de publicación. Algunas aplicaciones de podcast lo requieren.",
"NoteUploaderFoldersWithMediaFiles": "Las carpetas con archivos multimedia se manejarán como elementos separados en la biblioteca.", "NoteUploaderFoldersWithMediaFiles": "Las carpetas con archivos multimedia se manejarán como elementos separados en la biblioteca.",
"NoteUploaderOnlyAudioFiles": "Si subes solamente un archivos de audio, cada archivo se manejara como un audiolibro.", "NoteUploaderOnlyAudioFiles": "Si sube solamente archivos de audio, cada archivo se manejará como un audiolibro por separado.",
"NoteUploaderUnsupportedFiles": "Los archivos no soportados se ignoran. Al elegir o soltar una carpeta, los archivos que no estén en una carpeta serán ignorados.", "NoteUploaderUnsupportedFiles": "Se ignorarán los archivos no soportados. Al elegir o arrastrar una carpeta, los archivos que no estén dentro de una subcarpeta serán ignorados.",
"PlaceholderNewCollection": "Nuevo nombre de la colección", "PlaceholderNewCollection": "Nuevo nombre de la colección",
"PlaceholderNewFolderPath": "Nueva ruta de carpeta", "PlaceholderNewFolderPath": "Nueva ruta de carpeta",
"PlaceholderNewPlaylist": "Nuevo nombre de la lista de reproducción", "PlaceholderNewPlaylist": "Nuevo nombre de la lista de reproducción",
"PlaceholderSearch": "Buscando..", "PlaceholderSearch": "Buscar..",
"PlaceholderSearchEpisode": "Search episode..", "PlaceholderSearchEpisode": "Buscar Episodio..",
"ToastAccountUpdateFailed": "Error al actualizar cuenta", "ToastAccountUpdateFailed": "Error al actualizar cuenta",
"ToastAccountUpdateSuccess": "Cuenta actualizada", "ToastAccountUpdateSuccess": "Cuenta actualizada",
"ToastAuthorImageRemoveFailed": "Error al eliminar la imagen", "ToastAuthorImageRemoveFailed": "Error al eliminar la imagen",
@ -657,16 +657,16 @@
"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",
"ToastBatchUpdateFailed": "Batch update failed", "ToastBatchUpdateFailed": "Subida masiva fallida",
"ToastBatchUpdateSuccess": "Batch update success", "ToastBatchUpdateSuccess": "Subida masiva exitosa",
"ToastBookmarkCreateFailed": "Error al crear marcador", "ToastBookmarkCreateFailed": "Error al crear marcador",
"ToastBookmarkCreateSuccess": "Marca Agregado", "ToastBookmarkCreateSuccess": "Marcador Agregado",
"ToastBookmarkRemoveFailed": "Error al eliminar marcador", "ToastBookmarkRemoveFailed": "Error al eliminar marcador",
"ToastBookmarkRemoveSuccess": "Marcador eliminado", "ToastBookmarkRemoveSuccess": "Marcador eliminado",
"ToastBookmarkUpdateFailed": "Error al eliminar el marcador", "ToastBookmarkUpdateFailed": "Error al actualizar el marcador",
"ToastBookmarkUpdateSuccess": "Marcador actualizado", "ToastBookmarkUpdateSuccess": "Marcador actualizado",
"ToastChaptersHaveErrors": "Los capítulos tienen errores", "ToastChaptersHaveErrors": "Los capítulos tienen errores",
"ToastChaptersMustHaveTitles": "Los capítulos tienen que tener titulo", "ToastChaptersMustHaveTitles": "Los capítulos tienen que tener un título",
"ToastCollectionItemsRemoveFailed": "Error al remover elemento(s) de la colección", "ToastCollectionItemsRemoveFailed": "Error al remover elemento(s) de la colección",
"ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección", "ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección",
"ToastCollectionRemoveFailed": "Error al remover la colección", "ToastCollectionRemoveFailed": "Error al remover la colección",
@ -676,7 +676,7 @@
"ToastItemCoverUpdateFailed": "Error al actualizar la portada del elemento", "ToastItemCoverUpdateFailed": "Error al actualizar la portada del elemento",
"ToastItemCoverUpdateSuccess": "Portada del elemento actualizada", "ToastItemCoverUpdateSuccess": "Portada del elemento actualizada",
"ToastItemDetailsUpdateFailed": "Error al actualizar los detalles del elemento", "ToastItemDetailsUpdateFailed": "Error al actualizar los detalles del elemento",
"ToastItemDetailsUpdateSuccess": "Detalles de Elemento Actualizados", "ToastItemDetailsUpdateSuccess": "Detalles del Elemento Actualizados",
"ToastItemDetailsUpdateUnneeded": "No se necesitan actualizaciones para los detalles del Elemento", "ToastItemDetailsUpdateUnneeded": "No se necesitan actualizaciones para los detalles del Elemento",
"ToastItemMarkedAsFinishedFailed": "Error al marcar como Terminado", "ToastItemMarkedAsFinishedFailed": "Error al marcar como Terminado",
"ToastItemMarkedAsFinishedSuccess": "Elemento marcado como terminado", "ToastItemMarkedAsFinishedSuccess": "Elemento marcado como terminado",
@ -684,11 +684,11 @@
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado", "ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
"ToastLibraryCreateFailed": "Error al crear biblioteca", "ToastLibraryCreateFailed": "Error al crear biblioteca",
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada", "ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada",
"ToastLibraryDeleteFailed": "Error al eliminar la biblioteca", "ToastLibraryDeleteFailed": "Error al eliminar biblioteca",
"ToastLibraryDeleteSuccess": "Biblioteca eliminada", "ToastLibraryDeleteSuccess": "Biblioteca eliminada",
"ToastLibraryScanFailedToStart": "Error al iniciar la exploración", "ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca", "ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
"ToastLibraryUpdateFailed": "Error al actualizar biblioteca", "ToastLibraryUpdateFailed": "Error al actualizar la biblioteca",
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada", "ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada",
"ToastPlaylistCreateFailed": "Error al crear la lista de reproducción.", "ToastPlaylistCreateFailed": "Error al crear la lista de reproducción.",
"ToastPlaylistCreateSuccess": "Lista de reproducción creada", "ToastPlaylistCreateSuccess": "Lista de reproducción creada",
@ -697,15 +697,15 @@
"ToastPlaylistUpdateFailed": "Error al actualizar la lista de reproducción.", "ToastPlaylistUpdateFailed": "Error al actualizar la lista de reproducción.",
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada", "ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
"ToastPodcastCreateFailed": "Error al crear podcast", "ToastPodcastCreateFailed": "Error al crear podcast",
"ToastPodcastCreateSuccess": "Podcast creada", "ToastPodcastCreateSuccess": "Podcast creado",
"ToastRemoveItemFromCollectionFailed": "Error al eliminar el elemento de la colección", "ToastRemoveItemFromCollectionFailed": "Error al eliminar el elemento de la colección",
"ToastRemoveItemFromCollectionSuccess": "Elemento eliminado de la colección.", "ToastRemoveItemFromCollectionSuccess": "Elemento eliminado de la colección.",
"ToastRSSFeedCloseFailed": "Error al cerrar fuente RSS", "ToastRSSFeedCloseFailed": "Error al cerrar fuente RSS",
"ToastRSSFeedCloseSuccess": "Fuente RSS cerrada", "ToastRSSFeedCloseSuccess": "Fuente RSS cerrada",
"ToastSendEbookToDeviceFailed": "Failed to Send Ebook to device", "ToastSendEbookToDeviceFailed": "Error al enviar el ebook al dispositivo",
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"", "ToastSendEbookToDeviceSuccess": "Ebook enviado al dispositivo \"{0}\"",
"ToastSeriesUpdateFailed": "Error al actualizar la serie", "ToastSeriesUpdateFailed": "Error al actualizar la serie",
"ToastSeriesUpdateSuccess": "Series actualizada", "ToastSeriesUpdateSuccess": "Serie actualizada",
"ToastSessionDeleteFailed": "Error al eliminar sesión", "ToastSessionDeleteFailed": "Error al eliminar sesión",
"ToastSessionDeleteSuccess": "Sesión eliminada", "ToastSessionDeleteSuccess": "Sesión eliminada",
"ToastSocketConnected": "Socket conectado", "ToastSocketConnected": "Socket conectado",

View File

@ -99,11 +99,11 @@
"HeaderDetails": "Details", "HeaderDetails": "Details",
"HeaderDownloadQueue": "Download-wachtrij", "HeaderDownloadQueue": "Download-wachtrij",
"HeaderEbookFiles": "Ebook Files", "HeaderEbookFiles": "Ebook Files",
"HeaderEmail": "Email", "HeaderEmail": "E-mail",
"HeaderEmailSettings": "Email Settings", "HeaderEmailSettings": "E-mail instellingen",
"HeaderEpisodes": "Afleveringen", "HeaderEpisodes": "Afleveringen",
"HeaderEreaderDevices": "Ereader Devices", "HeaderEreaderDevices": "Ereader-apparaten",
"HeaderEreaderSettings": "Ereader Settings", "HeaderEreaderSettings": "Ereader-instellingen",
"HeaderFiles": "Bestanden", "HeaderFiles": "Bestanden",
"HeaderFindChapters": "Zoek hoofdstukken", "HeaderFindChapters": "Zoek hoofdstukken",
"HeaderIgnoredFiles": "Genegeerde bestanden", "HeaderIgnoredFiles": "Genegeerde bestanden",
@ -138,7 +138,7 @@
"HeaderRemoveEpisodes": "Verwijder {0} afleveringen", "HeaderRemoveEpisodes": "Verwijder {0} afleveringen",
"HeaderRSSFeedGeneral": "RSS-details", "HeaderRSSFeedGeneral": "RSS-details",
"HeaderRSSFeedIsOpen": "RSS-feed is open", "HeaderRSSFeedIsOpen": "RSS-feed is open",
"HeaderRSSFeeds": "RSS Feeds", "HeaderRSSFeeds": "RSS-feeds",
"HeaderSavedMediaProgress": "Opgeslagen mediavoortgang", "HeaderSavedMediaProgress": "Opgeslagen mediavoortgang",
"HeaderSchedule": "Schema", "HeaderSchedule": "Schema",
"HeaderScheduleLibraryScans": "Schema automatische bibliotheekscans", "HeaderScheduleLibraryScans": "Schema automatische bibliotheekscans",
@ -156,7 +156,7 @@
"HeaderStatsRecentSessions": "Recente sessies", "HeaderStatsRecentSessions": "Recente sessies",
"HeaderStatsTop10Authors": "Top 10 auteurs", "HeaderStatsTop10Authors": "Top 10 auteurs",
"HeaderStatsTop5Genres": "Top 5 genres", "HeaderStatsTop5Genres": "Top 5 genres",
"HeaderTableOfContents": "Table of Contents", "HeaderTableOfContents": "Inhoudsopgave",
"HeaderTools": "Tools", "HeaderTools": "Tools",
"HeaderUpdateAccount": "Account bijwerken", "HeaderUpdateAccount": "Account bijwerken",
"HeaderUpdateAuthor": "Auteur bijwerken", "HeaderUpdateAuthor": "Auteur bijwerken",
@ -179,14 +179,14 @@
"LabelAll": "Alle", "LabelAll": "Alle",
"LabelAllUsers": "Alle gebruikers", "LabelAllUsers": "Alle gebruikers",
"LabelAlreadyInYourLibrary": "Reeds in je bibliotheek", "LabelAlreadyInYourLibrary": "Reeds in je bibliotheek",
"LabelAppend": "Append", "LabelAppend": "Achteraan toevoegen",
"LabelAuthor": "Auteur", "LabelAuthor": "Auteur",
"LabelAuthorFirstLast": "Auteur (Voornaam Achternaam)", "LabelAuthorFirstLast": "Auteur (Voornaam Achternaam)",
"LabelAuthorLastFirst": "Auteur (Achternaam, Voornaam)", "LabelAuthorLastFirst": "Auteur (Achternaam, Voornaam)",
"LabelAuthors": "Auteurs", "LabelAuthors": "Auteurs",
"LabelAutoDownloadEpisodes": "Afleveringen automatisch downloaden", "LabelAutoDownloadEpisodes": "Afleveringen automatisch downloaden",
"LabelBackToUser": "Terug naar gebruiker", "LabelBackToUser": "Terug naar gebruiker",
"LabelBackupLocation": "Backup Location", "LabelBackupLocation": "Back-up locatie",
"LabelBackupsEnableAutomaticBackups": "Automatische back-ups inschakelen", "LabelBackupsEnableAutomaticBackups": "Automatische back-ups inschakelen",
"LabelBackupsEnableAutomaticBackupsHelp": "Back-ups opgeslagen in /metadata/backups", "LabelBackupsEnableAutomaticBackupsHelp": "Back-ups opgeslagen in /metadata/backups",
"LabelBackupsMaxBackupSize": "Maximale back-up-grootte (in GB)", "LabelBackupsMaxBackupSize": "Maximale back-up-grootte (in GB)",
@ -208,7 +208,7 @@
"LabelComplete": "Compleet", "LabelComplete": "Compleet",
"LabelConfirmPassword": "Bevestig wachtwoord", "LabelConfirmPassword": "Bevestig wachtwoord",
"LabelContinueListening": "Verder luisteren", "LabelContinueListening": "Verder luisteren",
"LabelContinueReading": "Continue Reading", "LabelContinueReading": "Verder luisteren",
"LabelContinueSeries": "Ga verder met serie", "LabelContinueSeries": "Ga verder met serie",
"LabelCover": "Cover", "LabelCover": "Cover",
"LabelCoverImageURL": "Coverafbeelding URL", "LabelCoverImageURL": "Coverafbeelding URL",
@ -225,7 +225,7 @@
"LabelDirectory": "Map", "LabelDirectory": "Map",
"LabelDiscFromFilename": "Schijf uit bestandsnaam", "LabelDiscFromFilename": "Schijf uit bestandsnaam",
"LabelDiscFromMetadata": "Schijf uit metadata", "LabelDiscFromMetadata": "Schijf uit metadata",
"LabelDiscover": "Discover", "LabelDiscover": "Ontdek",
"LabelDownload": "Download", "LabelDownload": "Download",
"LabelDownloadNEpisodes": "Download {0} episodes", "LabelDownloadNEpisodes": "Download {0} episodes",
"LabelDuration": "Duur", "LabelDuration": "Duur",
@ -234,10 +234,10 @@
"LabelEbooks": "Ebooks", "LabelEbooks": "Ebooks",
"LabelEdit": "Wijzig", "LabelEdit": "Wijzig",
"LabelEmail": "Email", "LabelEmail": "Email",
"LabelEmailSettingsFromAddress": "From Address", "LabelEmailSettingsFromAddress": "Van-adres",
"LabelEmailSettingsSecure": "Secure", "LabelEmailSettingsSecure": "Veilig",
"LabelEmailSettingsSecureHelp": "If true the connection will use TLS when connecting to server. If false then TLS is used if server supports the STARTTLS extension. In most cases set this value to true if you are connecting to port 465. For port 587 or 25 keep it false. (from nodemailer.com/smtp/#authentication)", "LabelEmailSettingsSecureHelp": "Als 'waar', dan gebruikt de verbinding TLS om met de server te verbinden. Als 'onwaar', dan wordt TLS gebruikt als de server de STARTTLS-extensie ondersteunt. In de meeste gevallen kies je voor 'waar' verbindt met poort 465. Voo poort 587 of 25, laat op 'onwaar'. (van nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "Test Address", "LabelEmailSettingsTestAddress": "Test-adres",
"LabelEmbeddedCover": "Ingesloten cover", "LabelEmbeddedCover": "Ingesloten cover",
"LabelEnable": "Inschakelen", "LabelEnable": "Inschakelen",
"LabelEnd": "Einde", "LabelEnd": "Einde",
@ -256,13 +256,13 @@
"LabelFinished": "Voltooid", "LabelFinished": "Voltooid",
"LabelFolder": "Map", "LabelFolder": "Map",
"LabelFolders": "Mappen", "LabelFolders": "Mappen",
"LabelFontScale": "Font scale", "LabelFontScale": "Lettertype schaal",
"LabelFormat": "Format", "LabelFormat": "Formaat",
"LabelGenre": "Genre", "LabelGenre": "Genre",
"LabelGenres": "Genres", "LabelGenres": "Genres",
"LabelHardDeleteFile": "Hard-delete bestand", "LabelHardDeleteFile": "Hard-delete bestand",
"LabelHasEbook": "Has ebook", "LabelHasEbook": "Heeft ebook",
"LabelHasSupplementaryEbook": "Has supplementary ebook", "LabelHasSupplementaryEbook": "Heeft supplementair ebook",
"LabelHost": "Host", "LabelHost": "Host",
"LabelHour": "Uur", "LabelHour": "Uur",
"LabelIcon": "Icoon", "LabelIcon": "Icoon",
@ -289,15 +289,15 @@
"LabelLastTime": "Laatste keer", "LabelLastTime": "Laatste keer",
"LabelLastUpdate": "Laatste update", "LabelLastUpdate": "Laatste update",
"LabelLayout": "Layout", "LabelLayout": "Layout",
"LabelLayoutSinglePage": "Single page", "LabelLayoutSinglePage": "Enkele pagina",
"LabelLayoutSplitPage": "Split page", "LabelLayoutSplitPage": "Gesplitste pagina",
"LabelLess": "Minder", "LabelLess": "Minder",
"LabelLibrariesAccessibleToUser": "Voor gebruiker toegankelijke bibliotheken", "LabelLibrariesAccessibleToUser": "Voor gebruiker toegankelijke bibliotheken",
"LabelLibrary": "Bibliotheek", "LabelLibrary": "Bibliotheek",
"LabelLibraryItem": "Library Item", "LabelLibraryItem": "Bibliotheekonderdeel",
"LabelLibraryName": "Library Name", "LabelLibraryName": "Bibliotheeknaam",
"LabelLimit": "Limiet", "LabelLimit": "Limiet",
"LabelLineSpacing": "Line spacing", "LabelLineSpacing": "Regelruimte",
"LabelListenAgain": "Luister opnieuw", "LabelListenAgain": "Luister opnieuw",
"LabelLogLevelDebug": "Debug", "LabelLogLevelDebug": "Debug",
"LabelLogLevelInfo": "Info", "LabelLogLevelInfo": "Info",
@ -322,7 +322,7 @@
"LabelNewPassword": "Nieuw wachtwoord", "LabelNewPassword": "Nieuw wachtwoord",
"LabelNextBackupDate": "Volgende back-up datum", "LabelNextBackupDate": "Volgende back-up datum",
"LabelNextScheduledRun": "Volgende geplande run", "LabelNextScheduledRun": "Volgende geplande run",
"LabelNoEpisodesSelected": "No episodes selected", "LabelNoEpisodesSelected": "Geen afleveringen geselecteerd",
"LabelNotes": "Notities", "LabelNotes": "Notities",
"LabelNotFinished": "Niet Voltooid", "LabelNotFinished": "Niet Voltooid",
"LabelNotificationAppriseURL": "Apprise URL(s)", "LabelNotificationAppriseURL": "Apprise URL(s)",
@ -354,18 +354,18 @@
"LabelPodcast": "Podcast", "LabelPodcast": "Podcast",
"LabelPodcasts": "Podcasts", "LabelPodcasts": "Podcasts",
"LabelPodcastType": "Podcasttype", "LabelPodcastType": "Podcasttype",
"LabelPort": "Port", "LabelPort": "Poort",
"LabelPrefixesToIgnore": "Te negeren voorzetsels (ongeacht hoofdlettergebruik)", "LabelPrefixesToIgnore": "Te negeren voorzetsels (ongeacht hoofdlettergebruik)",
"LabelPreventIndexing": "Voorkom indexering van je feed door iTunes- en Google podcastmappen", "LabelPreventIndexing": "Voorkom indexering van je feed door iTunes- en Google podcastmappen",
"LabelPrimaryEbook": "Primary ebook", "LabelPrimaryEbook": "Primair ebook",
"LabelProgress": "Voortgang", "LabelProgress": "Voortgang",
"LabelProvider": "Bron", "LabelProvider": "Bron",
"LabelPubDate": "Publicatiedatum", "LabelPubDate": "Publicatiedatum",
"LabelPublisher": "Uitgever", "LabelPublisher": "Uitgever",
"LabelPublishYear": "Jaar van uitgave", "LabelPublishYear": "Jaar van uitgave",
"LabelRead": "Read", "LabelRead": "Lees",
"LabelReadAgain": "Read Again", "LabelReadAgain": "Lees opnieuw",
"LabelReadEbookWithoutProgress": "Read ebook without keeping progress", "LabelReadEbookWithoutProgress": "Lees ebook zonder voortgang bij te houden",
"LabelRecentlyAdded": "Recent toegevoegd", "LabelRecentlyAdded": "Recent toegevoegd",
"LabelRecentSeries": "Recente series", "LabelRecentSeries": "Recente series",
"LabelRecommended": "Aangeraden", "LabelRecommended": "Aangeraden",
@ -382,32 +382,32 @@
"LabelSearchTitle": "Zoek titel", "LabelSearchTitle": "Zoek titel",
"LabelSearchTitleOrASIN": "Zoek titel of ASIN", "LabelSearchTitleOrASIN": "Zoek titel of ASIN",
"LabelSeason": "Seizoen", "LabelSeason": "Seizoen",
"LabelSelectAllEpisodes": "Select all episodes", "LabelSelectAllEpisodes": "Selecteer alle afleveringen",
"LabelSelectEpisodesShowing": "Select {0} episodes showing", "LabelSelectEpisodesShowing": "Selecteer {0} afleveringen laten zien",
"LabelSendEbookToDevice": "Send Ebook to...", "LabelSendEbookToDevice": "Stuur ebook naar...",
"LabelSequence": "Sequentie", "LabelSequence": "Sequentie",
"LabelSeries": "Serie", "LabelSeries": "Serie",
"LabelSeriesName": "Naam serie", "LabelSeriesName": "Naam serie",
"LabelSeriesProgress": "Voortgang serie", "LabelSeriesProgress": "Voortgang serie",
"LabelSetEbookAsPrimary": "Set as primary", "LabelSetEbookAsPrimary": "Stel in als primair",
"LabelSetEbookAsSupplementary": "Set as supplementary", "LabelSetEbookAsSupplementary": "Stel in als supplementair",
"LabelSettingsAudiobooksOnly": "Audiobooks only", "LabelSettingsAudiobooksOnly": "Alleen audiobooks",
"LabelSettingsAudiobooksOnlyHelp": "Enabling this setting will ignore ebook files unless they are inside an audiobook folder in which case they will be set as supplementary ebooks", "LabelSettingsAudiobooksOnlyHelp": "Deze instelling inschakelen zorgt ervoor dat ebook-bestanden genegeerd worden tenzij ze in een audiobook-map staan, in welk geval ze worden ingesteld als supplementaire ebooks",
"LabelSettingsBookshelfViewHelp": "Skeumorphisch design met houten planken", "LabelSettingsBookshelfViewHelp": "Skeumorphisch design met houten planken",
"LabelSettingsChromecastSupport": "Chromecast support", "LabelSettingsChromecastSupport": "Chromecast support",
"LabelSettingsDateFormat": "Datum format", "LabelSettingsDateFormat": "Datum format",
"LabelSettingsDisableWatcher": "Watcher uitschakelen", "LabelSettingsDisableWatcher": "Watcher uitschakelen",
"LabelSettingsDisableWatcherForLibrary": "Map-watcher voor bibliotheek uitschakelen", "LabelSettingsDisableWatcherForLibrary": "Map-watcher voor bibliotheek uitschakelen",
"LabelSettingsDisableWatcherHelp": "Schakelt het automatisch toevoegen/bijwerken van onderdelen wanneer bestandswijzigingen gedetecteerd zijn uit. *Vereist herstart server", "LabelSettingsDisableWatcherHelp": "Schakelt het automatisch toevoegen/bijwerken van onderdelen wanneer bestandswijzigingen gedetecteerd zijn uit. *Vereist herstart server",
"LabelSettingsEnableWatcher": "Enable Watcher", "LabelSettingsEnableWatcher": "Watcher inschakelen",
"LabelSettingsEnableWatcherForLibrary": "Enable folder watcher for library", "LabelSettingsEnableWatcherForLibrary": "Map-watcher voor bibliotheek inschakelen",
"LabelSettingsEnableWatcherHelp": "Enables the automatic adding/updating of items when file changes are detected. *Requires server restart", "LabelSettingsEnableWatcherHelp": "Zorgt voor het automatisch toevoegen/bijwerken van onderdelen als bestandswijzigingen worden gedetecteerd. *Vereist herstarten van server",
"LabelSettingsExperimentalFeatures": "Experimentele functies", "LabelSettingsExperimentalFeatures": "Experimentele functies",
"LabelSettingsExperimentalFeaturesHelp": "Functies in ontwikkeling die je feedback en testing kunnen gebruiken. Klik om de Github-discussie te openen.", "LabelSettingsExperimentalFeaturesHelp": "Functies in ontwikkeling die je feedback en testing kunnen gebruiken. Klik om de Github-discussie te openen.",
"LabelSettingsFindCovers": "Zoek covers", "LabelSettingsFindCovers": "Zoek covers",
"LabelSettingsFindCoversHelp": "Als je audioboek geen ingesloten cover of cover in de map heeft, zal de scanner proberen een cover te vinden.<br>Opmerking: Dit zal de scan-duur verlengen", "LabelSettingsFindCoversHelp": "Als je audioboek geen ingesloten cover of cover in de map heeft, zal de scanner proberen een cover te vinden.<br>Opmerking: Dit zal de scan-duur verlengen",
"LabelSettingsHideSingleBookSeries": "Hide single book series", "LabelSettingsHideSingleBookSeries": "Verberg series met een enkel boek",
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.", "LabelSettingsHideSingleBookSeriesHelp": "Series die slechts een enkel boek bevatten worden verborgen op de seriespagina en de homepagina-planken.",
"LabelSettingsHomePageBookshelfView": "Boekenplank-view voor homepagina", "LabelSettingsHomePageBookshelfView": "Boekenplank-view voor homepagina",
"LabelSettingsLibraryBookshelfView": "Boekenplank-view voor bibliotheek", "LabelSettingsLibraryBookshelfView": "Boekenplank-view voor bibliotheek",
"LabelSettingsOverdriveMediaMarkers": "Gebruik Overdrive media markers voor hoofdstukken", "LabelSettingsOverdriveMediaMarkers": "Gebruik Overdrive media markers voor hoofdstukken",
@ -461,9 +461,9 @@
"LabelTagsAccessibleToUser": "Tags toegankelijk voor de gebruiker", "LabelTagsAccessibleToUser": "Tags toegankelijk voor de gebruiker",
"LabelTagsNotAccessibleToUser": "Tags niet toegankelijk voor de gebruiker", "LabelTagsNotAccessibleToUser": "Tags niet toegankelijk voor de gebruiker",
"LabelTasks": "Lopende taken", "LabelTasks": "Lopende taken",
"LabelTheme": "Theme", "LabelTheme": "Thema",
"LabelThemeDark": "Dark", "LabelThemeDark": "Donker",
"LabelThemeLight": "Light", "LabelThemeLight": "Licht",
"LabelTimeBase": "Tijdsbasis", "LabelTimeBase": "Tijdsbasis",
"LabelTimeListened": "Tijd geluisterd", "LabelTimeListened": "Tijd geluisterd",
"LabelTimeListenedToday": "Tijd geluisterd vandaag", "LabelTimeListenedToday": "Tijd geluisterd vandaag",
@ -482,8 +482,8 @@
"LabelTrackFromMetadata": "Track vanuit metadata", "LabelTrackFromMetadata": "Track vanuit metadata",
"LabelTracks": "Tracks", "LabelTracks": "Tracks",
"LabelTracksMultiTrack": "Multi-track", "LabelTracksMultiTrack": "Multi-track",
"LabelTracksNone": "No tracks", "LabelTracksNone": "Geen tracks",
"LabelTracksSingleTrack": "Single-track", "LabelTracksSingleTrack": "Enkele track",
"LabelType": "Type", "LabelType": "Type",
"LabelUnabridged": "Onverkort", "LabelUnabridged": "Onverkort",
"LabelUnknown": "Onbekend", "LabelUnknown": "Onbekend",
@ -525,16 +525,16 @@
"MessageCheckingCron": "Cron aan het checken...", "MessageCheckingCron": "Cron aan het checken...",
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?", "MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
"MessageConfirmDeleteBackup": "Weet je zeker dat je de backup voor {0} wil verwijderen?", "MessageConfirmDeleteBackup": "Weet je zeker dat je de backup voor {0} wil verwijderen?",
"MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", "MessageConfirmDeleteFile": "Dit verwijdert het bestand uit het bestandssysteem. Weet je het zeker?",
"MessageConfirmDeleteLibrary": "Weet je zeker dat je de bibliotheek \"{0}\" permanent wil verwijderen?", "MessageConfirmDeleteLibrary": "Weet je zeker dat je de bibliotheek \"{0}\" permanent wil verwijderen?",
"MessageConfirmDeleteSession": "Weet je zeker dat je deze sessie wil verwijderen?", "MessageConfirmDeleteSession": "Weet je zeker dat je deze sessie wil verwijderen?",
"MessageConfirmForceReScan": "Weet je zeker dat je geforceerd opnieuw wil scannen?", "MessageConfirmForceReScan": "Weet je zeker dat je geforceerd opnieuw wil scannen?",
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?", "MessageConfirmMarkAllEpisodesFinished": "Weet je zeker dat je alle afleveringen als voltooid wil markeren?",
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?", "MessageConfirmMarkAllEpisodesNotFinished": "Weet je zeker dat je alle afleveringen als niet-voltooid wil markeren?",
"MessageConfirmMarkSeriesFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als voltooid?", "MessageConfirmMarkSeriesFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als voltooid?",
"MessageConfirmMarkSeriesNotFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als niet voltooid?", "MessageConfirmMarkSeriesNotFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als niet voltooid?",
"MessageConfirmRemoveAllChapters": "Weet je zeker dat je alle hoofdstukken wil verwijderen?", "MessageConfirmRemoveAllChapters": "Weet je zeker dat je alle hoofdstukken wil verwijderen?",
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?", "MessageConfirmRemoveAuthor": "Weet je zeker dat je auteur \"{0}\" wil verwijderen?",
"MessageConfirmRemoveCollection": "Weet je zeker dat je de collectie \"{0}\" wil verwijderen?", "MessageConfirmRemoveCollection": "Weet je zeker dat je de collectie \"{0}\" wil verwijderen?",
"MessageConfirmRemoveEpisode": "Weet je zeker dat je de aflevering \"{0}\" wil verwijderen?", "MessageConfirmRemoveEpisode": "Weet je zeker dat je de aflevering \"{0}\" wil verwijderen?",
"MessageConfirmRemoveEpisodes": "Weet je zeker dat je {0} afleveringen wil verwijderen?", "MessageConfirmRemoveEpisodes": "Weet je zeker dat je {0} afleveringen wil verwijderen?",
@ -546,7 +546,7 @@
"MessageConfirmRenameTag": "Weet je zeker dat je tag \"{0}\" wil hernoemen naar\"{1}\" voor alle onderdelen?", "MessageConfirmRenameTag": "Weet je zeker dat je tag \"{0}\" wil hernoemen naar\"{1}\" voor alle onderdelen?",
"MessageConfirmRenameTagMergeNote": "Opmerking: Deze tag bestaat al, dus zullen ze worden samengevoegd.", "MessageConfirmRenameTagMergeNote": "Opmerking: Deze tag bestaat al, dus zullen ze worden samengevoegd.",
"MessageConfirmRenameTagWarning": "Waarschuwing! Een gelijknamige tag met ander hoofdlettergebruik bestaat al: \"{0}\".", "MessageConfirmRenameTagWarning": "Waarschuwing! Een gelijknamige tag met ander hoofdlettergebruik bestaat al: \"{0}\".",
"MessageConfirmSendEbookToDevice": "Are you sure you want to send {0} ebook \"{1}\" to device \"{2}\"?", "MessageConfirmSendEbookToDevice": "Weet je zeker dat je {0} ebook \"{1}\" naar apparaat \"{2}\" wil sturen?",
"MessageDownloadingEpisode": "Aflevering aan het dowloaden", "MessageDownloadingEpisode": "Aflevering aan het dowloaden",
"MessageDragFilesIntoTrackOrder": "Sleep bestanden in de juiste trackvolgorde", "MessageDragFilesIntoTrackOrder": "Sleep bestanden in de juiste trackvolgorde",
"MessageEmbedFinished": "Insluiting voltooid!", "MessageEmbedFinished": "Insluiting voltooid!",
@ -565,8 +565,8 @@
"MessageM4BFailed": "M4B mislukt!", "MessageM4BFailed": "M4B mislukt!",
"MessageM4BFinished": "M4B voltooid!", "MessageM4BFinished": "M4B voltooid!",
"MessageMapChapterTitles": "Map hoofdstuktitels naar je bestaande audioboekhoofdstukken zonder aanpassing van tijden", "MessageMapChapterTitles": "Map hoofdstuktitels naar je bestaande audioboekhoofdstukken zonder aanpassing van tijden",
"MessageMarkAllEpisodesFinished": "Mark all episodes finished", "MessageMarkAllEpisodesFinished": "Markeer alle afleveringen als voltooid",
"MessageMarkAllEpisodesNotFinished": "Mark all episodes not finished", "MessageMarkAllEpisodesNotFinished": "Markeer alle afleveringen als niet voltooid",
"MessageMarkAsFinished": "Markeer als Voltooid", "MessageMarkAsFinished": "Markeer als Voltooid",
"MessageMarkAsNotFinished": "Markeer als Niet Voltooid", "MessageMarkAsNotFinished": "Markeer als Niet Voltooid",
"MessageMatchBooksDescription": "zal proberen boeken in de bibliotheek te matchen met een boek uit de geselecteerde bron en lege details en coverafbeelding te vullen. Overschrijft details niet.", "MessageMatchBooksDescription": "zal proberen boeken in de bibliotheek te matchen met een boek uit de geselecteerde bron en lege details en coverafbeelding te vullen. Overschrijft details niet.",
@ -702,8 +702,8 @@
"ToastRemoveItemFromCollectionSuccess": "Onderdeel verwijderd uit collectie", "ToastRemoveItemFromCollectionSuccess": "Onderdeel verwijderd uit collectie",
"ToastRSSFeedCloseFailed": "Sluiten RSS-feed mislukt", "ToastRSSFeedCloseFailed": "Sluiten RSS-feed mislukt",
"ToastRSSFeedCloseSuccess": "RSS-feed gesloten", "ToastRSSFeedCloseSuccess": "RSS-feed gesloten",
"ToastSendEbookToDeviceFailed": "Failed to Send Ebook to device", "ToastSendEbookToDeviceFailed": "Ebook naar apparaat sturen mislukt",
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"", "ToastSendEbookToDeviceSuccess": "Ebook verstuurd naar apparaat \"{0}\"",
"ToastSeriesUpdateFailed": "Bijwerken serie mislukt", "ToastSeriesUpdateFailed": "Bijwerken serie mislukt",
"ToastSeriesUpdateSuccess": "Bijwerken serie gelukt", "ToastSeriesUpdateSuccess": "Bijwerken serie gelukt",
"ToastSessionDeleteFailed": "Verwijderen sessie mislukt", "ToastSessionDeleteFailed": "Verwijderen sessie mislukt",

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.4.3", "version": "2.4.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.4.3", "version": "2.4.4",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"axios": "^0.27.2", "axios": "^0.27.2",

View File

@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.4.3", "version": "2.4.4",
"description": "Self-hosted audiobook and podcast server", "description": "Self-hosted audiobook and podcast server",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -92,7 +92,7 @@ class Logger {
* @param {...any} args * @param {...any} args
*/ */
dev(...args) { dev(...args) {
if (!this.isDev) return if (!this.isDev || process.env.HIDE_DEV_LOGS === '1') return
console.log(`[${this.timestamp}] DEV:`, ...args) console.log(`[${this.timestamp}] DEV:`, ...args)
} }

View File

@ -28,6 +28,8 @@ class FolderWatcher extends EventEmitter {
this.ignoreDirs = [] this.ignoreDirs = []
/** @type {string[]} */ /** @type {string[]} */
this.pendingDirsToRemoveFromIgnore = [] this.pendingDirsToRemoveFromIgnore = []
/** @type {NodeJS.Timeout} */
this.removeFromIgnoreTimer = null
this.disabled = false this.disabled = false
} }
@ -240,9 +242,12 @@ class FolderWatcher extends EventEmitter {
*/ */
addIgnoreDir(path) { addIgnoreDir(path) {
path = this.cleanDirPath(path) path = this.cleanDirPath(path)
if (this.ignoreDirs.includes(path)) return
this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path) this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path)
Logger.debug(`[Watcher] Ignoring directory "${path}"`) if (this.ignoreDirs.includes(path)) {
// Already ignoring dir
return
}
Logger.debug(`[Watcher] addIgnoreDir: Ignoring directory "${path}"`)
this.ignoreDirs.push(path) this.ignoreDirs.push(path)
} }
@ -255,18 +260,24 @@ class FolderWatcher extends EventEmitter {
*/ */
removeIgnoreDir(path) { removeIgnoreDir(path) {
path = this.cleanDirPath(path) path = this.cleanDirPath(path)
if (!this.ignoreDirs.includes(path) || this.pendingDirsToRemoveFromIgnore.includes(path)) return if (!this.ignoreDirs.includes(path)) {
Logger.debug(`[Watcher] removeIgnoreDir: Path is not being ignored "${path}"`)
return
}
// Add a 5 second delay before removing the ignore from this dir // Add a 5 second delay before removing the ignore from this dir
if (!this.pendingDirsToRemoveFromIgnore.includes(path)) {
this.pendingDirsToRemoveFromIgnore.push(path) this.pendingDirsToRemoveFromIgnore.push(path)
setTimeout(() => { }
clearTimeout(this.removeFromIgnoreTimer)
this.removeFromIgnoreTimer = setTimeout(() => {
if (this.pendingDirsToRemoveFromIgnore.includes(path)) { if (this.pendingDirsToRemoveFromIgnore.includes(path)) {
this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path) this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path)
Logger.debug(`[Watcher] No longer ignoring directory "${path}"`) Logger.debug(`[Watcher] removeIgnoreDir: No longer ignoring directory "${path}"`)
this.ignoreDirs = this.ignoreDirs.filter(p => p !== path) this.ignoreDirs = this.ignoreDirs.filter(p => p !== path)
} }
}, 5000) }, 5000)
} }
} }
module.exports = FolderWatcher module.exports = FolderWatcher

View File

@ -9,7 +9,8 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter
const libraryItemFilters = require('../utils/queries/libraryItemFilters') const libraryItemFilters = require('../utils/queries/libraryItemFilters')
const seriesFilters = require('../utils/queries/seriesFilters') const seriesFilters = require('../utils/queries/seriesFilters')
const fileUtils = require('../utils/fileUtils') const fileUtils = require('../utils/fileUtils')
const { sort, createNewSortInstance } = require('../libs/fastSort') const { asciiOnlyToLowerCase } = require('../utils/index')
const { createNewSortInstance } = require('../libs/fastSort')
const naturalSort = createNewSortInstance({ const naturalSort = createNewSortInstance({
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
}) })
@ -555,7 +556,7 @@ class LibraryController {
return res.status(400).send('No query string') return res.status(400).send('No query string')
} }
const limit = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12 const limit = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12
const query = req.query.q.trim().toLowerCase() const query = asciiOnlyToLowerCase(req.query.q.trim())
const matches = await libraryItemFilters.search(req.user, req.library, query, limit) const matches = await libraryItemFilters.search(req.user, req.library, query, limit)
res.json(matches) res.json(matches)

View File

@ -259,7 +259,6 @@ class LibraryItemController {
// Check if library item media has a cover path // Check if library item media has a cover path
if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) { if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) {
Logger.debug(`[LibraryItemController] getCover: Library item "${req.params.id}" has no cover path`)
return res.sendStatus(404) return res.sendStatus(404)
} }
@ -280,12 +279,6 @@ class LibraryItemController {
return CacheManager.handleCoverCache(res, libraryItem.id, libraryItem.media.coverPath, options) return CacheManager.handleCoverCache(res, libraryItem.id, libraryItem.media.coverPath, options)
} }
// GET: api/items/:id/stream
openStream(req, res) {
// this.streamManager.openStreamApiRequest(res, req.user, req.libraryItem)
res.sendStatus(500)
}
// POST: api/items/:id/play // POST: api/items/:id/play
startPlaybackSession(req, res) { startPlaybackSession(req, res) {
if (!req.libraryItem.media.numTracks && req.libraryItem.mediaType !== 'video') { if (!req.libraryItem.media.numTracks && req.libraryItem.mediaType !== 'video') {

View File

@ -196,7 +196,7 @@ class MeController {
const libraryItem = await Database.libraryItemModel.getOldById(localProgress.libraryItemId) const libraryItem = await Database.libraryItemModel.getOldById(localProgress.libraryItemId)
if (!libraryItem) { if (!libraryItem) {
Logger.error(`[MeController] syncLocalMediaProgress invalid local media progress object no library item`, localProgress) Logger.error(`[MeController] syncLocalMediaProgress invalid local media progress object no library item with id "${localProgress.libraryItemId}"`, localProgress)
continue continue
} }

View File

@ -26,7 +26,7 @@ class SearchController {
let results = null let results = null
if (podcast) results = await PodcastFinder.findCovers(query.title) if (podcast) results = await PodcastFinder.findCovers(query.title)
else results = await BookFinder.findCovers(query.provider || 'google', query.title, query.author || null) else results = await BookFinder.findCovers(query.provider || 'google', query.title, query.author || '')
res.json({ res.json({
results results
}) })

View File

@ -115,6 +115,13 @@ class UserController {
} }
} }
/**
* PATCH: /api/users/:id
* Update user
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async update(req, res) { async update(req, res) {
const user = req.reqUser const user = req.reqUser
@ -126,6 +133,7 @@ class UserController {
var account = req.body var account = req.body
var shouldUpdateToken = false var shouldUpdateToken = false
// When changing username create a new API token
if (account.username !== undefined && account.username !== user.username) { if (account.username !== undefined && account.username !== user.username) {
const usernameExists = await Database.userModel.getUserByUsername(account.username) const usernameExists = await Database.userModel.getUserByUsername(account.username)
if (usernameExists) { if (usernameExists) {

View File

@ -374,7 +374,7 @@ class BookFinder {
if (!books.length && maxFuzzySearches > 0) { if (!books.length && maxFuzzySearches > 0) {
// Normalize title and author // Normalize title and author
title = title.trim().toLowerCase() title = title.trim().toLowerCase()
author = author.trim().toLowerCase() author = author?.trim().toLowerCase() || ''
const cleanAuthor = this.cleanAuthorForCompares(author) const cleanAuthor = this.cleanAuthorForCompares(author)

View File

@ -100,7 +100,7 @@ class BackupManager {
let entries let entries
try { try {
entries = await zip.entries() entries = await zip.entries()
} catch(error){ } catch (error) {
// Not a valid zip file // Not a valid zip file
Logger.error('[BackupManager] Failed to read backup file - backup might not be a valid .zip file', tempPath, error) Logger.error('[BackupManager] Failed to read backup file - backup might not be a valid .zip file', tempPath, error)
return res.status(400).send('Failed to read backup file - backup might not be a valid .zip file') return res.status(400).send('Failed to read backup file - backup might not be a valid .zip file')
@ -182,7 +182,6 @@ class BackupManager {
data = await zip.entryData('details') data = await zip.entryData('details')
} catch (error) { } catch (error) {
Logger.error(`[BackupManager] Failed to unzip backup "${fullFilePath}"`, error) Logger.error(`[BackupManager] Failed to unzip backup "${fullFilePath}"`, error)
await zip.close()
continue continue
} }

View File

@ -794,6 +794,9 @@ class LibraryItem extends Model {
{ {
fields: ['libraryId', 'mediaType'] fields: ['libraryId', 'mediaType']
}, },
{
fields: ['libraryId', 'mediaId', 'mediaType']
},
{ {
fields: ['birthtime'] fields: ['birthtime']
}, },

View File

@ -59,6 +59,7 @@ class User extends Model {
id: userExpanded.id, id: userExpanded.id,
oldUserId: userExpanded.extraData?.oldUserId || null, oldUserId: userExpanded.extraData?.oldUserId || null,
username: userExpanded.username, username: userExpanded.username,
email: userExpanded.email || null,
pash: userExpanded.pash, pash: userExpanded.pash,
type: userExpanded.type, type: userExpanded.type,
token: userExpanded.token, token: userExpanded.token,
@ -96,6 +97,7 @@ class User extends Model {
return { return {
id: oldUser.id, id: oldUser.id,
username: oldUser.username, username: oldUser.username,
email: oldUser.email || null,
pash: oldUser.pash || null, pash: oldUser.pash || null,
type: oldUser.type || null, type: oldUser.type || null,
token: oldUser.token || null, token: oldUser.token || null,

View File

@ -168,7 +168,13 @@ class PlaybackSession {
this.currentTime = session.currentTime || 0 this.currentTime = session.currentTime || 0
this.startedAt = session.startedAt this.startedAt = session.startedAt
this.updatedAt = session.updatedAt || null this.updatedAt = session.updatedAt || session.startedAt
// Local playback sessions dont set this date field so set using updatedAt
if (!this.date && session.updatedAt) {
this.date = date.format(new Date(session.updatedAt), 'YYYY-MM-DD')
this.dayOfWeek = date.format(new Date(session.updatedAt), 'dddd')
}
} }
get mediaItemId() { get mediaItemId() {

View File

@ -339,9 +339,9 @@ class Stream extends EventEmitter {
} else { } else {
Logger.error('Ffmpeg Err', '"' + err.message + '"') Logger.error('Ffmpeg Err', '"' + err.message + '"')
// Temporary workaround for https://github.com/advplyr/audiobookshelf/issues/172 // Temporary workaround for https://github.com/advplyr/audiobookshelf/issues/172 and https://github.com/advplyr/audiobookshelf/issues/2157
const aacErrorMsg = 'ffmpeg exited with code 1: Could not write header for output file #0 (incorrect codec parameters ?)' const aacErrorMsg = 'ffmpeg exited with code 1:'
if (audioCodec === 'copy' && this.isAACEncodable && err.message && err.message.startsWith(aacErrorMsg)) { if (audioCodec === 'copy' && this.isAACEncodable && err.message?.startsWith(aacErrorMsg)) {
Logger.info(`[Stream] Re-attempting stream with AAC encode`) Logger.info(`[Stream] Re-attempting stream with AAC encode`)
this.transcodeOptions.forceAAC = true this.transcodeOptions.forceAAC = true
this.reset(this.startTime) this.reset(this.startTime)

View File

@ -7,6 +7,7 @@ class User {
this.id = null this.id = null
this.oldUserId = null // TODO: Temp for keeping old access tokens this.oldUserId = null // TODO: Temp for keeping old access tokens
this.username = null this.username = null
this.email = null
this.pash = null this.pash = null
this.type = null this.type = null
this.token = null this.token = null
@ -76,6 +77,7 @@ class User {
id: this.id, id: this.id,
oldUserId: this.oldUserId, oldUserId: this.oldUserId,
username: this.username, username: this.username,
email: this.email,
pash: this.pash, pash: this.pash,
type: this.type, type: this.type,
token: this.token, token: this.token,
@ -97,6 +99,7 @@ class User {
id: this.id, id: this.id,
oldUserId: this.oldUserId, oldUserId: this.oldUserId,
username: this.username, username: this.username,
email: this.email,
type: this.type, type: this.type,
token: (this.type === 'root' && hideRootToken) ? '' : this.token, token: (this.type === 'root' && hideRootToken) ? '' : this.token,
mediaProgress: this.mediaProgress ? this.mediaProgress.map(li => li.toJSON()) : [], mediaProgress: this.mediaProgress ? this.mediaProgress.map(li => li.toJSON()) : [],
@ -140,6 +143,7 @@ class User {
this.id = user.id this.id = user.id
this.oldUserId = user.oldUserId this.oldUserId = user.oldUserId
this.username = user.username this.username = user.username
this.email = user.email || null
this.pash = user.pash this.pash = user.pash
this.type = user.type this.type = user.type
this.token = user.token this.token = user.token
@ -184,7 +188,7 @@ class User {
update(payload) { update(payload) {
var hasUpdates = false var hasUpdates = false
// Update the following keys: // Update the following keys:
const keysToCheck = ['pash', 'type', 'username', 'isActive'] const keysToCheck = ['pash', 'type', 'username', 'email', 'isActive']
keysToCheck.forEach((key) => { keysToCheck.forEach((key) => {
if (payload[key] !== undefined) { if (payload[key] !== undefined) {
if (key === 'isActive' || payload[key]) { // pash, type, username must evaluate to true (cannot be null or empty) if (key === 'isActive' || payload[key]) { // pash, type, username must evaluate to true (cannot be null or empty)

View File

@ -1111,7 +1111,7 @@ class BookScanner {
const result = await CoverManager.downloadCoverFromUrlNew(results[i], libraryItemId, libraryItemPath) const result = await CoverManager.downloadCoverFromUrlNew(results[i], libraryItemId, libraryItemPath)
if (result.error) { if (result.error) {
Logger.error(`[Scanner] Failed to download cover from url "${results[i]}" | Attempt ${i + 1}`, result.error) libraryScan.addLog(LogLevel.ERROR, `Failed to download cover from url "${results[i]}" | Attempt ${i + 1}`, result.error)
} else if (result.cover) { } else if (result.cover) {
return result.cover return result.cover
} }

View File

@ -167,3 +167,26 @@ module.exports.getTitlePrefixAtEnd = (title) => {
let [sort, prefix] = getTitleParts(title) let [sort, prefix] = getTitleParts(title)
return prefix ? `${sort}, ${prefix}` : title return prefix ? `${sort}, ${prefix}` : title
} }
/**
* to lower case for only ascii characters
* used to handle sqlite that doesnt support unicode lower
* @see https://github.com/advplyr/audiobookshelf/issues/2187
*
* @param {string} str
* @returns {string}
*/
module.exports.asciiOnlyToLowerCase = (str) => {
if (!str) return ''
let temp = ''
for (let chars of str) {
let value = chars.charCodeAt()
if (value >= 65 && value <= 90) {
temp += String.fromCharCode(value + 32)
} else {
temp += chars
}
}
return temp
}

View File

@ -205,6 +205,15 @@ module.exports = {
} }
} }
] ]
// Handle library setting to hide single book series
// TODO: Merge with existing query
if (library.settings.hideSingleBookSeries) {
seriesWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM books b, bookSeries bs WHERE bs.seriesId = series.id AND bs.bookId = b.id)`), {
[Sequelize.Op.gt]: 1
}))
}
// Handle user permissions to only include series with at least 1 book // Handle user permissions to only include series with at least 1 book
// TODO: Simplify to a single query // TODO: Simplify to a single query
if (userPermissionBookWhere.bookWhere.length) { if (userPermissionBookWhere.bookWhere.length) {

View File

@ -2,6 +2,7 @@ const Sequelize = require('sequelize')
const Database = require('../../Database') const Database = require('../../Database')
const Logger = require('../../Logger') const Logger = require('../../Logger')
const authorFilters = require('./authorFilters') const authorFilters = require('./authorFilters')
const { asciiOnlyToLowerCase } = require('../index')
module.exports = { module.exports = {
/** /**
@ -1013,7 +1014,8 @@ module.exports = {
let matchText = null let matchText = null
let matchKey = null let matchKey = null
for (const key of ['title', 'subtitle', 'asin', 'isbn']) { for (const key of ['title', 'subtitle', 'asin', 'isbn']) {
if (book[key]?.toLowerCase().includes(query)) { const valueToLower = asciiOnlyToLowerCase(book[key])
if (valueToLower.includes(query)) {
matchText = book[key] matchText = book[key]
matchKey = key matchKey = key
break break

View File

@ -2,6 +2,7 @@
const Sequelize = require('sequelize') const Sequelize = require('sequelize')
const Database = require('../../Database') const Database = require('../../Database')
const Logger = require('../../Logger') const Logger = require('../../Logger')
const { asciiOnlyToLowerCase } = require('../index')
module.exports = { module.exports = {
/** /**
@ -247,7 +248,7 @@ module.exports = {
podcastEpisodeWhere['$mediaProgresses.isFinished$'] = true podcastEpisodeWhere['$mediaProgresses.isFinished$'] = true
} }
} else if (filterGroup === 'recent') { } else if (filterGroup === 'recent') {
libraryItemWhere['createdAt'] = { podcastEpisodeWhere['createdAt'] = {
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago [Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
} }
} }
@ -364,7 +365,8 @@ module.exports = {
let matchText = null let matchText = null
let matchKey = null let matchKey = null
for (const key of ['title', 'author', 'itunesId', 'itunesArtistId']) { for (const key of ['title', 'author', 'itunesId', 'itunesArtistId']) {
if (podcast[key]?.toLowerCase().includes(query)) { const valueToLower = asciiOnlyToLowerCase(podcast[key])
if (valueToLower.includes(query)) {
matchText = podcast[key] matchText = podcast[key]
matchKey = key matchKey = key
break break