mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-07 16:44:16 +01:00
Merge branch 'master' into Fuzzy-Matching-Continued
This commit is contained in:
commit
786df450e5
@ -15,8 +15,8 @@
|
||||
</div>
|
||||
<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.duration" class="text-gray-400 text-xs">Runtime: {{ $elapsedPrettyExtended(book.duration * 60) }}</p>
|
||||
<div v-if="book.series && book.series.length" class="flex py-1 -mx-1">
|
||||
<p v-if="book.duration" class="text-gray-400 text-xs">Runtime: {{ $elapsedPrettyExtended(bookDuration, false) }} {{ bookDurationComparison }}</p>
|
||||
<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">
|
||||
<p class="leading-3 text-xs text-gray-400">
|
||||
{{ series.series }}<span v-if="series.sequence"> #{{ series.sequence }}</span>
|
||||
@ -29,9 +29,7 @@
|
||||
</div>
|
||||
<div v-else class="px-4 flex-grow">
|
||||
<h1>
|
||||
<div class="flex items-center">
|
||||
{{ book.title }}<widgets-explicit-indicator :explicit="book.explicit" />
|
||||
</div>
|
||||
<div class="flex items-center">{{ book.title }}<widgets-explicit-indicator :explicit="book.explicit" /></div>
|
||||
</h1>
|
||||
<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>
|
||||
@ -56,7 +54,8 @@ export default {
|
||||
default: () => {}
|
||||
},
|
||||
isPodcast: Boolean,
|
||||
bookCoverAspectRatio: Number
|
||||
bookCoverAspectRatio: Number,
|
||||
currentBookDuration: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -65,12 +64,27 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
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: {
|
||||
selectMatch() {
|
||||
var book = { ...this.book }
|
||||
const book = { ...this.book }
|
||||
book.cover = this.selectedCover
|
||||
this.$emit('select', book)
|
||||
},
|
||||
|
@ -14,13 +14,17 @@
|
||||
</div>
|
||||
<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-else v-model="newUser.email" :label="$strings.LabelEmail" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="!isEditingRoot" class="flex py-2">
|
||||
<div class="px-2 w-52">
|
||||
<ui-dropdown v-model="newUser.type" :label="$strings.LabelAccountType" :disabled="isEditingRoot" :items="accountTypes" @input="userTypeUpdated" />
|
||||
<div class="w-1/2 px-2">
|
||||
<ui-text-input-with-label v-model="newUser.email" :label="$strings.LabelEmail" />
|
||||
</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">
|
||||
<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" />
|
||||
@ -257,7 +261,6 @@ export default {
|
||||
if (account.type === 'root' && !account.isActive) return
|
||||
|
||||
this.processing = true
|
||||
console.log('Calling update', account)
|
||||
this.$axios
|
||||
.$patch(`/api/users/${this.account.id}`, account)
|
||||
.then((data) => {
|
||||
@ -329,6 +332,7 @@ export default {
|
||||
if (this.account) {
|
||||
this.newUser = {
|
||||
username: this.account.username,
|
||||
email: this.account.email,
|
||||
password: this.account.password,
|
||||
type: this.account.type,
|
||||
isActive: this.account.isActive,
|
||||
@ -337,9 +341,9 @@ export default {
|
||||
itemTagsSelected: [...(this.account.itemTagsSelected || [])]
|
||||
}
|
||||
} else {
|
||||
this.fetchAllTags()
|
||||
this.newUser = {
|
||||
username: null,
|
||||
email: null,
|
||||
password: null,
|
||||
type: 'user',
|
||||
isActive: true,
|
||||
|
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
<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">
|
||||
<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>
|
||||
</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">
|
||||
@ -205,7 +205,7 @@ export default {
|
||||
processing: Boolean,
|
||||
libraryItem: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -290,13 +290,17 @@ export default {
|
||||
return this.$strings.LabelSearchTitle
|
||||
},
|
||||
media() {
|
||||
return this.libraryItem ? this.libraryItem.media || {} : {}
|
||||
return this.libraryItem?.media || {}
|
||||
},
|
||||
mediaMetadata() {
|
||||
return this.media.metadata || {}
|
||||
},
|
||||
currentBookDuration() {
|
||||
if (this.isPodcast) return 0
|
||||
return this.media.duration || 0
|
||||
},
|
||||
mediaType() {
|
||||
return this.libraryItem ? this.libraryItem.mediaType : null
|
||||
return this.libraryItem?.mediaType || null
|
||||
},
|
||||
isPodcast() {
|
||||
return this.mediaType == 'podcast'
|
||||
|
@ -129,7 +129,6 @@ export default {
|
||||
this.users = res.users.sort((a, b) => {
|
||||
return a.createdAt - b.createdAt
|
||||
})
|
||||
console.log('Loaded users', this.users)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
|
@ -191,7 +191,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
search() {},
|
||||
submit() {},
|
||||
inputUpdate() {
|
||||
clearTimeout(this.searchTimeout)
|
||||
this.searchTimeout = setTimeout(() => {
|
||||
|
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
{{ item }}
|
||||
</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>
|
||||
</form>
|
||||
|
||||
@ -145,6 +145,31 @@ export default {
|
||||
this.menu.style.left = boundingBox.x + '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() {
|
||||
if (!this.menu) {
|
||||
this.unmountMountMenu()
|
||||
|
@ -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">
|
||||
<span class="material-icons text-white hover:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem">add</span>
|
||||
</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>
|
||||
</form>
|
||||
|
||||
@ -112,6 +112,7 @@ export default {
|
||||
return !!this.selected.find((i) => i.id === itemValue)
|
||||
},
|
||||
search() {
|
||||
if (!this.textInput) return
|
||||
this.currentSearch = this.textInput
|
||||
const dataToSearch = this.filterData[this.filterKey] || []
|
||||
|
||||
@ -165,6 +166,34 @@ export default {
|
||||
this.menu.style.left = boundingBox.x + '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() {
|
||||
if (!this.menu) {
|
||||
this.unmountMountMenu()
|
||||
|
4
client/package-lock.json
generated
4
client/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nuxtjs/axios": "^5.13.6",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"description": "Self-hosted audiobook and podcast client",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -54,7 +54,7 @@ Vue.prototype.$secondsToTimestamp = (seconds, includeMs = false, alwaysIncludeHo
|
||||
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 ''
|
||||
seconds = Math.round(seconds)
|
||||
|
||||
@ -69,11 +69,16 @@ Vue.prototype.$elapsedPrettyExtended = (seconds, useDays = true) => {
|
||||
hours -= days * 24
|
||||
}
|
||||
|
||||
// If not showing seconds then round minutes up
|
||||
if (minutes && seconds && !showSeconds) {
|
||||
if (seconds >= 30) minutes++
|
||||
}
|
||||
|
||||
const strs = []
|
||||
if (days) strs.push(`${days}d`)
|
||||
if (hours) strs.push(`${hours}h`)
|
||||
if (minutes) strs.push(`${minutes}m`)
|
||||
if (seconds) strs.push(`${seconds}s`)
|
||||
if (seconds && showSeconds) strs.push(`${seconds}s`)
|
||||
return strs.join(' ')
|
||||
}
|
||||
|
||||
|
@ -417,7 +417,7 @@
|
||||
"LabelSettingsPreferAudioMetadata": "Prefer audio metadata",
|
||||
"LabelSettingsPreferAudioMetadataHelp": "Audio file ID3 meta tags will be used for book details over folder names",
|
||||
"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",
|
||||
"LabelSettingsPreferOPFMetadataHelp": "OPF file metadata will be used for book details over folder names",
|
||||
"LabelSettingsSkipMatchingBooksWithASIN": "Skip matching books that already have an ASIN",
|
||||
@ -713,4 +713,4 @@
|
||||
"ToastSocketFailedToConnect": "Socket failed to connect",
|
||||
"ToastUserDeleteFailed": "Failed to delete user",
|
||||
"ToastUserDeleteSuccess": "User deleted"
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@
|
||||
"ButtonCreate": "Crear",
|
||||
"ButtonCreateBackup": "Crear Respaldo",
|
||||
"ButtonDelete": "Eliminar",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonDownloadQueue": "Fila",
|
||||
"ButtonEdit": "Editar",
|
||||
"ButtonEditChapters": "Editar Capitulo",
|
||||
"ButtonEditChapters": "Editar Capítulo",
|
||||
"ButtonEditPodcast": "Editar Podcast",
|
||||
"ButtonForceReScan": "Forzar Re-Escanear",
|
||||
"ButtonForceReScan": "Forzar Re-Escaneo",
|
||||
"ButtonFullPath": "Ruta de Acceso Completa",
|
||||
"ButtonHide": "Esconder",
|
||||
"ButtonHome": "Inicio",
|
||||
@ -34,13 +34,13 @@
|
||||
"ButtonLogout": "Cerrar Sesión",
|
||||
"ButtonLookup": "Buscar",
|
||||
"ButtonManageTracks": "Administrar Pistas de Audio",
|
||||
"ButtonMapChapterTitles": "Map Chapter Titles",
|
||||
"ButtonMapChapterTitles": "Asignar Títulos a Capítulos",
|
||||
"ButtonMatchAllAuthors": "Encontrar Todos los Autores",
|
||||
"ButtonMatchBooks": "Encontrar Libros",
|
||||
"ButtonNevermind": "Olvidar",
|
||||
"ButtonOk": "Ok",
|
||||
"ButtonOpenFeed": "Abrir Fuente",
|
||||
"ButtonOpenManager": "Open Manager",
|
||||
"ButtonOpenManager": "Abrir Editor",
|
||||
"ButtonPlay": "Reproducir",
|
||||
"ButtonPlaying": "Reproduciendo",
|
||||
"ButtonPlaylists": "Listas de Reproducción",
|
||||
@ -55,7 +55,7 @@
|
||||
"ButtonRemoveAll": "Remover Todos",
|
||||
"ButtonRemoveAllLibraryItems": "Remover Todos los Elementos de la Biblioteca",
|
||||
"ButtonRemoveFromContinueListening": "Remover de Continuar Escuchando",
|
||||
"ButtonRemoveFromContinueReading": "Remove from Continue Reading",
|
||||
"ButtonRemoveFromContinueReading": "Remover de Continuar Leyendo",
|
||||
"ButtonRemoveSeriesFromContinueSeries": "Remover Serie de Continuar Series",
|
||||
"ButtonReScan": "Re-Escanear",
|
||||
"ButtonReset": "Reiniciar",
|
||||
@ -74,7 +74,7 @@
|
||||
"ButtonStartM4BEncode": "Iniciar Codificación M4B",
|
||||
"ButtonStartMetadataEmbed": "Iniciar la Inserción de Metadata",
|
||||
"ButtonSubmit": "Enviar",
|
||||
"ButtonTest": "Test",
|
||||
"ButtonTest": "Prueba",
|
||||
"ButtonUpload": "Subir",
|
||||
"ButtonUploadBackup": "Subir Respaldo",
|
||||
"ButtonUploadCover": "Subir Portada",
|
||||
@ -98,12 +98,12 @@
|
||||
"HeaderCurrentDownloads": "Descargando Actualmente",
|
||||
"HeaderDetails": "Detalles",
|
||||
"HeaderDownloadQueue": "Lista de Descarga",
|
||||
"HeaderEbookFiles": "Ebook Files",
|
||||
"HeaderEbookFiles": "Archivos de Ebook",
|
||||
"HeaderEmail": "Email",
|
||||
"HeaderEmailSettings": "Email Settings",
|
||||
"HeaderEmailSettings": "Opciones de Email",
|
||||
"HeaderEpisodes": "Episodios",
|
||||
"HeaderEreaderDevices": "Ereader Devices",
|
||||
"HeaderEreaderSettings": "Ereader Settings",
|
||||
"HeaderEreaderDevices": "Dispositivos Ereader",
|
||||
"HeaderEreaderSettings": "Opciones de Ereader",
|
||||
"HeaderFiles": "Elemento",
|
||||
"HeaderFindChapters": "Buscar Capitulo",
|
||||
"HeaderIgnoredFiles": "Ignorar Elemento",
|
||||
@ -120,7 +120,7 @@
|
||||
"HeaderLogs": "Logs",
|
||||
"HeaderManageGenres": "Administrar Géneros",
|
||||
"HeaderManageTags": "Administrar Etiquetas",
|
||||
"HeaderMapDetails": "Map details",
|
||||
"HeaderMapDetails": "Asignar Detalles",
|
||||
"HeaderMatch": "Encontrar",
|
||||
"HeaderMetadataToEmbed": "Metadatos para Insertar",
|
||||
"HeaderNewAccount": "Nueva Cuenta",
|
||||
@ -129,7 +129,7 @@
|
||||
"HeaderOpenRSSFeed": "Abrir fuente RSS",
|
||||
"HeaderOtherFiles": "Otros Archivos",
|
||||
"HeaderPermissions": "Permisos",
|
||||
"HeaderPlayerQueue": "Player Queue",
|
||||
"HeaderPlayerQueue": "Fila del Reproductor",
|
||||
"HeaderPlaylist": "Lista de Reproducción",
|
||||
"HeaderPlaylistItems": "Elementos de Lista de Reproducción",
|
||||
"HeaderPodcastsToAdd": "Podcasts para agregar",
|
||||
@ -139,13 +139,13 @@
|
||||
"HeaderRSSFeedGeneral": "Detalles RSS",
|
||||
"HeaderRSSFeedIsOpen": "Fuente RSS esta abierta",
|
||||
"HeaderRSSFeeds": "RSS Feeds",
|
||||
"HeaderSavedMediaProgress": "Guardar Progreso de multimedia",
|
||||
"HeaderSavedMediaProgress": "Guardar Progreso de Multimedia",
|
||||
"HeaderSchedule": "Horario",
|
||||
"HeaderScheduleLibraryScans": "Programar Escaneo Automático de Biblioteca",
|
||||
"HeaderSession": "Session",
|
||||
"HeaderSetBackupSchedule": "Programar Respaldo",
|
||||
"HeaderSettings": "Configuraciones",
|
||||
"HeaderSettingsDisplay": "Display",
|
||||
"HeaderSettingsDisplay": "Interfaz",
|
||||
"HeaderSettingsExperimental": "Funciones Experimentales",
|
||||
"HeaderSettingsGeneral": "General",
|
||||
"HeaderSettingsScanner": "Escáner",
|
||||
@ -156,21 +156,21 @@
|
||||
"HeaderStatsRecentSessions": "Sesiones Recientes",
|
||||
"HeaderStatsTop10Authors": "Top 10 Autores",
|
||||
"HeaderStatsTop5Genres": "Top 5 Géneros",
|
||||
"HeaderTableOfContents": "Table of Contents",
|
||||
"HeaderTableOfContents": "Tabla de Contenidos",
|
||||
"HeaderTools": "Herramientas",
|
||||
"HeaderUpdateAccount": "Actualizar Cuenta",
|
||||
"HeaderUpdateAuthor": "Actualizar Autor",
|
||||
"HeaderUpdateDetails": "Actualizar Detalles",
|
||||
"HeaderUpdateLibrary": "Actualizar Biblioteca",
|
||||
"HeaderUsers": "Usuarios",
|
||||
"HeaderYourStats": "Tus Estáticas",
|
||||
"LabelAbridged": "Abridged",
|
||||
"HeaderYourStats": "Tus Estadísticas",
|
||||
"LabelAbridged": "Abreviado",
|
||||
"LabelAccountType": "Tipo de Cuenta",
|
||||
"LabelAccountTypeAdmin": "Administrador",
|
||||
"LabelAccountTypeGuest": "Invitado",
|
||||
"LabelAccountTypeUser": "Usuario",
|
||||
"LabelActivity": "Actividad",
|
||||
"LabelAdded": "Added",
|
||||
"LabelAdded": "Añadido",
|
||||
"LabelAddedAt": "Añadido",
|
||||
"LabelAddToCollection": "Añadido a la Colección",
|
||||
"LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección",
|
||||
@ -186,38 +186,38 @@
|
||||
"LabelAuthors": "Autores",
|
||||
"LabelAutoDownloadEpisodes": "Descargar Episodios Automáticamente",
|
||||
"LabelBackToUser": "Regresar a Usuario",
|
||||
"LabelBackupLocation": "Backup Location",
|
||||
"LabelBackupLocation": "Ubicación del Respaldo",
|
||||
"LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Respaldo Guardado en /metadata/backups",
|
||||
"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",
|
||||
"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",
|
||||
"LabelBooks": "Libros",
|
||||
"LabelChangePassword": "Cambiar Contraseña",
|
||||
"LabelChannels": "Canales",
|
||||
"LabelChapters": "Capitulos",
|
||||
"LabelChaptersFound": "Capitulo Encontrado",
|
||||
"LabelChapterTitle": "Titulo del Capitulo",
|
||||
"LabelClosePlayer": "Close player",
|
||||
"LabelChapters": "Capítulos",
|
||||
"LabelChaptersFound": "Capítulo Encontrado",
|
||||
"LabelChapterTitle": "Titulo del Capítulo",
|
||||
"LabelClosePlayer": "Cerrar Reproductor",
|
||||
"LabelCodec": "Codec",
|
||||
"LabelCollapseSeries": "Colapsar Series",
|
||||
"LabelCollection": "Collection",
|
||||
"LabelCollapseSeries": "Colapsar Serie",
|
||||
"LabelCollection": "Colección",
|
||||
"LabelCollections": "Colecciones",
|
||||
"LabelComplete": "Completo",
|
||||
"LabelConfirmPassword": "Confirmar Contraseña",
|
||||
"LabelContinueListening": "Continuar Escuchando",
|
||||
"LabelContinueReading": "Continue Reading",
|
||||
"LabelContinueSeries": "Continuar Series",
|
||||
"LabelContinueReading": "Continuar Leyendo",
|
||||
"LabelContinueSeries": "Continuar Serie",
|
||||
"LabelCover": "Portada",
|
||||
"LabelCoverImageURL": "URL de Imagen de Portada",
|
||||
"LabelCreatedAt": "Creado",
|
||||
"LabelCronExpression": "Cron Expression",
|
||||
"LabelCronExpression": "Expresión de Cron",
|
||||
"LabelCurrent": "Actual",
|
||||
"LabelCurrently": "En este momento:",
|
||||
"LabelCustomCronExpression": "Custom Cron Expression:",
|
||||
"LabelDatetime": "Datetime",
|
||||
"LabelCustomCronExpression": "Expresión de Cron Personalizada:",
|
||||
"LabelDatetime": "Hora y Fecha",
|
||||
"LabelDescription": "Descripción",
|
||||
"LabelDeselectAll": "Deseleccionar Todos",
|
||||
"LabelDevice": "Dispositivo",
|
||||
@ -225,19 +225,19 @@
|
||||
"LabelDirectory": "Directorio",
|
||||
"LabelDiscFromFilename": "Disco a partir del Nombre del Archivo",
|
||||
"LabelDiscFromMetadata": "Disco a partir de Metadata",
|
||||
"LabelDiscover": "Discover",
|
||||
"LabelDiscover": "Descubrir",
|
||||
"LabelDownload": "Descargar",
|
||||
"LabelDownloadNEpisodes": "Download {0} episodes",
|
||||
"LabelDownloadNEpisodes": "Descargar {0} episodios",
|
||||
"LabelDuration": "Duración",
|
||||
"LabelDurationFound": "Duración Comprobada:",
|
||||
"LabelEbook": "Ebook",
|
||||
"LabelEbooks": "Ebooks",
|
||||
"LabelEdit": "Editar",
|
||||
"LabelEmail": "Email",
|
||||
"LabelEmailSettingsFromAddress": "From Address",
|
||||
"LabelEmailSettingsSecure": "Secure",
|
||||
"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)",
|
||||
"LabelEmailSettingsTestAddress": "Test Address",
|
||||
"LabelEmailSettingsFromAddress": "Remitente",
|
||||
"LabelEmailSettingsSecure": "Seguridad",
|
||||
"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": "Probar Dirección",
|
||||
"LabelEmbeddedCover": "Portada Integrada",
|
||||
"LabelEnable": "Habilitar",
|
||||
"LabelEnd": "Fin",
|
||||
@ -256,13 +256,13 @@
|
||||
"LabelFinished": "Terminado",
|
||||
"LabelFolder": "Carpeta",
|
||||
"LabelFolders": "Carpetas",
|
||||
"LabelFontScale": "Font scale",
|
||||
"LabelFontScale": "Tamaño de Fuente",
|
||||
"LabelFormat": "Formato",
|
||||
"LabelGenre": "Genero",
|
||||
"LabelGenres": "Géneros",
|
||||
"LabelHardDeleteFile": "Eliminar Definitivamente",
|
||||
"LabelHasEbook": "Has ebook",
|
||||
"LabelHasSupplementaryEbook": "Has supplementary ebook",
|
||||
"LabelHasEbook": "Tiene Ebook",
|
||||
"LabelHasSupplementaryEbook": "Tiene Ebook Suplementario",
|
||||
"LabelHost": "Host",
|
||||
"LabelHour": "Hora",
|
||||
"LabelIcon": "Icono",
|
||||
@ -276,34 +276,34 @@
|
||||
"LabelIntervalEvery2Hours": "Cada 2 Horas",
|
||||
"LabelIntervalEvery30Minutes": "Cada 30 minutos",
|
||||
"LabelIntervalEvery6Hours": "Cada 6 Horas",
|
||||
"LabelIntervalEveryDay": "Cada Dia",
|
||||
"LabelIntervalEveryDay": "Cada Día",
|
||||
"LabelIntervalEveryHour": "Cada Hora",
|
||||
"LabelInvalidParts": "Partes Invalidas",
|
||||
"LabelInvert": "Invert",
|
||||
"LabelInvalidParts": "Partes Inválidas",
|
||||
"LabelInvert": "Invertir",
|
||||
"LabelItem": "Elemento",
|
||||
"LabelLanguage": "Lenguaje",
|
||||
"LabelLanguageDefaultServer": "Lenguaje Predeterminado del Servidor",
|
||||
"LabelLastBookAdded": "Last Book Added",
|
||||
"LabelLastBookUpdated": "Last Book Updated",
|
||||
"LabelLastSeen": "Ultima Vez Visto",
|
||||
"LabelLastTime": "Ultima Vez",
|
||||
"LabelLastUpdate": "Ultima Actualización",
|
||||
"LabelLayout": "Layout",
|
||||
"LabelLayoutSinglePage": "Single page",
|
||||
"LabelLayoutSplitPage": "Split page",
|
||||
"LabelLastBookAdded": "Último Libro Agregado",
|
||||
"LabelLastBookUpdated": "Último Libro Actualizado",
|
||||
"LabelLastSeen": "Última Vez Visto",
|
||||
"LabelLastTime": "Última Vez",
|
||||
"LabelLastUpdate": "Última Actualización",
|
||||
"LabelLayout": "Distribución",
|
||||
"LabelLayoutSinglePage": "Una Página",
|
||||
"LabelLayoutSplitPage": "Dos Páginas",
|
||||
"LabelLess": "Menos",
|
||||
"LabelLibrariesAccessibleToUser": "Bibliotecas Disponibles para el Usuario",
|
||||
"LabelLibrary": "Biblioteca",
|
||||
"LabelLibraryItem": "Elemento de Biblioteca",
|
||||
"LabelLibraryName": "Nombre de Biblioteca",
|
||||
"LabelLimit": "Limites",
|
||||
"LabelLineSpacing": "Line spacing",
|
||||
"LabelLineSpacing": "Interlineado",
|
||||
"LabelListenAgain": "Escuchar Otra Vez",
|
||||
"LabelLogLevelDebug": "Debug",
|
||||
"LabelLogLevelInfo": "Info",
|
||||
"LabelLogLevelInfo": "Información",
|
||||
"LabelLogLevelWarn": "Advertencia",
|
||||
"LabelLookForNewEpisodesAfterDate": "Buscar Nuevos Episodios a partir de esta Fecha",
|
||||
"LabelMediaPlayer": "Media Player",
|
||||
"LabelMediaPlayer": "Reproductor de Medios",
|
||||
"LabelMediaType": "Tipo de Multimedia",
|
||||
"LabelMetadataProvider": "Proveedor de Metadata",
|
||||
"LabelMetaTag": "Meta Tag",
|
||||
@ -311,8 +311,8 @@
|
||||
"LabelMinute": "Minuto",
|
||||
"LabelMissing": "Ausente",
|
||||
"LabelMissingParts": "Partes Ausentes",
|
||||
"LabelMore": "Mas",
|
||||
"LabelMoreInfo": "Mas Información",
|
||||
"LabelMore": "Más",
|
||||
"LabelMoreInfo": "Más Información",
|
||||
"LabelName": "Nombre",
|
||||
"LabelNarrator": "Narrador",
|
||||
"LabelNarrators": "Narradores",
|
||||
@ -322,17 +322,17 @@
|
||||
"LabelNewPassword": "Nueva Contraseña",
|
||||
"LabelNextBackupDate": "Fecha del Siguiente Respaldo",
|
||||
"LabelNextScheduledRun": "Próxima Ejecución Programada",
|
||||
"LabelNoEpisodesSelected": "No episodes selected",
|
||||
"LabelNoEpisodesSelected": "Ningún Episodio Seleccionado",
|
||||
"LabelNotes": "Notas",
|
||||
"LabelNotFinished": "No Terminado",
|
||||
"LabelNotificationAppriseURL": "Apprise URL(s)",
|
||||
"LabelNotificationAppriseURL": "URL(s) de Apprise",
|
||||
"LabelNotificationAvailableVariables": "Variables Disponibles",
|
||||
"LabelNotificationBodyTemplate": "Plantilla de Cuerpo",
|
||||
"LabelNotificationEvent": "Evento de Notificación",
|
||||
"LabelNotificationsMaxFailedAttempts": "Máximo de Intentos Fallidos",
|
||||
"LabelNotificationsMaxFailedAttemptsHelp": "Las notificaciones se desactivan después de fallar este numero de veces",
|
||||
"LabelNotificationsMaxQueueSize": "Tamaño máximo de la cola de notificación",
|
||||
"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.",
|
||||
"LabelNotificationsMaxFailedAttemptsHelp": "Las notificaciones se desactivan después de fallar este número de veces",
|
||||
"LabelNotificationsMaxQueueSize": "Tamaño máximo de la cola de notificaciones",
|
||||
"LabelNotificationsMaxQueueSizeHelp": "Las notificaciones están limitadas a 1 por segundo. Las notificaciones serán ignoradas si llegan al numero máximo de cola para prevenir spam de eventos.",
|
||||
"LabelNotificationTitleTemplate": "Plantilla de Titulo",
|
||||
"LabelNotStarted": "Sin Iniciar",
|
||||
"LabelNumberOfBooks": "Numero de Libros",
|
||||
@ -354,97 +354,97 @@
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcasts": "Podcasts",
|
||||
"LabelPodcastType": "Tipo Podcast",
|
||||
"LabelPort": "Port",
|
||||
"LabelPort": "Puerto",
|
||||
"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",
|
||||
"LabelPrimaryEbook": "Primary ebook",
|
||||
"LabelPreventIndexing": "Evite que su fuente sea indexada por los directorios de podcasts de iTunes y Google",
|
||||
"LabelPrimaryEbook": "Ebook principal",
|
||||
"LabelProgress": "Progreso",
|
||||
"LabelProvider": "Proveedor",
|
||||
"LabelPubDate": "Fecha de Publicación",
|
||||
"LabelPublisher": "Editor",
|
||||
"LabelPublishYear": "Año de Publicación",
|
||||
"LabelRead": "Read",
|
||||
"LabelReadAgain": "Read Again",
|
||||
"LabelReadEbookWithoutProgress": "Read ebook without keeping progress",
|
||||
"LabelRecentlyAdded": "Agregado Reciente",
|
||||
"LabelRead": "Leído",
|
||||
"LabelReadAgain": "Volver a leer",
|
||||
"LabelReadEbookWithoutProgress": "Leer Ebook sin guardar progreso",
|
||||
"LabelRecentlyAdded": "Agregado Recientemente",
|
||||
"LabelRecentSeries": "Series Recientes",
|
||||
"LabelRecommended": "Recomendados",
|
||||
"LabelRegion": "Region",
|
||||
"LabelRegion": "Región",
|
||||
"LabelReleaseDate": "Fecha de Estreno",
|
||||
"LabelRemoveCover": "Remover Portada",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
||||
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
||||
"LabelRSSFeedCustomOwnerEmail": "Email de dueño personalizado",
|
||||
"LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
|
||||
"LabelRSSFeedOpen": "Fuente RSS Abierta",
|
||||
"LabelRSSFeedPreventIndexing": "Prevenir Indaxación",
|
||||
"LabelRSSFeedPreventIndexing": "Prevenir Indexado",
|
||||
"LabelRSSFeedSlug": "Fuente RSS Slug",
|
||||
"LabelRSSFeedURL": "URL de Fuente RSS",
|
||||
"LabelSearchTerm": "Buscar Termino",
|
||||
"LabelSearchTitle": "Buscar Titulo",
|
||||
"LabelSearchTitleOrASIN": "Buscar Titulo o ASIN",
|
||||
"LabelSearchTitleOrASIN": "Buscar Título o ASIN",
|
||||
"LabelSeason": "Temporada",
|
||||
"LabelSelectAllEpisodes": "Select all episodes",
|
||||
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
|
||||
"LabelSendEbookToDevice": "Send Ebook to...",
|
||||
"LabelSelectAllEpisodes": "Seleccionar todos los episodios",
|
||||
"LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
|
||||
"LabelSendEbookToDevice": "Enviar Ebook a...",
|
||||
"LabelSequence": "Secuencia",
|
||||
"LabelSeries": "Series",
|
||||
"LabelSeriesName": "Nombre de la Serie",
|
||||
"LabelSeriesProgress": "Progreso de la Serie",
|
||||
"LabelSetEbookAsPrimary": "Set as primary",
|
||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
||||
"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",
|
||||
"LabelSettingsBookshelfViewHelp": "Diseño Skeumorphic con Estantes de Madera",
|
||||
"LabelSetEbookAsPrimary": "Establecer como primario",
|
||||
"LabelSetEbookAsSupplementary": "Establecer como suplementario",
|
||||
"LabelSettingsAudiobooksOnly": "Sólo Audiolibros",
|
||||
"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 Esqueuomorfo con Estantes de Madera",
|
||||
"LabelSettingsChromecastSupport": "Soporte para Chromecast",
|
||||
"LabelSettingsDateFormat": "Formato de Fecha",
|
||||
"LabelSettingsDisableWatcher": "Deshabilitar Watcher",
|
||||
"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",
|
||||
"LabelSettingsEnableWatcher": "Enable Watcher",
|
||||
"LabelSettingsEnableWatcherForLibrary": "Enable folder watcher for library",
|
||||
"LabelSettingsEnableWatcherHelp": "Enables the automatic adding/updating of items when file changes are detected. *Requires server restart",
|
||||
"LabelSettingsDisableWatcherHelp": "Deshabilitar la función de agregar/actualizar elementos automáticamente cuando se detectan cambios en los archivos. *Require Reiniciar el Servidor",
|
||||
"LabelSettingsEnableWatcher": "Habilitar Watcher",
|
||||
"LabelSettingsEnableWatcherForLibrary": "Habilitar Watcher para la carpeta de esta biblioteca",
|
||||
"LabelSettingsEnableWatcherHelp": "Permite agregar/actualizar elementos automáticamente cuando se detectan cambios en los archivos. *Requires server restart",
|
||||
"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",
|
||||
"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",
|
||||
"LabelSettingsHideSingleBookSeries": "Hide single book series",
|
||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
||||
"LabelSettingsHomePageBookshelfView": "La pagina de inicio usa la vista de librero",
|
||||
"LabelSettingsLibraryBookshelfView": "La biblioteca usa la vista de librero",
|
||||
"LabelSettingsOverdriveMediaMarkers": "Usar Markers de multimedia en Overdrive 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.",
|
||||
"LabelSettingsParseSubtitles": "Parse subtitles",
|
||||
"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\"",
|
||||
"LabelSettingsPreferAudioMetadata": "Preferir metadata del audio",
|
||||
"LabelSettingsPreferAudioMetadataHelp": "Archivos de Audio ID3 meta tags se utilizarán para detalles de libros en vez de los nombres de carpetas",
|
||||
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
||||
"LabelSettingsPreferMatchedMetadataHelp": "Los datos coincidentes anularán los detalles del elemento cuando se utilice Quick Match. Por defecto, Quick Match solo completará los detalles faltantes.",
|
||||
"LabelSettingsPreferOPFMetadata": "Preferir OPF metadata",
|
||||
"LabelSettingsPreferOPFMetadataHelp": "Los archivos de metadata OPF serán usados para los detalles del libro en vez de el nombre de las carpetas",
|
||||
"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": "Esconder series con un solo libro",
|
||||
"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": "Usar la vista de librero en la página principal",
|
||||
"LabelSettingsLibraryBookshelfView": "Usar la vista de librero en la biblioteca",
|
||||
"LabelSettingsOverdriveMediaMarkers": "Usar Overdrive Media Markers para estos capítulos",
|
||||
"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": "Extraer Subtítulos",
|
||||
"LabelSettingsParseSubtitlesHelp": "Extraer subtítulos de los nombres de las carpetas de los audiolibros.<br>Los subtítulos deben estar separados por \" - \"<br>Por ejemplo: \"Ejemplo de Título - Subtítulo Aquí\" tiene el subtítulo \"Subtítulo Aquí\"",
|
||||
"LabelSettingsPreferAudioMetadata": "Preferir metadatos del archivo de audio",
|
||||
"LabelSettingsPreferAudioMetadataHelp": "Preferir los metadatos ID3 del archivo de audio en vez de los nombres de carpetas para los detalles de libros",
|
||||
"LabelSettingsPreferMatchedMetadata": "Preferir metadatos encontrados",
|
||||
"LabelSettingsPreferMatchedMetadataHelp": "Los datos encontrados sobreescribirán los detalles del elemento cuando se use \"Encontrar Rápido\". Por defecto, \"Encontrar Rápido\" sólo completará los detalles faltantes.",
|
||||
"LabelSettingsPreferOPFMetadata": "Preferir Metadatos OPF",
|
||||
"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",
|
||||
"LabelSettingsSkipMatchingBooksWithISBN": "Omitir libros coincidentes que ya tengan un ISBN",
|
||||
"LabelSettingsSortingIgnorePrefixes": "Ignorar prefijos al ordenando",
|
||||
"LabelSettingsSortingIgnorePrefixesHelp": "ejemplo. el prefijo \"el\" del titulo \"El titulo del libro\" sera ordenado como \"Titulo del Libro, el\"",
|
||||
"LabelSettingsSortingIgnorePrefixes": "Ignorar prefijos al ordenar",
|
||||
"LabelSettingsSortingIgnorePrefixesHelp": "Por ejemplo: El prefijo \"el\" del titulo \"El titulo del libro\" se ordenará como \"Titulo del Libro, el\".",
|
||||
"LabelSettingsSquareBookCovers": "Usar portadas cuadradas",
|
||||
"LabelSettingsSquareBookCoversHelp": "Prefiere usar portadas cuadradas sobre las portadas estándar 1.6:1",
|
||||
"LabelSettingsStoreCoversWithItem": "Guardar portada con elemento",
|
||||
"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.",
|
||||
"LabelSettingsStoreMetadataWithItem": "Guardar metadata con elemento",
|
||||
"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",
|
||||
"LabelSettingsTimeFormat": "Format de Tiempo",
|
||||
"LabelSettingsSquareBookCoversHelp": "Prefierir usar portadas cuadradas sobre las portadas estándar 1.6:1",
|
||||
"LabelSettingsStoreCoversWithItem": "Guardar portadas con elementos",
|
||||
"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 metadatos con elementos",
|
||||
"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": "Formato de Tiempo",
|
||||
"LabelShowAll": "Mostrar Todos",
|
||||
"LabelSize": "Tamaño",
|
||||
"LabelSleepTimer": "Temporizador para Dormir",
|
||||
"LabelSlug": "Slug",
|
||||
"LabelStart": "Iniciar",
|
||||
"LabelStarted": "Indiciado",
|
||||
"LabelStarted": "Iniciado",
|
||||
"LabelStartedAt": "Iniciado En",
|
||||
"LabelStartTime": "Tiempo de Inicio",
|
||||
"LabelStatsAudioTracks": "Pistas de Audio",
|
||||
"LabelStatsAuthors": "Autores",
|
||||
"LabelStatsBestDay": "Mejor Dia",
|
||||
"LabelStatsBestDay": "Mejor Día",
|
||||
"LabelStatsDailyAverage": "Promedio Diario",
|
||||
"LabelStatsDays": "Dias",
|
||||
"LabelStatsDaysListened": "Dias Escuchando",
|
||||
"LabelStatsDays": "Días",
|
||||
"LabelStatsDaysListened": "Días Escuchando",
|
||||
"LabelStatsHours": "Horas",
|
||||
"LabelStatsInARow": "seguidos",
|
||||
"LabelStatsItemsFinished": "Elementos Terminados",
|
||||
@ -453,42 +453,42 @@
|
||||
"LabelStatsMinutesListening": "Minutos Escuchando",
|
||||
"LabelStatsOverallDays": "Total de Dias",
|
||||
"LabelStatsOverallHours": "Total de Horas",
|
||||
"LabelStatsWeekListening": "Escuchando en la Semana",
|
||||
"LabelSubtitle": "Subtitulo",
|
||||
"LabelSupportedFileTypes": "Tipo de Archivos Soportados",
|
||||
"LabelStatsWeekListening": "Tiempo escuchando en la Semana",
|
||||
"LabelSubtitle": "Subtítulo",
|
||||
"LabelSupportedFileTypes": "Tipos de Archivos Soportados",
|
||||
"LabelTag": "Etiqueta",
|
||||
"LabelTags": "Etiquetas",
|
||||
"LabelTagsAccessibleToUser": "Etiquetas Accessible para el Usuario",
|
||||
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
||||
"LabelTagsAccessibleToUser": "Etiquetas Accessibles al Usuario",
|
||||
"LabelTagsNotAccessibleToUser": "Etiquetas no Accesibles al Usuario",
|
||||
"LabelTasks": "Tareas Corriendo",
|
||||
"LabelTheme": "Theme",
|
||||
"LabelThemeDark": "Dark",
|
||||
"LabelThemeLight": "Light",
|
||||
"LabelTheme": "Tema",
|
||||
"LabelThemeDark": "Oscuro",
|
||||
"LabelThemeLight": "Claro",
|
||||
"LabelTimeBase": "Time Base",
|
||||
"LabelTimeListened": "Tiempo Escuchando",
|
||||
"LabelTimeListenedToday": "Tiempo Escuchando Hoy",
|
||||
"LabelTimeRemaining": "{0} restante",
|
||||
"LabelTimeToShift": "Tiempo para Cambiar en Segundos",
|
||||
"LabelTitle": "Titulo",
|
||||
"LabelToolsEmbedMetadata": "Incorporar Metadata",
|
||||
"LabelToolsEmbedMetadataDescription": "Incorpora metadata en archivos de audio incluyendo la portada y capítulos.",
|
||||
"LabelToolsMakeM4b": "Hacer Archivo M4B de Audiolibro",
|
||||
"LabelToolsMakeM4bDescription": "Generar archivo .M4B de audiolibro con metadata, imágenes de portada y capítulos incorporados.",
|
||||
"LabelTitle": "Título",
|
||||
"LabelToolsEmbedMetadata": "Incrustar Metadatos",
|
||||
"LabelToolsEmbedMetadataDescription": "Incrusta metadatos en los archivos de audio, incluyendo la portada y capítulos.",
|
||||
"LabelToolsMakeM4b": "Hacer Archivo de Audiolibro M4B",
|
||||
"LabelToolsMakeM4bDescription": "Generar archivo de audiolibro .M4B con metadatos, imágenes de portada y capítulos incorporados.",
|
||||
"LabelToolsSplitM4b": "Dividir M4B en Archivos MP3",
|
||||
"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",
|
||||
"LabelTotalTimeListened": "Tiempo Total Escuchado",
|
||||
"LabelTrackFromFilename": "Pista desde el Nombre del Archivo",
|
||||
"LabelTrackFromMetadata": "Pista desde Metadata",
|
||||
"LabelTrackFromMetadata": "Pista desde Metadatos",
|
||||
"LabelTracks": "Pistas",
|
||||
"LabelTracksMultiTrack": "Multi-track",
|
||||
"LabelTracksNone": "No tracks",
|
||||
"LabelTracksSingleTrack": "Single-track",
|
||||
"LabelTracksMultiTrack": "Varias pistas",
|
||||
"LabelTracksNone": "Ninguna pista",
|
||||
"LabelTracksSingleTrack": "Una pista",
|
||||
"LabelType": "Tipo",
|
||||
"LabelUnabridged": "Unabridged",
|
||||
"LabelUnabridged": "No Abreviado",
|
||||
"LabelUnknown": "Desconocido",
|
||||
"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",
|
||||
"LabelUpdateDetails": "Actualizar Detalles",
|
||||
"LabelUpdateDetailsHelp": "Permitir sobrescribir detalles existentes de los libros seleccionados cuando sean encontrados",
|
||||
@ -497,79 +497,79 @@
|
||||
"LabelUseChapterTrack": "Usar pista por capitulo",
|
||||
"LabelUseFullTrack": "Usar pista completa",
|
||||
"LabelUser": "Usuario",
|
||||
"LabelUsername": "Nombré de Usuario",
|
||||
"LabelUsername": "Nombre de Usuario",
|
||||
"LabelValue": "Valor",
|
||||
"LabelVersion": "Versión",
|
||||
"LabelViewBookmarks": "Ver Marcadores",
|
||||
"LabelViewChapters": "Ver Capítulos",
|
||||
"LabelViewQueue": "Ver player queue",
|
||||
"LabelViewQueue": "Ver Fila del Reproductor",
|
||||
"LabelVolume": "Volumen",
|
||||
"LabelWeekdaysToRun": "Correr en Dias de la Semana",
|
||||
"LabelWeekdaysToRun": "Correr en Días de la Semana",
|
||||
"LabelYourAudiobookDuration": "Duración de tu Audiolibro",
|
||||
"LabelYourBookmarks": "Tus Marcadores Bookmarks",
|
||||
"LabelYourBookmarks": "Tus Marcadores",
|
||||
"LabelYourPlaylists": "Tus Listas",
|
||||
"LabelYourProgress": "Tu Progreso",
|
||||
"MessageAddToPlayerQueue": "Agregar a player queue",
|
||||
"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>.",
|
||||
"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.",
|
||||
"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.",
|
||||
"MessageAddToPlayerQueue": "Agregar a fila del Reproductor",
|
||||
"MessageAppriseDescription": "Para usar esta función deberás tener <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">la API de Apprise</a> corriendo o una API que maneje los mismos resultados. <br/>La URL de la API de Apprise debe tener la misma ruta de archivos que donde se envían las notificaciones. Por ejemplo: si su API esta en <code>http://192.168.1.1:8337</code> entonces pondría <code>http://192.168.1.1:8337/notify</code>.",
|
||||
"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": "\"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.",
|
||||
"MessageBookshelfNoResultsForFilter": "Ningún Resultado para el filtro \"{0}: {1}\"",
|
||||
"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.",
|
||||
"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.",
|
||||
"MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válida debe ser mayor o igual que la hora de inicio del capítulo anterior",
|
||||
"MessageChapterErrorStartGteDuration": "El tiempo de inicio no es válido: debe ser inferior a la duración del audiolibro.",
|
||||
"MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válido: debe ser mayor o igual que el tiempo de inicio del capítulo anterior",
|
||||
"MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro",
|
||||
"MessageCheckingCron": "Checking cron...",
|
||||
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
|
||||
"MessageConfirmDeleteBackup": "Esta seguro que desea eliminar el respaldo {0}?",
|
||||
"MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?",
|
||||
"MessageConfirmDeleteLibrary": "Esta seguro que desea eliminar permanentemente la biblioteca \"{0}\"?",
|
||||
"MessageConfirmDeleteSession": "Esta seguro que desea eliminar esta session?",
|
||||
"MessageConfirmForceReScan": "Esta seguro que desea forzar re-escanear?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?",
|
||||
"MessageConfirmMarkSeriesFinished": "Esta seguro 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?",
|
||||
"MessageConfirmRemoveAllChapters": "Esta seguro que desea remover todos los capitulos?",
|
||||
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?",
|
||||
"MessageConfirmRemoveCollection": "Esta seguro que desea remover la colección \"{0}\"?",
|
||||
"MessageConfirmRemoveEpisode": "Esta seguro que desea remover el episodio \"{0}\"?",
|
||||
"MessageConfirmRemoveEpisodes": "Esta seguro que desea remover {0} episodios?",
|
||||
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
||||
"MessageConfirmRemovePlaylist": "Esta seguro que desea remover su lista de reproducción \"{0}\"?",
|
||||
"MessageConfirmRenameGenre": "Esta seguro que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
|
||||
"MessageConfirmRenameGenreMergeNote": "Nota: Este genero ya existe por lo que se fusionarán.",
|
||||
"MessageConfirmRenameGenreWarning": "Advertencia! un genero similar ya existe \"{0}\".",
|
||||
"MessageConfirmRenameTag": "Esta seguro que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?",
|
||||
"MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe por lo que se fusionarán.",
|
||||
"MessageCheckingCron": "Revisando cron...",
|
||||
"MessageConfirmCloseFeed": "Está seguro de que desea cerrar esta fuente?",
|
||||
"MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?",
|
||||
"MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?",
|
||||
"MessageConfirmDeleteLibrary": "¿Está seguro de que desea eliminar permanentemente la biblioteca \"{0}\"?",
|
||||
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
|
||||
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
|
||||
"MessageConfirmMarkSeriesFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como terminados?",
|
||||
"MessageConfirmMarkSeriesNotFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como no terminados?",
|
||||
"MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?",
|
||||
"MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?",
|
||||
"MessageConfirmRemoveCollection": "¿Está seguro de que desea remover la colección \"{0}\"?",
|
||||
"MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?",
|
||||
"MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?",
|
||||
"MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?",
|
||||
"MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{0}\"?",
|
||||
"MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
|
||||
"MessageConfirmRenameGenreMergeNote": "Nota: Este género ya existe, por lo que se fusionarán.",
|
||||
"MessageConfirmRenameGenreWarning": "Advertencia! Un genero similar ya existe \"{0}\".",
|
||||
"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.",
|
||||
"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",
|
||||
"MessageDragFilesIntoTrackOrder": "Arrastras los archivos en el orden correcto de la pista.",
|
||||
"MessageEmbedFinished": "Incorporación Terminada!",
|
||||
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas.",
|
||||
"MessageEmbedFinished": "Incrustación Terminada!",
|
||||
"MessageEpisodesQueuedForDownload": "{0} Episodio(s) en cola para descargar",
|
||||
"MessageFeedURLWillBe": "Fuente URL sera {0}",
|
||||
"MessageFeedURLWillBe": "URL de la fuente será {0}",
|
||||
"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.",
|
||||
"MessageImportantNotice": "Noticia importante!",
|
||||
"MessageForceReScanDescription": "Escaneará todos los archivos como un nuevo escaneo. Archivos de audio con etiquetas ID3, archivos OPF y archivos de texto serán escaneados como nuevos.",
|
||||
"MessageImportantNotice": "¡Notificación importante!",
|
||||
"MessageInsertChapterBelow": "Insertar Capítulo Abajo",
|
||||
"MessageItemsSelected": "{0} Elementos Seleccionados",
|
||||
"MessageItemsUpdated": "{0} Elementos Actualizados",
|
||||
"MessageJoinUsOn": "Únete en",
|
||||
"MessageListeningSessionsInTheLastYear": "{0} sesiones de escuchadas en el último año",
|
||||
"MessageJoinUsOn": "Únetenos en",
|
||||
"MessageListeningSessionsInTheLastYear": "{0} sesiones de escucha en el último año",
|
||||
"MessageLoading": "Cargando...",
|
||||
"MessageLoadingFolders": "Cargando archivos...",
|
||||
"MessageM4BFailed": "M4B Fallo!",
|
||||
"MessageM4BFinished": "M4B Terminado!",
|
||||
"MessageMapChapterTitles": "Map chapter titles to your existing audiobook chapters without adjusting timestamps",
|
||||
"MessageMarkAllEpisodesFinished": "Mark all episodes finished",
|
||||
"MessageMarkAllEpisodesNotFinished": "Mark all episodes not finished",
|
||||
"MessageM4BFailed": "¡Fallo de M4B!",
|
||||
"MessageM4BFinished": "¡M4B Terminado!",
|
||||
"MessageMapChapterTitles": "Asignar los nombres de capítulos a los capítulos existentes en tu audiolibro sin ajustar sus tiempos",
|
||||
"MessageMarkAllEpisodesFinished": "Marcar todos los episodios como terminados",
|
||||
"MessageMarkAllEpisodesNotFinished": "Marcar todos los episodios como no terminados",
|
||||
"MessageMarkAsFinished": "Marcar como 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",
|
||||
"MessageNoAuthors": "Sin Autores",
|
||||
"MessageNoBackups": "Sin Respaldos",
|
||||
@ -582,18 +582,18 @@
|
||||
"MessageNoDownloadsQueued": "Sin Lista de Descarga",
|
||||
"MessageNoEpisodeMatchesFound": "No se encontraron episodios que coinciden",
|
||||
"MessageNoEpisodes": "Sin Episodios",
|
||||
"MessageNoFoldersAvailable": "No Carpetas Disponibles",
|
||||
"MessageNoFoldersAvailable": "No Hay Carpetas Disponibles",
|
||||
"MessageNoGenres": "Sin Géneros",
|
||||
"MessageNoIssues": "Sin Problemas",
|
||||
"MessageNoItems": "Sin Elementos",
|
||||
"MessageNoItemsFound": "Ningún Elemento Encontrado",
|
||||
"MessageNoListeningSessions": "Ninguna Session Escuchada",
|
||||
"MessageNoLogs": "No Logs",
|
||||
"MessageNoMediaProgress": "Multimedia sin Progreso ",
|
||||
"MessageNoLogs": "No hay logs",
|
||||
"MessageNoMediaProgress": "Multimedia sin Progreso",
|
||||
"MessageNoNotifications": "Ninguna Notificación",
|
||||
"MessageNoPodcastsFound": "Ningún podcasts encontrado",
|
||||
"MessageNoPodcastsFound": "Ningún podcast encontrado",
|
||||
"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",
|
||||
"MessageNoTags": "Sin Etiquetas",
|
||||
"MessageNoTasksRunning": "Ninguna Tarea Corriendo",
|
||||
@ -603,45 +603,45 @@
|
||||
"MessageNoUserPlaylists": "No tienes lista de reproducciones",
|
||||
"MessageOr": "o",
|
||||
"MessagePauseChapter": "Pausar la reproducción del capítulo",
|
||||
"MessagePlayChapter": "Escuche para comenzar el capítulo",
|
||||
"MessagePlaylistCreateFromCollection": "Crear lista de reproducción a partir de colección",
|
||||
"MessagePodcastHasNoRSSFeedForMatching": "El podcast no tiene una URL de fuente RSS que pueda usar que coincida",
|
||||
"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.",
|
||||
"MessagePlayChapter": "Escuchar el comienzo del capítulo",
|
||||
"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",
|
||||
"MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.",
|
||||
"MessageRemoveChapter": "Remover capítulos",
|
||||
"MessageRemoveEpisodes": "Remover {0} episodio(s)",
|
||||
"MessageRemoveFromPlayerQueue": "Romover la cola de reporduccion",
|
||||
"MessageRemoveUserWarning": "Esta seguro que desea eliminar el usuario \"{0}\"?",
|
||||
"MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuye en",
|
||||
"MessageResetChaptersConfirm": "Esta seguro que desea reiniciar el capitulo y deshacer los cambios que hiciste?",
|
||||
"MessageRestoreBackupConfirm": "Esta seguro 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.",
|
||||
"MessageRemoveFromPlayerQueue": "Romover la cola de reproducción",
|
||||
"MessageRemoveUserWarning": "¿Está seguro de que desea eliminar el usuario \"{0}\"?",
|
||||
"MessageReportBugsAndContribute": "Reporte erres, solicite funciones y contribuya en",
|
||||
"MessageResetChaptersConfirm": "¿Está seguro de que desea deshacer los cambios y revertir los capítulos a su estado original?",
|
||||
"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 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",
|
||||
"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",
|
||||
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
|
||||
"MessageThinking": "Pensando...",
|
||||
"MessageUploaderItemFailed": "Error al Subir",
|
||||
"MessageUploaderItemSuccess": "Éxito al Subir!",
|
||||
"MessageUploaderItemSuccess": "¡Éxito al Subir!",
|
||||
"MessageUploading": "Subiendo...",
|
||||
"MessageValidCronExpression": "Valid cron expression",
|
||||
"MessageWatcherIsDisabledGlobally": "Watcher es desactivado globalmente en la configuración del servidor",
|
||||
"MessageXLibraryIsEmpty": "{0} La biblioteca esta vacía!",
|
||||
"MessageYourAudiobookDurationIsLonger": "La duración de tu audiolibro es más larga que la duración encontrada",
|
||||
"MessageValidCronExpression": "Expresión de Cron bálida",
|
||||
"MessageWatcherIsDisabledGlobally": "El watcher está desactivado globalmente en la configuración del servidor",
|
||||
"MessageXLibraryIsEmpty": "La biblioteca {0} está vacía!",
|
||||
"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",
|
||||
"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.",
|
||||
"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.",
|
||||
"NoteRSSFeedPodcastAppsHttps": "Advertencia: La mayoría de las aplicaciones de podcast requieren que 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.",
|
||||
"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",
|
||||
"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 la URL de la fuente RSS use HTTPS",
|
||||
"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.",
|
||||
"NoteUploaderOnlyAudioFiles": "Si subes solamente un archivos de audio, cada archivo se manejara como un audiolibro.",
|
||||
"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.",
|
||||
"NoteUploaderOnlyAudioFiles": "Si sube solamente archivos de audio, cada archivo se manejará como un audiolibro por separado.",
|
||||
"NoteUploaderUnsupportedFiles": "Se ignorarán los archivos no soportados. Al elegir o arrastrar una carpeta, los archivos que no estén dentro de una subcarpeta serán ignorados.",
|
||||
"PlaceholderNewCollection": "Nuevo nombre de la colección",
|
||||
"PlaceholderNewFolderPath": "Nueva ruta de carpeta",
|
||||
"PlaceholderNewPlaylist": "Nuevo nombre de la lista de reproducción",
|
||||
"PlaceholderSearch": "Buscando..",
|
||||
"PlaceholderSearchEpisode": "Search episode..",
|
||||
"PlaceholderSearch": "Buscar..",
|
||||
"PlaceholderSearchEpisode": "Buscar Episodio..",
|
||||
"ToastAccountUpdateFailed": "Error al actualizar cuenta",
|
||||
"ToastAccountUpdateSuccess": "Cuenta actualizada",
|
||||
"ToastAuthorImageRemoveFailed": "Error al eliminar la imagen",
|
||||
@ -657,16 +657,16 @@
|
||||
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
|
||||
"ToastBackupUploadFailed": "Error al subir el respaldo",
|
||||
"ToastBackupUploadSuccess": "Respaldo cargado",
|
||||
"ToastBatchUpdateFailed": "Batch update failed",
|
||||
"ToastBatchUpdateSuccess": "Batch update success",
|
||||
"ToastBatchUpdateFailed": "Subida masiva fallida",
|
||||
"ToastBatchUpdateSuccess": "Subida masiva exitosa",
|
||||
"ToastBookmarkCreateFailed": "Error al crear marcador",
|
||||
"ToastBookmarkCreateSuccess": "Marca Agregado",
|
||||
"ToastBookmarkCreateSuccess": "Marcador Agregado",
|
||||
"ToastBookmarkRemoveFailed": "Error al eliminar marcador",
|
||||
"ToastBookmarkRemoveSuccess": "Marcador eliminado",
|
||||
"ToastBookmarkUpdateFailed": "Error al eliminar el marcador",
|
||||
"ToastBookmarkUpdateFailed": "Error al actualizar el marcador",
|
||||
"ToastBookmarkUpdateSuccess": "Marcador actualizado",
|
||||
"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",
|
||||
"ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección",
|
||||
"ToastCollectionRemoveFailed": "Error al remover la colección",
|
||||
@ -676,7 +676,7 @@
|
||||
"ToastItemCoverUpdateFailed": "Error al actualizar la portada del elemento",
|
||||
"ToastItemCoverUpdateSuccess": "Portada del elemento actualizada",
|
||||
"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",
|
||||
"ToastItemMarkedAsFinishedFailed": "Error al marcar como Terminado",
|
||||
"ToastItemMarkedAsFinishedSuccess": "Elemento marcado como terminado",
|
||||
@ -684,11 +684,11 @@
|
||||
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
|
||||
"ToastLibraryCreateFailed": "Error al crear biblioteca",
|
||||
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada",
|
||||
"ToastLibraryDeleteFailed": "Error al eliminar la biblioteca",
|
||||
"ToastLibraryDeleteFailed": "Error al eliminar biblioteca",
|
||||
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
|
||||
"ToastLibraryScanFailedToStart": "Error al iniciar la exploración",
|
||||
"ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
|
||||
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
|
||||
"ToastLibraryUpdateFailed": "Error al actualizar biblioteca",
|
||||
"ToastLibraryUpdateFailed": "Error al actualizar la biblioteca",
|
||||
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada",
|
||||
"ToastPlaylistCreateFailed": "Error al crear la lista de reproducción.",
|
||||
"ToastPlaylistCreateSuccess": "Lista de reproducción creada",
|
||||
@ -697,15 +697,15 @@
|
||||
"ToastPlaylistUpdateFailed": "Error al actualizar la lista de reproducción.",
|
||||
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
|
||||
"ToastPodcastCreateFailed": "Error al crear podcast",
|
||||
"ToastPodcastCreateSuccess": "Podcast creada",
|
||||
"ToastPodcastCreateSuccess": "Podcast creado",
|
||||
"ToastRemoveItemFromCollectionFailed": "Error al eliminar el elemento de la colección",
|
||||
"ToastRemoveItemFromCollectionSuccess": "Elemento eliminado de la colección.",
|
||||
"ToastRSSFeedCloseFailed": "Error al cerrar fuente RSS",
|
||||
"ToastRSSFeedCloseSuccess": "Fuente RSS cerrada",
|
||||
"ToastSendEbookToDeviceFailed": "Failed to Send Ebook to device",
|
||||
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"",
|
||||
"ToastSendEbookToDeviceFailed": "Error al enviar el ebook al dispositivo",
|
||||
"ToastSendEbookToDeviceSuccess": "Ebook enviado al dispositivo \"{0}\"",
|
||||
"ToastSeriesUpdateFailed": "Error al actualizar la serie",
|
||||
"ToastSeriesUpdateSuccess": "Series actualizada",
|
||||
"ToastSeriesUpdateSuccess": "Serie actualizada",
|
||||
"ToastSessionDeleteFailed": "Error al eliminar sesión",
|
||||
"ToastSessionDeleteSuccess": "Sesión eliminada",
|
||||
"ToastSocketConnected": "Socket conectado",
|
||||
@ -713,4 +713,4 @@
|
||||
"ToastSocketFailedToConnect": "Error al conectar al Socket",
|
||||
"ToastUserDeleteFailed": "Error al eliminar el usuario",
|
||||
"ToastUserDeleteSuccess": "Usuario eliminado"
|
||||
}
|
||||
}
|
||||
|
@ -99,11 +99,11 @@
|
||||
"HeaderDetails": "Details",
|
||||
"HeaderDownloadQueue": "Download-wachtrij",
|
||||
"HeaderEbookFiles": "Ebook Files",
|
||||
"HeaderEmail": "Email",
|
||||
"HeaderEmailSettings": "Email Settings",
|
||||
"HeaderEmail": "E-mail",
|
||||
"HeaderEmailSettings": "E-mail instellingen",
|
||||
"HeaderEpisodes": "Afleveringen",
|
||||
"HeaderEreaderDevices": "Ereader Devices",
|
||||
"HeaderEreaderSettings": "Ereader Settings",
|
||||
"HeaderEreaderDevices": "Ereader-apparaten",
|
||||
"HeaderEreaderSettings": "Ereader-instellingen",
|
||||
"HeaderFiles": "Bestanden",
|
||||
"HeaderFindChapters": "Zoek hoofdstukken",
|
||||
"HeaderIgnoredFiles": "Genegeerde bestanden",
|
||||
@ -138,7 +138,7 @@
|
||||
"HeaderRemoveEpisodes": "Verwijder {0} afleveringen",
|
||||
"HeaderRSSFeedGeneral": "RSS-details",
|
||||
"HeaderRSSFeedIsOpen": "RSS-feed is open",
|
||||
"HeaderRSSFeeds": "RSS Feeds",
|
||||
"HeaderRSSFeeds": "RSS-feeds",
|
||||
"HeaderSavedMediaProgress": "Opgeslagen mediavoortgang",
|
||||
"HeaderSchedule": "Schema",
|
||||
"HeaderScheduleLibraryScans": "Schema automatische bibliotheekscans",
|
||||
@ -156,7 +156,7 @@
|
||||
"HeaderStatsRecentSessions": "Recente sessies",
|
||||
"HeaderStatsTop10Authors": "Top 10 auteurs",
|
||||
"HeaderStatsTop5Genres": "Top 5 genres",
|
||||
"HeaderTableOfContents": "Table of Contents",
|
||||
"HeaderTableOfContents": "Inhoudsopgave",
|
||||
"HeaderTools": "Tools",
|
||||
"HeaderUpdateAccount": "Account bijwerken",
|
||||
"HeaderUpdateAuthor": "Auteur bijwerken",
|
||||
@ -179,14 +179,14 @@
|
||||
"LabelAll": "Alle",
|
||||
"LabelAllUsers": "Alle gebruikers",
|
||||
"LabelAlreadyInYourLibrary": "Reeds in je bibliotheek",
|
||||
"LabelAppend": "Append",
|
||||
"LabelAppend": "Achteraan toevoegen",
|
||||
"LabelAuthor": "Auteur",
|
||||
"LabelAuthorFirstLast": "Auteur (Voornaam Achternaam)",
|
||||
"LabelAuthorLastFirst": "Auteur (Achternaam, Voornaam)",
|
||||
"LabelAuthors": "Auteurs",
|
||||
"LabelAutoDownloadEpisodes": "Afleveringen automatisch downloaden",
|
||||
"LabelBackToUser": "Terug naar gebruiker",
|
||||
"LabelBackupLocation": "Backup Location",
|
||||
"LabelBackupLocation": "Back-up locatie",
|
||||
"LabelBackupsEnableAutomaticBackups": "Automatische back-ups inschakelen",
|
||||
"LabelBackupsEnableAutomaticBackupsHelp": "Back-ups opgeslagen in /metadata/backups",
|
||||
"LabelBackupsMaxBackupSize": "Maximale back-up-grootte (in GB)",
|
||||
@ -208,7 +208,7 @@
|
||||
"LabelComplete": "Compleet",
|
||||
"LabelConfirmPassword": "Bevestig wachtwoord",
|
||||
"LabelContinueListening": "Verder luisteren",
|
||||
"LabelContinueReading": "Continue Reading",
|
||||
"LabelContinueReading": "Verder luisteren",
|
||||
"LabelContinueSeries": "Ga verder met serie",
|
||||
"LabelCover": "Cover",
|
||||
"LabelCoverImageURL": "Coverafbeelding URL",
|
||||
@ -225,7 +225,7 @@
|
||||
"LabelDirectory": "Map",
|
||||
"LabelDiscFromFilename": "Schijf uit bestandsnaam",
|
||||
"LabelDiscFromMetadata": "Schijf uit metadata",
|
||||
"LabelDiscover": "Discover",
|
||||
"LabelDiscover": "Ontdek",
|
||||
"LabelDownload": "Download",
|
||||
"LabelDownloadNEpisodes": "Download {0} episodes",
|
||||
"LabelDuration": "Duur",
|
||||
@ -234,10 +234,10 @@
|
||||
"LabelEbooks": "Ebooks",
|
||||
"LabelEdit": "Wijzig",
|
||||
"LabelEmail": "Email",
|
||||
"LabelEmailSettingsFromAddress": "From Address",
|
||||
"LabelEmailSettingsSecure": "Secure",
|
||||
"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)",
|
||||
"LabelEmailSettingsTestAddress": "Test Address",
|
||||
"LabelEmailSettingsFromAddress": "Van-adres",
|
||||
"LabelEmailSettingsSecure": "Veilig",
|
||||
"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-adres",
|
||||
"LabelEmbeddedCover": "Ingesloten cover",
|
||||
"LabelEnable": "Inschakelen",
|
||||
"LabelEnd": "Einde",
|
||||
@ -256,13 +256,13 @@
|
||||
"LabelFinished": "Voltooid",
|
||||
"LabelFolder": "Map",
|
||||
"LabelFolders": "Mappen",
|
||||
"LabelFontScale": "Font scale",
|
||||
"LabelFormat": "Format",
|
||||
"LabelFontScale": "Lettertype schaal",
|
||||
"LabelFormat": "Formaat",
|
||||
"LabelGenre": "Genre",
|
||||
"LabelGenres": "Genres",
|
||||
"LabelHardDeleteFile": "Hard-delete bestand",
|
||||
"LabelHasEbook": "Has ebook",
|
||||
"LabelHasSupplementaryEbook": "Has supplementary ebook",
|
||||
"LabelHasEbook": "Heeft ebook",
|
||||
"LabelHasSupplementaryEbook": "Heeft supplementair ebook",
|
||||
"LabelHost": "Host",
|
||||
"LabelHour": "Uur",
|
||||
"LabelIcon": "Icoon",
|
||||
@ -289,15 +289,15 @@
|
||||
"LabelLastTime": "Laatste keer",
|
||||
"LabelLastUpdate": "Laatste update",
|
||||
"LabelLayout": "Layout",
|
||||
"LabelLayoutSinglePage": "Single page",
|
||||
"LabelLayoutSplitPage": "Split page",
|
||||
"LabelLayoutSinglePage": "Enkele pagina",
|
||||
"LabelLayoutSplitPage": "Gesplitste pagina",
|
||||
"LabelLess": "Minder",
|
||||
"LabelLibrariesAccessibleToUser": "Voor gebruiker toegankelijke bibliotheken",
|
||||
"LabelLibrary": "Bibliotheek",
|
||||
"LabelLibraryItem": "Library Item",
|
||||
"LabelLibraryName": "Library Name",
|
||||
"LabelLibraryItem": "Bibliotheekonderdeel",
|
||||
"LabelLibraryName": "Bibliotheeknaam",
|
||||
"LabelLimit": "Limiet",
|
||||
"LabelLineSpacing": "Line spacing",
|
||||
"LabelLineSpacing": "Regelruimte",
|
||||
"LabelListenAgain": "Luister opnieuw",
|
||||
"LabelLogLevelDebug": "Debug",
|
||||
"LabelLogLevelInfo": "Info",
|
||||
@ -322,7 +322,7 @@
|
||||
"LabelNewPassword": "Nieuw wachtwoord",
|
||||
"LabelNextBackupDate": "Volgende back-up datum",
|
||||
"LabelNextScheduledRun": "Volgende geplande run",
|
||||
"LabelNoEpisodesSelected": "No episodes selected",
|
||||
"LabelNoEpisodesSelected": "Geen afleveringen geselecteerd",
|
||||
"LabelNotes": "Notities",
|
||||
"LabelNotFinished": "Niet Voltooid",
|
||||
"LabelNotificationAppriseURL": "Apprise URL(s)",
|
||||
@ -354,18 +354,18 @@
|
||||
"LabelPodcast": "Podcast",
|
||||
"LabelPodcasts": "Podcasts",
|
||||
"LabelPodcastType": "Podcasttype",
|
||||
"LabelPort": "Port",
|
||||
"LabelPort": "Poort",
|
||||
"LabelPrefixesToIgnore": "Te negeren voorzetsels (ongeacht hoofdlettergebruik)",
|
||||
"LabelPreventIndexing": "Voorkom indexering van je feed door iTunes- en Google podcastmappen",
|
||||
"LabelPrimaryEbook": "Primary ebook",
|
||||
"LabelPrimaryEbook": "Primair ebook",
|
||||
"LabelProgress": "Voortgang",
|
||||
"LabelProvider": "Bron",
|
||||
"LabelPubDate": "Publicatiedatum",
|
||||
"LabelPublisher": "Uitgever",
|
||||
"LabelPublishYear": "Jaar van uitgave",
|
||||
"LabelRead": "Read",
|
||||
"LabelReadAgain": "Read Again",
|
||||
"LabelReadEbookWithoutProgress": "Read ebook without keeping progress",
|
||||
"LabelRead": "Lees",
|
||||
"LabelReadAgain": "Lees opnieuw",
|
||||
"LabelReadEbookWithoutProgress": "Lees ebook zonder voortgang bij te houden",
|
||||
"LabelRecentlyAdded": "Recent toegevoegd",
|
||||
"LabelRecentSeries": "Recente series",
|
||||
"LabelRecommended": "Aangeraden",
|
||||
@ -382,32 +382,32 @@
|
||||
"LabelSearchTitle": "Zoek titel",
|
||||
"LabelSearchTitleOrASIN": "Zoek titel of ASIN",
|
||||
"LabelSeason": "Seizoen",
|
||||
"LabelSelectAllEpisodes": "Select all episodes",
|
||||
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
|
||||
"LabelSendEbookToDevice": "Send Ebook to...",
|
||||
"LabelSelectAllEpisodes": "Selecteer alle afleveringen",
|
||||
"LabelSelectEpisodesShowing": "Selecteer {0} afleveringen laten zien",
|
||||
"LabelSendEbookToDevice": "Stuur ebook naar...",
|
||||
"LabelSequence": "Sequentie",
|
||||
"LabelSeries": "Serie",
|
||||
"LabelSeriesName": "Naam serie",
|
||||
"LabelSeriesProgress": "Voortgang serie",
|
||||
"LabelSetEbookAsPrimary": "Set as primary",
|
||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
||||
"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",
|
||||
"LabelSetEbookAsPrimary": "Stel in als primair",
|
||||
"LabelSetEbookAsSupplementary": "Stel in als supplementair",
|
||||
"LabelSettingsAudiobooksOnly": "Alleen audiobooks",
|
||||
"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",
|
||||
"LabelSettingsChromecastSupport": "Chromecast support",
|
||||
"LabelSettingsDateFormat": "Datum format",
|
||||
"LabelSettingsDisableWatcher": "Watcher uitschakelen",
|
||||
"LabelSettingsDisableWatcherForLibrary": "Map-watcher voor bibliotheek uitschakelen",
|
||||
"LabelSettingsDisableWatcherHelp": "Schakelt het automatisch toevoegen/bijwerken van onderdelen wanneer bestandswijzigingen gedetecteerd zijn uit. *Vereist herstart server",
|
||||
"LabelSettingsEnableWatcher": "Enable Watcher",
|
||||
"LabelSettingsEnableWatcherForLibrary": "Enable folder watcher for library",
|
||||
"LabelSettingsEnableWatcherHelp": "Enables the automatic adding/updating of items when file changes are detected. *Requires server restart",
|
||||
"LabelSettingsEnableWatcher": "Watcher inschakelen",
|
||||
"LabelSettingsEnableWatcherForLibrary": "Map-watcher voor bibliotheek inschakelen",
|
||||
"LabelSettingsEnableWatcherHelp": "Zorgt voor het automatisch toevoegen/bijwerken van onderdelen als bestandswijzigingen worden gedetecteerd. *Vereist herstarten van server",
|
||||
"LabelSettingsExperimentalFeatures": "Experimentele functies",
|
||||
"LabelSettingsExperimentalFeaturesHelp": "Functies in ontwikkeling die je feedback en testing kunnen gebruiken. Klik om de Github-discussie te openen.",
|
||||
"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",
|
||||
"LabelSettingsHideSingleBookSeries": "Hide single book series",
|
||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
||||
"LabelSettingsHideSingleBookSeries": "Verberg series met een enkel boek",
|
||||
"LabelSettingsHideSingleBookSeriesHelp": "Series die slechts een enkel boek bevatten worden verborgen op de seriespagina en de homepagina-planken.",
|
||||
"LabelSettingsHomePageBookshelfView": "Boekenplank-view voor homepagina",
|
||||
"LabelSettingsLibraryBookshelfView": "Boekenplank-view voor bibliotheek",
|
||||
"LabelSettingsOverdriveMediaMarkers": "Gebruik Overdrive media markers voor hoofdstukken",
|
||||
@ -461,9 +461,9 @@
|
||||
"LabelTagsAccessibleToUser": "Tags toegankelijk voor de gebruiker",
|
||||
"LabelTagsNotAccessibleToUser": "Tags niet toegankelijk voor de gebruiker",
|
||||
"LabelTasks": "Lopende taken",
|
||||
"LabelTheme": "Theme",
|
||||
"LabelThemeDark": "Dark",
|
||||
"LabelThemeLight": "Light",
|
||||
"LabelTheme": "Thema",
|
||||
"LabelThemeDark": "Donker",
|
||||
"LabelThemeLight": "Licht",
|
||||
"LabelTimeBase": "Tijdsbasis",
|
||||
"LabelTimeListened": "Tijd geluisterd",
|
||||
"LabelTimeListenedToday": "Tijd geluisterd vandaag",
|
||||
@ -482,8 +482,8 @@
|
||||
"LabelTrackFromMetadata": "Track vanuit metadata",
|
||||
"LabelTracks": "Tracks",
|
||||
"LabelTracksMultiTrack": "Multi-track",
|
||||
"LabelTracksNone": "No tracks",
|
||||
"LabelTracksSingleTrack": "Single-track",
|
||||
"LabelTracksNone": "Geen tracks",
|
||||
"LabelTracksSingleTrack": "Enkele track",
|
||||
"LabelType": "Type",
|
||||
"LabelUnabridged": "Onverkort",
|
||||
"LabelUnknown": "Onbekend",
|
||||
@ -525,16 +525,16 @@
|
||||
"MessageCheckingCron": "Cron aan het checken...",
|
||||
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
|
||||
"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?",
|
||||
"MessageConfirmDeleteSession": "Weet je zeker dat je deze sessie wil verwijderen?",
|
||||
"MessageConfirmForceReScan": "Weet je zeker dat je geforceerd opnieuw wil scannen?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?",
|
||||
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?",
|
||||
"MessageConfirmMarkAllEpisodesFinished": "Weet je zeker dat je alle afleveringen als voltooid wil markeren?",
|
||||
"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?",
|
||||
"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?",
|
||||
"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?",
|
||||
"MessageConfirmRemoveEpisode": "Weet je zeker dat je de aflevering \"{0}\" 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?",
|
||||
"MessageConfirmRenameTagMergeNote": "Opmerking: Deze tag bestaat al, dus zullen ze worden samengevoegd.",
|
||||
"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",
|
||||
"MessageDragFilesIntoTrackOrder": "Sleep bestanden in de juiste trackvolgorde",
|
||||
"MessageEmbedFinished": "Insluiting voltooid!",
|
||||
@ -565,8 +565,8 @@
|
||||
"MessageM4BFailed": "M4B mislukt!",
|
||||
"MessageM4BFinished": "M4B voltooid!",
|
||||
"MessageMapChapterTitles": "Map hoofdstuktitels naar je bestaande audioboekhoofdstukken zonder aanpassing van tijden",
|
||||
"MessageMarkAllEpisodesFinished": "Mark all episodes finished",
|
||||
"MessageMarkAllEpisodesNotFinished": "Mark all episodes not finished",
|
||||
"MessageMarkAllEpisodesFinished": "Markeer alle afleveringen als voltooid",
|
||||
"MessageMarkAllEpisodesNotFinished": "Markeer alle afleveringen als niet voltooid",
|
||||
"MessageMarkAsFinished": "Markeer als 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.",
|
||||
@ -702,8 +702,8 @@
|
||||
"ToastRemoveItemFromCollectionSuccess": "Onderdeel verwijderd uit collectie",
|
||||
"ToastRSSFeedCloseFailed": "Sluiten RSS-feed mislukt",
|
||||
"ToastRSSFeedCloseSuccess": "RSS-feed gesloten",
|
||||
"ToastSendEbookToDeviceFailed": "Failed to Send Ebook to device",
|
||||
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"",
|
||||
"ToastSendEbookToDeviceFailed": "Ebook naar apparaat sturen mislukt",
|
||||
"ToastSendEbookToDeviceSuccess": "Ebook verstuurd naar apparaat \"{0}\"",
|
||||
"ToastSeriesUpdateFailed": "Bijwerken serie mislukt",
|
||||
"ToastSeriesUpdateSuccess": "Bijwerken serie gelukt",
|
||||
"ToastSessionDeleteFailed": "Verwijderen sessie mislukt",
|
||||
@ -713,4 +713,4 @@
|
||||
"ToastSocketFailedToConnect": "Verbinding Socket mislukt",
|
||||
"ToastUserDeleteFailed": "Verwijderen gebruiker mislukt",
|
||||
"ToastUserDeleteSuccess": "Gebruiker verwijderd"
|
||||
}
|
||||
}
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "audiobookshelf",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "audiobookshelf",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"description": "Self-hosted audiobook and podcast server",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -92,7 +92,7 @@ class Logger {
|
||||
* @param {...any} args
|
||||
*/
|
||||
dev(...args) {
|
||||
if (!this.isDev) return
|
||||
if (!this.isDev || process.env.HIDE_DEV_LOGS === '1') return
|
||||
console.log(`[${this.timestamp}] DEV:`, ...args)
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,8 @@ class FolderWatcher extends EventEmitter {
|
||||
this.ignoreDirs = []
|
||||
/** @type {string[]} */
|
||||
this.pendingDirsToRemoveFromIgnore = []
|
||||
/** @type {NodeJS.Timeout} */
|
||||
this.removeFromIgnoreTimer = null
|
||||
|
||||
this.disabled = false
|
||||
}
|
||||
@ -240,9 +242,12 @@ class FolderWatcher extends EventEmitter {
|
||||
*/
|
||||
addIgnoreDir(path) {
|
||||
path = this.cleanDirPath(path)
|
||||
if (this.ignoreDirs.includes(path)) return
|
||||
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)
|
||||
}
|
||||
|
||||
@ -255,18 +260,24 @@ class FolderWatcher extends EventEmitter {
|
||||
*/
|
||||
removeIgnoreDir(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
|
||||
this.pendingDirsToRemoveFromIgnore.push(path)
|
||||
setTimeout(() => {
|
||||
if (!this.pendingDirsToRemoveFromIgnore.includes(path)) {
|
||||
this.pendingDirsToRemoveFromIgnore.push(path)
|
||||
}
|
||||
|
||||
clearTimeout(this.removeFromIgnoreTimer)
|
||||
this.removeFromIgnoreTimer = setTimeout(() => {
|
||||
if (this.pendingDirsToRemoveFromIgnore.includes(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)
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
}
|
||||
}
|
||||
module.exports = FolderWatcher
|
@ -9,7 +9,8 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter
|
||||
const libraryItemFilters = require('../utils/queries/libraryItemFilters')
|
||||
const seriesFilters = require('../utils/queries/seriesFilters')
|
||||
const fileUtils = require('../utils/fileUtils')
|
||||
const { sort, createNewSortInstance } = require('../libs/fastSort')
|
||||
const { asciiOnlyToLowerCase } = require('../utils/index')
|
||||
const { createNewSortInstance } = require('../libs/fastSort')
|
||||
const naturalSort = createNewSortInstance({
|
||||
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
||||
})
|
||||
@ -555,7 +556,7 @@ class LibraryController {
|
||||
return res.status(400).send('No query string')
|
||||
}
|
||||
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)
|
||||
res.json(matches)
|
||||
|
@ -259,7 +259,6 @@ class LibraryItemController {
|
||||
|
||||
// Check if library item media has a cover path
|
||||
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)
|
||||
}
|
||||
|
||||
@ -280,12 +279,6 @@ class LibraryItemController {
|
||||
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
|
||||
startPlaybackSession(req, res) {
|
||||
if (!req.libraryItem.media.numTracks && req.libraryItem.mediaType !== 'video') {
|
||||
|
@ -196,7 +196,7 @@ class MeController {
|
||||
|
||||
const libraryItem = await Database.libraryItemModel.getOldById(localProgress.libraryItemId)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ class SearchController {
|
||||
|
||||
let results = null
|
||||
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({
|
||||
results
|
||||
})
|
||||
|
@ -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) {
|
||||
const user = req.reqUser
|
||||
|
||||
@ -126,6 +133,7 @@ class UserController {
|
||||
var account = req.body
|
||||
var shouldUpdateToken = false
|
||||
|
||||
// When changing username create a new API token
|
||||
if (account.username !== undefined && account.username !== user.username) {
|
||||
const usernameExists = await Database.userModel.getUserByUsername(account.username)
|
||||
if (usernameExists) {
|
||||
|
@ -374,7 +374,7 @@ class BookFinder {
|
||||
if (!books.length && maxFuzzySearches > 0) {
|
||||
// Normalize title and author
|
||||
title = title.trim().toLowerCase()
|
||||
author = author.trim().toLowerCase()
|
||||
author = author?.trim().toLowerCase() || ''
|
||||
|
||||
const cleanAuthor = this.cleanAuthorForCompares(author)
|
||||
|
||||
|
@ -100,7 +100,7 @@ class BackupManager {
|
||||
let entries
|
||||
try {
|
||||
entries = await zip.entries()
|
||||
} catch(error){
|
||||
} catch (error) {
|
||||
// Not a valid zip file
|
||||
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')
|
||||
@ -182,7 +182,6 @@ class BackupManager {
|
||||
data = await zip.entryData('details')
|
||||
} catch (error) {
|
||||
Logger.error(`[BackupManager] Failed to unzip backup "${fullFilePath}"`, error)
|
||||
await zip.close()
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -794,6 +794,9 @@ class LibraryItem extends Model {
|
||||
{
|
||||
fields: ['libraryId', 'mediaType']
|
||||
},
|
||||
{
|
||||
fields: ['libraryId', 'mediaId', 'mediaType']
|
||||
},
|
||||
{
|
||||
fields: ['birthtime']
|
||||
},
|
||||
|
@ -59,6 +59,7 @@ class User extends Model {
|
||||
id: userExpanded.id,
|
||||
oldUserId: userExpanded.extraData?.oldUserId || null,
|
||||
username: userExpanded.username,
|
||||
email: userExpanded.email || null,
|
||||
pash: userExpanded.pash,
|
||||
type: userExpanded.type,
|
||||
token: userExpanded.token,
|
||||
@ -96,6 +97,7 @@ class User extends Model {
|
||||
return {
|
||||
id: oldUser.id,
|
||||
username: oldUser.username,
|
||||
email: oldUser.email || null,
|
||||
pash: oldUser.pash || null,
|
||||
type: oldUser.type || null,
|
||||
token: oldUser.token || null,
|
||||
|
@ -168,7 +168,13 @@ class PlaybackSession {
|
||||
this.currentTime = session.currentTime || 0
|
||||
|
||||
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() {
|
||||
|
@ -339,9 +339,9 @@ class Stream extends EventEmitter {
|
||||
} else {
|
||||
Logger.error('Ffmpeg Err', '"' + err.message + '"')
|
||||
|
||||
// Temporary workaround for https://github.com/advplyr/audiobookshelf/issues/172
|
||||
const aacErrorMsg = 'ffmpeg exited with code 1: Could not write header for output file #0 (incorrect codec parameters ?)'
|
||||
if (audioCodec === 'copy' && this.isAACEncodable && err.message && err.message.startsWith(aacErrorMsg)) {
|
||||
// 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:'
|
||||
if (audioCodec === 'copy' && this.isAACEncodable && err.message?.startsWith(aacErrorMsg)) {
|
||||
Logger.info(`[Stream] Re-attempting stream with AAC encode`)
|
||||
this.transcodeOptions.forceAAC = true
|
||||
this.reset(this.startTime)
|
||||
@ -435,4 +435,4 @@ class Stream extends EventEmitter {
|
||||
return newAudioTrack
|
||||
}
|
||||
}
|
||||
module.exports = Stream
|
||||
module.exports = Stream
|
||||
|
@ -7,6 +7,7 @@ class User {
|
||||
this.id = null
|
||||
this.oldUserId = null // TODO: Temp for keeping old access tokens
|
||||
this.username = null
|
||||
this.email = null
|
||||
this.pash = null
|
||||
this.type = null
|
||||
this.token = null
|
||||
@ -76,6 +77,7 @@ class User {
|
||||
id: this.id,
|
||||
oldUserId: this.oldUserId,
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
pash: this.pash,
|
||||
type: this.type,
|
||||
token: this.token,
|
||||
@ -97,6 +99,7 @@ class User {
|
||||
id: this.id,
|
||||
oldUserId: this.oldUserId,
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
type: this.type,
|
||||
token: (this.type === 'root' && hideRootToken) ? '' : this.token,
|
||||
mediaProgress: this.mediaProgress ? this.mediaProgress.map(li => li.toJSON()) : [],
|
||||
@ -140,6 +143,7 @@ class User {
|
||||
this.id = user.id
|
||||
this.oldUserId = user.oldUserId
|
||||
this.username = user.username
|
||||
this.email = user.email || null
|
||||
this.pash = user.pash
|
||||
this.type = user.type
|
||||
this.token = user.token
|
||||
@ -184,7 +188,7 @@ class User {
|
||||
update(payload) {
|
||||
var hasUpdates = false
|
||||
// Update the following keys:
|
||||
const keysToCheck = ['pash', 'type', 'username', 'isActive']
|
||||
const keysToCheck = ['pash', 'type', 'username', 'email', 'isActive']
|
||||
keysToCheck.forEach((key) => {
|
||||
if (payload[key] !== undefined) {
|
||||
if (key === 'isActive' || payload[key]) { // pash, type, username must evaluate to true (cannot be null or empty)
|
||||
|
@ -1111,7 +1111,7 @@ class BookScanner {
|
||||
const result = await CoverManager.downloadCoverFromUrlNew(results[i], libraryItemId, libraryItemPath)
|
||||
|
||||
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) {
|
||||
return result.cover
|
||||
}
|
||||
|
@ -166,4 +166,27 @@ module.exports.getTitleIgnorePrefix = (title) => {
|
||||
module.exports.getTitlePrefixAtEnd = (title) => {
|
||||
let [sort, prefix] = getTitleParts(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
|
||||
}
|
@ -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
|
||||
// TODO: Simplify to a single query
|
||||
if (userPermissionBookWhere.bookWhere.length) {
|
||||
|
@ -2,6 +2,7 @@ const Sequelize = require('sequelize')
|
||||
const Database = require('../../Database')
|
||||
const Logger = require('../../Logger')
|
||||
const authorFilters = require('./authorFilters')
|
||||
const { asciiOnlyToLowerCase } = require('../index')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@ -1013,7 +1014,8 @@ module.exports = {
|
||||
let matchText = null
|
||||
let matchKey = null
|
||||
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]
|
||||
matchKey = key
|
||||
break
|
||||
|
@ -2,6 +2,7 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const Database = require('../../Database')
|
||||
const Logger = require('../../Logger')
|
||||
const { asciiOnlyToLowerCase } = require('../index')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@ -247,7 +248,7 @@ module.exports = {
|
||||
podcastEpisodeWhere['$mediaProgresses.isFinished$'] = true
|
||||
}
|
||||
} else if (filterGroup === 'recent') {
|
||||
libraryItemWhere['createdAt'] = {
|
||||
podcastEpisodeWhere['createdAt'] = {
|
||||
[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 matchKey = null
|
||||
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]
|
||||
matchKey = key
|
||||
break
|
||||
|
Loading…
Reference in New Issue
Block a user