Merge branch 'master' into multi-select-keyboard-navigation

This commit is contained in:
Greg Lorenzen 2024-11-15 12:06:25 -08:00
commit 27c9381e1d
27 changed files with 675 additions and 136 deletions

View File

@ -1,11 +1,25 @@
name: "CodeQL" name: 'CodeQL'
on: on:
push: push:
branches: [ 'master' ] branches: ['master']
# Only build when files in these directories have been changed
paths:
- client/**
- server/**
- test/**
- index.js
- package.json
pull_request: pull_request:
# The branches below must be a subset of the branches above # The branches below must be a subset of the branches above
branches: [ 'master' ] branches: ['master']
# Only build when files in these directories have been changed
paths:
- client/**
- server/**
- test/**
- index.js
- package.json
schedule: schedule:
- cron: '16 5 * * 4' - cron: '16 5 * * 4'
@ -21,45 +35,44 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
language: [ 'javascript' ] language: ['javascript']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both # Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file. # By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file. # Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality # queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # Command-line programs to run using the OS shell.
# If this step fails, then you should remove it and run the build manually (see below) # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell. # If the Autobuild fails above, remove it and uncomment the following three lines.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# If the Autobuild fails above, remove it and uncomment the following three lines. # - run: |
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
# - run: | - name: Perform CodeQL Analysis
# echo "Run, Build Application using script" uses: github/codeql-action/analyze@v2
# ./location_of_script_within_repo/buildscript.sh with:
category: '/language:${{matrix.language}}'
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@ -5,6 +5,13 @@ on:
push: push:
branches-ignore: branches-ignore:
- 'dependabot/**' # Don't run dependabot branches, as they are already covered by pull requests - 'dependabot/**' # Don't run dependabot branches, as they are already covered by pull requests
# Only build when files in these directories have been changed
paths:
- client/**
- server/**
- test/**
- index.js
- package.json
jobs: jobs:
build: build:

View File

@ -638,6 +638,11 @@ export default {
this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id) this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id)
} }
}, },
episodeDownloadQueueCleared(libraryItemId) {
if (libraryItemId === this.libraryItemId) {
this.episodeDownloadsQueued = []
}
},
rssFeedOpen(data) { rssFeedOpen(data) {
if (data.entityId === this.libraryItemId) { if (data.entityId === this.libraryItemId) {
console.log('RSS Feed Opened', data) console.log('RSS Feed Opened', data)
@ -776,6 +781,7 @@ export default {
this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued) this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued)
this.$root.socket.on('episode_download_started', this.episodeDownloadStarted) this.$root.socket.on('episode_download_started', this.episodeDownloadStarted)
this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished) this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished)
this.$root.socket.on('episode_download_queue_cleared', this.episodeDownloadQueueCleared)
}, },
beforeDestroy() { beforeDestroy() {
this.$eventBus.$off(`${this.libraryItem.id}_updated`, this.libraryItemUpdated) this.$eventBus.$off(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
@ -787,6 +793,7 @@ export default {
this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued) this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued)
this.$root.socket.off('episode_download_started', this.episodeDownloadStarted) this.$root.socket.off('episode_download_started', this.episodeDownloadStarted)
this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished) this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished)
this.$root.socket.off('episode_download_queue_cleared', this.episodeDownloadQueueCleared)
} }
} }
</script> </script>

View File

@ -104,9 +104,6 @@ export default {
this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id) this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id)
} }
}, },
episodeDownloadQueueUpdated(downloadQueueDetails) {
this.episodeDownloadsQueued = downloadQueueDetails.queue.filter((q) => q.libraryId == this.libraryId)
},
async loadInitialDownloadQueue() { async loadInitialDownloadQueue() {
this.processing = true this.processing = true
const queuePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/episode-downloads`).catch((error) => { const queuePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/episode-downloads`).catch((error) => {
@ -128,7 +125,6 @@ export default {
this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued) this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued)
this.$root.socket.on('episode_download_started', this.episodeDownloadStarted) this.$root.socket.on('episode_download_started', this.episodeDownloadStarted)
this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished) this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished)
this.$root.socket.on('episode_download_queue_updated', this.episodeDownloadQueueUpdated)
} }
}, },
mounted() { mounted() {
@ -138,7 +134,6 @@ export default {
this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued) this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued)
this.$root.socket.off('episode_download_started', this.episodeDownloadStarted) this.$root.socket.off('episode_download_started', this.episodeDownloadStarted)
this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished) this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished)
this.$root.socket.off('episode_download_queue_updated', this.episodeDownloadQueueUpdated)
} }
} }
</script> </script>

View File

@ -67,7 +67,7 @@
"ButtonQueueAddItem": "Přidat do fronty", "ButtonQueueAddItem": "Přidat do fronty",
"ButtonQueueRemoveItem": "Odstranit z fronty", "ButtonQueueRemoveItem": "Odstranit z fronty",
"ButtonQuickEmbed": "Rychle Zapsat", "ButtonQuickEmbed": "Rychle Zapsat",
"ButtonQuickEmbedMetadata": "Rychle Zapsat Metadata", "ButtonQuickEmbedMetadata": "Rychle zapsat Metadata",
"ButtonQuickMatch": "Rychlé přiřazení", "ButtonQuickMatch": "Rychlé přiřazení",
"ButtonReScan": "Znovu prohledat", "ButtonReScan": "Znovu prohledat",
"ButtonRead": "Číst", "ButtonRead": "Číst",
@ -163,6 +163,7 @@
"HeaderNotificationUpdate": "Aktualizovat notifikaci", "HeaderNotificationUpdate": "Aktualizovat notifikaci",
"HeaderNotifications": "Oznámení", "HeaderNotifications": "Oznámení",
"HeaderOpenIDConnectAuthentication": "Ověřování pomocí OpenID Connect", "HeaderOpenIDConnectAuthentication": "Ověřování pomocí OpenID Connect",
"HeaderOpenListeningSessions": "Otevřené relace přehrávače",
"HeaderOpenRSSFeed": "Otevřít RSS kanál", "HeaderOpenRSSFeed": "Otevřít RSS kanál",
"HeaderOtherFiles": "Ostatní soubory", "HeaderOtherFiles": "Ostatní soubory",
"HeaderPasswordAuthentication": "Autentizace heslem", "HeaderPasswordAuthentication": "Autentizace heslem",
@ -258,6 +259,7 @@
"LabelByAuthor": "od {0}", "LabelByAuthor": "od {0}",
"LabelChangePassword": "Změnit heslo", "LabelChangePassword": "Změnit heslo",
"LabelChannels": "Kanály", "LabelChannels": "Kanály",
"LabelChapterCount": "{0} Kapitol",
"LabelChapterTitle": "Název kapitoly", "LabelChapterTitle": "Název kapitoly",
"LabelChapters": "Kapitoly", "LabelChapters": "Kapitoly",
"LabelChaptersFound": "Kapitoly nalezeny", "LabelChaptersFound": "Kapitoly nalezeny",

View File

@ -163,6 +163,7 @@
"HeaderNotificationUpdate": "Notificación de actualización", "HeaderNotificationUpdate": "Notificación de actualización",
"HeaderNotifications": "Notificaciones", "HeaderNotifications": "Notificaciones",
"HeaderOpenIDConnectAuthentication": "Autenticación OpenID Connect", "HeaderOpenIDConnectAuthentication": "Autenticación OpenID Connect",
"HeaderOpenListeningSessions": "Sesiones públicas de escucha",
"HeaderOpenRSSFeed": "Abrir fuente RSS", "HeaderOpenRSSFeed": "Abrir fuente RSS",
"HeaderOtherFiles": "Otros Archivos", "HeaderOtherFiles": "Otros Archivos",
"HeaderPasswordAuthentication": "Autenticación por contraseña", "HeaderPasswordAuthentication": "Autenticación por contraseña",
@ -226,6 +227,7 @@
"LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados", "LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados",
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados", "LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
"LabelAlreadyInYourLibrary": "Ya existe en la Biblioteca", "LabelAlreadyInYourLibrary": "Ya existe en la Biblioteca",
"LabelApiToken": "Token de la API",
"LabelAppend": "Adjuntar", "LabelAppend": "Adjuntar",
"LabelAudioBitrate": "Tasa de bits del audio (por ejemplo, 128k)", "LabelAudioBitrate": "Tasa de bits del audio (por ejemplo, 128k)",
"LabelAudioChannels": "Canales de audio (1 o 2)", "LabelAudioChannels": "Canales de audio (1 o 2)",
@ -415,6 +417,9 @@
"LabelMatchExistingUsersBy": "Emparejar a los usuarios existentes por", "LabelMatchExistingUsersBy": "Emparejar a los usuarios existentes por",
"LabelMatchExistingUsersByDescription": "Se utiliza para conectar usuarios existentes. Una vez conectados, los usuarios serán emparejados por un identificador único de su proveedor de SSO", "LabelMatchExistingUsersByDescription": "Se utiliza para conectar usuarios existentes. Una vez conectados, los usuarios serán emparejados por un identificador único de su proveedor de SSO",
"LabelMaxEpisodesToDownload": "Número máximo # de episodios para descargar. Usa 0 para descargar una cantidad ilimitada.", "LabelMaxEpisodesToDownload": "Número máximo # de episodios para descargar. Usa 0 para descargar una cantidad ilimitada.",
"LabelMaxEpisodesToDownloadPerCheck": "Número máximo de episodios nuevos que se descargarán por comprobación",
"LabelMaxEpisodesToKeep": "Número máximo de episodios que se mantendrán",
"LabelMaxEpisodesToKeepHelp": "El valor 0 no establece un límite máximo. Después de que se descargue automáticamente un nuevo episodio, esto eliminará el episodio más antiguo si tiene más de X episodios. Esto solo eliminará 1 episodio por nueva descarga.",
"LabelMediaPlayer": "Reproductor de Medios", "LabelMediaPlayer": "Reproductor de Medios",
"LabelMediaType": "Tipo de multimedia", "LabelMediaType": "Tipo de multimedia",
"LabelMetaTag": "Metaetiqueta", "LabelMetaTag": "Metaetiqueta",
@ -460,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Nombre de la declaración OpenID que contiene una lista de grupos del usuario. Comúnmente conocidos como <code>grupos</code>. <b>Si se configura</b>, la aplicación asignará automáticamente roles en función de la pertenencia a grupos del usuario, siempre que estos grupos se denominen \"admin\", \"user\" o \"guest\" en la notificación. La solicitud debe contener una lista, y si un usuario pertenece a varios grupos, la aplicación asignará el rol correspondiente al mayor nivel de acceso. Si ningún grupo coincide, se denegará el acceso.", "LabelOpenIDGroupClaimDescription": "Nombre de la declaración OpenID que contiene una lista de grupos del usuario. Comúnmente conocidos como <code>grupos</code>. <b>Si se configura</b>, la aplicación asignará automáticamente roles en función de la pertenencia a grupos del usuario, siempre que estos grupos se denominen \"admin\", \"user\" o \"guest\" en la notificación. La solicitud debe contener una lista, y si un usuario pertenece a varios grupos, la aplicación asignará el rol correspondiente al mayor nivel de acceso. Si ningún grupo coincide, se denegará el acceso.",
"LabelOpenRSSFeed": "Abrir Fuente RSS", "LabelOpenRSSFeed": "Abrir Fuente RSS",
"LabelOverwrite": "Sobrescribir", "LabelOverwrite": "Sobrescribir",
"LabelPaginationPageXOfY": "Página {0} de {1}",
"LabelPassword": "Contraseña", "LabelPassword": "Contraseña",
"LabelPath": "Ruta de carpeta", "LabelPath": "Ruta de carpeta",
"LabelPermanent": "Permanente", "LabelPermanent": "Permanente",
"LabelPermissionsAccessAllLibraries": "Puede Accesar a Todas las bibliotecas", "LabelPermissionsAccessAllLibraries": "Puede Accesar a Todas las bibliotecas",
"LabelPermissionsAccessAllTags": "Pueda Accesar a Todas las Etiquetas", "LabelPermissionsAccessAllTags": "Pueda Accesar a Todas las Etiquetas",
"LabelPermissionsAccessExplicitContent": "Puede Accesar a Contenido Explicito", "LabelPermissionsAccessExplicitContent": "Puede Accesar a Contenido Explicito",
"LabelPermissionsCreateEreader": "Puede crear un gestor de proyectos",
"LabelPermissionsDelete": "Puede Eliminar", "LabelPermissionsDelete": "Puede Eliminar",
"LabelPermissionsDownload": "Puede Descargar", "LabelPermissionsDownload": "Puede Descargar",
"LabelPermissionsUpdate": "Puede Actualizar", "LabelPermissionsUpdate": "Puede Actualizar",
@ -510,18 +517,24 @@
"LabelRedo": "Rehacer", "LabelRedo": "Rehacer",
"LabelRegion": "Región", "LabelRegion": "Región",
"LabelReleaseDate": "Fecha de Estreno", "LabelReleaseDate": "Fecha de Estreno",
"LabelRemoveAllMetadataAbs": "Eliminar todos los archivos metadata.abs",
"LabelRemoveAllMetadataJson": "Eliminar todos los archivos metadata.json",
"LabelRemoveCover": "Remover Portada", "LabelRemoveCover": "Remover Portada",
"LabelRemoveMetadataFile": "Eliminar archivos de metadatos en carpetas de elementos de biblioteca",
"LabelRemoveMetadataFileHelp": "Elimine todos los archivos metadata.json y metadata.abs de sus carpetas {0}.",
"LabelRowsPerPage": "Filas por página", "LabelRowsPerPage": "Filas por página",
"LabelSearchTerm": "Buscar Termino", "LabelSearchTerm": "Buscar Termino",
"LabelSearchTitle": "Buscar Titulo", "LabelSearchTitle": "Buscar Titulo",
"LabelSearchTitleOrASIN": "Buscar Título o ASIN", "LabelSearchTitleOrASIN": "Buscar Título o ASIN",
"LabelSeason": "Temporada", "LabelSeason": "Temporada",
"LabelSeasonNumber": "Sesión #{0}",
"LabelSelectAll": "Seleccionar todo", "LabelSelectAll": "Seleccionar todo",
"LabelSelectAllEpisodes": "Seleccionar todos los episodios", "LabelSelectAllEpisodes": "Seleccionar todos los episodios",
"LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles", "LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
"LabelSelectUsers": "Seleccionar usuarios", "LabelSelectUsers": "Seleccionar usuarios",
"LabelSendEbookToDevice": "Enviar Ebook a...", "LabelSendEbookToDevice": "Enviar Ebook a...",
"LabelSequence": "Secuencia", "LabelSequence": "Secuencia",
"LabelSerial": "Serial",
"LabelSeries": "Series", "LabelSeries": "Series",
"LabelSeriesName": "Nombre de la Serie", "LabelSeriesName": "Nombre de la Serie",
"LabelSeriesProgress": "Progreso de la Serie", "LabelSeriesProgress": "Progreso de la Serie",
@ -550,6 +563,9 @@
"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.", "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", "LabelSettingsHomePageBookshelfView": "Usar la vista de librero en la página principal",
"LabelSettingsLibraryBookshelfView": "Usar la vista de librero en la biblioteca", "LabelSettingsLibraryBookshelfView": "Usar la vista de librero en la biblioteca",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "El porcentaje completado es mayor que",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "El tiempo restante es menor a (segundos)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Marcar el archivo multimedia como terminado cuando",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Saltar libros anteriores de la serie Continuada", "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Saltar libros anteriores de la serie Continuada",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "El estante de la página de inicio de Continuar Serie muestra el primer libro no iniciado de una serie que tenga por lo menos un libro finalizado y no tenga libros en progreso. Habilitar esta opción le permitirá continuar series desde el último libro que ha completado en vez del primer libro que no ha empezado.", "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "El estante de la página de inicio de Continuar Serie muestra el primer libro no iniciado de una serie que tenga por lo menos un libro finalizado y no tenga libros en progreso. Habilitar esta opción le permitirá continuar series desde el último libro que ha completado en vez del primer libro que no ha empezado.",
"LabelSettingsParseSubtitles": "Extraer Subtítulos", "LabelSettingsParseSubtitles": "Extraer Subtítulos",
@ -614,6 +630,7 @@
"LabelTimeDurationXMinutes": "{0} minutos", "LabelTimeDurationXMinutes": "{0} minutos",
"LabelTimeDurationXSeconds": "{0} segundos", "LabelTimeDurationXSeconds": "{0} segundos",
"LabelTimeInMinutes": "Tiempo en minutos", "LabelTimeInMinutes": "Tiempo en minutos",
"LabelTimeLeft": "Quedan {0}",
"LabelTimeListened": "Tiempo Escuchando", "LabelTimeListened": "Tiempo Escuchando",
"LabelTimeListenedToday": "Tiempo Escuchando Hoy", "LabelTimeListenedToday": "Tiempo Escuchando Hoy",
"LabelTimeRemaining": "{0} restante", "LabelTimeRemaining": "{0} restante",
@ -634,6 +651,7 @@
"LabelTracksMultiTrack": "Varias pistas", "LabelTracksMultiTrack": "Varias pistas",
"LabelTracksNone": "Ninguna pista", "LabelTracksNone": "Ninguna pista",
"LabelTracksSingleTrack": "Una pista", "LabelTracksSingleTrack": "Una pista",
"LabelTrailer": "Tráiler",
"LabelType": "Tipo", "LabelType": "Tipo",
"LabelUnabridged": "No Abreviado", "LabelUnabridged": "No Abreviado",
"LabelUndo": "Deshacer", "LabelUndo": "Deshacer",
@ -650,6 +668,7 @@
"LabelUseAdvancedOptions": "Usar opciones avanzadas", "LabelUseAdvancedOptions": "Usar opciones avanzadas",
"LabelUseChapterTrack": "Usar pista por capitulo", "LabelUseChapterTrack": "Usar pista por capitulo",
"LabelUseFullTrack": "Usar pista completa", "LabelUseFullTrack": "Usar pista completa",
"LabelUseZeroForUnlimited": "Utilice 0 para ilimitado",
"LabelUser": "Usuario", "LabelUser": "Usuario",
"LabelUsername": "Nombre de Usuario", "LabelUsername": "Nombre de Usuario",
"LabelValue": "Valor", "LabelValue": "Valor",
@ -708,6 +727,7 @@
"MessageConfirmPurgeCache": "Purgar el caché eliminará el directorio completo ubicado en <code>/metadata/cache</code>. <br /><br />¿Está seguro que desea eliminar el directorio del caché?", "MessageConfirmPurgeCache": "Purgar el caché eliminará el directorio completo ubicado en <code>/metadata/cache</code>. <br /><br />¿Está seguro que desea eliminar el directorio del caché?",
"MessageConfirmPurgeItemsCache": "Purgar la caché de los elementos eliminará todo el directorio <code>/metadata/cache/items</code>.<br />¿Estás seguro?", "MessageConfirmPurgeItemsCache": "Purgar la caché de los elementos eliminará todo el directorio <code>/metadata/cache/items</code>.<br />¿Estás seguro?",
"MessageConfirmQuickEmbed": "¡Advertencia! La integración rápida no realiza copias de seguridad a ninguno de tus archivos de audio. Asegúrate de haber realizado una copia de los mismos previamente. <br><br>¿Deseas continuar?", "MessageConfirmQuickEmbed": "¡Advertencia! La integración rápida no realiza copias de seguridad a ninguno de tus archivos de audio. Asegúrate de haber realizado una copia de los mismos previamente. <br><br>¿Deseas continuar?",
"MessageConfirmQuickMatchEpisodes": "El reconocimiento rápido de extensiones sobrescribirá los detalles si se encuentra una coincidencia. Se actualizarán las extensiones no reconocidas. ¿Está seguro?",
"MessageConfirmReScanLibraryItems": "¿Estás seguro de querer re escanear {0} elemento(s)?", "MessageConfirmReScanLibraryItems": "¿Estás seguro de querer re escanear {0} elemento(s)?",
"MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?", "MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?",
"MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?", "MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?",
@ -715,6 +735,7 @@
"MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?", "MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?",
"MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?", "MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?",
"MessageConfirmRemoveListeningSessions": "¿Está seguro que desea remover {0} sesiones de escuchar?", "MessageConfirmRemoveListeningSessions": "¿Está seguro que desea remover {0} sesiones de escuchar?",
"MessageConfirmRemoveMetadataFiles": "¿Está seguro de que desea eliminar todos los archivos de metadatos.{0} en las carpetas de elementos de su biblioteca?",
"MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?", "MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?",
"MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{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?", "MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
@ -795,6 +816,7 @@
"MessagePodcastSearchField": "Introduzca el término de búsqueda o la URL de la fuente RSS", "MessagePodcastSearchField": "Introduzca el término de búsqueda o la URL de la fuente RSS",
"MessageQuickEmbedInProgress": "Integración rápida en proceso", "MessageQuickEmbedInProgress": "Integración rápida en proceso",
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)", "MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
"MessageQuickMatchAllEpisodes": "Combina rápidamente todos los episodios",
"MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.", "MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.",
"MessageRemoveChapter": "Remover capítulos", "MessageRemoveChapter": "Remover capítulos",
"MessageRemoveEpisodes": "Remover {0} episodio(s)", "MessageRemoveEpisodes": "Remover {0} episodio(s)",
@ -893,6 +915,7 @@
"StatsYearInReview": "RESEÑA DEL AÑO", "StatsYearInReview": "RESEÑA DEL AÑO",
"ToastAccountUpdateSuccess": "Cuenta actualizada", "ToastAccountUpdateSuccess": "Cuenta actualizada",
"ToastAppriseUrlRequired": "Debes ingresar una URL de Apprise", "ToastAppriseUrlRequired": "Debes ingresar una URL de Apprise",
"ToastAsinRequired": "Se requiere ASIN",
"ToastAuthorImageRemoveSuccess": "Se eliminó la imagen del autor", "ToastAuthorImageRemoveSuccess": "Se eliminó la imagen del autor",
"ToastAuthorNotFound": "No se encontró el autor \"{0}\"", "ToastAuthorNotFound": "No se encontró el autor \"{0}\"",
"ToastAuthorRemoveSuccess": "Autor eliminado", "ToastAuthorRemoveSuccess": "Autor eliminado",
@ -912,6 +935,8 @@
"ToastBackupUploadSuccess": "Respaldo cargado", "ToastBackupUploadSuccess": "Respaldo cargado",
"ToastBatchDeleteFailed": "Error al eliminar por lotes", "ToastBatchDeleteFailed": "Error al eliminar por lotes",
"ToastBatchDeleteSuccess": "Borrado por lotes correcto", "ToastBatchDeleteSuccess": "Borrado por lotes correcto",
"ToastBatchQuickMatchFailed": "¡Error en la sincronización rápida por lotes!",
"ToastBatchQuickMatchStarted": "¡Se inició el lote de búsqueda rápida de {0} libros!",
"ToastBatchUpdateFailed": "Subida masiva fallida", "ToastBatchUpdateFailed": "Subida masiva fallida",
"ToastBatchUpdateSuccess": "Subida masiva exitosa", "ToastBatchUpdateSuccess": "Subida masiva exitosa",
"ToastBookmarkCreateFailed": "Error al crear marcador", "ToastBookmarkCreateFailed": "Error al crear marcador",
@ -923,6 +948,7 @@
"ToastChaptersHaveErrors": "Los capítulos tienen errores", "ToastChaptersHaveErrors": "Los capítulos tienen errores",
"ToastChaptersMustHaveTitles": "Los capítulos tienen que tener un título", "ToastChaptersMustHaveTitles": "Los capítulos tienen que tener un título",
"ToastChaptersRemoved": "Capítulos eliminados", "ToastChaptersRemoved": "Capítulos eliminados",
"ToastChaptersUpdated": "Capítulos actualizados",
"ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)", "ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)",
"ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente", "ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente",
"ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección", "ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección",
@ -940,11 +966,14 @@
"ToastEncodeCancelSucces": "Codificación cancelada", "ToastEncodeCancelSucces": "Codificación cancelada",
"ToastEpisodeDownloadQueueClearFailed": "No se pudo borrar la cola", "ToastEpisodeDownloadQueueClearFailed": "No se pudo borrar la cola",
"ToastEpisodeDownloadQueueClearSuccess": "Se borró la cola de descargas de los episodios", "ToastEpisodeDownloadQueueClearSuccess": "Se borró la cola de descargas de los episodios",
"ToastEpisodeUpdateSuccess": "{0} episodio(s) actualizado(s)",
"ToastErrorCannotShare": "No se puede compartir de forma nativa en este dispositivo", "ToastErrorCannotShare": "No se puede compartir de forma nativa en este dispositivo",
"ToastFailedToLoadData": "Error al cargar data", "ToastFailedToLoadData": "Error al cargar data",
"ToastFailedToMatch": "Error al emparejar",
"ToastFailedToShare": "Error al compartir", "ToastFailedToShare": "Error al compartir",
"ToastFailedToUpdate": "Error al actualizar", "ToastFailedToUpdate": "Error al actualizar",
"ToastInvalidImageUrl": "URL de la imagen no válida", "ToastInvalidImageUrl": "URL de la imagen no válida",
"ToastInvalidMaxEpisodesToDownload": "Número máximo de episodios para descargar no válidos",
"ToastInvalidUrl": "URL no válida", "ToastInvalidUrl": "URL no válida",
"ToastItemCoverUpdateSuccess": "Portada del elemento actualizada", "ToastItemCoverUpdateSuccess": "Portada del elemento actualizada",
"ToastItemDeletedFailed": "Error al eliminar el elemento", "ToastItemDeletedFailed": "Error al eliminar el elemento",
@ -963,14 +992,21 @@
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca", "ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada", "ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada",
"ToastMatchAllAuthorsFailed": "No coincide con todos los autores", "ToastMatchAllAuthorsFailed": "No coincide con todos los autores",
"ToastMetadataFilesRemovedError": "Error al eliminar metadatos de {0} archivo(s)",
"ToastMetadataFilesRemovedNoneFound": "No hay metadatos.{0} archivo(s) encontrado(s) en la biblioteca",
"ToastMetadataFilesRemovedNoneRemoved": "Sin metadatos.{0} archivo(s) eliminado(s)",
"ToastMetadataFilesRemovedSuccess": "{0} metadatos.{1} archivos eliminados",
"ToastMustHaveAtLeastOnePath": "Debe tener al menos una ruta",
"ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico", "ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico",
"ToastNameRequired": "Nombre obligatorio", "ToastNameRequired": "Nombre obligatorio",
"ToastNewEpisodesFound": "{0} nuevo(s) episodio(s) encontrado(s)",
"ToastNewUserCreatedFailed": "Error al crear la cuenta: \"{0}\"", "ToastNewUserCreatedFailed": "Error al crear la cuenta: \"{0}\"",
"ToastNewUserCreatedSuccess": "Nueva cuenta creada", "ToastNewUserCreatedSuccess": "Nueva cuenta creada",
"ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca", "ToastNewUserLibraryError": "Debes seleccionar al menos una biblioteca",
"ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña", "ToastNewUserPasswordError": "Debes tener una contraseña, solo el usuario root puede estar sin contraseña",
"ToastNewUserTagError": "Debes seleccionar al menos una etiqueta", "ToastNewUserTagError": "Debes seleccionar al menos una etiqueta",
"ToastNewUserUsernameError": "Introduce un nombre de usuario", "ToastNewUserUsernameError": "Introduce un nombre de usuario",
"ToastNoNewEpisodesFound": "No se encontraron nuevos episodios",
"ToastNoUpdatesNecessary": "No es necesario actualizar", "ToastNoUpdatesNecessary": "No es necesario actualizar",
"ToastNotificationCreateFailed": "Error al crear notificación", "ToastNotificationCreateFailed": "Error al crear notificación",
"ToastNotificationDeleteFailed": "Error al borrar la notificación", "ToastNotificationDeleteFailed": "Error al borrar la notificación",
@ -989,6 +1025,7 @@
"ToastPodcastGetFeedFailed": "No se puede obtener el podcast", "ToastPodcastGetFeedFailed": "No se puede obtener el podcast",
"ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS", "ToastPodcastNoEpisodesInFeed": "No se han encontrado episodios en el feed del RSS",
"ToastPodcastNoRssFeed": "El podcast no tiene feed RSS", "ToastPodcastNoRssFeed": "El podcast no tiene feed RSS",
"ToastProgressIsNotBeingSynced": "El progreso no se sincroniza, reinicia la reproducción",
"ToastProviderCreatedFailed": "Error al añadir el proveedor", "ToastProviderCreatedFailed": "Error al añadir el proveedor",
"ToastProviderCreatedSuccess": "Nuevo proveedor añadido", "ToastProviderCreatedSuccess": "Nuevo proveedor añadido",
"ToastProviderNameAndUrlRequired": "Nombre y Url obligatorios", "ToastProviderNameAndUrlRequired": "Nombre y Url obligatorios",
@ -1015,6 +1052,7 @@
"ToastSessionCloseFailed": "Error al cerrar la sesión", "ToastSessionCloseFailed": "Error al cerrar la sesión",
"ToastSessionDeleteFailed": "Error al eliminar sesión", "ToastSessionDeleteFailed": "Error al eliminar sesión",
"ToastSessionDeleteSuccess": "Sesión eliminada", "ToastSessionDeleteSuccess": "Sesión eliminada",
"ToastSleepTimerDone": "Temporizador de apagado automático activado... zZzzZz",
"ToastSlugMustChange": "El slug contiene caracteres no válidos", "ToastSlugMustChange": "El slug contiene caracteres no válidos",
"ToastSlugRequired": "Slug obligatorio", "ToastSlugRequired": "Slug obligatorio",
"ToastSocketConnected": "Socket conectado", "ToastSocketConnected": "Socket conectado",

View File

@ -122,7 +122,7 @@
"HeaderBackups": "Sauvegardes", "HeaderBackups": "Sauvegardes",
"HeaderChangePassword": "Modifier le mot de passe", "HeaderChangePassword": "Modifier le mot de passe",
"HeaderChapters": "Chapitres", "HeaderChapters": "Chapitres",
"HeaderChooseAFolder": "Choisir un dossier", "HeaderChooseAFolder": "Sélectionner un dossier",
"HeaderCollection": "Collection", "HeaderCollection": "Collection",
"HeaderCollectionItems": "Entrées de la collection", "HeaderCollectionItems": "Entrées de la collection",
"HeaderCover": "Couverture", "HeaderCover": "Couverture",
@ -163,6 +163,7 @@
"HeaderNotificationUpdate": "Mise à jour de la notification", "HeaderNotificationUpdate": "Mise à jour de la notification",
"HeaderNotifications": "Notifications", "HeaderNotifications": "Notifications",
"HeaderOpenIDConnectAuthentication": "Authentification via OpenID Connect", "HeaderOpenIDConnectAuthentication": "Authentification via OpenID Connect",
"HeaderOpenListeningSessions": "Ouvrir les sessions d'écoutes",
"HeaderOpenRSSFeed": "Ouvrir le flux RSS", "HeaderOpenRSSFeed": "Ouvrir le flux RSS",
"HeaderOtherFiles": "Autres fichiers", "HeaderOtherFiles": "Autres fichiers",
"HeaderPasswordAuthentication": "Authentification par mot de passe", "HeaderPasswordAuthentication": "Authentification par mot de passe",
@ -180,6 +181,7 @@
"HeaderRemoveEpisodes": "Suppression de {0} épisodes", "HeaderRemoveEpisodes": "Suppression de {0} épisodes",
"HeaderSavedMediaProgress": "Progression de la sauvegarde des médias", "HeaderSavedMediaProgress": "Progression de la sauvegarde des médias",
"HeaderSchedule": "Programmation", "HeaderSchedule": "Programmation",
"HeaderScheduleEpisodeDownloads": "Programmer des téléchargements automatiques d'épisodes",
"HeaderScheduleLibraryScans": "Analyse automatique de la bibliothèque", "HeaderScheduleLibraryScans": "Analyse automatique de la bibliothèque",
"HeaderSession": "Session", "HeaderSession": "Session",
"HeaderSetBackupSchedule": "Activer la sauvegarde automatique", "HeaderSetBackupSchedule": "Activer la sauvegarde automatique",
@ -225,6 +227,7 @@
"LabelAllUsersExcludingGuests": "Tous les utilisateurs à lexception des invités", "LabelAllUsersExcludingGuests": "Tous les utilisateurs à lexception des invités",
"LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités", "LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités",
"LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque", "LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
"LabelApiToken": "Token API",
"LabelAppend": "Ajouter", "LabelAppend": "Ajouter",
"LabelAudioBitrate": "Débit audio (par exemple 128k)", "LabelAudioBitrate": "Débit audio (par exemple 128k)",
"LabelAudioChannels": "Canaux audio (1 ou 2)", "LabelAudioChannels": "Canaux audio (1 ou 2)",
@ -261,6 +264,7 @@
"LabelChapters": "Chapitres", "LabelChapters": "Chapitres",
"LabelChaptersFound": "chapitres trouvés", "LabelChaptersFound": "chapitres trouvés",
"LabelClickForMoreInfo": "Cliquez ici pour plus dinformations", "LabelClickForMoreInfo": "Cliquez ici pour plus dinformations",
"LabelClickToUseCurrentValue": "Cliquez pour utiliser la valeur actuelle",
"LabelClosePlayer": "Fermer le lecteur", "LabelClosePlayer": "Fermer le lecteur",
"LabelCodec": "Codec", "LabelCodec": "Codec",
"LabelCollapseSeries": "Réduire les séries", "LabelCollapseSeries": "Réduire les séries",
@ -306,7 +310,7 @@
"LabelEmailSettingsRejectUnauthorized": "Rejeter les certificats non autorisés", "LabelEmailSettingsRejectUnauthorized": "Rejeter les certificats non autorisés",
"LabelEmailSettingsRejectUnauthorizedHelp": "Désactiver la validation du certificat SSL peut exposer votre connexion à des risques de sécurité, tels que des attaques de type « Attaque de lhomme du milieu ». Ne désactivez cette option que si vous en comprenez les implications et si vous faites confiance au serveur de messagerie auquel vous vous connectez.", "LabelEmailSettingsRejectUnauthorizedHelp": "Désactiver la validation du certificat SSL peut exposer votre connexion à des risques de sécurité, tels que des attaques de type « Attaque de lhomme du milieu ». Ne désactivez cette option que si vous en comprenez les implications et si vous faites confiance au serveur de messagerie auquel vous vous connectez.",
"LabelEmailSettingsSecure": "Sécurisé", "LabelEmailSettingsSecure": "Sécurisé",
"LabelEmailSettingsSecureHelp": "Si cette option est activée, la connexion utilisera TLS lors de la connexion au serveur. Si elle est désactivée, TLS sera utilisé uniquement si le serveur prend en charge lextension STARTTLS. Dans la plupart des cas, définissez cette valeur sur « true » si vous vous connectez au port 465. Pour les ports 587 ou 25, laissez-la sur « false ». (source: nodemailer.com/smtp/#authentication)", "LabelEmailSettingsSecureHelp": "Si cette option est activée, la connexion utilisera TLS lors de la connexion au serveur. Si elle est désactivée, TLS sera utilisé uniquement si le serveur prend en charge lextension STARTTLS. Dans la plupart des cas, définissez cette valeur sur « true » si vous vous connectez au port 465. Pour les ports 587 ou 25, laissez-la sur « false ». (source : nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "Adresse de test", "LabelEmailSettingsTestAddress": "Adresse de test",
"LabelEmbeddedCover": "Couverture du livre intégrée", "LabelEmbeddedCover": "Couverture du livre intégrée",
"LabelEnable": "Activer", "LabelEnable": "Activer",
@ -322,9 +326,13 @@
"LabelEnd": "Fin", "LabelEnd": "Fin",
"LabelEndOfChapter": "Fin du chapitre", "LabelEndOfChapter": "Fin du chapitre",
"LabelEpisode": "Épisode", "LabelEpisode": "Épisode",
"LabelEpisodeNotLinkedToRssFeed": "Épisode non lié au flux RSS",
"LabelEpisodeNumber": "Épisode n°{0}",
"LabelEpisodeTitle": "Titre de lépisode", "LabelEpisodeTitle": "Titre de lépisode",
"LabelEpisodeType": "Type de lépisode", "LabelEpisodeType": "Type de lépisode",
"LabelEpisodeUrlFromRssFeed": "URL de lépisode à partir du flux RSS",
"LabelEpisodes": "Épisodes", "LabelEpisodes": "Épisodes",
"LabelEpisodic": "Épisodique",
"LabelExample": "Exemple", "LabelExample": "Exemple",
"LabelExpandSeries": "Développer la série", "LabelExpandSeries": "Développer la série",
"LabelExpandSubSeries": "Développer les sous-séries", "LabelExpandSubSeries": "Développer les sous-séries",
@ -352,6 +360,7 @@
"LabelFontScale": "Taille de la police de caractère", "LabelFontScale": "Taille de la police de caractère",
"LabelFontStrikethrough": "Barrer", "LabelFontStrikethrough": "Barrer",
"LabelFormat": "Format", "LabelFormat": "Format",
"LabelFull": "Complet",
"LabelGenre": "Genre", "LabelGenre": "Genre",
"LabelGenres": "Genres", "LabelGenres": "Genres",
"LabelHardDeleteFile": "Suppression du fichier", "LabelHardDeleteFile": "Suppression du fichier",
@ -407,6 +416,10 @@
"LabelLowestPriority": "Priorité la plus basse", "LabelLowestPriority": "Priorité la plus basse",
"LabelMatchExistingUsersBy": "Correspondance avec les utilisateurs existants", "LabelMatchExistingUsersBy": "Correspondance avec les utilisateurs existants",
"LabelMatchExistingUsersByDescription": "Utilisé pour connecter les utilisateurs existants. Une fois connectés, les utilisateurs seront associés à un identifiant unique provenant de votre fournisseur SSO", "LabelMatchExistingUsersByDescription": "Utilisé pour connecter les utilisateurs existants. Une fois connectés, les utilisateurs seront associés à un identifiant unique provenant de votre fournisseur SSO",
"LabelMaxEpisodesToDownload": "Nombre maximum dépisodes à télécharger. 0 pour illimité.",
"LabelMaxEpisodesToDownloadPerCheck": "Nombre maximum de nouveaux épisodes à télécharger par vérification",
"LabelMaxEpisodesToKeep": "Nombre maximum dépisodes à conserver",
"LabelMaxEpisodesToKeepHelp": "La valeur 0 ne définit aucune limite maximale. Une fois quun nouvel épisode est téléchargé automatiquement, lépisode le plus ancien sera supprimé si vous avez plus de X épisodes. Cela ne supprimera quun seul épisode par nouveau téléchargement.",
"LabelMediaPlayer": "Lecteur multimédia", "LabelMediaPlayer": "Lecteur multimédia",
"LabelMediaType": "Type de média", "LabelMediaType": "Type de média",
"LabelMetaTag": "Balise de métadonnée", "LabelMetaTag": "Balise de métadonnée",
@ -452,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Nom de la demande OpenID qui contient une liste des groupes de lutilisateur. Communément appelé <code>groups</code>. <b>Si elle est configurée</b>, lapplication attribuera automatiquement des rôles en fonction de lappartenance de lutilisateur à un groupe, à condition que ces groupes soient nommés -sensible à la casse- tel que « admin », « user » ou « guest » dans la demande. Elle doit contenir une liste, et si un utilisateur appartient à plusieurs groupes, lapplication attribuera le rôle correspondant au niveau daccès le plus élevé. Si aucun groupe ne correspond, laccès sera refusé.", "LabelOpenIDGroupClaimDescription": "Nom de la demande OpenID qui contient une liste des groupes de lutilisateur. Communément appelé <code>groups</code>. <b>Si elle est configurée</b>, lapplication attribuera automatiquement des rôles en fonction de lappartenance de lutilisateur à un groupe, à condition que ces groupes soient nommés -sensible à la casse- tel que « admin », « user » ou « guest » dans la demande. Elle doit contenir une liste, et si un utilisateur appartient à plusieurs groupes, lapplication attribuera le rôle correspondant au niveau daccès le plus élevé. Si aucun groupe ne correspond, laccès sera refusé.",
"LabelOpenRSSFeed": "Ouvrir le flux RSS", "LabelOpenRSSFeed": "Ouvrir le flux RSS",
"LabelOverwrite": "Écraser", "LabelOverwrite": "Écraser",
"LabelPaginationPageXOfY": "Page {0} sur {1}",
"LabelPassword": "Mot de passe", "LabelPassword": "Mot de passe",
"LabelPath": "Chemin", "LabelPath": "Chemin",
"LabelPermanent": "Permanent", "LabelPermanent": "Permanent",
"LabelPermissionsAccessAllLibraries": "Peut accéder à toutes les bibliothèque", "LabelPermissionsAccessAllLibraries": "Peut accéder à toutes les bibliothèque",
"LabelPermissionsAccessAllTags": "Peut accéder à toutes les étiquettes", "LabelPermissionsAccessAllTags": "Peut accéder à toutes les étiquettes",
"LabelPermissionsAccessExplicitContent": "Peut accéder au contenu restreint", "LabelPermissionsAccessExplicitContent": "Peut accéder au contenu restreint",
"LabelPermissionsCreateEreader": "Peut créer une liseuse",
"LabelPermissionsDelete": "Peut supprimer", "LabelPermissionsDelete": "Peut supprimer",
"LabelPermissionsDownload": "Peut télécharger", "LabelPermissionsDownload": "Peut télécharger",
"LabelPermissionsUpdate": "Peut mettre à jour", "LabelPermissionsUpdate": "Peut mettre à jour",
@ -502,18 +517,24 @@
"LabelRedo": "Refaire", "LabelRedo": "Refaire",
"LabelRegion": "Région", "LabelRegion": "Région",
"LabelReleaseDate": "Date de parution", "LabelReleaseDate": "Date de parution",
"LabelRemoveAllMetadataAbs": "Supprimer tous les fichiers metadata.abs",
"LabelRemoveAllMetadataJson": "Supprimer tous les fichiers metadata.json",
"LabelRemoveCover": "Supprimer la couverture", "LabelRemoveCover": "Supprimer la couverture",
"LabelRemoveMetadataFile": "Supprimer les fichiers de métadonnées dans les dossiers des éléments de la bibliothèque",
"LabelRemoveMetadataFileHelp": "Supprimer tous les fichiers metadata.json et metadata.abs de vos dossiers {0}.",
"LabelRowsPerPage": "Lignes par page", "LabelRowsPerPage": "Lignes par page",
"LabelSearchTerm": "Terme de recherche", "LabelSearchTerm": "Terme de recherche",
"LabelSearchTitle": "Titre de recherche", "LabelSearchTitle": "Titre de recherche",
"LabelSearchTitleOrASIN": "Recherche du titre ou ASIN", "LabelSearchTitleOrASIN": "Recherche du titre ou ASIN",
"LabelSeason": "Saison", "LabelSeason": "Saison",
"LabelSeasonNumber": "Saison n°{0}",
"LabelSelectAll": "Tout sélectionner", "LabelSelectAll": "Tout sélectionner",
"LabelSelectAllEpisodes": "Sélectionner tous les épisodes", "LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
"LabelSelectEpisodesShowing": "Sélectionner {0} épisode(s) en cours", "LabelSelectEpisodesShowing": "Sélectionner {0} épisode(s) en cours",
"LabelSelectUsers": "Sélectionner les utilisateurs", "LabelSelectUsers": "Sélectionner les utilisateurs",
"LabelSendEbookToDevice": "Envoyer le livre numérique à…", "LabelSendEbookToDevice": "Envoyer le livre numérique à…",
"LabelSequence": "Séquence", "LabelSequence": "Séquence",
"LabelSerial": "N° de série",
"LabelSeries": "Séries", "LabelSeries": "Séries",
"LabelSeriesName": "Nom de la série", "LabelSeriesName": "Nom de la série",
"LabelSeriesProgress": "Progression de séries", "LabelSeriesProgress": "Progression de séries",
@ -542,6 +563,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Les séries qui ne comportent quun seul livre seront masquées sur la page de la série et sur les étagères de la page daccueil.", "LabelSettingsHideSingleBookSeriesHelp": "Les séries qui ne comportent quun seul livre seront masquées sur la page de la série et sur les étagères de la page daccueil.",
"LabelSettingsHomePageBookshelfView": "Utiliser la vue étagère sur la page daccueil", "LabelSettingsHomePageBookshelfView": "Utiliser la vue étagère sur la page daccueil",
"LabelSettingsLibraryBookshelfView": "Utiliser la vue étagère pour la bibliothèque", "LabelSettingsLibraryBookshelfView": "Utiliser la vue étagère pour la bibliothèque",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "Le pourcentage d'achèvement est supérieur à",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Le temps restant est inférieur à (secondes)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Marquer lélément multimédia comme terminé lorsque",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Sauter les livres précédents dans « Continuer la série »", "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Sauter les livres précédents dans « Continuer la série »",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Létagère de la page daccueil « Continuer la série » affiche le premier livre non commencé dans les séries dont au moins un livre est terminé et aucun livre nest en cours. Lactivation de ce paramètre permet de poursuivre la série à partir du dernier livre terminé au lieu du premier livre non commencé.", "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Létagère de la page daccueil « Continuer la série » affiche le premier livre non commencé dans les séries dont au moins un livre est terminé et aucun livre nest en cours. Lactivation de ce paramètre permet de poursuivre la série à partir du dernier livre terminé au lieu du premier livre non commencé.",
"LabelSettingsParseSubtitles": "Analyser les sous-titres", "LabelSettingsParseSubtitles": "Analyser les sous-titres",
@ -606,6 +630,7 @@
"LabelTimeDurationXMinutes": "{0} minutes", "LabelTimeDurationXMinutes": "{0} minutes",
"LabelTimeDurationXSeconds": "{0} secondes", "LabelTimeDurationXSeconds": "{0} secondes",
"LabelTimeInMinutes": "Temps en minutes", "LabelTimeInMinutes": "Temps en minutes",
"LabelTimeLeft": "{0} restant",
"LabelTimeListened": "Temps découte", "LabelTimeListened": "Temps découte",
"LabelTimeListenedToday": "Nombres découtes aujourdhui", "LabelTimeListenedToday": "Nombres découtes aujourdhui",
"LabelTimeRemaining": "{0} restantes", "LabelTimeRemaining": "{0} restantes",
@ -626,6 +651,7 @@
"LabelTracksMultiTrack": "Piste multiple", "LabelTracksMultiTrack": "Piste multiple",
"LabelTracksNone": "Aucune piste", "LabelTracksNone": "Aucune piste",
"LabelTracksSingleTrack": "Piste simple", "LabelTracksSingleTrack": "Piste simple",
"LabelTrailer": "Bande-annonce",
"LabelType": "Type", "LabelType": "Type",
"LabelUnabridged": "Version intégrale", "LabelUnabridged": "Version intégrale",
"LabelUndo": "Annuler", "LabelUndo": "Annuler",
@ -642,6 +668,7 @@
"LabelUseAdvancedOptions": "Utiliser les options avancées", "LabelUseAdvancedOptions": "Utiliser les options avancées",
"LabelUseChapterTrack": "Utiliser la piste du chapitre", "LabelUseChapterTrack": "Utiliser la piste du chapitre",
"LabelUseFullTrack": "Utiliser la piste complète", "LabelUseFullTrack": "Utiliser la piste complète",
"LabelUseZeroForUnlimited": "0 pour illimité",
"LabelUser": "Utilisateur", "LabelUser": "Utilisateur",
"LabelUsername": "Nom dutilisateur", "LabelUsername": "Nom dutilisateur",
"LabelValue": "Valeur", "LabelValue": "Valeur",
@ -688,7 +715,7 @@
"MessageConfirmDeleteMetadataProvider": "Êtes-vous sûr·e de vouloir supprimer le fournisseur de métadonnées personnalisées « {0} » ?", "MessageConfirmDeleteMetadataProvider": "Êtes-vous sûr·e de vouloir supprimer le fournisseur de métadonnées personnalisées « {0} » ?",
"MessageConfirmDeleteNotification": "Êtes-vous sûr·e de vouloir supprimer cette notification?", "MessageConfirmDeleteNotification": "Êtes-vous sûr·e de vouloir supprimer cette notification?",
"MessageConfirmDeleteSession": "Êtes-vous sûr·e de vouloir supprimer cette session?", "MessageConfirmDeleteSession": "Êtes-vous sûr·e de vouloir supprimer cette session?",
"MessageConfirmEmbedMetadataInAudioFiles": "Souhaitez-vous vraiment intégrer des métadonnées dans {0} fichiers audio?", "MessageConfirmEmbedMetadataInAudioFiles": "Êtes-vous sûr·e de vouloir intégrer des métadonnées dans {0} fichiers audio?",
"MessageConfirmForceReScan": "Êtes-vous sûr·e de vouloir lancer une analyse forcée?", "MessageConfirmForceReScan": "Êtes-vous sûr·e de vouloir lancer une analyse forcée?",
"MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr·e de marquer tous les épisodes comme terminés?", "MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr·e de marquer tous les épisodes comme terminés?",
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr·e de vouloir marquer tous les épisodes comme non terminés?", "MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr·e de vouloir marquer tous les épisodes comme non terminés?",
@ -699,7 +726,8 @@
"MessageConfirmNotificationTestTrigger": "Déclencher cette notification avec des données de test?", "MessageConfirmNotificationTestTrigger": "Déclencher cette notification avec des données de test?",
"MessageConfirmPurgeCache": "La purge du cache supprimera lintégralité du répertoire à <code>/metadata/cache</code>.<br /><br />Êtes-vous sûr·e de vouloir supprimer le répertoire de cache?", "MessageConfirmPurgeCache": "La purge du cache supprimera lintégralité du répertoire à <code>/metadata/cache</code>.<br /><br />Êtes-vous sûr·e de vouloir supprimer le répertoire de cache?",
"MessageConfirmPurgeItemsCache": "Purger le cache des éléments supprimera l'ensemble du répertoire <code>/metadata/cache/items</code>.<br />Êtes-vous sûr?", "MessageConfirmPurgeItemsCache": "Purger le cache des éléments supprimera l'ensemble du répertoire <code>/metadata/cache/items</code>.<br />Êtes-vous sûr?",
"MessageConfirmQuickEmbed": "Attention ! L'intégration rapide ne permet pas de sauvegarder vos fichiers audio. Assurez-vous davoir effectuer une sauvegarde de vos fichiers audio.<br><br>Souhaitez-vous continuer ?", "MessageConfirmQuickEmbed": "Attention! L'intégration rapide ne permet pas de sauvegarder vos fichiers audio. Assurez-vous davoir effectuer une sauvegarde de vos fichiers audio.<br><br>Êtes-vous sûr·e de vouloir continuer?",
"MessageConfirmQuickMatchEpisodes": "Les épisodes correspondants seront écrasés si une correspondance est trouvée. Seuls les épisodes non correspondants seront mis à jour. Êtes-vous sûr·e?",
"MessageConfirmReScanLibraryItems": "Êtes-vous sûr·e de vouloir réanalyser {0} éléments?", "MessageConfirmReScanLibraryItems": "Êtes-vous sûr·e de vouloir réanalyser {0} éléments?",
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr·e de vouloir supprimer tous les chapitres?", "MessageConfirmRemoveAllChapters": "Êtes-vous sûr·e de vouloir supprimer tous les chapitres?",
"MessageConfirmRemoveAuthor": "Êtes-vous sûr·e de vouloir supprimer lauteur « {0} » ?", "MessageConfirmRemoveAuthor": "Êtes-vous sûr·e de vouloir supprimer lauteur « {0} » ?",
@ -707,6 +735,7 @@
"MessageConfirmRemoveEpisode": "Êtes-vous sûr·e de vouloir supprimer lépisode « {0} » ?", "MessageConfirmRemoveEpisode": "Êtes-vous sûr·e de vouloir supprimer lépisode « {0} » ?",
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr·e de vouloir supprimer {0} épisodes?", "MessageConfirmRemoveEpisodes": "Êtes-vous sûr·e de vouloir supprimer {0} épisodes?",
"MessageConfirmRemoveListeningSessions": "Êtes-vous sûr·e de vouloir supprimer {0} sessions découte?", "MessageConfirmRemoveListeningSessions": "Êtes-vous sûr·e de vouloir supprimer {0} sessions découte?",
"MessageConfirmRemoveMetadataFiles": "Êtes-vous sûr·e de vouloir supprimer tous les fichiers « metatadata.{0} » des dossiers déléments de votre bibliothèque?",
"MessageConfirmRemoveNarrator": "Êtes-vous sûr·e de vouloir supprimer le narrateur « {0} » ?", "MessageConfirmRemoveNarrator": "Êtes-vous sûr·e de vouloir supprimer le narrateur « {0} » ?",
"MessageConfirmRemovePlaylist": "Êtes-vous sûr·e de vouloir supprimer la liste de lecture « {0} » ?", "MessageConfirmRemovePlaylist": "Êtes-vous sûr·e de vouloir supprimer la liste de lecture « {0} » ?",
"MessageConfirmRenameGenre": "Êtes-vous sûr·e de vouloir renommer le genre « {0} » en « {1} » pour tous les éléments?", "MessageConfirmRenameGenre": "Êtes-vous sûr·e de vouloir renommer le genre « {0} » en « {1} » pour tous les éléments?",
@ -787,6 +816,7 @@
"MessagePodcastSearchField": "Saisissez le terme de recherche ou l'URL du flux RSS", "MessagePodcastSearchField": "Saisissez le terme de recherche ou l'URL du flux RSS",
"MessageQuickEmbedInProgress": "Intégration rapide en cours", "MessageQuickEmbedInProgress": "Intégration rapide en cours",
"MessageQuickEmbedQueue": "En file d'attente pour une intégration rapide ({0} dans la file d'attente)", "MessageQuickEmbedQueue": "En file d'attente pour une intégration rapide ({0} dans la file d'attente)",
"MessageQuickMatchAllEpisodes": "Associer rapidement tous les épisodes",
"MessageQuickMatchDescription": "Renseigne les détails manquants ainsi que la couverture avec la première correspondance de « {0} ». Nécrase pas les données présentes à moins que le paramètre « Préférer les Métadonnées par correspondance » soit activé.", "MessageQuickMatchDescription": "Renseigne les détails manquants ainsi que la couverture avec la première correspondance de « {0} ». Nécrase pas les données présentes à moins que le paramètre « Préférer les Métadonnées par correspondance » soit activé.",
"MessageRemoveChapter": "Supprimer le chapitre", "MessageRemoveChapter": "Supprimer le chapitre",
"MessageRemoveEpisodes": "Suppression de {0} épisode(s)", "MessageRemoveEpisodes": "Suppression de {0} épisode(s)",
@ -885,6 +915,7 @@
"StatsYearInReview": "BILAN DE LANNÉE", "StatsYearInReview": "BILAN DE LANNÉE",
"ToastAccountUpdateSuccess": "Compte mis à jour", "ToastAccountUpdateSuccess": "Compte mis à jour",
"ToastAppriseUrlRequired": "Vous devez entrer une URL Apprise", "ToastAppriseUrlRequired": "Vous devez entrer une URL Apprise",
"ToastAsinRequired": "ASIN requis",
"ToastAuthorImageRemoveSuccess": "Image de lauteur supprimée", "ToastAuthorImageRemoveSuccess": "Image de lauteur supprimée",
"ToastAuthorNotFound": "Auteur \"{0}\" non trouvé", "ToastAuthorNotFound": "Auteur \"{0}\" non trouvé",
"ToastAuthorRemoveSuccess": "Auteur supprimé", "ToastAuthorRemoveSuccess": "Auteur supprimé",
@ -904,6 +935,8 @@
"ToastBackupUploadSuccess": "Sauvegarde téléversée", "ToastBackupUploadSuccess": "Sauvegarde téléversée",
"ToastBatchDeleteFailed": "Échec de la suppression par lot", "ToastBatchDeleteFailed": "Échec de la suppression par lot",
"ToastBatchDeleteSuccess": "Suppression par lot réussie", "ToastBatchDeleteSuccess": "Suppression par lot réussie",
"ToastBatchQuickMatchFailed": "Échec de la correspondance rapide par lot!",
"ToastBatchQuickMatchStarted": "La correspondance rapide par lots de {0} livres a commencé!",
"ToastBatchUpdateFailed": "Échec de la mise à jour par lot", "ToastBatchUpdateFailed": "Échec de la mise à jour par lot",
"ToastBatchUpdateSuccess": "Mise à jour par lot terminée", "ToastBatchUpdateSuccess": "Mise à jour par lot terminée",
"ToastBookmarkCreateFailed": "Échec de la création de signet", "ToastBookmarkCreateFailed": "Échec de la création de signet",
@ -915,6 +948,7 @@
"ToastChaptersHaveErrors": "Les chapitres contiennent des erreurs", "ToastChaptersHaveErrors": "Les chapitres contiennent des erreurs",
"ToastChaptersMustHaveTitles": "Les chapitre doivent avoir un titre", "ToastChaptersMustHaveTitles": "Les chapitre doivent avoir un titre",
"ToastChaptersRemoved": "Chapitres supprimés", "ToastChaptersRemoved": "Chapitres supprimés",
"ToastChaptersUpdated": "Chapitres mis à jour",
"ToastCollectionItemsAddFailed": "Échec de lajout de(s) élément(s) à la collection", "ToastCollectionItemsAddFailed": "Échec de lajout de(s) élément(s) à la collection",
"ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi", "ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi",
"ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection", "ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection",
@ -932,11 +966,14 @@
"ToastEncodeCancelSucces": "Encodage annulé", "ToastEncodeCancelSucces": "Encodage annulé",
"ToastEpisodeDownloadQueueClearFailed": "Échec de la suppression de la file d'attente", "ToastEpisodeDownloadQueueClearFailed": "Échec de la suppression de la file d'attente",
"ToastEpisodeDownloadQueueClearSuccess": "File dattente de téléchargement des épisodes effacée", "ToastEpisodeDownloadQueueClearSuccess": "File dattente de téléchargement des épisodes effacée",
"ToastEpisodeUpdateSuccess": "{0} épisodes mis à jour",
"ToastErrorCannotShare": "Impossible de partager nativement sur cet appareil", "ToastErrorCannotShare": "Impossible de partager nativement sur cet appareil",
"ToastFailedToLoadData": "Échec du chargement des données", "ToastFailedToLoadData": "Échec du chargement des données",
"ToastFailedToMatch": "Échec de la correspondance",
"ToastFailedToShare": "Échec du partage", "ToastFailedToShare": "Échec du partage",
"ToastFailedToUpdate": "Échec de la mise à jour", "ToastFailedToUpdate": "Échec de la mise à jour",
"ToastInvalidImageUrl": "URL de l'image invalide", "ToastInvalidImageUrl": "URL de l'image invalide",
"ToastInvalidMaxEpisodesToDownload": "Nombre maximum dépisodes à télécharger non valide",
"ToastInvalidUrl": "URL invalide", "ToastInvalidUrl": "URL invalide",
"ToastItemCoverUpdateSuccess": "Couverture mise à jour", "ToastItemCoverUpdateSuccess": "Couverture mise à jour",
"ToastItemDeletedFailed": "La suppression de l'élément à échouée", "ToastItemDeletedFailed": "La suppression de l'élément à échouée",
@ -955,14 +992,21 @@
"ToastLibraryScanStarted": "Analyse de la bibliothèque démarrée", "ToastLibraryScanStarted": "Analyse de la bibliothèque démarrée",
"ToastLibraryUpdateSuccess": "Bibliothèque « {0} » mise à jour", "ToastLibraryUpdateSuccess": "Bibliothèque « {0} » mise à jour",
"ToastMatchAllAuthorsFailed": "Tous les auteurs et autrices nont pas pu être classés", "ToastMatchAllAuthorsFailed": "Tous les auteurs et autrices nont pas pu être classés",
"ToastMetadataFilesRemovedError": "Erreur lors de la suppression des fichiers « metadata.{0} »",
"ToastMetadataFilesRemovedNoneFound": "Aucun fichier « metadata.{0} » trouvé dans la bibliothèque",
"ToastMetadataFilesRemovedNoneRemoved": "Aucun fichier « metadata.{0} » na été supprimé",
"ToastMetadataFilesRemovedSuccess": "{0} fichiers metadata.{1} supprimés",
"ToastMustHaveAtLeastOnePath": "Doit avoir au moins un chemin",
"ToastNameEmailRequired": "Le nom et le courriel sont requis", "ToastNameEmailRequired": "Le nom et le courriel sont requis",
"ToastNameRequired": "Le nom est requis", "ToastNameRequired": "Le nom est requis",
"ToastNewEpisodesFound": "{0} nouveaux épisodes trouvés",
"ToastNewUserCreatedFailed": "La création du compte à échouée: « {0} »", "ToastNewUserCreatedFailed": "La création du compte à échouée: « {0} »",
"ToastNewUserCreatedSuccess": "Nouveau compte créé", "ToastNewUserCreatedSuccess": "Nouveau compte créé",
"ToastNewUserLibraryError": "Au moins une bibliothèque est requise", "ToastNewUserLibraryError": "Au moins une bibliothèque est requise",
"ToastNewUserPasswordError": "Un mot de passe est requis, seul lutilisateur root peut avoir un mot de passe vide", "ToastNewUserPasswordError": "Un mot de passe est requis, seul lutilisateur root peut avoir un mot de passe vide",
"ToastNewUserTagError": "Au moins une étiquette est requise", "ToastNewUserTagError": "Au moins une étiquette est requise",
"ToastNewUserUsernameError": "Entrez un nom dutilisateur", "ToastNewUserUsernameError": "Entrez un nom dutilisateur",
"ToastNoNewEpisodesFound": "Aucun nouvel épisode trouvé",
"ToastNoUpdatesNecessary": "Aucune mise à jour nécessaire", "ToastNoUpdatesNecessary": "Aucune mise à jour nécessaire",
"ToastNotificationCreateFailed": "La création de la notification à échouée", "ToastNotificationCreateFailed": "La création de la notification à échouée",
"ToastNotificationDeleteFailed": "La suppression de la notification à échouée", "ToastNotificationDeleteFailed": "La suppression de la notification à échouée",
@ -981,6 +1025,7 @@
"ToastPodcastGetFeedFailed": "Échec de la récupération du flux du podcast", "ToastPodcastGetFeedFailed": "Échec de la récupération du flux du podcast",
"ToastPodcastNoEpisodesInFeed": "Aucun épisode trouvé dans le flux RSS", "ToastPodcastNoEpisodesInFeed": "Aucun épisode trouvé dans le flux RSS",
"ToastPodcastNoRssFeed": "Le podcast na pas de flux RSS", "ToastPodcastNoRssFeed": "Le podcast na pas de flux RSS",
"ToastProgressIsNotBeingSynced": "La progression nest pas synchronisée, redémarrez la lecture",
"ToastProviderCreatedFailed": "Échec de lajout du fournisseur", "ToastProviderCreatedFailed": "Échec de lajout du fournisseur",
"ToastProviderCreatedSuccess": "Nouveau fournisseur ajouté", "ToastProviderCreatedSuccess": "Nouveau fournisseur ajouté",
"ToastProviderNameAndUrlRequired": "Nom et URL requis", "ToastProviderNameAndUrlRequired": "Nom et URL requis",
@ -1007,6 +1052,7 @@
"ToastSessionCloseFailed": "Échec de la fermeture de la session", "ToastSessionCloseFailed": "Échec de la fermeture de la session",
"ToastSessionDeleteFailed": "Échec de la suppression de session", "ToastSessionDeleteFailed": "Échec de la suppression de session",
"ToastSessionDeleteSuccess": "Session supprimée", "ToastSessionDeleteSuccess": "Session supprimée",
"ToastSleepTimerDone": "Minuterie de mise en veille terminée… zZzzZz",
"ToastSlugMustChange": "Lidentifiant dURL contient des caractères invalides", "ToastSlugMustChange": "Lidentifiant dURL contient des caractères invalides",
"ToastSlugRequired": "Lidentifiant dURL est requis", "ToastSlugRequired": "Lidentifiant dURL est requis",
"ToastSocketConnected": "WebSocket connecté", "ToastSocketConnected": "WebSocket connecté",

View File

@ -163,6 +163,7 @@
"HeaderNotificationUpdate": "Aggiornamento della notifica", "HeaderNotificationUpdate": "Aggiornamento della notifica",
"HeaderNotifications": "Notifiche", "HeaderNotifications": "Notifiche",
"HeaderOpenIDConnectAuthentication": "Autenticazione OpenID Connect", "HeaderOpenIDConnectAuthentication": "Autenticazione OpenID Connect",
"HeaderOpenListeningSessions": "Apri sessioni di ascolto",
"HeaderOpenRSSFeed": "Apri il flusso RSS", "HeaderOpenRSSFeed": "Apri il flusso RSS",
"HeaderOtherFiles": "Altri File", "HeaderOtherFiles": "Altri File",
"HeaderPasswordAuthentication": "Autenticazione della password", "HeaderPasswordAuthentication": "Autenticazione della password",
@ -180,6 +181,7 @@
"HeaderRemoveEpisodes": "Rimuovi {0} Episodi", "HeaderRemoveEpisodes": "Rimuovi {0} Episodi",
"HeaderSavedMediaProgress": "Progressi salvati", "HeaderSavedMediaProgress": "Progressi salvati",
"HeaderSchedule": "Schedula", "HeaderSchedule": "Schedula",
"HeaderScheduleEpisodeDownloads": "Imposta il download automatico degli episodi",
"HeaderScheduleLibraryScans": "Schedula la scansione della libreria", "HeaderScheduleLibraryScans": "Schedula la scansione della libreria",
"HeaderSession": "Sessione", "HeaderSession": "Sessione",
"HeaderSetBackupSchedule": "Imposta programmazione Backup", "HeaderSetBackupSchedule": "Imposta programmazione Backup",
@ -225,6 +227,7 @@
"LabelAllUsersExcludingGuests": "Tutti gli Utenti Esclusi gli ospiti", "LabelAllUsersExcludingGuests": "Tutti gli Utenti Esclusi gli ospiti",
"LabelAllUsersIncludingGuests": "Tutti gli Utenti Inclusi gli ospiti", "LabelAllUsersIncludingGuests": "Tutti gli Utenti Inclusi gli ospiti",
"LabelAlreadyInYourLibrary": "Già esistente nella libreria", "LabelAlreadyInYourLibrary": "Già esistente nella libreria",
"LabelApiToken": "API Token",
"LabelAppend": "Appese", "LabelAppend": "Appese",
"LabelAudioBitrate": "Audio Bitrate (es. 128k)", "LabelAudioBitrate": "Audio Bitrate (es. 128k)",
"LabelAudioChannels": "Canali Audio (1 o 2)", "LabelAudioChannels": "Canali Audio (1 o 2)",
@ -250,15 +253,18 @@
"LabelBackupsNumberToKeep": "Numero di backup da mantenere", "LabelBackupsNumberToKeep": "Numero di backup da mantenere",
"LabelBackupsNumberToKeepHelp": "Verrà rimosso solo 1 backup alla volta, quindi se hai più backup, dovrai rimuoverli manualmente.", "LabelBackupsNumberToKeepHelp": "Verrà rimosso solo 1 backup alla volta, quindi se hai più backup, dovrai rimuoverli manualmente.",
"LabelBitrate": "Velocità di trasmissione", "LabelBitrate": "Velocità di trasmissione",
"LabelBonus": "Bonus",
"LabelBooks": "Libri", "LabelBooks": "Libri",
"LabelButtonText": "Buttone Testo", "LabelButtonText": "Buttone Testo",
"LabelByAuthor": "da {0}", "LabelByAuthor": "da {0}",
"LabelChangePassword": "Cambia Password", "LabelChangePassword": "Cambia Password",
"LabelChannels": "Canali", "LabelChannels": "Canali",
"LabelChapterCount": "{0} Capitoli",
"LabelChapterTitle": "Titoli dei Capitoli", "LabelChapterTitle": "Titoli dei Capitoli",
"LabelChapters": "Capitoli", "LabelChapters": "Capitoli",
"LabelChaptersFound": "Capitoli Trovati", "LabelChaptersFound": "Capitoli Trovati",
"LabelClickForMoreInfo": "Click per altre Info", "LabelClickForMoreInfo": "Click per altre Info",
"LabelClickToUseCurrentValue": "Clicca per usare il valore corrente",
"LabelClosePlayer": "Chiudi player", "LabelClosePlayer": "Chiudi player",
"LabelCodec": "Codec", "LabelCodec": "Codec",
"LabelCollapseSeries": "Comprimi Serie", "LabelCollapseSeries": "Comprimi Serie",
@ -320,9 +326,13 @@
"LabelEnd": "Fine", "LabelEnd": "Fine",
"LabelEndOfChapter": "Fine Capitolo", "LabelEndOfChapter": "Fine Capitolo",
"LabelEpisode": "Episodio", "LabelEpisode": "Episodio",
"LabelEpisodeNotLinkedToRssFeed": "Episode non linkati nel RSS feed",
"LabelEpisodeNumber": "Episodio #{0}",
"LabelEpisodeTitle": "Titolo Episodio", "LabelEpisodeTitle": "Titolo Episodio",
"LabelEpisodeType": "Tipo Episodio", "LabelEpisodeType": "Tipo Episodio",
"LabelEpisodeUrlFromRssFeed": "URL dell'episodio dal RSS feed",
"LabelEpisodes": "Episodi", "LabelEpisodes": "Episodi",
"LabelEpisodic": "Episodico",
"LabelExample": "Esempio", "LabelExample": "Esempio",
"LabelExpandSeries": "Espandi Serie", "LabelExpandSeries": "Espandi Serie",
"LabelExpandSubSeries": "Espandi Sub Serie", "LabelExpandSubSeries": "Espandi Sub Serie",
@ -350,6 +360,7 @@
"LabelFontScale": "Dimensione font", "LabelFontScale": "Dimensione font",
"LabelFontStrikethrough": "Barrato", "LabelFontStrikethrough": "Barrato",
"LabelFormat": "Formato", "LabelFormat": "Formato",
"LabelFull": "Pieno",
"LabelGenre": "Genere", "LabelGenre": "Genere",
"LabelGenres": "Generi", "LabelGenres": "Generi",
"LabelHardDeleteFile": "Elimina Definitivamente", "LabelHardDeleteFile": "Elimina Definitivamente",
@ -405,6 +416,10 @@
"LabelLowestPriority": "Priorità Minima", "LabelLowestPriority": "Priorità Minima",
"LabelMatchExistingUsersBy": "Abbina gli utenti esistenti per", "LabelMatchExistingUsersBy": "Abbina gli utenti esistenti per",
"LabelMatchExistingUsersByDescription": "Utilizzato per connettere gli utenti esistenti. Una volta connessi, gli utenti verranno abbinati a un ID univoco dal tuo provider SSO", "LabelMatchExistingUsersByDescription": "Utilizzato per connettere gli utenti esistenti. Una volta connessi, gli utenti verranno abbinati a un ID univoco dal tuo provider SSO",
"LabelMaxEpisodesToDownload": "Max # di episodi da scaricare. Usa 0 per illimitati.",
"LabelMaxEpisodesToDownloadPerCheck": "Massimo # di nuovi episodi da scaricare per il controllo",
"LabelMaxEpisodesToKeep": "Massimo # di episodi da tenere",
"LabelMaxEpisodesToKeepHelp": "Il valore 0 non imposta alcun limite massimo. Dopo che un nuovo episodio è stato scaricato automaticamente, questo eliminerà l'episodio più vecchio se hai più di X episodi. Questo eliminerà solo 1 episodio per ogni nuovo download.",
"LabelMediaPlayer": "Media Player", "LabelMediaPlayer": "Media Player",
"LabelMediaType": "Tipo Media", "LabelMediaType": "Tipo Media",
"LabelMetaTag": "Meta Tag", "LabelMetaTag": "Meta Tag",
@ -450,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Nome dell'attestazione OpenID che contiene un elenco dei gruppi dell'utente. Comunemente indicato come <code>gruppo</code>. <b>se configurato</b>, l'applicazione assegnerà automaticamente i ruoli in base alle appartenenze ai gruppi dell'utente, a condizione che tali gruppi siano denominati \"admin\", \"utente\" o \"ospite\" senza distinzione tra maiuscole e minuscole nell'attestazione. L'attestazione deve contenere un elenco e, se un utente appartiene a più gruppi, l'applicazione assegnerà il ruolo corrispondente al livello di accesso più alto. Se nessun gruppo corrisponde, l'accesso verrà negato.", "LabelOpenIDGroupClaimDescription": "Nome dell'attestazione OpenID che contiene un elenco dei gruppi dell'utente. Comunemente indicato come <code>gruppo</code>. <b>se configurato</b>, l'applicazione assegnerà automaticamente i ruoli in base alle appartenenze ai gruppi dell'utente, a condizione che tali gruppi siano denominati \"admin\", \"utente\" o \"ospite\" senza distinzione tra maiuscole e minuscole nell'attestazione. L'attestazione deve contenere un elenco e, se un utente appartiene a più gruppi, l'applicazione assegnerà il ruolo corrispondente al livello di accesso più alto. Se nessun gruppo corrisponde, l'accesso verrà negato.",
"LabelOpenRSSFeed": "Apri RSS Feed", "LabelOpenRSSFeed": "Apri RSS Feed",
"LabelOverwrite": "Sovrascrivi", "LabelOverwrite": "Sovrascrivi",
"LabelPaginationPageXOfY": "Pagina {0} di {1}",
"LabelPassword": "Password", "LabelPassword": "Password",
"LabelPath": "Percorso", "LabelPath": "Percorso",
"LabelPermanent": "Permanente", "LabelPermanent": "Permanente",
"LabelPermissionsAccessAllLibraries": "Può accedere a tutte le librerie", "LabelPermissionsAccessAllLibraries": "Può accedere a tutte le librerie",
"LabelPermissionsAccessAllTags": "Può accedere a tutti i tag", "LabelPermissionsAccessAllTags": "Può accedere a tutti i tag",
"LabelPermissionsAccessExplicitContent": "Può accedere a contenuti espliciti", "LabelPermissionsAccessExplicitContent": "Può accedere a contenuti espliciti",
"LabelPermissionsCreateEreader": "Può creare un e-reader",
"LabelPermissionsDelete": "Può Cancellare", "LabelPermissionsDelete": "Può Cancellare",
"LabelPermissionsDownload": "Può Scaricare", "LabelPermissionsDownload": "Può Scaricare",
"LabelPermissionsUpdate": "Può Aggiornare", "LabelPermissionsUpdate": "Può Aggiornare",
@ -500,18 +517,24 @@
"LabelRedo": "Rifai", "LabelRedo": "Rifai",
"LabelRegion": "Regione", "LabelRegion": "Regione",
"LabelReleaseDate": "Data Release", "LabelReleaseDate": "Data Release",
"LabelRemoveAllMetadataAbs": "Remuovi tutti i metadata.abs files",
"LabelRemoveAllMetadataJson": "Rimuovi tutti i metadata.json files",
"LabelRemoveCover": "Rimuovi cover", "LabelRemoveCover": "Rimuovi cover",
"LabelRemoveMetadataFile": "Rimuovi i file metadata nella cartella della libreria",
"LabelRemoveMetadataFileHelp": "Rimuovi tutti i file metadata.json e i file metadata.abs nelle tue {0} cartelle.",
"LabelRowsPerPage": "Righe per pagina", "LabelRowsPerPage": "Righe per pagina",
"LabelSearchTerm": "Ricerca", "LabelSearchTerm": "Ricerca",
"LabelSearchTitle": "Cerca Titolo", "LabelSearchTitle": "Cerca Titolo",
"LabelSearchTitleOrASIN": "Cerca titolo o ASIN", "LabelSearchTitleOrASIN": "Cerca titolo o ASIN",
"LabelSeason": "Stagione", "LabelSeason": "Stagione",
"LabelSeasonNumber": "Stagione #{0}",
"LabelSelectAll": "Seleziona tutto", "LabelSelectAll": "Seleziona tutto",
"LabelSelectAllEpisodes": "Seleziona tutti gli Episodi", "LabelSelectAllEpisodes": "Seleziona tutti gli Episodi",
"LabelSelectEpisodesShowing": "Selezionati {0} episodi da visualizzare", "LabelSelectEpisodesShowing": "Selezionati {0} episodi da visualizzare",
"LabelSelectUsers": "Selezione Utenti", "LabelSelectUsers": "Selezione Utenti",
"LabelSendEbookToDevice": "Invia il libro a...", "LabelSendEbookToDevice": "Invia il libro a...",
"LabelSequence": "Sequenza", "LabelSequence": "Sequenza",
"LabelSerial": "Seriale",
"LabelSeries": "Serie", "LabelSeries": "Serie",
"LabelSeriesName": "Nome Serie", "LabelSeriesName": "Nome Serie",
"LabelSeriesProgress": "Cominciato", "LabelSeriesProgress": "Cominciato",
@ -540,6 +563,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Le serie che hanno un solo libro saranno nascoste dalla pagina della serie e dagli scaffali della home page.", "LabelSettingsHideSingleBookSeriesHelp": "Le serie che hanno un solo libro saranno nascoste dalla pagina della serie e dagli scaffali della home page.",
"LabelSettingsHomePageBookshelfView": "Home page con sfondo legno", "LabelSettingsHomePageBookshelfView": "Home page con sfondo legno",
"LabelSettingsLibraryBookshelfView": "Libreria con sfondo legno", "LabelSettingsLibraryBookshelfView": "Libreria con sfondo legno",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "La percentuale di completamento è maggiore di",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Il tempo rimanente è inferiore a (secondi)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Contrassegna l'elemento multimediale come terminato quando",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Salta i libri precedenti nella serie Continua", "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Salta i libri precedenti nella serie Continua",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Lo scaffale della home page Continua serie mostra il primo libro non iniziato della serie che ha almeno un libro finito e nessun libro in corso. Abilitando questa impostazione le serie continueranno dal libro completato più lontano invece che dal primo libro non iniziato.", "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Lo scaffale della home page Continua serie mostra il primo libro non iniziato della serie che ha almeno un libro finito e nessun libro in corso. Abilitando questa impostazione le serie continueranno dal libro completato più lontano invece che dal primo libro non iniziato.",
"LabelSettingsParseSubtitles": "Analizza sottotitoli", "LabelSettingsParseSubtitles": "Analizza sottotitoli",
@ -604,6 +630,7 @@
"LabelTimeDurationXMinutes": "{0} minuti", "LabelTimeDurationXMinutes": "{0} minuti",
"LabelTimeDurationXSeconds": "{0} secondi", "LabelTimeDurationXSeconds": "{0} secondi",
"LabelTimeInMinutes": "Tempo in minuti", "LabelTimeInMinutes": "Tempo in minuti",
"LabelTimeLeft": "{0} sinistra",
"LabelTimeListened": "Tempo di Ascolto", "LabelTimeListened": "Tempo di Ascolto",
"LabelTimeListenedToday": "Tempo di Ascolto Oggi", "LabelTimeListenedToday": "Tempo di Ascolto Oggi",
"LabelTimeRemaining": "{0} rimanente", "LabelTimeRemaining": "{0} rimanente",
@ -624,6 +651,7 @@
"LabelTracksMultiTrack": "Multi-traccia", "LabelTracksMultiTrack": "Multi-traccia",
"LabelTracksNone": "Nessuna traccia", "LabelTracksNone": "Nessuna traccia",
"LabelTracksSingleTrack": "Traccia-singola", "LabelTracksSingleTrack": "Traccia-singola",
"LabelTrailer": "Trailer",
"LabelType": "Tipo", "LabelType": "Tipo",
"LabelUnabridged": "Integrale", "LabelUnabridged": "Integrale",
"LabelUndo": "Annulla", "LabelUndo": "Annulla",
@ -640,6 +668,7 @@
"LabelUseAdvancedOptions": "Usa le opzioni avanzate", "LabelUseAdvancedOptions": "Usa le opzioni avanzate",
"LabelUseChapterTrack": "Usa il Capitolo della Traccia", "LabelUseChapterTrack": "Usa il Capitolo della Traccia",
"LabelUseFullTrack": "Usa la traccia totale", "LabelUseFullTrack": "Usa la traccia totale",
"LabelUseZeroForUnlimited": "Usa 0 per illimitato",
"LabelUser": "Utente", "LabelUser": "Utente",
"LabelUsername": "Nome utente", "LabelUsername": "Nome utente",
"LabelValue": "Valore", "LabelValue": "Valore",
@ -698,6 +727,7 @@
"MessageConfirmPurgeCache": "L'eliminazione della cache eliminerà l'intera directory dei <code>/metadata/cache</code>. <br /><br />Sei sicuro di voler rimuovere la directory della cache?", "MessageConfirmPurgeCache": "L'eliminazione della cache eliminerà l'intera directory dei <code>/metadata/cache</code>. <br /><br />Sei sicuro di voler rimuovere la directory della cache?",
"MessageConfirmPurgeItemsCache": "L'eliminazione della cache degli elementi eliminerà l'intera directory <code>/metadata/cache/oggetti</code>.<br />Sei sicuro?", "MessageConfirmPurgeItemsCache": "L'eliminazione della cache degli elementi eliminerà l'intera directory <code>/metadata/cache/oggetti</code>.<br />Sei sicuro?",
"MessageConfirmQuickEmbed": "Attenzione! L'incorporamento rapido non eseguirà il backup dei file audio. Assicurati di avere un backup dei tuoi file audio. <br><br>Vuoi Continuare?", "MessageConfirmQuickEmbed": "Attenzione! L'incorporamento rapido non eseguirà il backup dei file audio. Assicurati di avere un backup dei tuoi file audio. <br><br>Vuoi Continuare?",
"MessageConfirmQuickMatchEpisodes": "Gli episodi di corrispondenza rapida sovrascriveranno i dettagli se viene trovata una corrispondenza. Saranno aggiornati solo gli episodi non corrispondenti. Sei sicuro?",
"MessageConfirmReScanLibraryItems": "Sei sicuro di voler ripetere la scansione? {0} oggetti?", "MessageConfirmReScanLibraryItems": "Sei sicuro di voler ripetere la scansione? {0} oggetti?",
"MessageConfirmRemoveAllChapters": "Sei sicuro di voler rimuovere tutti i capitoli?", "MessageConfirmRemoveAllChapters": "Sei sicuro di voler rimuovere tutti i capitoli?",
"MessageConfirmRemoveAuthor": "Sei sicuro di voler rimuovere l'autore? \"{0}\"?", "MessageConfirmRemoveAuthor": "Sei sicuro di voler rimuovere l'autore? \"{0}\"?",
@ -705,6 +735,7 @@
"MessageConfirmRemoveEpisode": "Sei sicuro di voler rimuovere l'episodio \"{0}\"?", "MessageConfirmRemoveEpisode": "Sei sicuro di voler rimuovere l'episodio \"{0}\"?",
"MessageConfirmRemoveEpisodes": "Sei sicuro di voler rimuovere {0} episodi?", "MessageConfirmRemoveEpisodes": "Sei sicuro di voler rimuovere {0} episodi?",
"MessageConfirmRemoveListeningSessions": "Sei sicuro di voler rimuovere {0} sessioni di Ascolto?", "MessageConfirmRemoveListeningSessions": "Sei sicuro di voler rimuovere {0} sessioni di Ascolto?",
"MessageConfirmRemoveMetadataFiles": "Vuoi davvero rimuovere tutti i metadati.{0} file nelle cartelle degli elementi della tua libreria?",
"MessageConfirmRemoveNarrator": "Sei sicuro di voler rimuovere il narratore \"{0}\"?", "MessageConfirmRemoveNarrator": "Sei sicuro di voler rimuovere il narratore \"{0}\"?",
"MessageConfirmRemovePlaylist": "Sei sicuro di voler rimuovere la tua playlist \"{0}\"?", "MessageConfirmRemovePlaylist": "Sei sicuro di voler rimuovere la tua playlist \"{0}\"?",
"MessageConfirmRenameGenre": "Sei sicuro di voler rinominare il genere \"{0}\" in \"{1}\" per tutti gli oggetti?", "MessageConfirmRenameGenre": "Sei sicuro di voler rinominare il genere \"{0}\" in \"{1}\" per tutti gli oggetti?",
@ -785,6 +816,7 @@
"MessagePodcastSearchField": "Inserisci il termine di ricerca o l'URL del feed RSS", "MessagePodcastSearchField": "Inserisci il termine di ricerca o l'URL del feed RSS",
"MessageQuickEmbedInProgress": "Incorporamento rapido in corso", "MessageQuickEmbedInProgress": "Incorporamento rapido in corso",
"MessageQuickEmbedQueue": "In coda per incorporamento rapido ({0} in coda)", "MessageQuickEmbedQueue": "In coda per incorporamento rapido ({0} in coda)",
"MessageQuickMatchAllEpisodes": "Associamento veloce di Tutti gli episodi",
"MessageQuickMatchDescription": "Compila i dettagli dell'articolo vuoto e copri con il risultato della prima corrispondenza di '{0}'. Non sovrascrive i dettagli a meno che non sia abilitata l'impostazione del server \"Preferisci metadati corrispondenti\".", "MessageQuickMatchDescription": "Compila i dettagli dell'articolo vuoto e copri con il risultato della prima corrispondenza di '{0}'. Non sovrascrive i dettagli a meno che non sia abilitata l'impostazione del server \"Preferisci metadati corrispondenti\".",
"MessageRemoveChapter": "Rimuovi Capitolo", "MessageRemoveChapter": "Rimuovi Capitolo",
"MessageRemoveEpisodes": "rimuovi {0} episodio(i)", "MessageRemoveEpisodes": "rimuovi {0} episodio(i)",
@ -883,6 +915,7 @@
"StatsYearInReview": "ANNO IN RASSEGNA", "StatsYearInReview": "ANNO IN RASSEGNA",
"ToastAccountUpdateSuccess": "Account Aggiornato", "ToastAccountUpdateSuccess": "Account Aggiornato",
"ToastAppriseUrlRequired": "È necessario immettere un indirizzo Apprise", "ToastAppriseUrlRequired": "È necessario immettere un indirizzo Apprise",
"ToastAsinRequired": "L'ASIN è obbligatorio",
"ToastAuthorImageRemoveSuccess": "Immagine Autore Rimossa", "ToastAuthorImageRemoveSuccess": "Immagine Autore Rimossa",
"ToastAuthorNotFound": "Autore\"{0}\" non trovato", "ToastAuthorNotFound": "Autore\"{0}\" non trovato",
"ToastAuthorRemoveSuccess": "Autore rimosso", "ToastAuthorRemoveSuccess": "Autore rimosso",
@ -902,6 +935,8 @@
"ToastBackupUploadSuccess": "Backup caricato", "ToastBackupUploadSuccess": "Backup caricato",
"ToastBatchDeleteFailed": "Eliminazione batch non riuscita", "ToastBatchDeleteFailed": "Eliminazione batch non riuscita",
"ToastBatchDeleteSuccess": "Eliminazione batch riuscita", "ToastBatchDeleteSuccess": "Eliminazione batch riuscita",
"ToastBatchQuickMatchFailed": "Batch Quick Match non riuscito!",
"ToastBatchQuickMatchStarted": "Avviata la ricerca rapida in batch di {0} libri!",
"ToastBatchUpdateFailed": "Batch di aggiornamento fallito", "ToastBatchUpdateFailed": "Batch di aggiornamento fallito",
"ToastBatchUpdateSuccess": "Batch di aggiornamento finito", "ToastBatchUpdateSuccess": "Batch di aggiornamento finito",
"ToastBookmarkCreateFailed": "Creazione segnalibro fallita", "ToastBookmarkCreateFailed": "Creazione segnalibro fallita",
@ -913,6 +948,7 @@
"ToastChaptersHaveErrors": "I capitoli contengono errori", "ToastChaptersHaveErrors": "I capitoli contengono errori",
"ToastChaptersMustHaveTitles": "I capitoli devono avere titoli", "ToastChaptersMustHaveTitles": "I capitoli devono avere titoli",
"ToastChaptersRemoved": "Capitoli rimossi", "ToastChaptersRemoved": "Capitoli rimossi",
"ToastChaptersUpdated": "Capitoli aggiornati",
"ToastCollectionItemsAddFailed": "l'aggiunta dell'elemento(i) alla raccolta non è riuscito", "ToastCollectionItemsAddFailed": "l'aggiunta dell'elemento(i) alla raccolta non è riuscito",
"ToastCollectionItemsAddSuccess": "L'aggiunta dell'elemento(i) alla raccolta è riuscito", "ToastCollectionItemsAddSuccess": "L'aggiunta dell'elemento(i) alla raccolta è riuscito",
"ToastCollectionItemsRemoveSuccess": "Oggetto(i) rimossi dalla Raccolta", "ToastCollectionItemsRemoveSuccess": "Oggetto(i) rimossi dalla Raccolta",
@ -930,11 +966,14 @@
"ToastEncodeCancelSucces": "Codifica annullata", "ToastEncodeCancelSucces": "Codifica annullata",
"ToastEpisodeDownloadQueueClearFailed": "Impossibile cancellare la coda", "ToastEpisodeDownloadQueueClearFailed": "Impossibile cancellare la coda",
"ToastEpisodeDownloadQueueClearSuccess": "Coda di download degli episodi cancellata", "ToastEpisodeDownloadQueueClearSuccess": "Coda di download degli episodi cancellata",
"ToastEpisodeUpdateSuccess": "{0} episodi aggiornati",
"ToastErrorCannotShare": "Impossibile condividere in modo nativo su questo dispositivo", "ToastErrorCannotShare": "Impossibile condividere in modo nativo su questo dispositivo",
"ToastFailedToLoadData": "Impossibile caricare i dati", "ToastFailedToLoadData": "Impossibile caricare i dati",
"ToastFailedToMatch": "Impossibile abbinare",
"ToastFailedToShare": "Impossibile condividere", "ToastFailedToShare": "Impossibile condividere",
"ToastFailedToUpdate": "Non aggiornato", "ToastFailedToUpdate": "Non aggiornato",
"ToastInvalidImageUrl": "URL dell'immagine non valido", "ToastInvalidImageUrl": "URL dell'immagine non valido",
"ToastInvalidMaxEpisodesToDownload": "Numero massimo di episodi non valido da scaricare",
"ToastInvalidUrl": "URL non valido", "ToastInvalidUrl": "URL non valido",
"ToastItemCoverUpdateSuccess": "Cover aggiornata", "ToastItemCoverUpdateSuccess": "Cover aggiornata",
"ToastItemDeletedFailed": "Impossibile eliminare l'elemento", "ToastItemDeletedFailed": "Impossibile eliminare l'elemento",
@ -953,14 +992,21 @@
"ToastLibraryScanStarted": "Scansione Libreria iniziata", "ToastLibraryScanStarted": "Scansione Libreria iniziata",
"ToastLibraryUpdateSuccess": "Libreria \"{0}\" aggiornata", "ToastLibraryUpdateSuccess": "Libreria \"{0}\" aggiornata",
"ToastMatchAllAuthorsFailed": "Tutti gli autori non sono potuti essere classificati", "ToastMatchAllAuthorsFailed": "Tutti gli autori non sono potuti essere classificati",
"ToastMetadataFilesRemovedError": "Errore durante la rimozione dei metadati. {0} file",
"ToastMetadataFilesRemovedNoneFound": "Nessun metadato. {0} file trovati nella libreria",
"ToastMetadataFilesRemovedNoneRemoved": "Nessun metadato. {0} file rimossi",
"ToastMetadataFilesRemovedSuccess": "{0} metadati.{1} file rimossi",
"ToastMustHaveAtLeastOnePath": "Deve avere almeno un percorso",
"ToastNameEmailRequired": "Nome ed email sono obbligatori", "ToastNameEmailRequired": "Nome ed email sono obbligatori",
"ToastNameRequired": "Il nome è obbligatorio", "ToastNameRequired": "Il nome è obbligatorio",
"ToastNewEpisodesFound": "{0} nuovi episodi trovati",
"ToastNewUserCreatedFailed": "Impossibile creare l'account: \"{0}\"", "ToastNewUserCreatedFailed": "Impossibile creare l'account: \"{0}\"",
"ToastNewUserCreatedSuccess": "Nuovo account creato", "ToastNewUserCreatedSuccess": "Nuovo account creato",
"ToastNewUserLibraryError": "È necessario selezionare almeno una libreria", "ToastNewUserLibraryError": "È necessario selezionare almeno una libreria",
"ToastNewUserPasswordError": "Deve avere una password, solo l'utente root può avere una password vuota", "ToastNewUserPasswordError": "Deve avere una password, solo l'utente root può avere una password vuota",
"ToastNewUserTagError": "Devi selezionare almeno un tag", "ToastNewUserTagError": "Devi selezionare almeno un tag",
"ToastNewUserUsernameError": "Inserisci un nome utente", "ToastNewUserUsernameError": "Inserisci un nome utente",
"ToastNoNewEpisodesFound": "Nessun nuovo episodio trovato",
"ToastNoUpdatesNecessary": "Nessun aggiornamento necessario", "ToastNoUpdatesNecessary": "Nessun aggiornamento necessario",
"ToastNotificationCreateFailed": "Impossibile creare la notifica", "ToastNotificationCreateFailed": "Impossibile creare la notifica",
"ToastNotificationDeleteFailed": "Impossibile eliminare la notifica", "ToastNotificationDeleteFailed": "Impossibile eliminare la notifica",
@ -979,6 +1025,7 @@
"ToastPodcastGetFeedFailed": "Impossibile ottenere il feed del podcast", "ToastPodcastGetFeedFailed": "Impossibile ottenere il feed del podcast",
"ToastPodcastNoEpisodesInFeed": "Nessun episodio trovato nel feed RSS", "ToastPodcastNoEpisodesInFeed": "Nessun episodio trovato nel feed RSS",
"ToastPodcastNoRssFeed": "Il podcast non ha un feed RSS", "ToastPodcastNoRssFeed": "Il podcast non ha un feed RSS",
"ToastProgressIsNotBeingSynced": "L'avanzamento non è sincronizzato, riavviare la riproduzione",
"ToastProviderCreatedFailed": "Impossibile aggiungere il provider", "ToastProviderCreatedFailed": "Impossibile aggiungere il provider",
"ToastProviderCreatedSuccess": "Aggiunto nuovo provider", "ToastProviderCreatedSuccess": "Aggiunto nuovo provider",
"ToastProviderNameAndUrlRequired": "Nome e URL richiesti", "ToastProviderNameAndUrlRequired": "Nome e URL richiesti",
@ -1005,6 +1052,7 @@
"ToastSessionCloseFailed": "Disconnessione Fallita", "ToastSessionCloseFailed": "Disconnessione Fallita",
"ToastSessionDeleteFailed": "Errore eliminazione sessione", "ToastSessionDeleteFailed": "Errore eliminazione sessione",
"ToastSessionDeleteSuccess": "Sessione cancellata", "ToastSessionDeleteSuccess": "Sessione cancellata",
"ToastSleepTimerDone": "Timer di spegnimento eseguito... zZzzZz",
"ToastSlugMustChange": "Lo slug contiene caratteri non validi", "ToastSlugMustChange": "Lo slug contiene caratteri non validi",
"ToastSlugRequired": "È richiesto lo slug", "ToastSlugRequired": "È richiesto lo slug",
"ToastSocketConnected": "Socket connesso", "ToastSocketConnected": "Socket connesso",

View File

@ -229,7 +229,7 @@
"LabelAlreadyInYourLibrary": "Уже в Вашей библиотеке", "LabelAlreadyInYourLibrary": "Уже в Вашей библиотеке",
"LabelApiToken": "Токен API", "LabelApiToken": "Токен API",
"LabelAppend": "Добавить", "LabelAppend": "Добавить",
"LabelAudioBitrate": "Битрейт аудио (напр. 128k)", "LabelAudioBitrate": "Битрейт (напр. 128k)",
"LabelAudioChannels": "Аудиоканалы (1 или 2)", "LabelAudioChannels": "Аудиоканалы (1 или 2)",
"LabelAudioCodec": "Аудиокодек", "LabelAudioCodec": "Аудиокодек",
"LabelAuthor": "Автор", "LabelAuthor": "Автор",
@ -366,7 +366,7 @@
"LabelHardDeleteFile": "Жесткое удаление файла", "LabelHardDeleteFile": "Жесткое удаление файла",
"LabelHasEbook": "Есть e-книга", "LabelHasEbook": "Есть e-книга",
"LabelHasSupplementaryEbook": "Есть дополнительная e-книга", "LabelHasSupplementaryEbook": "Есть дополнительная e-книга",
"LabelHideSubtitles": "Скрыть субтитры", "LabelHideSubtitles": "Скрыть серии",
"LabelHighestPriority": "Наивысший приоритет", "LabelHighestPriority": "Наивысший приоритет",
"LabelHost": "Хост", "LabelHost": "Хост",
"LabelHour": "Часы", "LabelHour": "Часы",
@ -496,8 +496,8 @@
"LabelPubDate": "Дата публикации", "LabelPubDate": "Дата публикации",
"LabelPublishYear": "Год публикации", "LabelPublishYear": "Год публикации",
"LabelPublishedDate": "Опубликовано {0}", "LabelPublishedDate": "Опубликовано {0}",
"LabelPublishedDecade": "Опубликованное десятилетие", "LabelPublishedDecade": "Декада публикации",
"LabelPublishedDecades": "Опубликованные десятилетия", "LabelPublishedDecades": "Декады публикации",
"LabelPublisher": "Издатель", "LabelPublisher": "Издатель",
"LabelPublishers": "Издатели", "LabelPublishers": "Издатели",
"LabelRSSFeedCustomOwnerEmail": "Пользовательский Email владельца", "LabelRSSFeedCustomOwnerEmail": "Пользовательский Email владельца",
@ -588,7 +588,7 @@
"LabelShareURL": "Общедоступный URL", "LabelShareURL": "Общедоступный URL",
"LabelShowAll": "Показать все", "LabelShowAll": "Показать все",
"LabelShowSeconds": "Отображать секунды", "LabelShowSeconds": "Отображать секунды",
"LabelShowSubtitles": "Показать субтитры", "LabelShowSubtitles": "Показать серии",
"LabelSize": "Размер", "LabelSize": "Размер",
"LabelSleepTimer": "Таймер сна", "LabelSleepTimer": "Таймер сна",
"LabelSlug": "Слизень", "LabelSlug": "Слизень",

View File

@ -136,7 +136,7 @@
"HeaderEmailSettings": "Nastavitve e-pošte", "HeaderEmailSettings": "Nastavitve e-pošte",
"HeaderEpisodes": "Epizode", "HeaderEpisodes": "Epizode",
"HeaderEreaderDevices": "E-bralniki", "HeaderEreaderDevices": "E-bralniki",
"HeaderEreaderSettings": "Nastavitve ebralnika", "HeaderEreaderSettings": "Nastavitve e-bralnika",
"HeaderFiles": "Datoteke", "HeaderFiles": "Datoteke",
"HeaderFindChapters": "Najdi poglavja", "HeaderFindChapters": "Najdi poglavja",
"HeaderIgnoredFiles": "Prezrte datoteke", "HeaderIgnoredFiles": "Prezrte datoteke",
@ -366,7 +366,7 @@
"LabelHardDeleteFile": "Trdo brisanje datoteke", "LabelHardDeleteFile": "Trdo brisanje datoteke",
"LabelHasEbook": "Ima e-knjigo", "LabelHasEbook": "Ima e-knjigo",
"LabelHasSupplementaryEbook": "Ima dodatno e-knjigo", "LabelHasSupplementaryEbook": "Ima dodatno e-knjigo",
"LabelHideSubtitles": "Skrij podnapise", "LabelHideSubtitles": "Skrij podnaslove",
"LabelHighestPriority": "Najvišja prioriteta", "LabelHighestPriority": "Najvišja prioriteta",
"LabelHost": "Gostitelj", "LabelHost": "Gostitelj",
"LabelHour": "Ura", "LabelHour": "Ura",
@ -407,7 +407,7 @@
"LabelLibraryItem": "Element knjižnice", "LabelLibraryItem": "Element knjižnice",
"LabelLibraryName": "Ime knjižnice", "LabelLibraryName": "Ime knjižnice",
"LabelLimit": "Omejitev", "LabelLimit": "Omejitev",
"LabelLineSpacing": "Razmik med vrsticami", "LabelLineSpacing": "Vrstični razmak",
"LabelListenAgain": "Poslušaj znova", "LabelListenAgain": "Poslušaj znova",
"LabelLogLevelDebug": "Odpravljanje napak", "LabelLogLevelDebug": "Odpravljanje napak",
"LabelLogLevelInfo": "Info", "LabelLogLevelInfo": "Info",
@ -568,8 +568,8 @@
"LabelSettingsLibraryMarkAsFinishedWhen": "Označi medijski element kot končan, ko", "LabelSettingsLibraryMarkAsFinishedWhen": "Označi medijski element kot končan, ko",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Preskoči prejšnje knjige v nadaljevanju serije", "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Preskoči prejšnje knjige v nadaljevanju serije",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Polica z domačo stranjo Nadaljuj serijo prikazuje prvo nezačeto knjigo v seriji, ki ima vsaj eno dokončano knjigo in ni nobene knjige v teku. Če omogočite to nastavitev, se bo serija nadaljevala od najbolj dokončane knjige namesto od prve nezačete knjige.", "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Polica z domačo stranjo Nadaljuj serijo prikazuje prvo nezačeto knjigo v seriji, ki ima vsaj eno dokončano knjigo in ni nobene knjige v teku. Če omogočite to nastavitev, se bo serija nadaljevala od najbolj dokončane knjige namesto od prve nezačete knjige.",
"LabelSettingsParseSubtitles": "Uporabi podnapise", "LabelSettingsParseSubtitles": "Razčleni podnaslove",
"LabelSettingsParseSubtitlesHelp": "Izvleci podnapise iz imen map zvočnih knjig.<br>Podnapis mora biti ločen z \" - \"<br>npr. \"Naslov knjige tu podnapis\" ima podnapis \"tu podnapis\"", "LabelSettingsParseSubtitlesHelp": "Izvleci padnaslove iz imen map zvočnih knjig.<br>Podnaslov mora biti ločen z \" - \"<br>npr. \"Naslov knjige tu podnaslove\" ima podnaslov \"tu podnaslov\"",
"LabelSettingsPreferMatchedMetadata": "Prednost imajo ujemajoči se metapodatki", "LabelSettingsPreferMatchedMetadata": "Prednost imajo ujemajoči se metapodatki",
"LabelSettingsPreferMatchedMetadataHelp": "Pri uporabi hitrega ujemanja bodo ujemajoči se podatki preglasili podrobnosti artikla. Hitro ujemanje bo privzeto izpolnil samo manjkajoče podrobnosti.", "LabelSettingsPreferMatchedMetadataHelp": "Pri uporabi hitrega ujemanja bodo ujemajoči se podatki preglasili podrobnosti artikla. Hitro ujemanje bo privzeto izpolnil samo manjkajoče podrobnosti.",
"LabelSettingsSkipMatchingBooksWithASIN": "Preskoči ujemajoče se knjige, ki že imajo ASIN", "LabelSettingsSkipMatchingBooksWithASIN": "Preskoči ujemajoče se knjige, ki že imajo ASIN",
@ -588,7 +588,7 @@
"LabelShareURL": "Deli URL", "LabelShareURL": "Deli URL",
"LabelShowAll": "Prikaži vse", "LabelShowAll": "Prikaži vse",
"LabelShowSeconds": "Prikaži sekunde", "LabelShowSeconds": "Prikaži sekunde",
"LabelShowSubtitles": "Prikaži podnapise", "LabelShowSubtitles": "Prikaži podnaslove",
"LabelSize": "Velikost", "LabelSize": "Velikost",
"LabelSleepTimer": "Časovnik za spanje", "LabelSleepTimer": "Časovnik za spanje",
"LabelSlug": "Slug", "LabelSlug": "Slug",
@ -611,7 +611,7 @@
"LabelStatsOverallDays": "Skupaj dnevi", "LabelStatsOverallDays": "Skupaj dnevi",
"LabelStatsOverallHours": "Skupaj ur", "LabelStatsOverallHours": "Skupaj ur",
"LabelStatsWeekListening": "Tednov poslušanja", "LabelStatsWeekListening": "Tednov poslušanja",
"LabelSubtitle": "Podnapis", "LabelSubtitle": "Podnaslov",
"LabelSupportedFileTypes": "Podprte vrste datotek", "LabelSupportedFileTypes": "Podprte vrste datotek",
"LabelTag": "Oznaka", "LabelTag": "Oznaka",
"LabelTags": "Oznake", "LabelTags": "Oznake",
@ -625,7 +625,7 @@
"LabelTheme": "Tema", "LabelTheme": "Tema",
"LabelThemeDark": "Temna", "LabelThemeDark": "Temna",
"LabelThemeLight": "Svetla", "LabelThemeLight": "Svetla",
"LabelTimeBase": "Odvisna od časa", "LabelTimeBase": "Osnovni čas",
"LabelTimeDurationXHours": "{0} ur", "LabelTimeDurationXHours": "{0} ur",
"LabelTimeDurationXMinutes": "{0} minut", "LabelTimeDurationXMinutes": "{0} minut",
"LabelTimeDurationXSeconds": "{0} sekund", "LabelTimeDurationXSeconds": "{0} sekund",
@ -773,7 +773,7 @@
"MessageMarkAllEpisodesNotFinished": "Označi vse epizode kot nedokončane", "MessageMarkAllEpisodesNotFinished": "Označi vse epizode kot nedokončane",
"MessageMarkAsFinished": "Označi kot dokončano", "MessageMarkAsFinished": "Označi kot dokončano",
"MessageMarkAsNotFinished": "Označi kot nedokončano", "MessageMarkAsNotFinished": "Označi kot nedokončano",
"MessageMatchBooksDescription": "bo poskušal povezati knjige v knjižnici s knjigo izbranega ponudnika iskanja in izpolniti prazne podatke in naslovnico. Ne prepisuje čez obstoječe podatke.", "MessageMatchBooksDescription": "bo poskušalo povezati knjige v knjižnici s knjigo izbranega ponudnika iskanja in izpolniti prazne podatke in naslovnico. Ne prepisuje čez obstoječe podatke.",
"MessageNoAudioTracks": "Ni zvočnih posnetkov", "MessageNoAudioTracks": "Ni zvočnih posnetkov",
"MessageNoAuthors": "Brez avtorjev", "MessageNoAuthors": "Brez avtorjev",
"MessageNoBackups": "Brez varnostnih kopij", "MessageNoBackups": "Brez varnostnih kopij",
@ -902,7 +902,7 @@
"StatsBooksFinishedThisYear": "Nekaj knjig, ki so bile dokončane letos…", "StatsBooksFinishedThisYear": "Nekaj knjig, ki so bile dokončane letos…",
"StatsBooksListenedTo": "poslušanih knjig", "StatsBooksListenedTo": "poslušanih knjig",
"StatsCollectionGrewTo": "Vaša zbirka knjig se je povečala na …", "StatsCollectionGrewTo": "Vaša zbirka knjig se je povečala na …",
"StatsSessions": "sej", "StatsSessions": "seje",
"StatsSpentListening": "porabil za poslušanje", "StatsSpentListening": "porabil za poslušanje",
"StatsTopAuthor": "TOP AVTOR", "StatsTopAuthor": "TOP AVTOR",
"StatsTopAuthors": "TOP AVTORJI", "StatsTopAuthors": "TOP AVTORJI",

View File

@ -19,6 +19,7 @@
"ButtonChooseFiles": "Обрати файли", "ButtonChooseFiles": "Обрати файли",
"ButtonClearFilter": "Очистити фільтр", "ButtonClearFilter": "Очистити фільтр",
"ButtonCloseFeed": "Закрити стрічку", "ButtonCloseFeed": "Закрити стрічку",
"ButtonCloseSession": "Закрити відкритий сеанс",
"ButtonCollections": "Добірки", "ButtonCollections": "Добірки",
"ButtonConfigureScanner": "Налаштувати сканер", "ButtonConfigureScanner": "Налаштувати сканер",
"ButtonCreate": "Створити", "ButtonCreate": "Створити",
@ -28,6 +29,9 @@
"ButtonEdit": "Редагувати", "ButtonEdit": "Редагувати",
"ButtonEditChapters": "Редагувати глави", "ButtonEditChapters": "Редагувати глави",
"ButtonEditPodcast": "Редагувати подкаст", "ButtonEditPodcast": "Редагувати подкаст",
"ButtonEnable": "Увімкнути",
"ButtonFireAndFail": "Вогонь і невдача",
"ButtonFireOnTest": "Випробування на вогнестійкість",
"ButtonForceReScan": "Примусово сканувати", "ButtonForceReScan": "Примусово сканувати",
"ButtonFullPath": "Повний шлях", "ButtonFullPath": "Повний шлях",
"ButtonHide": "Приховати", "ButtonHide": "Приховати",
@ -46,19 +50,23 @@
"ButtonNevermind": "Скасувати", "ButtonNevermind": "Скасувати",
"ButtonNext": "Наступний", "ButtonNext": "Наступний",
"ButtonNextChapter": "Наступна глава", "ButtonNextChapter": "Наступна глава",
"ButtonNextItemInQueue": "Наступний елемент у черзі",
"ButtonOk": "Гаразд", "ButtonOk": "Гаразд",
"ButtonOpenFeed": "Відкрити стрічку", "ButtonOpenFeed": "Відкрити стрічку",
"ButtonOpenManager": "Відкрити менеджер", "ButtonOpenManager": "Відкрити менеджер",
"ButtonPause": "Пауза", "ButtonPause": "Пауза",
"ButtonPlay": "Слухати", "ButtonPlay": "Слухати",
"ButtonPlayAll": "Відтворити все",
"ButtonPlaying": "Відтворюється", "ButtonPlaying": "Відтворюється",
"ButtonPlaylists": "Списки відтворення", "ButtonPlaylists": "Списки відтворення",
"ButtonPrevious": "Попередній", "ButtonPrevious": "Попередній",
"ButtonPreviousChapter": "Попередня глава", "ButtonPreviousChapter": "Попередня глава",
"ButtonProbeAudioFile": "Перевірити аудіофайл",
"ButtonPurgeAllCache": "Очистити весь кеш", "ButtonPurgeAllCache": "Очистити весь кеш",
"ButtonPurgeItemsCache": "Очистити кеш елементів", "ButtonPurgeItemsCache": "Очистити кеш елементів",
"ButtonQueueAddItem": "Додати до черги", "ButtonQueueAddItem": "Додати до черги",
"ButtonQueueRemoveItem": "Вилучити з черги", "ButtonQueueRemoveItem": "Вилучити з черги",
"ButtonQuickEmbed": "Швидке вбудовування",
"ButtonQuickEmbedMetadata": "Швидко вбудувати метадані", "ButtonQuickEmbedMetadata": "Швидко вбудувати метадані",
"ButtonQuickMatch": "Швидкий пошук", "ButtonQuickMatch": "Швидкий пошук",
"ButtonReScan": "Пересканувати", "ButtonReScan": "Пересканувати",
@ -92,6 +100,7 @@
"ButtonStats": "Статистика", "ButtonStats": "Статистика",
"ButtonSubmit": "Надіслати", "ButtonSubmit": "Надіслати",
"ButtonTest": "Перевірити", "ButtonTest": "Перевірити",
"ButtonUnlinkOpenId": "Вимкнути OpenID",
"ButtonUpload": "Завантажити", "ButtonUpload": "Завантажити",
"ButtonUploadBackup": "Завантажити резервну копію", "ButtonUploadBackup": "Завантажити резервну копію",
"ButtonUploadCover": "Завантажити обкладинку", "ButtonUploadCover": "Завантажити обкладинку",
@ -104,6 +113,7 @@
"ErrorUploadFetchMetadataNoResults": "Не вдалося отримати метадані — спробуйте оновити заголовок та/або автора", "ErrorUploadFetchMetadataNoResults": "Не вдалося отримати метадані — спробуйте оновити заголовок та/або автора",
"ErrorUploadLacksTitle": "Назва обов'язкова", "ErrorUploadLacksTitle": "Назва обов'язкова",
"HeaderAccount": "Профіль", "HeaderAccount": "Профіль",
"HeaderAddCustomMetadataProvider": "Додати користувацький постачальник метаданих",
"HeaderAdvanced": "Розширені", "HeaderAdvanced": "Розширені",
"HeaderAppriseNotificationSettings": "Налаштування сповіщень Apprise", "HeaderAppriseNotificationSettings": "Налаштування сповіщень Apprise",
"HeaderAudioTracks": "Аудіодоріжки", "HeaderAudioTracks": "Аудіодоріжки",
@ -149,8 +159,11 @@
"HeaderMetadataToEmbed": "Вбудувати метадані", "HeaderMetadataToEmbed": "Вбудувати метадані",
"HeaderNewAccount": "Новий профіль", "HeaderNewAccount": "Новий профіль",
"HeaderNewLibrary": "Нова бібліотека", "HeaderNewLibrary": "Нова бібліотека",
"HeaderNotificationCreate": "Створити сповіщення",
"HeaderNotificationUpdate": "Оновити сповіщення",
"HeaderNotifications": "Сповіщення", "HeaderNotifications": "Сповіщення",
"HeaderOpenIDConnectAuthentication": "Автентифікація OpenID Connect", "HeaderOpenIDConnectAuthentication": "Автентифікація OpenID Connect",
"HeaderOpenListeningSessions": "Відкриті сеанси прослуховування",
"HeaderOpenRSSFeed": "Відкрити RSS-канал", "HeaderOpenRSSFeed": "Відкрити RSS-канал",
"HeaderOtherFiles": "Інші файли", "HeaderOtherFiles": "Інші файли",
"HeaderPasswordAuthentication": "Автентифікація за паролем", "HeaderPasswordAuthentication": "Автентифікація за паролем",
@ -168,6 +181,7 @@
"HeaderRemoveEpisodes": "Видалити епізодів: {0}", "HeaderRemoveEpisodes": "Видалити епізодів: {0}",
"HeaderSavedMediaProgress": "Збережений прогрес медіа", "HeaderSavedMediaProgress": "Збережений прогрес медіа",
"HeaderSchedule": "Розклад", "HeaderSchedule": "Розклад",
"HeaderScheduleEpisodeDownloads": "Запланувати автоматичне завантаження епізодів",
"HeaderScheduleLibraryScans": "Розклад автосканування бібліотеки", "HeaderScheduleLibraryScans": "Розклад автосканування бібліотеки",
"HeaderSession": "Сеанс", "HeaderSession": "Сеанс",
"HeaderSetBackupSchedule": "Встановити розклад резервного копіювання", "HeaderSetBackupSchedule": "Встановити розклад резервного копіювання",
@ -206,13 +220,18 @@
"LabelAddToPlaylist": "Додати до списку відтворення", "LabelAddToPlaylist": "Додати до списку відтворення",
"LabelAddToPlaylistBatch": "Додано елементів у список відтворення: {0}", "LabelAddToPlaylistBatch": "Додано елементів у список відтворення: {0}",
"LabelAddedAt": "Дата додавання", "LabelAddedAt": "Дата додавання",
"LabelAddedDate": "Додано {0}",
"LabelAdminUsersOnly": "Тільки для адміністраторів", "LabelAdminUsersOnly": "Тільки для адміністраторів",
"LabelAll": "Усе", "LabelAll": "Усе",
"LabelAllUsers": "Усі користувачі", "LabelAllUsers": "Усі користувачі",
"LabelAllUsersExcludingGuests": "Усі, крім гостей", "LabelAllUsersExcludingGuests": "Усі, крім гостей",
"LabelAllUsersIncludingGuests": "Усі, включно з гостями", "LabelAllUsersIncludingGuests": "Усі, включно з гостями",
"LabelAlreadyInYourLibrary": "Вже у вашій бібліотеці", "LabelAlreadyInYourLibrary": "Вже у вашій бібліотеці",
"LabelApiToken": "Токен API",
"LabelAppend": "Додати", "LabelAppend": "Додати",
"LabelAudioBitrate": "Бітрейт аудіо (напр. 128k)",
"LabelAudioChannels": "Канали аудіо (1 або 2)",
"LabelAudioCodec": "Аудіокодек",
"LabelAuthor": "Автор", "LabelAuthor": "Автор",
"LabelAuthorFirstLast": "Автор (за ім'ям)", "LabelAuthorFirstLast": "Автор (за ім'ям)",
"LabelAuthorLastFirst": "Автор (за прізвищем)", "LabelAuthorLastFirst": "Автор (за прізвищем)",
@ -225,6 +244,7 @@
"LabelAutoRegister": "Автореєстрація", "LabelAutoRegister": "Автореєстрація",
"LabelAutoRegisterDescription": "Автоматично створювати нових користувачів після входу", "LabelAutoRegisterDescription": "Автоматично створювати нових користувачів після входу",
"LabelBackToUser": "Повернутися до користувача", "LabelBackToUser": "Повернутися до користувача",
"LabelBackupAudioFiles": "Резервне копіювання аудіофайлів",
"LabelBackupLocation": "Розташування резервних копій", "LabelBackupLocation": "Розташування резервних копій",
"LabelBackupsEnableAutomaticBackups": "Автоматичне резервне копіювання", "LabelBackupsEnableAutomaticBackups": "Автоматичне резервне копіювання",
"LabelBackupsEnableAutomaticBackupsHelp": "Резервні копії збережено у /metadata/backups", "LabelBackupsEnableAutomaticBackupsHelp": "Резервні копії збережено у /metadata/backups",
@ -233,18 +253,22 @@
"LabelBackupsNumberToKeep": "Кількість резервних копій", "LabelBackupsNumberToKeep": "Кількість резервних копій",
"LabelBackupsNumberToKeepHelp": "Лиш 1 резервну копію буде видалено за раз, тож якщо їх багато, то вам варто видалити їх вручну.", "LabelBackupsNumberToKeepHelp": "Лиш 1 резервну копію буде видалено за раз, тож якщо їх багато, то вам варто видалити їх вручну.",
"LabelBitrate": "Бітрейт", "LabelBitrate": "Бітрейт",
"LabelBonus": "Бонус",
"LabelBooks": "Книги", "LabelBooks": "Книги",
"LabelButtonText": "Текст кнопки", "LabelButtonText": "Текст кнопки",
"LabelByAuthor": "від {0}", "LabelByAuthor": "від {0}",
"LabelChangePassword": "Змінити пароль", "LabelChangePassword": "Змінити пароль",
"LabelChannels": "Канали", "LabelChannels": "Канали",
"LabelChapterCount": "{0} Глав",
"LabelChapterTitle": "Назва глави", "LabelChapterTitle": "Назва глави",
"LabelChapters": "Глави", "LabelChapters": "Глави",
"LabelChaptersFound": "глав знайдено", "LabelChaptersFound": "глав знайдено",
"LabelClickForMoreInfo": "Натисніть, щоб дізнатися більше", "LabelClickForMoreInfo": "Натисніть, щоб дізнатися більше",
"LabelClickToUseCurrentValue": "Натисніть, щоб використати поточне значення",
"LabelClosePlayer": "Закрити програвач", "LabelClosePlayer": "Закрити програвач",
"LabelCodec": "Кодек", "LabelCodec": "Кодек",
"LabelCollapseSeries": "Згорнути серії", "LabelCollapseSeries": "Згорнути серії",
"LabelCollapseSubSeries": "Згорнути підсерії",
"LabelCollection": "Добірка", "LabelCollection": "Добірка",
"LabelCollections": "Добірки", "LabelCollections": "Добірки",
"LabelComplete": "Завершити", "LabelComplete": "Завершити",
@ -290,13 +314,28 @@
"LabelEmailSettingsTestAddress": "Тестова адреса", "LabelEmailSettingsTestAddress": "Тестова адреса",
"LabelEmbeddedCover": "Вбудована обкладинка", "LabelEmbeddedCover": "Вбудована обкладинка",
"LabelEnable": "Увімкнути", "LabelEnable": "Увімкнути",
"LabelEncodingBackupLocation": "Резервна копія ваших оригінальних аудіофайлів буде збережена в:",
"LabelEncodingChaptersNotEmbedded": "Глави не вбудовуються в багатодоріжкові аудіокниги.",
"LabelEncodingClearItemCache": "Переконайтесь, що періодично очищуєте кеш елементів.",
"LabelEncodingFinishedM4B": "Готовий M4B буде поміщений у вашу папку з аудіокнигами за адресою:",
"LabelEncodingInfoEmbedded": "Метадані будуть вбудовані в звукові доріжки всередині папки вашої аудіокниги.",
"LabelEncodingStartedNavigation": "Як тільки завдання розпочнеться, ви можете покинути цю сторінку.",
"LabelEncodingTimeWarning": "Кодування може зайняти до 30 хвилин.",
"LabelEncodingWarningAdvancedSettings": "Увага: не змінюйте ці налаштування, якщо ви не знайомі з параметрами кодування ffmpeg.",
"LabelEncodingWatcherDisabled": "Якщо у вас вимкнено спостереження за папкою, вам потрібно буде повторно відсканувати цю аудіокнигу.",
"LabelEnd": "Кінець", "LabelEnd": "Кінець",
"LabelEndOfChapter": "Кінець глави", "LabelEndOfChapter": "Кінець глави",
"LabelEpisode": "Епізод", "LabelEpisode": "Епізод",
"LabelEpisodeNotLinkedToRssFeed": "Епізод не прив'язаний до RSS-каналу",
"LabelEpisodeNumber": "Епізод #{0}",
"LabelEpisodeTitle": "Назва епізоду", "LabelEpisodeTitle": "Назва епізоду",
"LabelEpisodeType": "Тип епізоду", "LabelEpisodeType": "Тип епізоду",
"LabelEpisodeUrlFromRssFeed": "URL епізоду з RSS-каналу",
"LabelEpisodes": "Епізодов",
"LabelEpisodic": "Епізодичний",
"LabelExample": "Приклад", "LabelExample": "Приклад",
"LabelExpandSeries": "Розгорнути серії", "LabelExpandSeries": "Розгорнути серії",
"LabelExpandSubSeries": "Розгорнути підсерії",
"LabelExplicit": "Відверта", "LabelExplicit": "Відверта",
"LabelExplicitChecked": "Відверта (з прапорцем)", "LabelExplicitChecked": "Відверта (з прапорцем)",
"LabelExplicitUnchecked": "Не відверта (без прапорця)", "LabelExplicitUnchecked": "Не відверта (без прапорця)",
@ -305,7 +344,9 @@
"LabelFetchingMetadata": "Отримання метаданих", "LabelFetchingMetadata": "Отримання метаданих",
"LabelFile": "Файл", "LabelFile": "Файл",
"LabelFileBirthtime": "Дата створення", "LabelFileBirthtime": "Дата створення",
"LabelFileBornDate": "Народився {0}",
"LabelFileModified": "Дата змінення", "LabelFileModified": "Дата змінення",
"LabelFileModifiedDate": "Змінено {0}",
"LabelFilename": "Ім'я файлу", "LabelFilename": "Ім'я файлу",
"LabelFilterByUser": "Фільтрувати за користувачем", "LabelFilterByUser": "Фільтрувати за користувачем",
"LabelFindEpisodes": "Знайти епізоди", "LabelFindEpisodes": "Знайти епізоди",
@ -319,6 +360,7 @@
"LabelFontScale": "Розмір шрифту", "LabelFontScale": "Розмір шрифту",
"LabelFontStrikethrough": "Закреслений", "LabelFontStrikethrough": "Закреслений",
"LabelFormat": "Формат", "LabelFormat": "Формат",
"LabelFull": "Повний",
"LabelGenre": "Жанр", "LabelGenre": "Жанр",
"LabelGenres": "Жанри", "LabelGenres": "Жанри",
"LabelHardDeleteFile": "Остаточно видалити файл", "LabelHardDeleteFile": "Остаточно видалити файл",
@ -361,6 +403,7 @@
"LabelLess": "Менше", "LabelLess": "Менше",
"LabelLibrariesAccessibleToUser": "Бібліотеки, доступні користувачу", "LabelLibrariesAccessibleToUser": "Бібліотеки, доступні користувачу",
"LabelLibrary": "Бібліотека", "LabelLibrary": "Бібліотека",
"LabelLibraryFilterSublistEmpty": "Ні {0}",
"LabelLibraryItem": "Елемент бібліотеки", "LabelLibraryItem": "Елемент бібліотеки",
"LabelLibraryName": "Назва бібліотеки", "LabelLibraryName": "Назва бібліотеки",
"LabelLimit": "Обмеження", "LabelLimit": "Обмеження",
@ -373,6 +416,10 @@
"LabelLowestPriority": "Найнижчий пріоритет", "LabelLowestPriority": "Найнижчий пріоритет",
"LabelMatchExistingUsersBy": "Шукати наявних користувачів за", "LabelMatchExistingUsersBy": "Шукати наявних користувачів за",
"LabelMatchExistingUsersByDescription": "Використовується для підключення наявних користувачів. Після підключення користувач отримає унікальний id від вашого сервісу SSO", "LabelMatchExistingUsersByDescription": "Використовується для підключення наявних користувачів. Після підключення користувач отримає унікальний id від вашого сервісу SSO",
"LabelMaxEpisodesToDownload": "Максимальна кількість епізодів для завантаження. Використовуйте 0 для необмеженої кількості.",
"LabelMaxEpisodesToDownloadPerCheck": "Максимальна кількість нових епізодів для завантаження за перевірку",
"LabelMaxEpisodesToKeep": "Максимальна кількість епізодів для зберігання",
"LabelMaxEpisodesToKeepHelp": "Значення 0 не встановлює обмеження. Після автоматичного завантаження нового епізоду, буде видалено найстаріший епізод, якщо у вас більше ніж X епізодів. Видаляється лише 1 епізод за одне нове завантаження.",
"LabelMediaPlayer": "Програвач медіа", "LabelMediaPlayer": "Програвач медіа",
"LabelMediaType": "Тип медіа", "LabelMediaType": "Тип медіа",
"LabelMetaTag": "Метатег", "LabelMetaTag": "Метатег",
@ -418,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Ім'я OpenID claim, що містить список груп користувачів. Зазвичай їх називають <code>групами</code>. <b>Якщо налаштовано</b>, застосунок автоматично призначатиме ролі на основі членства користувача в групах, за умови, що ці групи названі в claim'і без урахування реєстру 'admin', 'user' або 'guest'. Claim мусить містити список, і якщо користувач належить до кількох груп, програма призначить йому роль, що відповідає найвищому рівню доступу. Якщо жодна група не збігається, у доступі буде відмовлено.", "LabelOpenIDGroupClaimDescription": "Ім'я OpenID claim, що містить список груп користувачів. Зазвичай їх називають <code>групами</code>. <b>Якщо налаштовано</b>, застосунок автоматично призначатиме ролі на основі членства користувача в групах, за умови, що ці групи названі в claim'і без урахування реєстру 'admin', 'user' або 'guest'. Claim мусить містити список, і якщо користувач належить до кількох груп, програма призначить йому роль, що відповідає найвищому рівню доступу. Якщо жодна група не збігається, у доступі буде відмовлено.",
"LabelOpenRSSFeed": "Відкрити RSS-канал", "LabelOpenRSSFeed": "Відкрити RSS-канал",
"LabelOverwrite": "Перезаписати", "LabelOverwrite": "Перезаписати",
"LabelPaginationPageXOfY": "Сторінка {0} з {1}",
"LabelPassword": "Пароль", "LabelPassword": "Пароль",
"LabelPath": "Шлях", "LabelPath": "Шлях",
"LabelPermanent": "Постійний", "LabelPermanent": "Постійний",
"LabelPermissionsAccessAllLibraries": "Доступ до усіх бібліотек", "LabelPermissionsAccessAllLibraries": "Доступ до усіх бібліотек",
"LabelPermissionsAccessAllTags": "Доступ до усіх міток", "LabelPermissionsAccessAllTags": "Доступ до усіх міток",
"LabelPermissionsAccessExplicitContent": "Доступ до відвертого вмісту", "LabelPermissionsAccessExplicitContent": "Доступ до відвертого вмісту",
"LabelPermissionsCreateEreader": "Можна створити читалку",
"LabelPermissionsDelete": "Може видаляти", "LabelPermissionsDelete": "Може видаляти",
"LabelPermissionsDownload": "Може завантажувати", "LabelPermissionsDownload": "Може завантажувати",
"LabelPermissionsUpdate": "Може оновлювати", "LabelPermissionsUpdate": "Може оновлювати",
@ -431,6 +480,7 @@
"LabelPersonalYearReview": "Ваші підсумки року ({0})", "LabelPersonalYearReview": "Ваші підсумки року ({0})",
"LabelPhotoPathURL": "Шлях/URL фото", "LabelPhotoPathURL": "Шлях/URL фото",
"LabelPlayMethod": "Метод відтворення", "LabelPlayMethod": "Метод відтворення",
"LabelPlayerChapterNumberMarker": "{0} з {1}",
"LabelPlaylists": "Списки відтворення", "LabelPlaylists": "Списки відтворення",
"LabelPodcast": "Подкаст", "LabelPodcast": "Подкаст",
"LabelPodcastSearchRegion": "Регіон пошуку подкасту", "LabelPodcastSearchRegion": "Регіон пошуку подкасту",
@ -442,8 +492,12 @@
"LabelPrimaryEbook": "Основна електронна книга", "LabelPrimaryEbook": "Основна електронна книга",
"LabelProgress": "Прогрес", "LabelProgress": "Прогрес",
"LabelProvider": "Джерело", "LabelProvider": "Джерело",
"LabelProviderAuthorizationValue": "Значення заголовка авторизації",
"LabelPubDate": "Дата публікації", "LabelPubDate": "Дата публікації",
"LabelPublishYear": "Рік публікації", "LabelPublishYear": "Рік публікації",
"LabelPublishedDate": "Опубліковано {0}",
"LabelPublishedDecade": "Десятиліття публікації",
"LabelPublishedDecades": "Опубліковані десятиліття",
"LabelPublisher": "Видавець", "LabelPublisher": "Видавець",
"LabelPublishers": "Видавці", "LabelPublishers": "Видавці",
"LabelRSSFeedCustomOwnerEmail": "Користувацька електронна адреса власника", "LabelRSSFeedCustomOwnerEmail": "Користувацька електронна адреса власника",
@ -463,21 +517,28 @@
"LabelRedo": "Повторити", "LabelRedo": "Повторити",
"LabelRegion": "Регіон", "LabelRegion": "Регіон",
"LabelReleaseDate": "Дата публікації", "LabelReleaseDate": "Дата публікації",
"LabelRemoveAllMetadataAbs": "Видалити всі файли metadata.abs",
"LabelRemoveAllMetadataJson": "Видалити всі файли metadata.json",
"LabelRemoveCover": "Видалити обкладинку", "LabelRemoveCover": "Видалити обкладинку",
"LabelRemoveMetadataFile": "Видалити файли метаданих у папках елементів бібліотеки",
"LabelRemoveMetadataFileHelp": "Видалити всі файли metadata.json та metadata.abs у ваших папках {0}.",
"LabelRowsPerPage": "Рядків на сторінку", "LabelRowsPerPage": "Рядків на сторінку",
"LabelSearchTerm": "Пошуковий запит", "LabelSearchTerm": "Пошуковий запит",
"LabelSearchTitle": "Пошук за назвою", "LabelSearchTitle": "Пошук за назвою",
"LabelSearchTitleOrASIN": "Пошук назви або ASIN", "LabelSearchTitleOrASIN": "Пошук назви або ASIN",
"LabelSeason": "Сезон", "LabelSeason": "Сезон",
"LabelSeasonNumber": "Сезон #{0}",
"LabelSelectAll": "Вибрати все", "LabelSelectAll": "Вибрати все",
"LabelSelectAllEpisodes": "Вибрати всі серії", "LabelSelectAllEpisodes": "Вибрати всі серії",
"LabelSelectEpisodesShowing": "Обрати показані епізоди: {0}", "LabelSelectEpisodesShowing": "Обрати показані епізоди: {0}",
"LabelSelectUsers": "Вибрати користувачів", "LabelSelectUsers": "Вибрати користувачів",
"LabelSendEbookToDevice": "Надіслати електронну книгу на...", "LabelSendEbookToDevice": "Надіслати електронну книгу на...",
"LabelSequence": "Послідовність", "LabelSequence": "Послідовність",
"LabelSerial": "Серійний",
"LabelSeries": "Серії", "LabelSeries": "Серії",
"LabelSeriesName": "Назва серії", "LabelSeriesName": "Назва серії",
"LabelSeriesProgress": "Прогрес серії", "LabelSeriesProgress": "Прогрес серії",
"LabelServerLogLevel": "Рівень журналу сервера",
"LabelServerYearReview": "Підсумки року сервера ({0})", "LabelServerYearReview": "Підсумки року сервера ({0})",
"LabelSetEbookAsPrimary": "Зробити основною", "LabelSetEbookAsPrimary": "Зробити основною",
"LabelSetEbookAsSupplementary": "Зробити додатковою", "LabelSetEbookAsSupplementary": "Зробити додатковою",
@ -502,6 +563,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Серії, що містять одну книгу, будуть приховані зі сторінки серій та полиць головної сторінки.", "LabelSettingsHideSingleBookSeriesHelp": "Серії, що містять одну книгу, будуть приховані зі сторінки серій та полиць головної сторінки.",
"LabelSettingsHomePageBookshelfView": "Полиці на головній сторінці", "LabelSettingsHomePageBookshelfView": "Полиці на головній сторінці",
"LabelSettingsLibraryBookshelfView": "Показувати полиці у бібліотеці", "LabelSettingsLibraryBookshelfView": "Показувати полиці у бібліотеці",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "Відсоток виконання більше ніж",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Час, що залишився, менше ніж (секунди)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Позначити медіа-елемент як завершений, коли",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Пропускати попередні книги у Продовжити серії", "LabelSettingsOnlyShowLaterBooksInContinueSeries": "Пропускати попередні книги у Продовжити серії",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Полиця Продовжити серії на головній сторінці показує найпершу непочату книгу з тих серій, у яких ви завершили хоча б одну книгу та не маєте книг у процесі. Якщо увімкнути це налаштування, то серії продовжуватимуться з останньої завершеної книги, а не з першої непочатої.", "LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Полиця Продовжити серії на головній сторінці показує найпершу непочату книгу з тих серій, у яких ви завершили хоча б одну книгу та не маєте книг у процесі. Якщо увімкнути це налаштування, то серії продовжуватимуться з останньої завершеної книги, а не з першої непочатої.",
"LabelSettingsParseSubtitles": "Дістати підзаголовки", "LabelSettingsParseSubtitles": "Дістати підзаголовки",
@ -566,6 +630,7 @@
"LabelTimeDurationXMinutes": "{0} хвилини", "LabelTimeDurationXMinutes": "{0} хвилини",
"LabelTimeDurationXSeconds": "{0} секунди", "LabelTimeDurationXSeconds": "{0} секунди",
"LabelTimeInMinutes": "Час у хвилинах", "LabelTimeInMinutes": "Час у хвилинах",
"LabelTimeLeft": "{0} залишилось",
"LabelTimeListened": "Часу прослухано", "LabelTimeListened": "Часу прослухано",
"LabelTimeListenedToday": "Сьогодні прослухано", "LabelTimeListenedToday": "Сьогодні прослухано",
"LabelTimeRemaining": "Лишилося: {0}", "LabelTimeRemaining": "Лишилося: {0}",
@ -573,6 +638,7 @@
"LabelTitle": "Назва", "LabelTitle": "Назва",
"LabelToolsEmbedMetadata": "Вбудувати метадані", "LabelToolsEmbedMetadata": "Вбудувати метадані",
"LabelToolsEmbedMetadataDescription": "Вбудувати метадані в аудіофайли, включно з обкладинками та главами.", "LabelToolsEmbedMetadataDescription": "Вбудувати метадані в аудіофайли, включно з обкладинками та главами.",
"LabelToolsM4bEncoder": "Кодувальник M4B",
"LabelToolsMakeM4b": "Створити M4B-файл аудіокниги", "LabelToolsMakeM4b": "Створити M4B-файл аудіокниги",
"LabelToolsMakeM4bDescription": "Створити .M4B-аудіокнигу з вбудованими метаданими, обкладинкою та главами.", "LabelToolsMakeM4bDescription": "Створити .M4B-аудіокнигу з вбудованими метаданими, обкладинкою та главами.",
"LabelToolsSplitM4b": "Розділити M4B на MP3", "LabelToolsSplitM4b": "Розділити M4B на MP3",
@ -585,10 +651,12 @@
"LabelTracksMultiTrack": "Декілька доріжок", "LabelTracksMultiTrack": "Декілька доріжок",
"LabelTracksNone": "Доріжки відсутні", "LabelTracksNone": "Доріжки відсутні",
"LabelTracksSingleTrack": "Одна доріжка", "LabelTracksSingleTrack": "Одна доріжка",
"LabelTrailer": "Трейлер",
"LabelType": "Тип", "LabelType": "Тип",
"LabelUnabridged": "Повна", "LabelUnabridged": "Повна",
"LabelUndo": "Скасувати", "LabelUndo": "Скасувати",
"LabelUnknown": "Невідомо", "LabelUnknown": "Невідомо",
"LabelUnknownPublishDate": "Невідома дата публікації",
"LabelUpdateCover": "Оновити обкладинку", "LabelUpdateCover": "Оновити обкладинку",
"LabelUpdateCoverHelp": "Дозволити перезапис наявних обкладинок обраних книг після віднайдення", "LabelUpdateCoverHelp": "Дозволити перезапис наявних обкладинок обраних книг після віднайдення",
"LabelUpdateDetails": "Оновити подробиці", "LabelUpdateDetails": "Оновити подробиці",
@ -597,8 +665,10 @@
"LabelUploaderDragAndDrop": "Перетягніть файли або теки", "LabelUploaderDragAndDrop": "Перетягніть файли або теки",
"LabelUploaderDropFiles": "Перетягніть файли", "LabelUploaderDropFiles": "Перетягніть файли",
"LabelUploaderItemFetchMetadataHelp": "Автоматично шукати назву, автора та серію", "LabelUploaderItemFetchMetadataHelp": "Автоматично шукати назву, автора та серію",
"LabelUseAdvancedOptions": "Використовувати розширені налаштування",
"LabelUseChapterTrack": "Прогрес глави", "LabelUseChapterTrack": "Прогрес глави",
"LabelUseFullTrack": "Використовувати доріжку повністю", "LabelUseFullTrack": "Використовувати доріжку повністю",
"LabelUseZeroForUnlimited": "Використовуйте 0 для необмеженої кількості",
"LabelUser": "Користувач", "LabelUser": "Користувач",
"LabelUsername": "Ім’я користувача", "LabelUsername": "Ім’я користувача",
"LabelValue": "Значення", "LabelValue": "Значення",
@ -637,19 +707,27 @@
"MessageCheckingCron": "Перевірка планувальника...", "MessageCheckingCron": "Перевірка планувальника...",
"MessageConfirmCloseFeed": "Ви дійсно бажаєте закрити цей канал?", "MessageConfirmCloseFeed": "Ви дійсно бажаєте закрити цей канал?",
"MessageConfirmDeleteBackup": "Ви дійсно бажаєте видалити резервну копію за {0}?", "MessageConfirmDeleteBackup": "Ви дійсно бажаєте видалити резервну копію за {0}?",
"MessageConfirmDeleteDevice": "Ви впевнені, що хочете видалити пристрій для читання \"{0}\"?",
"MessageConfirmDeleteFile": "Файл буде видалено з вашої файлової системи. Ви впевнені?", "MessageConfirmDeleteFile": "Файл буде видалено з вашої файлової системи. Ви впевнені?",
"MessageConfirmDeleteLibrary": "Ви дійсно бажаєте назавжди видалити бібліотеку \"{0}\"?", "MessageConfirmDeleteLibrary": "Ви дійсно бажаєте назавжди видалити бібліотеку \"{0}\"?",
"MessageConfirmDeleteLibraryItem": "Елемент бібліотеки буде видалено з бази даних та вашої файлової системи. Ви впевнені?", "MessageConfirmDeleteLibraryItem": "Елемент бібліотеки буде видалено з бази даних та вашої файлової системи. Ви впевнені?",
"MessageConfirmDeleteLibraryItems": "З бази даних та вашої файлової системи будуть видалені елементи бібліотеки: {0}. Ви впевнені?", "MessageConfirmDeleteLibraryItems": "З бази даних та вашої файлової системи будуть видалені елементи бібліотеки: {0}. Ви впевнені?",
"MessageConfirmDeleteMetadataProvider": "Ви впевнені, що хочете видалити користувацького постачальника метаданих \"{0}\"?",
"MessageConfirmDeleteNotification": "Ви впевнені, що хочете видалити це сповіщення?",
"MessageConfirmDeleteSession": "Ви дійсно бажаєте видалити цей сеанс?", "MessageConfirmDeleteSession": "Ви дійсно бажаєте видалити цей сеанс?",
"MessageConfirmEmbedMetadataInAudioFiles": "Ви впевнені, що хочете вставити метадані в {0} аудіофайлів?",
"MessageConfirmForceReScan": "Ви дійсно бажаєте примусово пересканувати?", "MessageConfirmForceReScan": "Ви дійсно бажаєте примусово пересканувати?",
"MessageConfirmMarkAllEpisodesFinished": "Ви дійсно бажаєте позначити усі епізоди завершеними?", "MessageConfirmMarkAllEpisodesFinished": "Ви дійсно бажаєте позначити усі епізоди завершеними?",
"MessageConfirmMarkAllEpisodesNotFinished": "Ви дійсно бажаєте позначити усі епізоди незавершеними?", "MessageConfirmMarkAllEpisodesNotFinished": "Ви дійсно бажаєте позначити усі епізоди незавершеними?",
"MessageConfirmMarkItemFinished": "Ви впевнені, що хочете позначити \"{0}\" як завершене?",
"MessageConfirmMarkItemNotFinished": "Ви впевнені, що хочете позначити \"{0}\" як незавершене?",
"MessageConfirmMarkSeriesFinished": "Ви дійсно бажаєте позначити усі книги серії завершеними?", "MessageConfirmMarkSeriesFinished": "Ви дійсно бажаєте позначити усі книги серії завершеними?",
"MessageConfirmMarkSeriesNotFinished": "Ви дійсно бажаєте позначити всі книги серії незавершеними?", "MessageConfirmMarkSeriesNotFinished": "Ви дійсно бажаєте позначити всі книги серії незавершеними?",
"MessageConfirmNotificationTestTrigger": "Активувати це сповіщення з тестовими даними?",
"MessageConfirmPurgeCache": "Очищення кешу видалить усю теку <code>/metadata/cache</code>. <br /><br />Ви дійсно бажаєте видалити теку кешу?", "MessageConfirmPurgeCache": "Очищення кешу видалить усю теку <code>/metadata/cache</code>. <br /><br />Ви дійсно бажаєте видалити теку кешу?",
"MessageConfirmPurgeItemsCache": "Очищення кешу елементів видалить усю теку <code>/metadata/cache/items</code>. <br />Ви певні?", "MessageConfirmPurgeItemsCache": "Очищення кешу елементів видалить усю теку <code>/metadata/cache/items</code>. <br />Ви певні?",
"MessageConfirmQuickEmbed": "Увага! Швидке вбудування не створює резервних копій ваших аудіо. Переконайтеся, що маєте копію ваших файлів.<br><br>Продовжити?", "MessageConfirmQuickEmbed": "Увага! Швидке вбудування не створює резервних копій ваших аудіо. Переконайтеся, що маєте копію ваших файлів.<br><br>Продовжити?",
"MessageConfirmQuickMatchEpisodes": "При виявленні співпадінь інформація про епізоди швидкого пошуку буде перезаписана. Будуть оновлені тільки несуперечливі епізоди. Ви впевнені?",
"MessageConfirmReScanLibraryItems": "Ви дійсно бажаєте пересканувати елементи: {0}?", "MessageConfirmReScanLibraryItems": "Ви дійсно бажаєте пересканувати елементи: {0}?",
"MessageConfirmRemoveAllChapters": "Ви дійсно бажаєте видалити усі глави?", "MessageConfirmRemoveAllChapters": "Ви дійсно бажаєте видалити усі глави?",
"MessageConfirmRemoveAuthor": "Ви дійсно бажаєте видалити автора \"{0}\"?", "MessageConfirmRemoveAuthor": "Ви дійсно бажаєте видалити автора \"{0}\"?",
@ -657,6 +735,7 @@
"MessageConfirmRemoveEpisode": "Ви дійсно бажаєте видалити епізод \"{0}\"?", "MessageConfirmRemoveEpisode": "Ви дійсно бажаєте видалити епізод \"{0}\"?",
"MessageConfirmRemoveEpisodes": "Ви дійсно бажаєте видалити епізодів: {0}?", "MessageConfirmRemoveEpisodes": "Ви дійсно бажаєте видалити епізодів: {0}?",
"MessageConfirmRemoveListeningSessions": "Ви дійсно бажаєте видалити сеанси прослуховування: {0}?", "MessageConfirmRemoveListeningSessions": "Ви дійсно бажаєте видалити сеанси прослуховування: {0}?",
"MessageConfirmRemoveMetadataFiles": "Ви впевнені, що хочете видалити всі файли metadata.{0} у папках елементів вашої бібліотеки?",
"MessageConfirmRemoveNarrator": "Ви дійсно бажаєте видалити читця \"{0}\"?", "MessageConfirmRemoveNarrator": "Ви дійсно бажаєте видалити читця \"{0}\"?",
"MessageConfirmRemovePlaylist": "Ви дійсно бажаєте видалити список відтворення \"{0}\"?", "MessageConfirmRemovePlaylist": "Ви дійсно бажаєте видалити список відтворення \"{0}\"?",
"MessageConfirmRenameGenre": "Ви дійсно бажаєте замінити жанр \"{0}\" на \"{1}\" для усіх елементів?", "MessageConfirmRenameGenre": "Ви дійсно бажаєте замінити жанр \"{0}\" на \"{1}\" для усіх елементів?",
@ -665,11 +744,14 @@
"MessageConfirmRenameTag": "Ви дійсно бажаєте замінити мітку \"{0}\" на \"{1}\" для усіх елементів?", "MessageConfirmRenameTag": "Ви дійсно бажаєте замінити мітку \"{0}\" на \"{1}\" для усіх елементів?",
"MessageConfirmRenameTagMergeNote": "Примітка: така мітка вже існує, тож їх буде об'єднано.", "MessageConfirmRenameTagMergeNote": "Примітка: така мітка вже існує, тож їх буде об'єднано.",
"MessageConfirmRenameTagWarning": "Увага! Вже існує схожа мітка у іншому регістрі \"{0}\".", "MessageConfirmRenameTagWarning": "Увага! Вже існує схожа мітка у іншому регістрі \"{0}\".",
"MessageConfirmResetProgress": "Ви впевнені, що хочете скинути свій прогрес?",
"MessageConfirmSendEbookToDevice": "Ви дійсно хочете відправити на пристрій \"{2}\" електроні книги: {0}, \"{1}\"?", "MessageConfirmSendEbookToDevice": "Ви дійсно хочете відправити на пристрій \"{2}\" електроні книги: {0}, \"{1}\"?",
"MessageConfirmUnlinkOpenId": "Ви впевнені, що хочете відв'язати цього користувача від OpenID?",
"MessageDownloadingEpisode": "Завантаження епізоду", "MessageDownloadingEpisode": "Завантаження епізоду",
"MessageDragFilesIntoTrackOrder": "Перетягніть файли до правильного порядку", "MessageDragFilesIntoTrackOrder": "Перетягніть файли до правильного порядку",
"MessageEmbedFailed": "Не вдалося вбудувати!", "MessageEmbedFailed": "Не вдалося вбудувати!",
"MessageEmbedFinished": "Вбудовано!", "MessageEmbedFinished": "Вбудовано!",
"MessageEmbedQueue": "В черзі на вбудовування метаданих ({0} в черзі)",
"MessageEpisodesQueuedForDownload": "Епізодів у черзі завантаження: {0}", "MessageEpisodesQueuedForDownload": "Епізодів у черзі завантаження: {0}",
"MessageEreaderDevices": "Аби гарантувати отримання електронних книг, вам може знадобитися додати вказану вище адресу електронної пошти як правильного відправника на кожному з пристроїв зі списку нижче.", "MessageEreaderDevices": "Аби гарантувати отримання електронних книг, вам може знадобитися додати вказану вище адресу електронної пошти як правильного відправника на кожному з пристроїв зі списку нижче.",
"MessageFeedURLWillBe": "URL-адреса каналу буде {0}", "MessageFeedURLWillBe": "URL-адреса каналу буде {0}",
@ -700,6 +782,7 @@
"MessageNoCollections": "Добірки відсутні", "MessageNoCollections": "Добірки відсутні",
"MessageNoCoversFound": "Обкладинок не знайдено", "MessageNoCoversFound": "Обкладинок не знайдено",
"MessageNoDescription": "Без опису", "MessageNoDescription": "Без опису",
"MessageNoDevices": "Немає пристроїв",
"MessageNoDownloadsInProgress": "Немає активних завантажень", "MessageNoDownloadsInProgress": "Немає активних завантажень",
"MessageNoDownloadsQueued": "Немає завантажень у черзі", "MessageNoDownloadsQueued": "Немає завантажень у черзі",
"MessageNoEpisodeMatchesFound": "Відповідних епізодів не знайдено", "MessageNoEpisodeMatchesFound": "Відповідних епізодів не знайдено",
@ -713,6 +796,7 @@
"MessageNoLogs": "Журнал порожній", "MessageNoLogs": "Журнал порожній",
"MessageNoMediaProgress": "Прогрес відсутній", "MessageNoMediaProgress": "Прогрес відсутній",
"MessageNoNotifications": "Сповіщення відсутні", "MessageNoNotifications": "Сповіщення відсутні",
"MessageNoPodcastFeed": "Невірний подкаст: Немає каналу",
"MessageNoPodcastsFound": "Подкастів не знайдено", "MessageNoPodcastsFound": "Подкастів не знайдено",
"MessageNoResults": "Немає результатів", "MessageNoResults": "Немає результатів",
"MessageNoSearchResultsFor": "Немає результатів пошуку для \"{0}\"", "MessageNoSearchResultsFor": "Немає результатів пошуку для \"{0}\"",
@ -727,7 +811,12 @@
"MessagePauseChapter": "Призупинити відтворення глави", "MessagePauseChapter": "Призупинити відтворення глави",
"MessagePlayChapter": "Слухати початок глави", "MessagePlayChapter": "Слухати початок глави",
"MessagePlaylistCreateFromCollection": "Створити список відтворення з добірки", "MessagePlaylistCreateFromCollection": "Створити список відтворення з добірки",
"MessagePleaseWait": "Будь ласка, зачекайте...",
"MessagePodcastHasNoRSSFeedForMatching": "Подкаст не має RSS-каналу для пошуку", "MessagePodcastHasNoRSSFeedForMatching": "Подкаст не має RSS-каналу для пошуку",
"MessagePodcastSearchField": "Введіть пошуковий запит або URL RSS фіду",
"MessageQuickEmbedInProgress": "Швидке вбудовування в процесі",
"MessageQuickEmbedQueue": "В черзі на швидке вбудовування ({0} в черзі)",
"MessageQuickMatchAllEpisodes": "Швидке співставлення всіх епізодів",
"MessageQuickMatchDescription": "Заповнити відсутні подробиці та обкладинку першим результатом пошуку '{0}'. Не перезаписує подробиці, якщо не увімкнено параметр \"Надавати перевагу віднайденим метаданим\".", "MessageQuickMatchDescription": "Заповнити відсутні подробиці та обкладинку першим результатом пошуку '{0}'. Не перезаписує подробиці, якщо не увімкнено параметр \"Надавати перевагу віднайденим метаданим\".",
"MessageRemoveChapter": "Видалити главу", "MessageRemoveChapter": "Видалити главу",
"MessageRemoveEpisodes": "Видалити епізодів: {0}", "MessageRemoveEpisodes": "Видалити епізодів: {0}",
@ -745,6 +834,41 @@
"MessageShareExpiresIn": "Сплине за {0}", "MessageShareExpiresIn": "Сплине за {0}",
"MessageShareURLWillBe": "Поширюваний URL - <strong>{0}</strong>", "MessageShareURLWillBe": "Поширюваний URL - <strong>{0}</strong>",
"MessageStartPlaybackAtTime": "Почати відтворення \"{0}\" з {1}?", "MessageStartPlaybackAtTime": "Почати відтворення \"{0}\" з {1}?",
"MessageTaskAudioFileNotWritable": "Аудіофайл \"{0}\" недоступний для запису",
"MessageTaskCanceledByUser": "Задача скасована користувачем",
"MessageTaskDownloadingEpisodeDescription": "Завантаження епізоду \"{0}\"",
"MessageTaskEmbeddingMetadata": "Вбудовування метаданих",
"MessageTaskEmbeddingMetadataDescription": "Вбудовування метаданих у аудіокнигу \"{0}\"",
"MessageTaskEncodingM4b": "Кодування M4B",
"MessageTaskEncodingM4bDescription": "Кодування аудіокниги \"{0}\" в один файл m4b",
"MessageTaskFailed": "Неуспішно",
"MessageTaskFailedToBackupAudioFile": "Не вдалося створити резервну копію аудіофайлу \"{0}\"",
"MessageTaskFailedToCreateCacheDirectory": "Не вдалося створити каталог кешу",
"MessageTaskFailedToEmbedMetadataInFile": "Не вдалося вбудувати метадані у файл \"{0}\"",
"MessageTaskFailedToMergeAudioFiles": "Не вдалося об’єднати аудіофайли",
"MessageTaskFailedToMoveM4bFile": "Не вдалося перемістити файл m4b",
"MessageTaskFailedToWriteMetadataFile": "Не вдалося записати файл метаданих",
"MessageTaskMatchingBooksInLibrary": "Відповідність книг у бібліотеці \"{0}\"",
"MessageTaskNoFilesToScan": "Немає файлів для сканування",
"MessageTaskOpmlImport": "Імпорт OPML",
"MessageTaskOpmlImportDescription": "Створення подкастів з {0} RSS-стрічок",
"MessageTaskOpmlImportFeed": "Канал імпорту OPML",
"MessageTaskOpmlImportFeedDescription": "Імпорт RSS-каналу \"{0}\"",
"MessageTaskOpmlImportFeedFailed": "Не вдалося отримати подкаст-стрічку",
"MessageTaskOpmlImportFeedPodcastDescription": "Створення подкасту \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Подкаст вже існує за цим шляхом",
"MessageTaskOpmlImportFeedPodcastFailed": "Не вдалося створити подкаст",
"MessageTaskOpmlImportFinished": "Додано {0} подкастів",
"MessageTaskOpmlParseFailed": "Не вдалося розібрати файл OPML",
"MessageTaskOpmlParseFastFail": "Невірний файл OPML: не знайдено тег <opml> або тег <outline>",
"MessageTaskOpmlParseNoneFound": "У файлі OPML не знайдено жодного канала",
"MessageTaskScanItemsAdded": "{0} додано",
"MessageTaskScanItemsMissing": "{0} відсутній",
"MessageTaskScanItemsUpdated": "{0} оновлено",
"MessageTaskScanNoChangesNeeded": "Змін не потрібно",
"MessageTaskScanningFileChanges": "Сканування змін файлів у \"{0}\"",
"MessageTaskScanningLibrary": "Сканування бібліотеки \"{0}\"",
"MessageTaskTargetDirectoryNotWritable": "Цільовий каталог недоступний для запису",
"MessageThinking": "Думаю…", "MessageThinking": "Думаю…",
"MessageUploaderItemFailed": "Не вдалося завантажити", "MessageUploaderItemFailed": "Не вдалося завантажити",
"MessageUploaderItemSuccess": "Успішно завантажено!", "MessageUploaderItemSuccess": "Успішно завантажено!",
@ -762,6 +886,10 @@
"NoteUploaderFoldersWithMediaFiles": "Теки з медіафайлами буде оброблено як окремі елементи бібліотеки.", "NoteUploaderFoldersWithMediaFiles": "Теки з медіафайлами буде оброблено як окремі елементи бібліотеки.",
"NoteUploaderOnlyAudioFiles": "Якщо завантажувати лише аудіофайли, то кожен файл буде оброблено як окрему книгу.", "NoteUploaderOnlyAudioFiles": "Якщо завантажувати лише аудіофайли, то кожен файл буде оброблено як окрему книгу.",
"NoteUploaderUnsupportedFiles": "Непідтримувані файли пропущено. Під час вибору або перетягування теки, файли, що знаходяться поза текою, пропускаються.", "NoteUploaderUnsupportedFiles": "Непідтримувані файли пропущено. Під час вибору або перетягування теки, файли, що знаходяться поза текою, пропускаються.",
"NotificationOnBackupCompletedDescription": "Запускається після завершення резервного копіювання",
"NotificationOnBackupFailedDescription": "Срабатывает при збої резервного копіювання",
"NotificationOnEpisodeDownloadedDescription": "Запускається при автоматичному завантаженні епізоду подкасту",
"NotificationOnTestDescription": "Подія для тестування системи сповіщень",
"PlaceholderNewCollection": "Нова назва добірки", "PlaceholderNewCollection": "Нова назва добірки",
"PlaceholderNewFolderPath": "Новий шлях до теки", "PlaceholderNewFolderPath": "Новий шлях до теки",
"PlaceholderNewPlaylist": "Нова назва списку", "PlaceholderNewPlaylist": "Нова назва списку",
@ -786,17 +914,29 @@
"StatsTotalDuration": "Загальною довжиною…", "StatsTotalDuration": "Загальною довжиною…",
"StatsYearInReview": "ОГЛЯД РОКУ", "StatsYearInReview": "ОГЛЯД РОКУ",
"ToastAccountUpdateSuccess": "Профіль оновлено", "ToastAccountUpdateSuccess": "Профіль оновлено",
"ToastAppriseUrlRequired": "Необхідно ввести URL для Apprise",
"ToastAsinRequired": "ASIN є обов'язковим",
"ToastAuthorImageRemoveSuccess": "Фото автора видалено", "ToastAuthorImageRemoveSuccess": "Фото автора видалено",
"ToastAuthorNotFound": "Автор \"{0}\" не знайдений",
"ToastAuthorRemoveSuccess": "Автор видалений",
"ToastAuthorSearchNotFound": "Автор не знайдений",
"ToastAuthorUpdateMerged": "Автора об'єднано", "ToastAuthorUpdateMerged": "Автора об'єднано",
"ToastAuthorUpdateSuccess": "Автора оновлено", "ToastAuthorUpdateSuccess": "Автора оновлено",
"ToastAuthorUpdateSuccessNoImageFound": "Автора оновлено (фото не знайдено)", "ToastAuthorUpdateSuccessNoImageFound": "Автора оновлено (фото не знайдено)",
"ToastBackupAppliedSuccess": "Резервна копія застосована",
"ToastBackupCreateFailed": "Не вдалося створити резервну копію", "ToastBackupCreateFailed": "Не вдалося створити резервну копію",
"ToastBackupCreateSuccess": "Резервну копію створено", "ToastBackupCreateSuccess": "Резервну копію створено",
"ToastBackupDeleteFailed": "Не вдалося видалити резервну копію", "ToastBackupDeleteFailed": "Не вдалося видалити резервну копію",
"ToastBackupDeleteSuccess": "Резервну копію видалено", "ToastBackupDeleteSuccess": "Резервну копію видалено",
"ToastBackupInvalidMaxKeep": "Невірна кількість резервних копій для зберігання",
"ToastBackupInvalidMaxSize": "Невірний максимальний розмір резервної копії",
"ToastBackupRestoreFailed": "Не вдалося відновити резервну копію", "ToastBackupRestoreFailed": "Не вдалося відновити резервну копію",
"ToastBackupUploadFailed": "Не вдалося завантажити резервну копію", "ToastBackupUploadFailed": "Не вдалося завантажити резервну копію",
"ToastBackupUploadSuccess": "Резервну копію завантажено", "ToastBackupUploadSuccess": "Резервну копію завантажено",
"ToastBatchDeleteFailed": "Помилка при пакетному видаленні",
"ToastBatchDeleteSuccess": "Пакетне видалення успішне",
"ToastBatchQuickMatchFailed": "Не вдалося виконати пакетне швидке співпадіння!",
"ToastBatchQuickMatchStarted": "Пакетне швидке співпадіння {0} книг розпочато!",
"ToastBatchUpdateFailed": "Не вдалося оновити обрані", "ToastBatchUpdateFailed": "Не вдалося оновити обрані",
"ToastBatchUpdateSuccess": "Обрані успішно оновлено", "ToastBatchUpdateSuccess": "Обрані успішно оновлено",
"ToastBookmarkCreateFailed": "Не вдалося створити закладку", "ToastBookmarkCreateFailed": "Не вдалося створити закладку",
@ -807,19 +947,43 @@
"ToastCachePurgeSuccess": "Кеш очищено", "ToastCachePurgeSuccess": "Кеш очищено",
"ToastChaptersHaveErrors": "Глави містять помилки", "ToastChaptersHaveErrors": "Глави містять помилки",
"ToastChaptersMustHaveTitles": "Глави повинні мати назви", "ToastChaptersMustHaveTitles": "Глави повинні мати назви",
"ToastChaptersRemoved": "Розділи видалені",
"ToastChaptersUpdated": "Розділи оновлені",
"ToastCollectionItemsAddFailed": "Не вдалося додати елемент(и) до колекції",
"ToastCollectionItemsAddSuccess": "Елемент(и) успішно додано до колекції",
"ToastCollectionItemsRemoveSuccess": "Елемент(и) видалено з добірки", "ToastCollectionItemsRemoveSuccess": "Елемент(и) видалено з добірки",
"ToastCollectionRemoveSuccess": "Добірку видалено", "ToastCollectionRemoveSuccess": "Добірку видалено",
"ToastCollectionUpdateSuccess": "Добірку оновлено", "ToastCollectionUpdateSuccess": "Добірку оновлено",
"ToastCoverUpdateFailed": "Не вдалося оновити обкладинку",
"ToastDeleteFileFailed": "Не вдалося видалити файл", "ToastDeleteFileFailed": "Не вдалося видалити файл",
"ToastDeleteFileSuccess": "Файл видалено", "ToastDeleteFileSuccess": "Файл видалено",
"ToastDeviceAddFailed": "Не вдалося додати пристрій",
"ToastDeviceNameAlreadyExists": "Пристрій для електронних книг з таким ім'ям вже існує",
"ToastDeviceTestEmailFailed": "Не вдалося надіслати тестовий електронний лист",
"ToastDeviceTestEmailSuccess": "Тестовий електронний лист надіслано",
"ToastEmailSettingsUpdateSuccess": "Налаштування електронної пошти оновлено",
"ToastEncodeCancelFailed": "Не вдалося скасувати кодування",
"ToastEncodeCancelSucces": "Кодування скасовано",
"ToastEpisodeDownloadQueueClearFailed": "Не вдалося очистити чергу",
"ToastEpisodeDownloadQueueClearSuccess": "Чергу на завантаження епізодів очищено",
"ToastEpisodeUpdateSuccess": "{0} епізодів оновлено",
"ToastErrorCannotShare": "Не можна типово поширити на цей пристрій", "ToastErrorCannotShare": "Не можна типово поширити на цей пристрій",
"ToastFailedToLoadData": "Не вдалося завантажити дані", "ToastFailedToLoadData": "Не вдалося завантажити дані",
"ToastFailedToMatch": "Не вдалося знайти відповідність",
"ToastFailedToShare": "Не вдалося поділитися",
"ToastFailedToUpdate": "Не вдалося оновити",
"ToastInvalidImageUrl": "Невірний URL зображення",
"ToastInvalidMaxEpisodesToDownload": "Невірна кількість епізодів для завантаження",
"ToastInvalidUrl": "Невірний URL",
"ToastItemCoverUpdateSuccess": "Обкладинку елемента оновлено", "ToastItemCoverUpdateSuccess": "Обкладинку елемента оновлено",
"ToastItemDeletedFailed": "Не вдалося видалити елемент",
"ToastItemDeletedSuccess": "Видалений елемент",
"ToastItemDetailsUpdateSuccess": "Подробиці про елемент оновлено", "ToastItemDetailsUpdateSuccess": "Подробиці про елемент оновлено",
"ToastItemMarkedAsFinishedFailed": "Не вдалося позначити як завершене", "ToastItemMarkedAsFinishedFailed": "Не вдалося позначити як завершене",
"ToastItemMarkedAsFinishedSuccess": "Елемент позначено як завершений", "ToastItemMarkedAsFinishedSuccess": "Елемент позначено як завершений",
"ToastItemMarkedAsNotFinishedFailed": "Не вдалося позначити незавершеним", "ToastItemMarkedAsNotFinishedFailed": "Не вдалося позначити незавершеним",
"ToastItemMarkedAsNotFinishedSuccess": "Елемент позначено незавершеним", "ToastItemMarkedAsNotFinishedSuccess": "Елемент позначено незавершеним",
"ToastItemUpdateSuccess": "Елемент оновлено",
"ToastLibraryCreateFailed": "Не вдалося створити бібліотеку", "ToastLibraryCreateFailed": "Не вдалося створити бібліотеку",
"ToastLibraryCreateSuccess": "Бібліотеку \"{0}\" створено", "ToastLibraryCreateSuccess": "Бібліотеку \"{0}\" створено",
"ToastLibraryDeleteFailed": "Не вдалося видалити бібліотеку", "ToastLibraryDeleteFailed": "Не вдалося видалити бібліотеку",
@ -827,28 +991,83 @@
"ToastLibraryScanFailedToStart": "Не вдалося розпочати сканування", "ToastLibraryScanFailedToStart": "Не вдалося розпочати сканування",
"ToastLibraryScanStarted": "Почалося сканування бібліотеки", "ToastLibraryScanStarted": "Почалося сканування бібліотеки",
"ToastLibraryUpdateSuccess": "Бібліотеку \"{0}\" оновлено", "ToastLibraryUpdateSuccess": "Бібліотеку \"{0}\" оновлено",
"ToastMatchAllAuthorsFailed": "Не вдалось знайти відповідності з усіма авторами",
"ToastMetadataFilesRemovedError": "Помилка при видаленні metadata.{0} файли",
"ToastMetadataFilesRemovedNoneFound": "У бібліотеці не знайдено metadata.{0} файлів",
"ToastMetadataFilesRemovedNoneRemoved": "Не видалено metadata.{0} файлів",
"ToastMetadataFilesRemovedSuccess": "{0} metadata.{1} файлів видалено",
"ToastMustHaveAtLeastOnePath": "Повинен бути хоча б один шлях",
"ToastNameEmailRequired": "Ім'я та електронна пошта обов'язкові",
"ToastNameRequired": "Ім'я обов'язкове",
"ToastNewEpisodesFound": "{0} нових епізодів знайдено",
"ToastNewUserCreatedFailed": "Не вдалося створити акаунт: \"{0}\"",
"ToastNewUserCreatedSuccess": "Новий акаунт створено",
"ToastNewUserLibraryError": "Потрібно вибрати хоча б одну бібліотеку",
"ToastNewUserPasswordError": "Пароль обов'язковий, лише користувач з правами root може мати порожній пароль",
"ToastNewUserTagError": "Потрібно вибрати хоча б один тег",
"ToastNewUserUsernameError": "Введіть ім'я користувача",
"ToastNoNewEpisodesFound": "Нових епізодів не знайдено",
"ToastNoUpdatesNecessary": "Оновлення не потрібні",
"ToastNotificationCreateFailed": "Не вдалося створити сповіщення",
"ToastNotificationDeleteFailed": "Не вдалося видалити сповіщення",
"ToastNotificationFailedMaximum": "Максимальна кількість невдалих спроб повинна бути >= 0",
"ToastNotificationQueueMaximum": "Максимальна кількість сповіщень у черзі повинна бути >= 0",
"ToastNotificationSettingsUpdateSuccess": "Налаштування сповіщень оновлено",
"ToastNotificationTestTriggerFailed": "Не вдалося ініціювати тестове сповіщення",
"ToastNotificationTestTriggerSuccess": "Спрацьовувало сповіщення про тестування",
"ToastNotificationUpdateSuccess": "Сповіщення оновлено",
"ToastPlaylistCreateFailed": "Не вдалося створити список", "ToastPlaylistCreateFailed": "Не вдалося створити список",
"ToastPlaylistCreateSuccess": "Список відтворення створено", "ToastPlaylistCreateSuccess": "Список відтворення створено",
"ToastPlaylistRemoveSuccess": "Список відтворення видалено", "ToastPlaylistRemoveSuccess": "Список відтворення видалено",
"ToastPlaylistUpdateSuccess": "Список відтворення оновлено", "ToastPlaylistUpdateSuccess": "Список відтворення оновлено",
"ToastPodcastCreateFailed": "Не вдалося створити подкаст", "ToastPodcastCreateFailed": "Не вдалося створити подкаст",
"ToastPodcastCreateSuccess": "Подкаст успішно створено", "ToastPodcastCreateSuccess": "Подкаст успішно створено",
"ToastPodcastGetFeedFailed": "Не вдалося отримати фід подкасту",
"ToastPodcastNoEpisodesInFeed": "У RSS-каналі не знайдено епізодів",
"ToastPodcastNoRssFeed": "Подкаст не має RSS-каналу",
"ToastProgressIsNotBeingSynced": "Прогрес не синхронізується, перезапустіть відтворення",
"ToastProviderCreatedFailed": "Не вдалося додати постачальника",
"ToastProviderCreatedSuccess": "Новий постачальник доданий",
"ToastProviderNameAndUrlRequired": "Ім'я та URL обов'язкові",
"ToastProviderRemoveSuccess": "Постачальник видалений",
"ToastRSSFeedCloseFailed": "Не вдалося закрити RSS-канал", "ToastRSSFeedCloseFailed": "Не вдалося закрити RSS-канал",
"ToastRSSFeedCloseSuccess": "RSS-канал закрито", "ToastRSSFeedCloseSuccess": "RSS-канал закрито",
"ToastRemoveFailed": "Не вдалося видалити",
"ToastRemoveItemFromCollectionFailed": "Не вдалося видалити елемент із добірки", "ToastRemoveItemFromCollectionFailed": "Не вдалося видалити елемент із добірки",
"ToastRemoveItemFromCollectionSuccess": "Елемент видалено з добірки", "ToastRemoveItemFromCollectionSuccess": "Елемент видалено з добірки",
"ToastRemoveItemsWithIssuesFailed": "Не вдалося видалити елементи бібліотеки з проблемами",
"ToastRemoveItemsWithIssuesSuccess": "Видалено елементи бібліотеки з проблемами",
"ToastRenameFailed": "Не вдалося перейменувати",
"ToastRescanFailed": "Не вдалося повторно сканувати для {0}",
"ToastRescanRemoved": "Повторне сканування завершено, елемент був видалений",
"ToastRescanUpToDate": "Повторне сканування завершено, елемент актуальний",
"ToastRescanUpdated": "Повторне сканування завершено, елемент оновлено",
"ToastScanFailed": "Не вдалося сканувати елемент бібліотеки",
"ToastSelectAtLeastOneUser": "Виберіть хоча б одного користувача",
"ToastSendEbookToDeviceFailed": "Не вдалося надіслати електронну книгу на пристрій", "ToastSendEbookToDeviceFailed": "Не вдалося надіслати електронну книгу на пристрій",
"ToastSendEbookToDeviceSuccess": "Електронну книгу надіслано на пристрій \"{0}\"", "ToastSendEbookToDeviceSuccess": "Електронну книгу надіслано на пристрій \"{0}\"",
"ToastSeriesUpdateFailed": "Не вдалося оновити серію", "ToastSeriesUpdateFailed": "Не вдалося оновити серію",
"ToastSeriesUpdateSuccess": "Серію успішно оновлено", "ToastSeriesUpdateSuccess": "Серію успішно оновлено",
"ToastServerSettingsUpdateSuccess": "Налаштування сервера оновлено", "ToastServerSettingsUpdateSuccess": "Налаштування сервера оновлено",
"ToastSessionCloseFailed": "Не вдалося закрити сесію",
"ToastSessionDeleteFailed": "Не вдалося видалити сесію", "ToastSessionDeleteFailed": "Не вдалося видалити сесію",
"ToastSessionDeleteSuccess": "Сесію видалено", "ToastSessionDeleteSuccess": "Сесію видалено",
"ToastSleepTimerDone": "Час сну завершено... зЗзЗз",
"ToastSlugMustChange": "Slug містить недопустимі символи",
"ToastSlugRequired": "Slug обов'язковий",
"ToastSocketConnected": "Сокет під'єднано", "ToastSocketConnected": "Сокет під'єднано",
"ToastSocketDisconnected": "Сокет від'єднано", "ToastSocketDisconnected": "Сокет від'єднано",
"ToastSocketFailedToConnect": "Не вдалося під'єднатися до сокета", "ToastSocketFailedToConnect": "Не вдалося під'єднатися до сокета",
"ToastSortingPrefixesEmptyError": "Мусить мати хоча б 1 префікс сортування", "ToastSortingPrefixesEmptyError": "Мусить мати хоча б 1 префікс сортування",
"ToastSortingPrefixesUpdateSuccess": "Префікси сортування оновлено ({0})", "ToastSortingPrefixesUpdateSuccess": "Префікси сортування оновлено ({0})",
"ToastTitleRequired": "Заголовок обов'язковий",
"ToastUnknownError": "Невідома помилка",
"ToastUnlinkOpenIdFailed": "Не вдалося відв'язати користувача від OpenID",
"ToastUnlinkOpenIdSuccess": "Користувача відв'язано від OpenID",
"ToastUserDeleteFailed": "Не вдалося видалити користувача", "ToastUserDeleteFailed": "Не вдалося видалити користувача",
"ToastUserDeleteSuccess": "Користувача видалено" "ToastUserDeleteSuccess": "Користувача видалено",
"ToastUserPasswordChangeSuccess": "Пароль успішно змінено",
"ToastUserPasswordMismatch": "Паролі не збігаються",
"ToastUserPasswordMustChange": "Новий пароль не може співпадати з попереднім",
"ToastUserRootRequireName": "Потрібно ввести ім'я користувача root"
} }

View File

@ -1,5 +1,5 @@
{ {
"ButtonAdd": "加", "ButtonAdd": "加",
"ButtonAddChapters": "新增章節", "ButtonAddChapters": "新增章節",
"ButtonAddDevice": "新增設備", "ButtonAddDevice": "新增設備",
"ButtonAddLibrary": "新增庫", "ButtonAddLibrary": "新增庫",
@ -17,7 +17,7 @@
"ButtonCheckAndDownloadNewEpisodes": "檢查並下載新劇集", "ButtonCheckAndDownloadNewEpisodes": "檢查並下載新劇集",
"ButtonChooseAFolder": "選擇資料夾", "ButtonChooseAFolder": "選擇資料夾",
"ButtonChooseFiles": "選擇檔案", "ButtonChooseFiles": "選擇檔案",
"ButtonClearFilter": "清過濾器", "ButtonClearFilter": "清過濾器",
"ButtonCloseFeed": "關閉源", "ButtonCloseFeed": "關閉源",
"ButtonCloseSession": "關閉開放會話", "ButtonCloseSession": "關閉開放會話",
"ButtonCollections": "收藏", "ButtonCollections": "收藏",
@ -35,6 +35,8 @@
"ButtonHide": "隱藏", "ButtonHide": "隱藏",
"ButtonHome": "首頁", "ButtonHome": "首頁",
"ButtonIssues": "問題", "ButtonIssues": "問題",
"ButtonJumpBackward": "向後跳轉",
"ButtonJumpForward": "向前跳轉",
"ButtonLatest": "最新", "ButtonLatest": "最新",
"ButtonLibrary": "媒體庫", "ButtonLibrary": "媒體庫",
"ButtonLogout": "登出", "ButtonLogout": "登出",
@ -53,6 +55,7 @@
"ButtonPlay": "播放", "ButtonPlay": "播放",
"ButtonPlaying": "正在播放", "ButtonPlaying": "正在播放",
"ButtonPlaylists": "播放列表", "ButtonPlaylists": "播放列表",
"ButtonPrevious": "上一個",
"ButtonPreviousChapter": "過去的章節", "ButtonPreviousChapter": "過去的章節",
"ButtonPurgeAllCache": "清理所有快取", "ButtonPurgeAllCache": "清理所有快取",
"ButtonPurgeItemsCache": "清理項目快取", "ButtonPurgeItemsCache": "清理項目快取",
@ -76,7 +79,7 @@
"ButtonSaveTracklist": "保存音軌列表", "ButtonSaveTracklist": "保存音軌列表",
"ButtonScan": "掃描", "ButtonScan": "掃描",
"ButtonScanLibrary": "掃描庫", "ButtonScanLibrary": "掃描庫",
"ButtonSearch": "查找", "ButtonSearch": "搜索",
"ButtonSelectFolderPath": "選擇資料夾路徑", "ButtonSelectFolderPath": "選擇資料夾路徑",
"ButtonSeries": "系列", "ButtonSeries": "系列",
"ButtonSetChaptersFromTracks": "將音軌設定為章節", "ButtonSetChaptersFromTracks": "將音軌設定為章節",
@ -97,7 +100,7 @@
"ErrorUploadFetchMetadataAPI": "獲取元數據時出錯", "ErrorUploadFetchMetadataAPI": "獲取元數據時出錯",
"ErrorUploadFetchMetadataNoResults": "無法獲取元數據 - 嘗試更新標題和/或作者", "ErrorUploadFetchMetadataNoResults": "無法獲取元數據 - 嘗試更新標題和/或作者",
"ErrorUploadLacksTitle": "必須有標題", "ErrorUploadLacksTitle": "必須有標題",
"HeaderAccount": "號", "HeaderAccount": "號",
"HeaderAdvanced": "高級", "HeaderAdvanced": "高級",
"HeaderAppriseNotificationSettings": "測試通知設定", "HeaderAppriseNotificationSettings": "測試通知設定",
"HeaderAudioTracks": "音軌", "HeaderAudioTracks": "音軌",
@ -111,6 +114,7 @@
"HeaderCollectionItems": "收藏項目", "HeaderCollectionItems": "收藏項目",
"HeaderCover": "封面", "HeaderCover": "封面",
"HeaderCurrentDownloads": "當前下載", "HeaderCurrentDownloads": "當前下載",
"HeaderCustomMessageOnLogin": "登錄時的自定義信息",
"HeaderCustomMetadataProviders": "自訂 Metadata 提供者", "HeaderCustomMetadataProviders": "自訂 Metadata 提供者",
"HeaderDetails": "詳情", "HeaderDetails": "詳情",
"HeaderDownloadQueue": "下載佇列", "HeaderDownloadQueue": "下載佇列",
@ -144,7 +148,7 @@
"HeaderNewLibrary": "新建媒體庫", "HeaderNewLibrary": "新建媒體庫",
"HeaderNotifications": "通知", "HeaderNotifications": "通知",
"HeaderOpenIDConnectAuthentication": "OpenID 連接身份驗證", "HeaderOpenIDConnectAuthentication": "OpenID 連接身份驗證",
"HeaderOpenRSSFeed": "打開 RSS 源", "HeaderOpenRSSFeed": "打開 Rss 源",
"HeaderOtherFiles": "其他檔案", "HeaderOtherFiles": "其他檔案",
"HeaderPasswordAuthentication": "密碼認證", "HeaderPasswordAuthentication": "密碼認證",
"HeaderPermissions": "權限", "HeaderPermissions": "權限",
@ -168,7 +172,7 @@
"HeaderSettingsExperimental": "實驗功能", "HeaderSettingsExperimental": "實驗功能",
"HeaderSettingsGeneral": "通用", "HeaderSettingsGeneral": "通用",
"HeaderSettingsScanner": "掃描", "HeaderSettingsScanner": "掃描",
"HeaderSleepTimer": "睡眠時", "HeaderSleepTimer": "睡眠時",
"HeaderStatsLargestItems": "最大的項目", "HeaderStatsLargestItems": "最大的項目",
"HeaderStatsLongestItems": "項目時長(小時)", "HeaderStatsLongestItems": "項目時長(小時)",
"HeaderStatsMinutesListeningChart": "收聽分鐘數(最近7天)", "HeaderStatsMinutesListeningChart": "收聽分鐘數(最近7天)",
@ -182,8 +186,12 @@
"HeaderUpdateDetails": "更新詳情", "HeaderUpdateDetails": "更新詳情",
"HeaderUpdateLibrary": "更新媒體庫", "HeaderUpdateLibrary": "更新媒體庫",
"HeaderUsers": "使用者", "HeaderUsers": "使用者",
"HeaderYearReview": "{0} 年回顧",
"HeaderYourStats": "你的統計數據", "HeaderYourStats": "你的統計數據",
"LabelAbridged": "概要", "LabelAbridged": "概要",
"LabelAbridgedChecked": "刪節版(已勾選)",
"LabelAbridgedUnchecked": "未刪節版(未勾選)",
"LabelAccessibleBy": "可訪問",
"LabelAccountType": "帳號類型", "LabelAccountType": "帳號類型",
"LabelAccountTypeAdmin": "管理員", "LabelAccountTypeAdmin": "管理員",
"LabelAccountTypeGuest": "來賓", "LabelAccountTypeGuest": "來賓",
@ -260,26 +268,32 @@
"LabelDownload": "下載", "LabelDownload": "下載",
"LabelDownloadNEpisodes": "下載 {0} 集", "LabelDownloadNEpisodes": "下載 {0} 集",
"LabelDuration": "持續時間", "LabelDuration": "持續時間",
"LabelDurationComparisonExactMatch": "(完全匹配)",
"LabelDurationComparisonLonger": "{0} 更長)",
"LabelDurationComparisonShorter": "{0} 更短)",
"LabelDurationFound": "找到持續時間:", "LabelDurationFound": "找到持續時間:",
"LabelEbook": "電子書", "LabelEbook": "電子書",
"LabelEbooks": "電子書", "LabelEbooks": "電子書",
"LabelEdit": "編輯", "LabelEdit": "編輯",
"LabelEmail": "郵箱", "LabelEmail": "郵箱",
"LabelEmailSettingsFromAddress": "發件人位址", "LabelEmailSettingsFromAddress": "發件人位址",
"LabelEmailSettingsRejectUnauthorized": "拒絕未經授權的證書",
"LabelEmailSettingsRejectUnauthorizedHelp": "停用 SSL 證書驗證可能會使您的連接暴露於安全風險中,例如中間人攻擊。僅在您了解其含義並信任您所連接的郵件伺服器的情況下才停用此選項。",
"LabelEmailSettingsSecure": "安全", "LabelEmailSettingsSecure": "安全",
"LabelEmailSettingsSecureHelp": "如果選是, 則連接將在連接到伺服器時使用TLS. 如果選否, 則若伺服器支援STARTTLS擴展, 則使用TLS. 在大多數情況下, 如果連接到465埠, 請將該值設定為是. 對於587或25埠, 請保持為否. (來自nodemailer.com/smtp/#authentication)", "LabelEmailSettingsSecureHelp": "如果選是, 則連接將在連接到伺服器時使用TLS. 如果選否, 則若伺服器支援STARTTLS擴展, 則使用TLS. 在大多數情況下, 如果連接到465埠, 請將該值設定為是. 對於587或25埠, 請保持為否. (來自nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "測試位址", "LabelEmailSettingsTestAddress": "測試位址",
"LabelEmbeddedCover": "嵌入封面", "LabelEmbeddedCover": "嵌入封面",
"LabelEnable": "啟用", "LabelEnable": "啟用",
"LabelEnd": "結束", "LabelEnd": "結束",
"LabelEndOfChapter": "章節結束",
"LabelEpisode": "劇集", "LabelEpisode": "劇集",
"LabelEpisodeTitle": "劇集標題", "LabelEpisodeTitle": "劇集標題",
"LabelEpisodeType": "劇集類型", "LabelEpisodeType": "劇集類型",
"LabelExample": "示例", "LabelExample": "示例",
"LabelExplicit": "信息準確", "LabelExplicit": "信息準確",
"LabelFeedURL": "源 URL", "LabelFeedURL": "源鏈接",
"LabelFetchingMetadata": "正在獲取元數據", "LabelFetchingMetadata": "正在獲取元數據",
"LabelFile": "檔案", "LabelFile": "文件",
"LabelFileBirthtime": "檔案創建時間", "LabelFileBirthtime": "檔案創建時間",
"LabelFileModified": "檔案修改時間", "LabelFileModified": "檔案修改時間",
"LabelFilename": "檔名", "LabelFilename": "檔名",
@ -288,6 +302,7 @@
"LabelFinished": "已聽完", "LabelFinished": "已聽完",
"LabelFolder": "資料夾", "LabelFolder": "資料夾",
"LabelFolders": "資料夾", "LabelFolders": "資料夾",
"LabelFontBoldness": "字體粗細",
"LabelFontFamily": "字體系列", "LabelFontFamily": "字體系列",
"LabelFontItalic": "斜體", "LabelFontItalic": "斜體",
"LabelFontScale": "字體比例", "LabelFontScale": "字體比例",
@ -353,7 +368,7 @@
"LabelMobileRedirectURIs": "允許移動應用重定向 URI", "LabelMobileRedirectURIs": "允許移動應用重定向 URI",
"LabelMobileRedirectURIsDescription": "這是移動應用程序的有效重定向 URI 白名單. 預設值為 <code>audiobookshelf://oauth</code>,您可以刪除它或加入其他 URI 以進行第三方應用集成. 使用星號 (<code>*</code>) 作為唯一條目允許任何 URI.", "LabelMobileRedirectURIsDescription": "這是移動應用程序的有效重定向 URI 白名單. 預設值為 <code>audiobookshelf://oauth</code>,您可以刪除它或加入其他 URI 以進行第三方應用集成. 使用星號 (<code>*</code>) 作為唯一條目允許任何 URI.",
"LabelMore": "更多", "LabelMore": "更多",
"LabelMoreInfo": "更多..", "LabelMoreInfo": "更多信息",
"LabelName": "名稱", "LabelName": "名稱",
"LabelNarrator": "講述者", "LabelNarrator": "講述者",
"LabelNarrators": "講述者", "LabelNarrators": "講述者",
@ -399,7 +414,7 @@
"LabelPodcasts": "播客", "LabelPodcasts": "播客",
"LabelPort": "埠", "LabelPort": "埠",
"LabelPrefixesToIgnore": "忽略的前綴 (不區分大小寫)", "LabelPrefixesToIgnore": "忽略的前綴 (不區分大小寫)",
"LabelPreventIndexing": "防止 iTunes 和 Google 播客目錄對你的源進行索引", "LabelPreventIndexing": "防止您的訂閱源被 iTunes 和 Google 播客目錄索引",
"LabelPrimaryEbook": "主電子書", "LabelPrimaryEbook": "主電子書",
"LabelProgress": "進度", "LabelProgress": "進度",
"LabelProvider": "供應商", "LabelProvider": "供應商",
@ -412,6 +427,7 @@
"LabelRSSFeedPreventIndexing": "防止索引", "LabelRSSFeedPreventIndexing": "防止索引",
"LabelRSSFeedSlug": "RSS 源段", "LabelRSSFeedSlug": "RSS 源段",
"LabelRSSFeedURL": "RSS 源 URL", "LabelRSSFeedURL": "RSS 源 URL",
"LabelRandomly": "隨機",
"LabelRead": "閱讀", "LabelRead": "閱讀",
"LabelReadAgain": "再次閱讀", "LabelReadAgain": "再次閱讀",
"LabelReadEbookWithoutProgress": "閱讀電子書而不保存進度", "LabelReadEbookWithoutProgress": "閱讀電子書而不保存進度",
@ -635,20 +651,20 @@
"MessageNoFoldersAvailable": "沒有可用資料夾", "MessageNoFoldersAvailable": "沒有可用資料夾",
"MessageNoGenres": "無流派", "MessageNoGenres": "無流派",
"MessageNoIssues": "無問題", "MessageNoIssues": "無問題",
"MessageNoItems": "項目", "MessageNoItems": "沒有項目",
"MessageNoItemsFound": "找到任何項目", "MessageNoItemsFound": "沒有找到任何項目",
"MessageNoListeningSessions": "收聽會話", "MessageNoListeningSessions": "沒有收聽會話",
"MessageNoLogs": "無日誌", "MessageNoLogs": "無日誌",
"MessageNoMediaProgress": "無媒體進度", "MessageNoMediaProgress": "無媒體進度",
"MessageNoNotifications": "無通知", "MessageNoNotifications": "無通知",
"MessageNoPodcastsFound": "找到播客", "MessageNoPodcastsFound": "沒有找到播客",
"MessageNoResults": "無結果", "MessageNoResults": "無結果",
"MessageNoSearchResultsFor": "沒有搜尋到結果 \"{0}\"", "MessageNoSearchResultsFor": "沒有搜尋到結果 \"{0}\"",
"MessageNoSeries": "無系列", "MessageNoSeries": "無系列",
"MessageNoTags": "無標籤", "MessageNoTags": "無標籤",
"MessageNoTasksRunning": "沒有正在運行的任務", "MessageNoTasksRunning": "沒有正在運行的任務",
"MessageNoUpdatesWereNecessary": "無需更新", "MessageNoUpdatesWereNecessary": "無需更新",
"MessageNoUserPlaylists": "沒有播放列表", "MessageNoUserPlaylists": "沒有播放列表",
"MessageNotYetImplemented": "尚未實施", "MessageNotYetImplemented": "尚未實施",
"MessageOr": "或", "MessageOr": "或",
"MessagePauseChapter": "暫停章節播放", "MessagePauseChapter": "暫停章節播放",
@ -660,7 +676,7 @@
"MessageRemoveEpisodes": "移除 {0} 劇集", "MessageRemoveEpisodes": "移除 {0} 劇集",
"MessageRemoveFromPlayerQueue": "從播放佇列中移除", "MessageRemoveFromPlayerQueue": "從播放佇列中移除",
"MessageRemoveUserWarning": "是否確實要永久刪除使用者 \"{0}\"?", "MessageRemoveUserWarning": "是否確實要永久刪除使用者 \"{0}\"?",
"MessageReportBugsAndContribute": "報告錯誤、請求功能和貢獻", "MessageReportBugsAndContribute": "報告錯誤、請求功能和做出貢獻",
"MessageResetChaptersConfirm": "你確定要重置章節並撤消你所做的更改嗎?", "MessageResetChaptersConfirm": "你確定要重置章節並撤消你所做的更改嗎?",
"MessageRestoreBackupConfirm": "你確定要恢復創建的這個備份", "MessageRestoreBackupConfirm": "你確定要恢復創建的這個備份",
"MessageRestoreBackupWarning": "恢復備份將覆蓋位於 /config 的整個資料庫並覆蓋 /metadata/items & /metadata/authors 中的圖像.<br /><br />備份不會修改媒體庫資料夾中的任何檔案. 如果您已啟用伺服器設定將封面和元數據存儲在庫資料夾中,則不會備份或覆蓋這些內容.<br /><br />將自動刷新使用伺服器的所有客戶端.", "MessageRestoreBackupWarning": "恢復備份將覆蓋位於 /config 的整個資料庫並覆蓋 /metadata/items & /metadata/authors 中的圖像.<br /><br />備份不會修改媒體庫資料夾中的任何檔案. 如果您已啟用伺服器設定將封面和元數據存儲在庫資料夾中,則不會備份或覆蓋這些內容.<br /><br />將自動刷新使用伺服器的所有客戶端.",
@ -681,8 +697,8 @@
"NoteChangeRootPassword": "Root 是唯一可以擁有空密碼的使用者", "NoteChangeRootPassword": "Root 是唯一可以擁有空密碼的使用者",
"NoteChapterEditorTimes": "注意: 第一章開始時間必須保持在 0:00, 最後一章開始時間不能超過有聲書持續時間.", "NoteChapterEditorTimes": "注意: 第一章開始時間必須保持在 0:00, 最後一章開始時間不能超過有聲書持續時間.",
"NoteFolderPicker": "注意: 將不顯示已映射的資料夾", "NoteFolderPicker": "注意: 將不顯示已映射的資料夾",
"NoteRSSFeedPodcastAppsHttps": "警告: 大多數播客應用程序都需要 RSS 源 URL 使用 HTTPS", "NoteRSSFeedPodcastAppsHttps": "警告:大多數播客應用程式要求 RSS 訂閱源 URL 使用 HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "警告: 您的一集或多集沒有發布日期. 一些播客應用程序要求這樣做.", "NoteRSSFeedPodcastAppsPubDate": "警告:您的一個或多個劇集沒有發布日期。某些播客應用程式要求提供此資訊。",
"NoteUploaderFoldersWithMediaFiles": "包含媒體檔案的資料夾將作為單獨的媒體庫項目處理.", "NoteUploaderFoldersWithMediaFiles": "包含媒體檔案的資料夾將作為單獨的媒體庫項目處理.",
"NoteUploaderOnlyAudioFiles": "如果只上傳音頻檔, 則每個音頻檔將作為單獨的有聲書處理.", "NoteUploaderOnlyAudioFiles": "如果只上傳音頻檔, 則每個音頻檔將作為單獨的有聲書處理.",
"NoteUploaderUnsupportedFiles": "不支援的檔案將被忽略. 選擇或刪除資料夾時, 將忽略不在項目資料夾中的其他檔案.", "NoteUploaderUnsupportedFiles": "不支援的檔案將被忽略. 選擇或刪除資料夾時, 將忽略不在項目資料夾中的其他檔案.",
@ -705,7 +721,7 @@
"ToastBackupUploadSuccess": "備份已上傳", "ToastBackupUploadSuccess": "備份已上傳",
"ToastBatchUpdateFailed": "批量更新失敗", "ToastBatchUpdateFailed": "批量更新失敗",
"ToastBatchUpdateSuccess": "批量更新成功", "ToastBatchUpdateSuccess": "批量更新成功",
"ToastBookmarkCreateFailed": "創建書失敗", "ToastBookmarkCreateFailed": "創建書失敗",
"ToastBookmarkCreateSuccess": "書籤已新增", "ToastBookmarkCreateSuccess": "書籤已新增",
"ToastBookmarkRemoveSuccess": "書籤已刪除", "ToastBookmarkRemoveSuccess": "書籤已刪除",
"ToastBookmarkUpdateSuccess": "書籤已更新", "ToastBookmarkUpdateSuccess": "書籤已更新",

View File

@ -114,7 +114,7 @@ server {
proxy_pass http://<URL_to_forward_to>; proxy_pass http://<URL_to_forward_to>;
proxy_redirect http:// https://; proxy_redirect http:// https://;
# Prevent 413 Request Entity Too Large error # Prevent 413 Request Entity Too Large error
# by increasing the maximum allowed size of the client request body # by increasing the maximum allowed size of the client request body
# For example, set it to 10 GiB # For example, set it to 10 GiB
client_max_body_size 10240M; client_max_body_size 10240M;
@ -339,7 +339,7 @@ This application is built using [NodeJs](https://nodejs.org/).
### Localization ### Localization
Thank you to [Weblate](https://hosted.weblate.org/engage/audiobookshelf/) for hosting our localization infrastructure pro-bono. If you want to see Audiobookshelf in your language, please help us localize. Additional information on helping with the translations [here](https://www.audiobookshelf.org/faq#how-do-i-help-with-translations). <a href="https://hosted.weblate.org/engage/audiobookshelf/"> <img src="https://hosted.weblate.org/widget/audiobookshelf/horizontal-auto.svg" alt="Translation status" /> </a> Thank you to [Weblate](https://hosted.weblate.org/engage/audiobookshelf/) for hosting our localization infrastructure pro-bono. If you want to see Audiobookshelf in your language, please help us localize. Additional information on helping with the translations [here](https://www.audiobookshelf.org/faq#how-do-i-help-with-translations). <a href="https://hosted.weblate.org/engage/audiobookshelf/"> <img src="https://hosted.weblate.org/widget/audiobookshelf/abs-web-client/horizontal-auto.svg" alt="Translation status" /> </a>
### Dev Container Setup ### Dev Container Setup

View File

@ -990,28 +990,18 @@ class Auth {
}) })
} }
} }
try {
Database.userModel await matchingUser.update({ pash: pw })
.update( Logger.info(`[Auth] User "${matchingUser.username}" changed password`)
{ res.json({
pash: pw success: true
},
{
where: { id: matchingUser.id }
}
)
.then(() => {
Logger.info(`[Auth] User "${matchingUser.username}" changed password`)
res.json({
success: true
})
}) })
.catch((error) => { } catch (error) {
Logger.error(`[Auth] User "${matchingUser.username}" failed to change password`, error) Logger.error(`[Auth] User "${matchingUser.username}" failed to change password`, error)
res.json({ res.json({
error: 'Unknown error' error: 'Unknown error'
})
}) })
}
} }
} }

View File

@ -62,7 +62,6 @@ class Server {
fs.mkdirSync(global.MetadataPath) fs.mkdirSync(global.MetadataPath)
} }
this.watcher = new Watcher()
this.auth = new Auth() this.auth = new Auth()
// Managers // Managers
@ -70,7 +69,7 @@ class Server {
this.backupManager = new BackupManager() this.backupManager = new BackupManager()
this.abMergeManager = new AbMergeManager() this.abMergeManager = new AbMergeManager()
this.playbackSessionManager = new PlaybackSessionManager() this.playbackSessionManager = new PlaybackSessionManager()
this.podcastManager = new PodcastManager(this.watcher) this.podcastManager = new PodcastManager()
this.audioMetadataManager = new AudioMetadataMangaer() this.audioMetadataManager = new AudioMetadataMangaer()
this.rssFeedManager = new RssFeedManager() this.rssFeedManager = new RssFeedManager()
this.cronManager = new CronManager(this.podcastManager, this.playbackSessionManager) this.cronManager = new CronManager(this.podcastManager, this.playbackSessionManager)
@ -147,9 +146,12 @@ class Server {
if (Database.serverSettings.scannerDisableWatcher) { if (Database.serverSettings.scannerDisableWatcher) {
Logger.info(`[Server] Watcher is disabled`) Logger.info(`[Server] Watcher is disabled`)
this.watcher.disabled = true Watcher.disabled = true
} else { } else {
this.watcher.initWatcher(libraries) Watcher.initWatcher(libraries)
Watcher.on('scanFilesChanged', (pendingFileUpdates, pendingTask) => {
LibraryScanner.scanFilesChanged(pendingFileUpdates, pendingTask)
})
} }
} }
@ -435,7 +437,7 @@ class Server {
*/ */
async stop() { async stop() {
Logger.info('=== Stopping Server ===') Logger.info('=== Stopping Server ===')
await this.watcher.close() Watcher.close()
Logger.info('Watcher Closed') Logger.info('Watcher Closed')
return new Promise((resolve) => { return new Promise((resolve) => {

View File

@ -2,7 +2,6 @@ const Path = require('path')
const EventEmitter = require('events') const EventEmitter = require('events')
const Watcher = require('./libs/watcher/watcher') const Watcher = require('./libs/watcher/watcher')
const Logger = require('./Logger') const Logger = require('./Logger')
const LibraryScanner = require('./scanner/LibraryScanner')
const Task = require('./objects/Task') const Task = require('./objects/Task')
const TaskManager = require('./managers/TaskManager') const TaskManager = require('./managers/TaskManager')
@ -31,6 +30,8 @@ class FolderWatcher extends EventEmitter {
this.filesBeingAdded = new Set() this.filesBeingAdded = new Set()
/** @type {Set<string>} */
this.ignoreFilePathsDownloading = new Set()
/** @type {string[]} */ /** @type {string[]} */
this.ignoreDirs = [] this.ignoreDirs = []
/** @type {string[]} */ /** @type {string[]} */
@ -333,7 +334,7 @@ class FolderWatcher extends EventEmitter {
} }
if (this.pendingFileUpdates.length) { if (this.pendingFileUpdates.length) {
LibraryScanner.scanFilesChanged(this.pendingFileUpdates, this.pendingTask) this.emit('scanFilesChanged', this.pendingFileUpdates, this.pendingTask)
} else { } else {
const taskFinishedString = { const taskFinishedString = {
text: 'No files to scan', text: 'No files to scan',
@ -348,12 +349,29 @@ class FolderWatcher extends EventEmitter {
}, this.pendingDelay) }, this.pendingDelay)
} }
/**
*
* @param {string} path
* @returns {boolean}
*/
checkShouldIgnorePath(path) { checkShouldIgnorePath(path) {
return !!this.ignoreDirs.find((dirpath) => { return !!this.ignoreDirs.find((dirpath) => {
return isSameOrSubPath(dirpath, path) return isSameOrSubPath(dirpath, path)
}) })
} }
/**
* When scanning a library item folder these files should be ignored
* Either a podcast episode downloading or a file that is pending by the watcher
*
* @param {string} path
* @returns {boolean}
*/
checkShouldIgnoreFilePath(path) {
if (this.pendingFilePaths.includes(path)) return true
return this.ignoreFilePathsDownloading.has(path)
}
/** /**
* Convert to POSIX and remove trailing slash * Convert to POSIX and remove trailing slash
* @param {string} path * @param {string} path
@ -409,4 +427,4 @@ class FolderWatcher extends EventEmitter {
}, 5000) }, 5000)
} }
} }
module.exports = FolderWatcher module.exports = new FolderWatcher()

View File

@ -17,6 +17,7 @@ const naturalSort = createNewSortInstance({
const LibraryScanner = require('../scanner/LibraryScanner') const LibraryScanner = require('../scanner/LibraryScanner')
const Scanner = require('../scanner/Scanner') const Scanner = require('../scanner/Scanner')
const Database = require('../Database') const Database = require('../Database')
const Watcher = require('../Watcher')
const libraryFilters = require('../utils/queries/libraryFilters') const libraryFilters = require('../utils/queries/libraryFilters')
const libraryItemsPodcastFilters = require('../utils/queries/libraryItemsPodcastFilters') const libraryItemsPodcastFilters = require('../utils/queries/libraryItemsPodcastFilters')
const authorFilters = require('../utils/queries/authorFilters') const authorFilters = require('../utils/queries/authorFilters')
@ -158,7 +159,7 @@ class LibraryController {
SocketAuthority.emitter('library_added', library.toOldJSON(), userFilter) SocketAuthority.emitter('library_added', library.toOldJSON(), userFilter)
// Add library watcher // Add library watcher
this.watcher.addLibrary(library) Watcher.addLibrary(library)
res.json(library.toOldJSON()) res.json(library.toOldJSON())
} }
@ -440,7 +441,7 @@ class LibraryController {
req.library.libraryFolders = await req.library.getLibraryFolders() req.library.libraryFolders = await req.library.getLibraryFolders()
// Update watcher // Update watcher
this.watcher.updateLibrary(req.library) Watcher.updateLibrary(req.library)
hasUpdates = true hasUpdates = true
} }
@ -466,7 +467,7 @@ class LibraryController {
*/ */
async delete(req, res) { async delete(req, res) {
// Remove library watcher // Remove library watcher
this.watcher.removeLibrary(req.library) Watcher.removeLibrary(req.library)
// Remove collections for library // Remove collections for library
const numCollectionsRemoved = await Database.collectionModel.removeAllForLibrary(req.library.id) const numCollectionsRemoved = await Database.collectionModel.removeAllForLibrary(req.library.id)

View File

@ -5,6 +5,7 @@ const fs = require('../libs/fsExtra')
const Logger = require('../Logger') const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority') const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database') const Database = require('../Database')
const Watcher = require('../Watcher')
const libraryItemFilters = require('../utils/queries/libraryItemFilters') const libraryItemFilters = require('../utils/queries/libraryItemFilters')
const patternValidation = require('../libs/nodeCron/pattern-validation') const patternValidation = require('../libs/nodeCron/pattern-validation')
@ -557,10 +558,10 @@ class MiscController {
switch (type) { switch (type) {
case 'add': case 'add':
this.watcher.onFileAdded(libraryId, path) Watcher.onFileAdded(libraryId, path)
break break
case 'unlink': case 'unlink':
this.watcher.onFileRemoved(libraryId, path) Watcher.onFileRemoved(libraryId, path)
break break
case 'rename': case 'rename':
const oldPath = req.body.oldPath const oldPath = req.body.oldPath
@ -568,7 +569,7 @@ class MiscController {
Logger.error(`[MiscController] Invalid request body for updateWatchedPath. oldPath is required for rename.`) Logger.error(`[MiscController] Invalid request body for updateWatchedPath. oldPath is required for rename.`)
return res.sendStatus(400) return res.sendStatus(400)
} }
this.watcher.onFileRename(libraryId, oldPath, path) Watcher.onFileRename(libraryId, oldPath, path)
break break
default: default:
Logger.error(`[MiscController] Invalid type for updateWatchedPath. type: "${type}"`) Logger.error(`[MiscController] Invalid type for updateWatchedPath. type: "${type}"`)

View File

@ -191,7 +191,21 @@ class MigrationManager {
const queryInterface = this.sequelize.getQueryInterface() const queryInterface = this.sequelize.getQueryInterface()
let migrationsMetaTableExists = await queryInterface.tableExists(MigrationManager.MIGRATIONS_META_TABLE) let migrationsMetaTableExists = await queryInterface.tableExists(MigrationManager.MIGRATIONS_META_TABLE)
// If the table exists, check that the `version` and `maxVersion` rows exist
if (migrationsMetaTableExists) {
const [{ count }] = await this.sequelize.query("SELECT COUNT(*) as count FROM :migrationsMeta WHERE key IN ('version', 'maxVersion')", {
replacements: { migrationsMeta: MigrationManager.MIGRATIONS_META_TABLE },
type: Sequelize.QueryTypes.SELECT
})
if (count < 2) {
Logger.warn(`[MigrationManager] migrationsMeta table exists but is missing 'version' or 'maxVersion' row. Dropping it...`)
await queryInterface.dropTable(MigrationManager.MIGRATIONS_META_TABLE)
migrationsMetaTableExists = false
}
}
if (this.isDatabaseNew && migrationsMetaTableExists) { if (this.isDatabaseNew && migrationsMetaTableExists) {
Logger.warn(`[MigrationManager] migrationsMeta table already exists. Dropping it...`)
// This can happen if database was initialized with force: true // This can happen if database was initialized with force: true
await queryInterface.dropTable(MigrationManager.MIGRATIONS_META_TABLE) await queryInterface.dropTable(MigrationManager.MIGRATIONS_META_TABLE)
migrationsMetaTableExists = false migrationsMetaTableExists = false

View File

@ -1,6 +1,7 @@
const Logger = require('../Logger') const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority') const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database') const Database = require('../Database')
const Watcher = require('../Watcher')
const fs = require('../libs/fsExtra') const fs = require('../libs/fsExtra')
@ -23,9 +24,7 @@ const AudioFile = require('../objects/files/AudioFile')
const LibraryItem = require('../objects/LibraryItem') const LibraryItem = require('../objects/LibraryItem')
class PodcastManager { class PodcastManager {
constructor(watcher) { constructor() {
this.watcher = watcher
this.downloadQueue = [] this.downloadQueue = []
this.currentDownload = null this.currentDownload = null
@ -47,6 +46,7 @@ class PodcastManager {
var itemDownloads = this.getEpisodeDownloadsInQueue(libraryItemId) var itemDownloads = this.getEpisodeDownloadsInQueue(libraryItemId)
Logger.info(`[PodcastManager] Clearing downloads in queue for item "${libraryItemId}" (${itemDownloads.length})`) Logger.info(`[PodcastManager] Clearing downloads in queue for item "${libraryItemId}" (${itemDownloads.length})`)
this.downloadQueue = this.downloadQueue.filter((d) => d.libraryItemId !== libraryItemId) this.downloadQueue = this.downloadQueue.filter((d) => d.libraryItemId !== libraryItemId)
SocketAuthority.emitter('episode_download_queue_cleared', libraryItemId)
} }
} }
@ -64,7 +64,6 @@ class PodcastManager {
} }
async startPodcastEpisodeDownload(podcastEpisodeDownload) { async startPodcastEpisodeDownload(podcastEpisodeDownload) {
SocketAuthority.emitter('episode_download_queue_updated', this.getDownloadQueueDetails())
if (this.currentDownload) { if (this.currentDownload) {
this.downloadQueue.push(podcastEpisodeDownload) this.downloadQueue.push(podcastEpisodeDownload)
SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient()) SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient())
@ -97,7 +96,8 @@ class PodcastManager {
} }
// Ignores all added files to this dir // Ignores all added files to this dir
this.watcher.addIgnoreDir(this.currentDownload.libraryItem.path) Watcher.addIgnoreDir(this.currentDownload.libraryItem.path)
Watcher.ignoreFilePathsDownloading.add(this.currentDownload.targetPath)
// Make sure podcast library item folder exists // Make sure podcast library item folder exists
if (!(await fs.pathExists(this.currentDownload.libraryItem.path))) { if (!(await fs.pathExists(this.currentDownload.libraryItem.path))) {
@ -149,9 +149,10 @@ class PodcastManager {
TaskManager.taskFinished(task) TaskManager.taskFinished(task)
SocketAuthority.emitter('episode_download_finished', this.currentDownload.toJSONForClient()) SocketAuthority.emitter('episode_download_finished', this.currentDownload.toJSONForClient())
SocketAuthority.emitter('episode_download_queue_updated', this.getDownloadQueueDetails())
this.watcher.removeIgnoreDir(this.currentDownload.libraryItem.path) Watcher.removeIgnoreDir(this.currentDownload.libraryItem.path)
Watcher.ignoreFilePathsDownloading.delete(this.currentDownload.targetPath)
this.currentDownload = null this.currentDownload = null
if (this.downloadQueue.length) { if (this.downloadQueue.length) {
this.startPodcastEpisodeDownload(this.downloadQueue.shift()) this.startPodcastEpisodeDownload(this.downloadQueue.shift())

View File

@ -3,6 +3,53 @@ const sequelize = require('sequelize')
const Logger = require('../Logger') const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority') const SocketAuthority = require('../SocketAuthority')
const { isNullOrNaN } = require('../utils') const { isNullOrNaN } = require('../utils')
const { LRUCache } = require('lru-cache')
class UserCache {
constructor() {
this.cache = new LRUCache({ max: 100 })
}
getById(id) {
const user = this.cache.get(id)
return user
}
getByEmail(email) {
const user = this.cache.find((u) => u.email === email)
return user
}
getByUsername(username) {
const user = this.cache.find((u) => u.username === username)
return user
}
getByOldId(oldUserId) {
const user = this.cache.find((u) => u.extraData?.oldUserId === oldUserId)
return user
}
getByOpenIDSub(sub) {
const user = this.cache.find((u) => u.extraData?.authOpenIDSub === sub)
return user
}
set(user) {
user.fromCache = true
this.cache.set(user.id, user)
}
delete(userId) {
this.cache.delete(userId)
}
maybeInvalidate(user) {
if (!user.fromCache) this.delete(user.id)
}
}
const userCache = new UserCache()
const { DataTypes, Model } = sequelize const { DataTypes, Model } = sequelize
@ -206,7 +253,11 @@ class User extends Model {
*/ */
static async getUserByUsername(username) { static async getUserByUsername(username) {
if (!username) return null if (!username) return null
return this.findOne({
const cachedUser = userCache.getByUsername(username)
if (cachedUser) return cachedUser
const user = await this.findOne({
where: { where: {
username: { username: {
[sequelize.Op.like]: username [sequelize.Op.like]: username
@ -214,6 +265,10 @@ class User extends Model {
}, },
include: this.sequelize.models.mediaProgress include: this.sequelize.models.mediaProgress
}) })
if (user) userCache.set(user)
return user
} }
/** /**
@ -223,7 +278,11 @@ class User extends Model {
*/ */
static async getUserByEmail(email) { static async getUserByEmail(email) {
if (!email) return null if (!email) return null
return this.findOne({
const cachedUser = userCache.getByEmail(email)
if (cachedUser) return cachedUser
const user = await this.findOne({
where: { where: {
email: { email: {
[sequelize.Op.like]: email [sequelize.Op.like]: email
@ -231,6 +290,10 @@ class User extends Model {
}, },
include: this.sequelize.models.mediaProgress include: this.sequelize.models.mediaProgress
}) })
if (user) userCache.set(user)
return user
} }
/** /**
@ -240,9 +303,17 @@ class User extends Model {
*/ */
static async getUserById(userId) { static async getUserById(userId) {
if (!userId) return null if (!userId) return null
return this.findByPk(userId, {
const cachedUser = userCache.getById(userId)
if (cachedUser) return cachedUser
const user = await this.findByPk(userId, {
include: this.sequelize.models.mediaProgress include: this.sequelize.models.mediaProgress
}) })
if (user) userCache.set(user)
return user
} }
/** /**
@ -254,12 +325,19 @@ class User extends Model {
*/ */
static async getUserByIdOrOldId(userId) { static async getUserByIdOrOldId(userId) {
if (!userId) return null if (!userId) return null
return this.findOne({ const cachedUser = userCache.getById(userId) || userCache.getByOldId(userId)
if (cachedUser) return cachedUser
const user = await this.findOne({
where: { where: {
[sequelize.Op.or]: [{ id: userId }, { 'extraData.oldUserId': userId }] [sequelize.Op.or]: [{ id: userId }, { 'extraData.oldUserId': userId }]
}, },
include: this.sequelize.models.mediaProgress include: this.sequelize.models.mediaProgress
}) })
if (user) userCache.set(user)
return user
} }
/** /**
@ -269,10 +347,18 @@ class User extends Model {
*/ */
static async getUserByOpenIDSub(sub) { static async getUserByOpenIDSub(sub) {
if (!sub) return null if (!sub) return null
return this.findOne({
const cachedUser = userCache.getByOpenIDSub(sub)
if (cachedUser) return cachedUser
const user = await this.findOne({
where: sequelize.where(sequelize.literal(`extraData->>"authOpenIDSub"`), sub), where: sequelize.where(sequelize.literal(`extraData->>"authOpenIDSub"`), sub),
include: this.sequelize.models.mediaProgress include: this.sequelize.models.mediaProgress
}) })
if (user) userCache.set(user)
return user
} }
/** /**
@ -623,6 +709,7 @@ class User extends Model {
mediaProgress = await this.sequelize.models.mediaProgress.create(newMediaProgressPayload) mediaProgress = await this.sequelize.models.mediaProgress.create(newMediaProgressPayload)
this.mediaProgresses.push(mediaProgress) this.mediaProgresses.push(mediaProgress)
} }
userCache.maybeInvalidate(this)
return { return {
mediaProgress mediaProgress
} }
@ -804,6 +891,21 @@ class User extends Model {
return hasUpdates return hasUpdates
} }
async update(values, options) {
userCache.maybeInvalidate(this)
return await super.update(values, options)
}
async save(options) {
userCache.maybeInvalidate(this)
return await super.save(options)
}
async destroy(options) {
userCache.delete(this.id)
await super.destroy(options)
}
} }
module.exports = User module.exports = User

View File

@ -1,6 +1,6 @@
const Path = require('path') const Path = require('path')
const uuidv4 = require("uuid").v4 const uuidv4 = require('uuid').v4
const { sanitizeFilename } = require('../utils/fileUtils') const { sanitizeFilename, filePathToPOSIX } = require('../utils/fileUtils')
const globals = require('../utils/globals') const globals = require('../utils/globals')
class PodcastEpisodeDownload { class PodcastEpisodeDownload {
@ -60,7 +60,7 @@ class PodcastEpisodeDownload {
return sanitizeFilename(filename) return sanitizeFilename(filename)
} }
get targetPath() { get targetPath() {
return Path.join(this.libraryItem.path, this.targetFilename) return filePathToPOSIX(Path.join(this.libraryItem.path, this.targetFilename))
} }
get targetRelPath() { get targetRelPath() {
return this.targetFilename return this.targetFilename
@ -74,7 +74,8 @@ class PodcastEpisodeDownload {
this.podcastEpisode = podcastEpisode this.podcastEpisode = podcastEpisode
const url = podcastEpisode.enclosure.url const url = podcastEpisode.enclosure.url
if (decodeURIComponent(url) !== url) { // Already encoded if (decodeURIComponent(url) !== url) {
// Already encoded
this.url = url this.url = url
} else { } else {
this.url = encodeURI(url) this.url = encodeURI(url)

View File

@ -45,8 +45,6 @@ class ApiRouter {
this.abMergeManager = Server.abMergeManager this.abMergeManager = Server.abMergeManager
/** @type {import('../managers/BackupManager')} */ /** @type {import('../managers/BackupManager')} */
this.backupManager = Server.backupManager this.backupManager = Server.backupManager
/** @type {import('../Watcher')} */
this.watcher = Server.watcher
/** @type {import('../managers/PodcastManager')} */ /** @type {import('../managers/PodcastManager')} */
this.podcastManager = Server.podcastManager this.podcastManager = Server.podcastManager
/** @type {import('../managers/AudioMetadataManager')} */ /** @type {import('../managers/AudioMetadataManager')} */

View File

@ -4,7 +4,9 @@ const { LogLevel, ScanResult } = require('../utils/constants')
const fileUtils = require('../utils/fileUtils') const fileUtils = require('../utils/fileUtils')
const scanUtils = require('../utils/scandir') const scanUtils = require('../utils/scandir')
const libraryFilters = require('../utils/queries/libraryFilters') const libraryFilters = require('../utils/queries/libraryFilters')
const Logger = require('../Logger')
const Database = require('../Database') const Database = require('../Database')
const Watcher = require('../Watcher')
const LibraryScan = require('./LibraryScan') const LibraryScan = require('./LibraryScan')
const LibraryItemScanData = require('./LibraryItemScanData') const LibraryItemScanData = require('./LibraryItemScanData')
const BookScanner = require('./BookScanner') const BookScanner = require('./BookScanner')
@ -128,6 +130,13 @@ class LibraryItemScanner {
const libraryFiles = [] const libraryFiles = []
for (let i = 0; i < fileItems.length; i++) { for (let i = 0; i < fileItems.length; i++) {
const fileItem = fileItems[i] const fileItem = fileItems[i]
if (Watcher.checkShouldIgnoreFilePath(fileItem.fullpath)) {
// Skip file if it's pending
Logger.info(`[LibraryItemScanner] Skipping watcher pending file "${fileItem.fullpath}" during scan of library item path "${libraryItemPath}"`)
continue
}
const newLibraryFile = new LibraryFile() const newLibraryFile = new LibraryFile()
// fileItem.path is the relative path // fileItem.path is the relative path
await newLibraryFile.setDataFromPath(fileItem.fullpath, fileItem.path) await newLibraryFile.setDataFromPath(fileItem.fullpath, fileItem.path)

View File

@ -228,6 +228,13 @@ module.exports.parsePodcastRssFeedXml = async (xml, excludeEpisodeMetadata = fal
module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => { module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => {
Logger.debug(`[podcastUtils] getPodcastFeed for "${feedUrl}"`) Logger.debug(`[podcastUtils] getPodcastFeed for "${feedUrl}"`)
let userAgent = 'audiobookshelf (+https://audiobookshelf.org; like iTMS)'
// Workaround for CBC RSS feeds rejecting our user agent string
// See: https://github.com/advplyr/audiobookshelf/issues/3322
if (feedUrl.startsWith('https://www.cbc.ca')) {
userAgent = 'audiobookshelf (+https://audiobookshelf.org; like iTMS) - CBC'
}
return axios({ return axios({
url: feedUrl, url: feedUrl,
method: 'GET', method: 'GET',
@ -235,7 +242,7 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => {
responseType: 'arraybuffer', responseType: 'arraybuffer',
headers: { headers: {
Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8', Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8',
'User-Agent': 'audiobookshelf (+https://audiobookshelf.org; like iTMS)' 'User-Agent': userAgent
}, },
httpAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(feedUrl), httpAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(feedUrl),
httpsAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(feedUrl) httpsAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(feedUrl)

View File

@ -73,15 +73,19 @@ module.exports = {
userPermissionBookWhere.replacements.filterValue = filterValue userPermissionBookWhere.replacements.filterValue = filterValue
} else if (filterGroup === 'progress') { } else if (filterGroup === 'progress') {
if (filterValue === 'not-finished') { if (filterValue === 'not-finished') {
attrQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.isFinished IS NULL OR mp.isFinished = 0)' attrQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id AND mp.userId = :userId WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.isFinished IS NULL OR mp.isFinished = 0)'
userPermissionBookWhere.replacements.userId = user.id
} else if (filterValue === 'finished') { } else if (filterValue === 'finished') {
const progQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.isFinished IS NULL OR mp.isFinished = 0)' const progQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id AND mp.userId = :userId WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.isFinished IS NULL OR mp.isFinished = 0)'
seriesWhere.push(Sequelize.where(Sequelize.literal(`(${progQuery})`), 0)) seriesWhere.push(Sequelize.where(Sequelize.literal(`(${progQuery})`), 0))
userPermissionBookWhere.replacements.userId = user.id
} else if (filterValue === 'not-started') { } else if (filterValue === 'not-started') {
const progQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.isFinished = 1 OR mp.currentTime > 0)' const progQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id AND mp.userId = :userId WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.isFinished = 1 OR mp.currentTime > 0)'
seriesWhere.push(Sequelize.where(Sequelize.literal(`(${progQuery})`), 0)) seriesWhere.push(Sequelize.where(Sequelize.literal(`(${progQuery})`), 0))
userPermissionBookWhere.replacements.userId = user.id
} else if (filterValue === 'in-progress') { } else if (filterValue === 'in-progress') {
attrQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.currentTime > 0 OR mp.ebookProgress > 0) AND mp.isFinished = 0' attrQuery = 'SELECT count(*) FROM books b, bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = b.id AND mp.userId = :userId WHERE bs.seriesId = series.id AND bs.bookId = b.id AND (mp.currentTime > 0 OR mp.ebookProgress > 0) AND mp.isFinished = 0'
userPermissionBookWhere.replacements.userId = user.id
} }
} }