Compare commits

...

113 Commits

Author SHA1 Message Date
advplyr 8b27c726d5 Version bump 2.9.0 2024-04-21 16:45:13 -05:00
advplyr 68418c1d3b Merge pull request #2820 from apocer/openid_signing_algorithm
Add option to set Signing Algorithm for OpenID Authentification
2024-04-21 16:07:30 -05:00
advplyr a8af6db3d6 Format update of authentication page for supported algorithms 2024-04-21 16:05:41 -05:00
advplyr af856ce1ec Merge branch 'master' into openid_signing_algorithm 2024-04-21 15:38:33 -05:00
advplyr aae8e7535a Fix:Home page always showing horizontal scrollbar 2024-04-21 15:36:01 -05:00
advplyr 359a2752d8 Fix:Server crash when scanning in invalid epub #2856 2024-04-21 15:07:53 -05:00
advplyr 9102a0045f Merge pull request #2803 from nichwall/vacuum_bundling
OpenAPI Spec, try 2
2024-04-20 14:57:32 -05:00
advplyr b124d61826 Update yaml docs to include BearerAuth 2024-04-20 14:57:38 -05:00
advplyr 8e6ead59ce Update yaml keys to camelCase 2024-04-20 14:55:57 -05:00
advplyr f74d741821 Fix:Server crash when updating media with external cover url that fails to download #2857 2024-04-20 11:34:21 -05:00
advplyr 15f83986e7 Update library stats previewicons padding 2024-04-18 17:45:47 -05:00
advplyr a57fe42dff Update:Library stats to format numbers using selected language #2861, clean up UI for library stats preview icons 2024-04-18 17:30:06 -05:00
advplyr b03198abd9 Add comments/jsdocs to i18n.js 2024-04-18 17:06:12 -05:00
advplyr ad30977781 Fix:Custom metadata provider including extra curly bracket in query string #2860 2024-04-18 16:16:59 -05:00
advplyr dbe10382fd Update:Podcast episode downloader only takes audio streams #2858 2024-04-17 17:09:36 -05:00
advplyr f0caf1a933 Update:Book matches support lowercase letters in audible ASIN #2849 2024-04-16 16:39:57 -05:00
advplyr 0f7c99d989 Fix:Retry transcode forcing AAC to handle the bad audible m4bs #2720 2024-04-15 15:14:30 -05:00
advplyr 60c65008dc Fix:Match all books only matching first 100 #2096 2024-04-14 17:19:21 -05:00
advplyr c4fd4ff9de Fix:Update metadata.json when using item metadata utils #2837 2024-04-12 17:34:10 -05:00
advplyr 29fc503503 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-04-11 17:29:31 -05:00
advplyr bca49616e1 Update:Podcast episode audio file ID3 tags use comment and description tag for description instead of subtitle #2843 2024-04-11 17:29:23 -05:00
advplyr cb49c17fc5 Merge pull request #2841 from nichwall/i18n-integration-fix
I18n integration fix
2024-04-11 17:17:13 -05:00
Nicholas Wallace 9e1686232b Example: es.json is fixed 2024-04-11 01:34:32 +00:00
Nicholas Wallace f702358bbd Example: missing key in es.json 2024-04-11 01:33:38 +00:00
Nicholas Wallace 9a0b8de354 Example: bad key in es.json 2024-04-11 01:31:22 +00:00
Nicholas Wallace 6ed6fff6bd Update i18n workflow to 1.2.0 2024-04-11 01:29:00 +00:00
Nicholas Wallace 75007bb371 Fix: i18n-integration not running on PRs 2024-04-11 01:28:40 +00:00
advplyr df9da095ef Map i18n strings to uk.json 2024-04-10 17:27:38 -05:00
advplyr 64c98722c3 Merge pull request #2840 from soaibsafi/master
Add Bengali translation
2024-04-10 17:25:59 -05:00
advplyr 36c1a8b2df Fix bn i18n string keys 2024-04-10 17:23:12 -05:00
soaibsafi 710d6af4b3 Adds Bengali translation 2024-04-10 19:46:39 +02:00
advplyr cd7ecb9933 Update:User permission tags accessible to user are alphabetized #2667 2024-04-09 17:54:09 -05:00
apocer f75f0b8cc8 show dropdown if issuer has list of algorithms 2024-04-09 22:29:06 +02:00
advplyr e60d2a9858 Add:Podcast library filter for languages and show language on podcast item page 2024-04-08 15:48:41 -05:00
advplyr 04993dd63d Update:Show language on book item page w/ link to filter #2834 2024-04-08 15:38:34 -05:00
advplyr 41af913280 Update:Edit item cover tab UI for small screen sizes #2832 2024-04-07 16:24:23 -05:00
advplyr 8dc0f2c67c Fix:Duplicate keys error when the same library item is shown twice in continue series 2024-04-06 17:48:40 -05:00
advplyr fc196180b3 Merge pull request #2805 from rasmuslos/master
Add client name to possible device info lines
2024-04-05 16:50:26 -05:00
advplyr 4a127d35b9 Update:Add client name and version to sessions table and session modal 2024-04-05 16:50:15 -05:00
advplyr 1525fdf4f6 Merge pull request #2821 from lkiesow/series-separator
Separator between multiple series
2024-04-04 17:55:53 -05:00
advplyr 8a29c998da Update item page series comma separated list to not include comma in link 2024-04-04 17:54:43 -05:00
Lars Kiesow f56d9f128f Separator between multiple series
If a book is part of multiple series, this patch adds a separator
between the series on the library item details page. With no separator,
it is not immediately clear that they are separate series.
2024-04-04 21:55:52 +02:00
advplyr c5785e9c20 Update:Increase breakpoint for player to change buttons to two lines #2799 2024-04-03 18:41:41 -05:00
advplyr 0ca91ecfff Merge pull request #2817 from springsunx/patch-2
Update zh-cn.json
2024-04-03 18:15:29 -05:00
basti 304d0f6d43 id_token_signed_respo... should be in new Client 2024-04-03 22:52:49 +02:00
basti 6c9a811472 Add ui and settings for OpenID Signing Algorithm 2024-04-03 16:18:13 +02:00
SunX 116a7fb994 Update zh-cn.json 2024-04-03 09:55:33 +08:00
advplyr 8e46181ba0 Update:Adding tooltips to player controls forward/backward and next/prev #2800 2024-04-02 18:05:44 -05:00
advplyr a336686e42 Merge pull request #2802 from pmangro/master
[PT-BR] OpenID permission strings
2024-04-01 17:14:16 -05:00
Rasmus Krämer c8957fe373 Add client name to possible device info lines 2024-04-01 16:20:09 +02:00
Nicholas Wallace ca7eaf9750 OpenAPI spec readme 2024-04-01 00:44:51 +00:00
Nicholas Wallace 74dd24febf Bundled spec 2024-04-01 00:26:55 +00:00
Nicholas Wallace 7b856474af Rename base document 2024-03-31 22:48:58 +00:00
Nicholas Wallace c7ac12a67a Split schema to sub files 2024-03-31 22:47:14 +00:00
pmangro 3264359771 [PT-BR] OpenID permission strings 2024-03-31 19:44:53 -03:00
advplyr c7cc994532 Fix:Handle enabling/disabling library watchers #2775 2024-03-31 14:57:55 -05:00
Nicholas Wallace afe40be957 Initial large file 2024-03-30 23:47:13 +00:00
advplyr a9c9c447f1 Merge pull request #2769 from Sapd/openid-permissions
OpenID: Integrate permissions (Fixes #2523)
2024-03-30 14:38:32 -05:00
advplyr aa1aeacc09 Map new translation strings 2024-03-30 14:26:55 -05:00
advplyr fc595bd799 Updates to authentication page for mobile screen sizes 2024-03-30 14:25:38 -05:00
advplyr a5d7a81519 Clean up formatting of advanced group/permission claims on authentication page 2024-03-30 14:17:34 -05:00
advplyr 7e8fd91fc5 Update OIDC advanced permissions check to only perform an update on changes
- Update permissions example to use UUIDv4 strings for allowedLibraries
- More validation on advanced permission JSON to ensure arrays are array of strings
- Only set allowedTags and allowedLibraries if the corresponding access all permission is false
2024-03-30 14:04:02 -05:00
advplyr c2ed0b7d3d Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-03-30 11:40:43 -05:00
advplyr aefda8bd51 Fix:Local sessions set date and dayOfWeek using the updatedAt timestamp passed in from the client #2795 2024-03-30 11:40:35 -05:00
advplyr 93bec282d2 Merge pull request #1888 from jorgectf/jorgectf/add-codeql-workflow
Add CodeQL workflow
2024-03-29 16:47:07 -05:00
advplyr 1396a432a4 Merge pull request #2797 from mikiher/rtl-fixes
Add dir="auto" attribute where RTL display is needed
2024-03-29 16:08:50 -05:00
Denis Arnst 90e1283058 OpenID: Allow email_verified null and also check username
Only disallow when email_verified explicitly false
Also check username besides preferred_username, even when its not included in OIDC checks (synology uses username)
2024-03-29 15:11:56 +01:00
Denis Arnst 8cd50d5684 OpenID: Don't downgrade root 2024-03-29 14:51:34 +01:00
advplyr 50bd2648aa Fix:Server crash on matching book with an author name ending in comma #2796 2024-03-28 17:00:07 -05:00
mikiher 33254654d5 Add dir="auto" attribute where it makes sense 2024-03-28 23:56:59 +02:00
Denis Arnst 617b8f4487 OpenID: Rename tags switch 2024-03-28 16:16:26 +01:00
advplyr f9b95bb003 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-03-27 16:11:57 -05:00
advplyr 740640884f Update:Support for comic files with webp images #2792 2024-03-27 16:11:47 -05:00
advplyr 86fea5c667 Merge pull request #2791 from mikiher/abs-windows-dispatch
Workflow to dispatch an ABS windows event on server release
2024-03-27 15:37:52 -05:00
mikiher 33e4b51aee Revert "add dummy pull_request event for the workflow to appear in the list"
This reverts commit 1cf0bd0f01.
2024-03-27 13:38:17 +02:00
mikiher 1cf0bd0f01 add dummy pull_request event for the workflow to appear in the list 2024-03-27 13:30:00 +02:00
mikiher 8ce5a5cdbd Add workflow to dispatch an abs-windows event 2024-03-27 13:18:02 +02:00
advplyr fc26b7af0a Merge pull request #2789 from justcallmelarry/bugfix/corretly-working-limit-for-continue-series-toggle
Fix book limit for the Continue Series shelf (with skip earlier books toggle active)
2024-03-25 15:45:45 -05:00
Lauri Vuorela 2d68fa2c27 fix book limit for the contiue series shelf 2024-03-25 16:32:29 +01:00
advplyr f241cb2280 Merge pull request #2787 from nichwall/translation_faq_link
Translation guide link added to readme
2024-03-24 05:07:08 -05:00
Nicholas Wallace 125346bb5c Translation guide link added to readme 2024-03-24 03:11:00 +00:00
advplyr b60f62cebf Merge pull request #2784 from rasmuslos/patch-1
Fix custom metadata provider crash
2024-03-23 15:27:51 -05:00
advplyr 51ff62356d Merge pull request #2773 from mikiher/fix-library-files-inconsistencies
Fix library files inconsistencies
2024-03-23 15:25:33 -05:00
advplyr f827aa97f8 Update library scanner findLibraryItemByItemToFileInoMatch query to iterate through json objects comparing inodes 2024-03-23 14:56:32 -05:00
mikiher 68276fe30b Fix handling of file moves from root folder to sub folder and back 2024-03-23 18:31:52 +02:00
Rasmus Krämer 961533765f Fix custom metadata provider crash 2024-03-23 14:54:34 +01:00
advplyr c1bbec22f0 Merge pull request #2780 from nichwall/i18n_integration_check
Add i18n integration workflow
2024-03-22 15:21:14 -05:00
Nicholas Wallace 7d0eb215d6 Add integration workflow 2024-03-22 01:28:50 +00:00
advplyr ff5226fa93 Update:Remove unused missing/invalid audiobook parts logic and keys 2024-03-21 14:38:52 -05:00
advplyr 8d7530254c Update:Re-order chapters table infront of audio tracks table on book item page #2778 2024-03-21 14:23:49 -05:00
advplyr 6957b4baf6 Merge pull request #2777 from burghy86/patch-14
Update it.json
2024-03-21 08:41:34 -05:00
burghy86 01c8d42291 Update it.json
month fix string
2024-03-21 12:52:49 +01:00
advplyr 1e21847852 Merge pull request #2772 from cor-bee/master
Add Ukrainian Translation and Podcast Region
2024-03-20 16:39:30 -05:00
mikiher 1bee082720 Update libraryFolderID correctly in scanFolderUpdates 2024-03-20 11:40:50 +02:00
Illia Pyshniak b0a9bed15a Update i18n.js
Add Ukrainian language and podcast region
2024-03-19 23:18:34 +02:00
Illia Pyshniak 1d7434cbbb Create uk.json 2024-03-19 23:12:29 +02:00
Denis Arnst 1646f0ebc2 OpenID: Ignore admin for advanced permissions
Also removed some semicolons
2024-03-19 19:35:34 +01:00
Denis Arnst 50330b0a60 Auth: Add translations 2024-03-19 19:18:47 +01:00
Denis Arnst f661e0835c Auth: Simplify Code 2024-03-19 19:18:38 +01:00
mikiher 9511122bae Fix LibraryItem and Media file update logic for library scans 2024-03-19 19:28:26 +02:00
Denis Arnst 56f1bfef50 Auth/OpenID: Implement Permissions via OpenID
* Ability to set group
* Ability to set more advanced permissions
* Modified TextInputWithLabel to provide an ability to specify a different placeholder then the name
2024-03-19 17:57:24 +01:00
advplyr 8e5b7504ae Merge pull request #2760 from pmangro/Continue-series
[PT-BR] Continue Series
2024-03-17 19:22:58 -05:00
advplyr 0a0006f949 Merge pull request #2756 from arcmagedr/master
Add Hebrew translation json and Hebrew to i18n.js
2024-03-17 19:22:21 -05:00
advplyr 5b836dfa28 Remove duplicate he language code 2024-03-17 19:19:52 -05:00
advplyr 8396900178 Merge pull request #2757 from mikiher/fix-unit-tests-flow
Change unit tests workflow to include conditional checkout steps
2024-03-17 19:14:41 -05:00
pmangro 8f80948211 [PT-BR] Continue Series 2024-03-17 19:09:16 -03:00
arcmagedr 4ad09ec3d8 Merge branch 'advplyr:master' into master 2024-03-17 22:26:49 +02:00
dor be4eb28b21 finished proofing Hebrew translation 2024-03-17 22:25:49 +02:00
mikiher f938fca2c7 Fix bug in workflow_dispatch checkout step 2024-03-17 07:57:28 +02:00
mikiher d562f6a69f Change unit-tests.yml workflow to include conditional checkout step 2024-03-17 07:36:13 +02:00
dor 752268effb mid point proofing 2024-03-16 21:00:44 +02:00
dor 9e3b3f3e12 add Hebrew translation json and Hebrew to i18n.js 2024-03-16 19:26:22 +02:00
Jorge 679bdf36b1 Add CodeQL workflow 2023-07-03 09:15:04 +02:00
122 changed files with 4580 additions and 444 deletions
+65
View File
@@ -0,0 +1,65 @@
name: "CodeQL"
on:
push:
branches: [ 'master' ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ 'master' ]
schedule:
- cron: '16 5 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin 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
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# 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.
# 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
# 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
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
+30
View File
@@ -0,0 +1,30 @@
name: Verify all i18n files are alphabetized
on:
pull_request:
paths:
- client/strings/** # Should only check if any strings changed
push:
paths:
- client/strings/** # Should only check if any strings changed
jobs:
update_translations:
runs-on: ubuntu-latest
steps:
# Check out the repository
- name: Checkout repository
uses: actions/checkout@v4
# Set up node to run the javascript
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: "20"
# The only argument is the `directory`, which is where the i18n files are
# stored.
- name: Run Update JSON Files action
uses: audiobookshelf/audiobookshelf-i18n-updater@v1.2.0
with:
directory: "client/strings/" # Adjust the directory path as needed
+17
View File
@@ -0,0 +1,17 @@
name: Dispatch an abs-windows event
on:
release:
types: [published]
workflow_dispatch:
jobs:
abs-windows-dispatch:
runs-on: ubuntu-latest
steps:
- name: Send a remote repository dispatch event
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.ABS_WINDOWS_PAT }}
repository: mikiher/audiobookshelf-windows
event-type: build-windows
+8 -2
View File
@@ -11,13 +11,19 @@ on:
jobs:
run-unit-tests:
name: Run Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
- name: Checkout (push/pull request)
uses: actions/checkout@v4
if: github.event_name != 'workflow_dispatch'
- name: Checkout (workflow_dispatch)
uses: actions/checkout@v4
with:
ref: ${{ github.event_name != 'workflow_dispatch' && github.ref_name || inputs.ref}}
ref: ${{ inputs.ref }}
if: github.event_name == 'workflow_dispatch'
- name: Set up Node.js
uses: actions/setup-node@v4
+1 -2
View File
@@ -30,8 +30,7 @@
}
.bookshelf-row {
/* Sidebar width + scrollbar width */
width: calc(100vw - 88px);
width: calc(100vw - (100vw - 100%));
}
@media (max-width: 768px) {
+1
View File
@@ -24,6 +24,7 @@
word-wrap: normal;
direction: ltr;
-webkit-font-smoothing: antialiased;
vertical-align: top;
}
.material-icons:not([class*="text-"]) {
+1 -1
View File
@@ -4,7 +4,7 @@
<div class="w-full h-full pt-6">
<div v-if="shelf.type === 'book' || shelf.type === 'podcast'" class="flex items-center">
<template v-for="(entity, index) in shelf.entities">
<cards-lazy-book-card :key="entity.id" :ref="`shelf-book-${entity.id}`" :index="index" :width="bookCoverWidth" :height="bookCoverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :book-mount="entity" :continue-listening-shelf="continueListeningShelf" class="relative mx-2" @hook:updated="updatedBookCard" @select="selectItem" @edit="editItem" />
<cards-lazy-book-card :key="`${entity.id}-${index}`" :ref="`shelf-book-${entity.id}`" :index="index" :width="bookCoverWidth" :height="bookCoverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :book-mount="entity" :continue-listening-shelf="continueListeningShelf" class="relative mx-2" @hook:updated="updatedBookCard" @select="selectItem" @edit="editItem" />
</template>
</div>
<div v-if="shelf.type === 'episode'" class="flex items-center">
@@ -1,10 +1,10 @@
<template>
<div v-if="streamLibraryItem" id="mediaPlayerContainer" class="w-full fixed bottom-0 left-0 right-0 h-48 md:h-40 z-50 bg-primary px-2 md:px-4 pb-1 md:pb-4 pt-2">
<div v-if="streamLibraryItem" id="mediaPlayerContainer" class="w-full fixed bottom-0 left-0 right-0 h-48 lg:h-40 z-50 bg-primary px-2 lg:px-4 pb-1 lg:pb-4 pt-2">
<div id="videoDock" />
<div class="absolute left-2 top-2 md:left-4 cursor-pointer">
<div class="absolute left-2 top-2 lg:left-4 cursor-pointer">
<covers-book-cover expand-on-click :library-item="streamLibraryItem" :width="bookCoverWidth" :book-cover-aspect-ratio="coverAspectRatio" />
</div>
<div class="flex items-start mb-6 md:mb-0" :class="playerHandler.isVideo ? 'ml-4 pl-96' : isSquareCover ? 'pl-18 sm:pl-24' : 'pl-12 sm:pl-16'">
<div class="flex items-start mb-6 lg:mb-0" :class="playerHandler.isVideo ? 'ml-4 pl-96' : isSquareCover ? 'pl-18 sm:pl-24' : 'pl-12 sm:pl-16'">
<div class="min-w-0">
<nuxt-link :to="`/item/${streamLibraryItem.id}`" class="hover:underline cursor-pointer text-sm sm:text-lg block truncate">
{{ title }}
@@ -29,7 +29,7 @@
</div>
<div class="flex-grow" />
<ui-tooltip direction="top" :text="$strings.LabelClosePlayer">
<button :aria-label="$strings.LabelClosePlayer" class="material-icons sm:px-2 py-1 md:p-4 cursor-pointer text-xl sm:text-2xl" @click="closePlayer">close</button>
<button :aria-label="$strings.LabelClosePlayer" class="material-icons sm:px-2 py-1 lg:p-4 cursor-pointer text-xl sm:text-2xl" @click="closePlayer">close</button>
</ui-tooltip>
</div>
<player-ui
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-4 mb-8">
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-2 sm:p-4 mb-8">
<div class="flex items-center mb-2">
<slot name="header-prefix"></slot>
<h1 class="text-xl">{{ headerText }}</h1>
+3 -19
View File
@@ -6,7 +6,7 @@
</div>
<!-- Alternative bookshelf title/author/sort -->
<div v-if="isAlternativeBookshelfView || isAuthorBookshelfView" class="absolute left-0 z-50 w-full" :style="{ bottom: `-${titleDisplayBottomOffset}rem` }">
<div v-if="isAlternativeBookshelfView || isAuthorBookshelfView" dir="auto" class="absolute left-0 z-50 w-full" :style="{ bottom: `-${titleDisplayBottomOffset}rem` }">
<div :style="{ fontSize: 0.9 * sizeMultiplier + 'rem' }">
<ui-tooltip v-if="displayTitle" :text="displayTitle" :disabled="!displayTitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<p ref="displayTitle" class="truncate">{{ displayTitle }}</p>
@@ -358,7 +358,7 @@ export default {
},
showError() {
if (this.recentEpisode) return false // Dont show podcast error on episode card
return this.numInvalidAudioFiles || this.numMissingParts || this.isMissing || this.isInvalid
return this.isMissing || this.isInvalid
},
libraryItemIdStreaming() {
return this.store.getters['getLibraryItemIdStreaming']
@@ -388,29 +388,13 @@ export default {
isInvalid() {
return this._libraryItem.isInvalid
},
numMissingParts() {
if (this.isPodcast) return 0
return this.media.numMissingParts
},
numInvalidAudioFiles() {
if (this.isPodcast) return 0
return this.media.numInvalidAudioFiles
},
errorText() {
if (this.isMissing) return 'Item directory is missing!'
else if (this.isInvalid) {
if (this.isPodcast) return 'Podcast has no episodes'
return 'Item has no audio tracks & ebook'
}
let txt = ''
if (this.numMissingParts) {
txt += `${this.numMissingParts} missing parts.`
}
if (this.numInvalidAudioFiles) {
if (txt) txt += ' '
txt += `${this.numInvalidAudioFiles} invalid audio files.`
}
return txt || 'Unknown Error'
return 'Unknown Error'
},
overlayWrapperClasslist() {
const classes = []
@@ -89,6 +89,14 @@
</template>
</div>
</div>
<div v-if="language" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelLanguage }}</span>
</div>
<div>
<nuxt-link :to="`/library/${libraryId}/bookshelf?filter=languages.${$encode(language)}`" class="hover:underline">{{ language }}</nuxt-link>
</div>
</div>
<div v-if="tracks.length || audioFile || (isPodcast && totalPodcastDuration)" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
@@ -182,6 +190,9 @@ export default {
narrators() {
return this.mediaMetadata.narrators || []
},
language() {
return this.mediaMetadata.language || null
},
durationPretty() {
if (this.isPodcast) return this.$elapsedPrettyExtended(this.totalPodcastDuration)
@@ -235,6 +235,11 @@ export default {
value: 'tags',
sublist: true
},
{
text: this.$strings.LabelLanguage,
value: 'languages',
sublist: true
},
{
text: this.$strings.ButtonIssues,
value: 'issues',
@@ -88,10 +88,11 @@
<p class="mb-1">{{ _session.mediaPlayer }}</p>
<p v-if="hasDeviceInfo" class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">{{ $strings.LabelDevice }}</p>
<p v-if="clientDisplayName" class="mb-1">{{ clientDisplayName }}</p>
<p v-if="deviceInfo.ipAddress" class="mb-1">{{ deviceInfo.ipAddress }}</p>
<p v-if="osDisplayName" class="mb-1">{{ osDisplayName }}</p>
<p v-if="deviceInfo.browserName" class="mb-1">{{ deviceInfo.browserName }}</p>
<p v-if="clientDisplayName" class="mb-1">{{ clientDisplayName }}</p>
<p v-if="deviceDisplayName" class="mb-1">{{ deviceDisplayName }}</p>
<p v-if="deviceInfo.sdkVersion" class="mb-1">SDK {{ $strings.LabelVersion }}: {{ deviceInfo.sdkVersion }}</p>
<p v-if="deviceInfo.deviceType" class="mb-1">{{ $strings.LabelType }}: {{ deviceInfo.deviceType }}</p>
</div>
@@ -141,10 +142,14 @@ export default {
if (!this.deviceInfo.osName) return null
return `${this.deviceInfo.osName} ${this.deviceInfo.osVersion}`
},
clientDisplayName() {
deviceDisplayName() {
if (!this.deviceInfo.manufacturer || !this.deviceInfo.model) return null
return `${this.deviceInfo.manufacturer} ${this.deviceInfo.model}`
},
clientDisplayName() {
if (!this.deviceInfo.clientName) return null
return `${this.deviceInfo.clientName} ${this.deviceInfo.clientVersion || ''}`
},
playMethodName() {
const playMethod = this._session.playMethod
if (playMethod === this.$constants.PlayMethod.DIRECTPLAY) return 'Direct Play'
+9 -9
View File
@@ -1,7 +1,7 @@
<template>
<div class="w-full h-full overflow-hidden overflow-y-auto px-2 sm:px-4 py-6 relative">
<div class="flex flex-wrap mb-4">
<div class="relative">
<div class="flex flex-col sm:flex-row mb-4">
<div class="relative self-center">
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId, libraryItemUpdatedAt, true)" :width="120" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<!-- book cover overlay -->
@@ -14,7 +14,7 @@
</div>
</div>
</div>
<div class="flex-grow sm:pl-2 md:pl-6 sm:pr-2 mt-2 md:mt-0">
<div class="flex-grow sm:pl-2 md:pl-6 sm:pr-2 mt-6 md:mt-0">
<div class="flex items-center">
<div v-if="userCanUpload" class="w-10 md:w-40 pr-2 md:min-w-32">
<ui-file-input ref="fileInput" @change="fileUploadSelected">
@@ -49,20 +49,20 @@
</div>
</div>
<form @submit.prevent="submitSearchForm">
<div class="flex items-center justify-start -mx-1 h-20">
<div class="w-48 px-1">
<div class="flex flex-wrap sm:flex-nowrap items-center justify-start -mx-1">
<div class="w-48 flex-grow p-1">
<ui-dropdown v-model="provider" :items="providers" :label="$strings.LabelProvider" small />
</div>
<div class="w-72 px-1">
<div class="w-72 flex-grow p-1">
<ui-text-input-with-label v-model="searchTitle" :label="searchTitleLabel" :placeholder="$strings.PlaceholderSearch" />
</div>
<div v-show="provider != 'itunes' && provider != 'audiobookcovers'" class="w-72 px-1">
<div v-show="provider != 'itunes' && provider != 'audiobookcovers'" class="w-72 flex-grow p-1">
<ui-text-input-with-label v-model="searchAuthor" :label="$strings.LabelAuthor" />
</div>
<ui-btn class="mt-5 ml-1" type="submit">{{ $strings.ButtonSearch }}</ui-btn>
<ui-btn class="mt-5 ml-1 md:min-w-24" :padding-x="4" type="submit">{{ $strings.ButtonSearch }}</ui-btn>
</div>
</form>
<div v-if="hasSearched" class="flex items-center flex-wrap justify-center max-h-80 overflow-y-scroll mt-2 max-w-full">
<div v-if="hasSearched" class="flex items-center flex-wrap justify-center sm:max-h-80 sm:overflow-y-scroll mt-2 max-w-full">
<p v-if="!coversFound.length">{{ $strings.MessageNoCoversFound }}</p>
<template v-for="cover in coversFound">
<div :key="cover" class="m-0.5 mb-5 border-2 border-transparent hover:border-yellow-300 cursor-pointer" :class="cover === coverPath ? 'border-yellow-300' : ''" @click="updateCover(cover)">
@@ -29,7 +29,7 @@
<td class="text-center w-20 min-w-20">
<p>{{ episode.episode }}</p>
</td>
<td>
<td dir="auto">
{{ episode.title }}
</td>
<td class="font-mono text-center">
+4 -1
View File
@@ -508,7 +508,10 @@ export default {
} else if (key === 'author' && !this.isPodcast) {
var authors = this.selectedMatch[key]
if (!Array.isArray(authors)) {
authors = authors.split(',').map((au) => au.trim())
authors = authors
.split(',')
.map((au) => au.trim())
.filter((au) => !!au)
}
var authorPayload = []
authors.forEach((authorName) =>
@@ -15,8 +15,8 @@
<p class="text-xs text-gray-300">{{ podcastAuthor }}</p>
</div>
</div>
<p class="text-lg font-semibold mb-6">{{ title }}</p>
<div v-if="description" class="default-style" v-html="description" />
<p dir="auto" class="text-lg font-semibold mb-6">{{ title }}</p>
<div v-if="description" dir="auto" class="default-style" v-html="description" />
<p v-else class="mb-2">{{ $strings.MessageNoDescription }}</p>
</div>
</modals-modal>
@@ -1,22 +1,30 @@
<template>
<div class="flex pt-4 pb-2 md:pt-0 md:pb-2">
<div class="flex items-center pt-4 pb-2 lg:pt-0 lg:pb-2">
<div class="flex-grow" />
<template v-if="!loading">
<button :aria-label="$strings.ButtonPreviousChapter" class="flex items-center justify-center text-gray-300 mr-4 md:mr-8" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
<span class="material-icons text-2xl sm:text-3xl">first_page</span>
</button>
<button :aria-label="$strings.ButtonJumpBackward" class="flex items-center justify-center text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
<span class="material-icons text-2xl sm:text-3xl">replay_10</span>
</button>
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 md:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
<ui-tooltip direction="top" :text="$strings.ButtonPreviousChapter" class="mr-4 lg:mr-8">
<button :aria-label="$strings.ButtonPreviousChapter" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
<span class="material-icons text-2xl sm:text-3xl">first_page</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="$strings.ButtonJumpBackward">
<button :aria-label="$strings.ButtonJumpBackward" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
<span class="material-icons text-2xl sm:text-3xl">replay_10</span>
</button>
</ui-tooltip>
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
<span class="material-icons text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
</button>
<button :aria-label="$strings.ButtonJumpForward" class="flex items-center justify-center text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
<span class="material-icons text-2xl sm:text-3xl">forward_10</span>
</button>
<button :aria-label="$strings.ButtonNextChapter" class="flex items-center justify-center ml-4 md:ml-8" :disabled="!hasNextChapter" :class="hasNextChapter ? 'text-gray-300' : 'text-gray-500'" @mousedown.prevent @mouseup.prevent @click.stop="nextChapter">
<span class="material-icons text-2xl sm:text-3xl">last_page</span>
</button>
<ui-tooltip direction="top" :text="$strings.ButtonJumpForward">
<button :aria-label="$strings.ButtonJumpForward" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
<span class="material-icons text-2xl sm:text-3xl">forward_10</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="$strings.ButtonNextChapter" class="ml-4 lg:ml-8">
<button :aria-label="$strings.ButtonNextChapter" :disabled="!hasNextChapter" :class="hasNextChapter ? 'text-gray-300' : 'text-gray-500'" @mousedown.prevent @mouseup.prevent @click.stop="nextChapter">
<span class="material-icons text-2xl sm:text-3xl">last_page</span>
</button>
</ui-tooltip>
<controls-playback-speed-control v-model="playbackRateInput" @input="playbackRateUpdated" @change="playbackRateChanged" />
</template>
<template v-else>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div class="w-full -mt-6">
<div class="w-full relative mb-1">
<div class="absolute -top-10 md:top-0 right-0 lg:right-2 flex items-center h-full">
<div class="absolute -top-10 lg:top-0 right-0 lg:right-2 flex items-center h-full">
<!-- <span class="material-icons text-2xl cursor-pointer" @click="toggleFullscreen(true)">expand_less</span> -->
<ui-tooltip direction="top" :text="$strings.LabelVolume">
+1 -1
View File
@@ -334,7 +334,7 @@ export default {
}
},
parseFilenames(filenames) {
const acceptableImages = ['.jpeg', '.jpg', '.png']
const acceptableImages = ['.jpeg', '.jpg', '.png', '.webp']
var imageFiles = filenames.filter((f) => {
return acceptableImages.includes((Path.extname(f) || '').toLowerCase())
})
+16 -16
View File
@@ -1,45 +1,45 @@
<template>
<div class="flex flex-wrap justify-center mt-6">
<div class="flex px-2">
<svg class="h-14 w-14 md:h-18 md:w-18" viewBox="0 0 24 24">
<div class="flex p-2">
<svg class="h-14 w-14" viewBox="0 0 24 24">
<path fill="currentColor" d="M9 3V18H12V3H9M12 5L16 18L19 17L15 4L12 5M5 5V18H8V5H5M3 19V21H21V19H3Z" />
</svg>
<div class="px-2">
<p class="text-4xl md:text-5xl font-bold">{{ totalItems }}</p>
<div class="px-1">
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(totalItems) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ $strings.LabelStatsItemsInLibrary }}</p>
</div>
</div>
<div class="flex px-4">
<span class="material-icons text-7xl">show_chart</span>
<div class="flex p-2">
<span class="material-icons text-5xl py-1">show_chart</span>
<div class="px-1">
<p class="text-4xl md:text-5xl font-bold">{{ totalTime }}</p>
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(totalTime) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ useOverallHours ? $strings.LabelStatsOverallHours : $strings.LabelStatsOverallDays }}</p>
</div>
</div>
<div v-if="isBookLibrary" class="flex px-4">
<svg class="h-14 w-14 md:h-18 md:w-18" viewBox="0 0 24 24">
<div v-if="isBookLibrary" class="flex p-2">
<svg class="h-14 w-14" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,6A2,2 0 0,0 10,8A2,2 0 0,0 12,10A2,2 0 0,0 14,8A2,2 0 0,0 12,6M12,13C14.67,13 20,14.33 20,17V20H4V17C4,14.33 9.33,13 12,13M12,14.9C9.03,14.9 5.9,16.36 5.9,17V18.1H18.1V17C18.1,16.36 14.97,14.9 12,14.9Z" />
</svg>
<div class="px-1">
<p class="text-4xl md:text-5xl font-bold">{{ totalAuthors }}</p>
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(totalAuthors) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ $strings.LabelStatsAuthors }}</p>
</div>
</div>
<div class="flex px-4">
<span class="material-icons-outlined text-6xl pt-1">insert_drive_file</span>
<div class="flex p-2">
<span class="material-icons-outlined text-5xl pt-1">insert_drive_file</span>
<div class="px-1">
<p class="text-4xl md:text-5xl font-bold">{{ totalSizeNum }}</p>
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(totalSizeNum) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ $strings.LabelSize }} ({{ totalSizeMod }})</p>
</div>
</div>
<div class="flex px-4">
<span class="material-icons-outlined text-6xl pt-1">audio_file</span>
<div class="flex p-2">
<span class="material-icons-outlined text-5xl pt-1">audio_file</span>
<div class="px-1">
<p class="text-4xl md:text-5xl font-bold">{{ numAudioTracks }}</p>
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(numAudioTracks) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ $strings.LabelStatsAudioTracks }}</p>
</div>
</div>
+1 -1
View File
@@ -21,7 +21,7 @@
<td class="text-left">
<p class="px-4">{{ chapter.id }}</p>
</td>
<td>
<td dir="auto">
{{ chapter.title }}
</td>
<td class="font-mono text-center hover:underline cursor-pointer" @click.stop="goToTimestamp(chapter.start)">
@@ -30,7 +30,7 @@
<widgets-podcast-type-indicator :type="downloadQueued.episodeType" />
</div>
</td>
<td class="px-4">
<td dir="auto" class="px-4">
{{ downloadQueued.episodeDisplayTitle }}
</td>
<td class="text-xs">
@@ -2,7 +2,7 @@
<div :id="`lazy-episode-${index}`" class="w-full h-full cursor-pointer" @mouseover="mouseover" @mouseleave="mouseleave">
<div class="flex" @click="clickedEpisode">
<div class="flex-grow">
<div class="flex items-center">
<div dir="auto" class="flex items-center">
<span class="text-sm font-semibold">{{ episodeTitle }}</span>
<widgets-podcast-type-indicator :type="episodeType" />
</div>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div>
<input ref="fileInput" type="file" :accept="accept" class="hidden" @change="inputChanged" />
<ui-btn @click="clickUpload" color="primary" class="hidden md:block" type="text"><slot /></ui-btn>
<ui-btn @click="clickUpload" color="primary" class="hidden md:block w-full" type="text"><slot /></ui-btn>
<ui-icon-btn @click="clickUpload" icon="upload" class="block md:hidden" />
</div>
</template>
+1 -1
View File
@@ -1,6 +1,6 @@
<template>
<div ref="wrapper" class="relative">
<input :id="inputId" :name="inputName" ref="input" v-model="inputValue" :type="actualType" :step="step" :min="min" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" class="rounded bg-primary text-gray-200 focus:border-gray-300 focus:bg-bg focus:outline-none border border-gray-600 h-full w-full" :class="classList" @keyup="keyup" @change="change" @focus="focused" @blur="blurred" />
<input :id="inputId" :name="inputName" ref="input" v-model="inputValue" :type="actualType" :step="step" :min="min" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" dir="auto" class="rounded bg-primary text-gray-200 focus:border-gray-300 focus:bg-bg focus:outline-none border border-gray-600 h-full w-full" :class="classList" @keyup="keyup" @change="change" @focus="focused" @blur="blurred" />
<div v-if="clearable && inputValue" class="absolute top-0 right-0 h-full px-2 flex items-center justify-center">
<span class="material-icons text-gray-300 cursor-pointer" style="font-size: 1.1rem" @click.stop.prevent="clear">close</span>
</div>
+2 -1
View File
@@ -5,7 +5,7 @@
>{{ label }}<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em></label
>
</slot>
<ui-text-input :placeholder="label" :inputId="identifier" ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" :class="inputClass" @blur="inputBlurred" />
<ui-text-input :placeholder="placeholder || label" :inputId="identifier" ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" :class="inputClass" @blur="inputBlurred" />
</div>
</template>
@@ -14,6 +14,7 @@ export default {
props: {
value: [String, Number],
label: String,
placeholder: String,
note: String,
type: {
type: String,
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<textarea ref="input" v-model="inputValue" :rows="rows" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" class="py-2 px-3 rounded bg-primary text-gray-200 focus:border-gray-500 focus:outline-none" :class="transparent ? '' : 'border border-gray-600'" @change="change" />
<textarea ref="input" v-model="inputValue" :rows="rows" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" dir="auto" class="py-2 px-3 rounded bg-primary text-gray-200 focus:border-gray-500 focus:outline-none" :class="transparent ? '' : 'border border-gray-600'" @change="change" />
</template>
<script>
@@ -1,84 +0,0 @@
<template>
<div class="w-full">
<div v-if="missingParts.length" class="bg-error border-red-800 shadow-md p-4">
<p class="text-sm mb-2">
{{ $strings.LabelMissingParts }} <span class="text-sm">({{ missingParts.length }})</span>
</p>
<p class="text-sm font-mono">{{ missingPartChunks.join(', ') }}</p>
</div>
<div v-if="invalidParts.length" class="bg-error border-red-800 shadow-md p-4">
<p class="text-sm mb-2">
{{ $strings.LabelInvalidParts }} <span class="text-sm">({{ invalidParts.length }})</span>
</p>
<div>
<p v-for="part in invalidParts" :key="part.filename" class="text-sm font-mono">{{ part.filename }}: {{ part.error }}</p>
</div>
</div>
<tables-tracks-table :title="$strings.LabelStatsAudioTracks" :tracks="tracksWithAudioFile" :is-file="isFile" :library-item-id="libraryItemId" class="mt-6" />
</div>
</template>
<script>
export default {
props: {
libraryItemId: String,
media: {
type: Object,
default: () => {}
},
isFile: Boolean
},
data() {
return {}
},
computed: {
tracksWithAudioFile() {
return this.media.tracks.map((track) => {
track.audioFile = this.media.audioFiles.find((af) => af.metadata.path === track.metadata.path)
return track
})
},
missingPartChunks() {
if (this.missingParts === 1) return this.missingParts[0]
var chunks = []
var currentIndex = this.missingParts[0]
var currentChunk = [this.missingParts[0]]
for (let i = 1; i < this.missingParts.length; i++) {
var partIndex = this.missingParts[i]
if (currentIndex === partIndex - 1) {
currentChunk.push(partIndex)
currentIndex = partIndex
} else {
// console.log('Chunk ended', currentChunk.join(', '), currentIndex, partIndex)
if (currentChunk.length === 0) {
console.error('How is current chunk 0?', currentChunk.join(', '))
}
chunks.push(currentChunk)
currentChunk = [partIndex]
currentIndex = partIndex
}
}
if (currentChunk.length) {
chunks.push(currentChunk)
}
chunks = chunks.map((chunk) => {
if (chunk.length === 1) return chunk[0]
else return `${chunk[0]}-${chunk[chunk.length - 1]}`
})
return chunks
},
missingParts() {
return this.media.missingParts || []
},
invalidParts() {
return this.media.invalidParts || []
}
},
methods: {},
mounted() {}
}
</script>
+3 -3
View File
@@ -10,10 +10,10 @@
<span class="material-icons text-2xl">chevron_right</span>
</button>
</div>
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll -mx-2" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex" :style="{ height: height + 'px' }">
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex space-x-4" :style="{ height: height + 'px' }">
<template v-for="(item, index) in items">
<cards-author-card :key="item.id" :ref="`slider-item-${item.id}`" :index="index" :author="item" :height="cardHeight" :width="cardWidth" class="relative mx-2" @edit="editAuthor" @hook:updated="setScrollVars" />
<cards-author-card :key="item.id" :ref="`slider-item-${item.id}`" :index="index" :author="item" :height="cardHeight" :width="cardWidth" class="relative" @edit="editAuthor" @hook:updated="setScrollVars" />
</template>
</div>
</div>
+3 -3
View File
@@ -10,8 +10,8 @@
<span class="material-icons text-2xl">chevron_right</span>
</button>
</div>
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll -mx-2" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex" :style="{ height: height + 'px' }">
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex space-x-4" :style="{ height: height + 'px' }">
<template v-for="(item, index) in items">
<cards-lazy-book-card
:key="item.recentEpisode.id"
@@ -23,7 +23,7 @@
:book-cover-aspect-ratio="bookCoverAspectRatio"
:bookshelf-view="bookshelfView"
:continue-listening-shelf="continueListeningShelf"
class="relative mx-2"
class="relative"
@edit="editEpisode"
@editPodcast="editPodcast"
@select="selectItem"
+17 -3
View File
@@ -10,10 +10,24 @@
<span class="material-icons text-2xl">chevron_right</span>
</button>
</div>
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll -mx-2" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex" :style="{ height: height + 'px' }">
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex space-x-4" :style="{ height: height + 'px' }">
<template v-for="(item, index) in items">
<cards-lazy-book-card :key="item.id + '-' + shelfId" :ref="`slider-item-${item.id}`" :index="index" :book-mount="item" :height="cardHeight" :width="cardWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" :bookshelf-view="bookshelfView" :continue-listening-shelf="continueListeningShelf" class="relative mx-2" @edit="editItem" @select="selectItem" @hook:updated="setScrollVars" />
<cards-lazy-book-card
:key="item.id + '-' + shelfId + '-' + index"
:ref="`slider-item-${item.id}`"
:index="index"
:book-mount="item"
:height="cardHeight"
:width="cardWidth"
:book-cover-aspect-ratio="bookCoverAspectRatio"
:bookshelf-view="bookshelfView"
:continue-listening-shelf="continueListeningShelf"
class="relative"
@edit="editItem"
@select="selectItem"
@hook:updated="setScrollVars"
/>
</template>
</div>
</div>
@@ -10,10 +10,10 @@
<span class="material-icons text-2xl">chevron_right</span>
</button>
</div>
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll -mx-2" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex" :style="{ height: height + 'px' }">
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex space-x-4" :style="{ height: height + 'px' }">
<template v-for="item in items">
<cards-narrator-card :key="item.name" :ref="`slider-item-${item.name}`" :narrator="item" :height="cardHeight" :width="cardWidth" class="relative mx-2" @hook:updated="setScrollVars" />
<cards-narrator-card :key="item.name" :ref="`slider-item-${item.name}`" :narrator="item" :height="cardHeight" :width="cardWidth" class="relative" @hook:updated="setScrollVars" />
</template>
</div>
</div>
+3 -3
View File
@@ -10,10 +10,10 @@
<span class="material-icons text-2xl">chevron_right</span>
</button>
</div>
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll -mx-2" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex" :style="{ height: height + 'px' }">
<div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll" style="scroll-behavior: smooth" @scroll="scrolled">
<div class="flex space-x-4" :style="{ height: height + 'px' }">
<template v-for="(item, index) in items">
<cards-lazy-series-card :key="item.id" :ref="`slider-item-${item.id}`" :index="index" :series-mount="item" :height="cardHeight" :width="cardWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" :bookshelf-view="$constants.BookshelfView.DETAIL" class="relative mx-2" @hook:updated="setScrollVars" />
<cards-lazy-series-card :key="item.id" :ref="`slider-item-${item.id}`" :index="index" :series-mount="item" :height="cardHeight" :width="cardWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" :bookshelf-view="$constants.BookshelfView.DETAIL" class="relative" @hook:updated="setScrollVars" />
</template>
</div>
</div>
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf-client",
"version": "2.8.1",
"version": "2.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf-client",
"version": "2.8.1",
"version": "2.9.0",
"license": "ISC",
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
"version": "2.8.1",
"version": "2.9.0",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast client",
"main": "index.js",
+1 -1
View File
@@ -281,7 +281,7 @@ export default {
return this.media.audioFiles || []
},
audioTracks() {
return this.audioFiles.filter((af) => !af.exclude && !af.invalid)
return this.audioFiles.filter((af) => !af.exclude)
},
selectedChapterId() {
return this.selectedChapter ? this.selectedChapter.id : null
-3
View File
@@ -137,9 +137,6 @@ export default {
})
return count
},
missingParts() {
return this.media.missingParts || []
},
libraryItemId() {
return this.libraryItem.id
},
+1 -1
View File
@@ -249,7 +249,7 @@ export default {
return this.media.metadata || {}
},
audioFiles() {
return (this.media.audioFiles || []).filter((af) => !af.exclude && !af.invalid)
return (this.media.audioFiles || []).filter((af) => !af.exclude)
},
isSingleM4b() {
return this.audioFiles.length === 1 && this.audioFiles[0].metadata.ext.toLowerCase() === '.m4b'
+67 -5
View File
@@ -58,29 +58,53 @@
<ui-text-input-with-label ref="openidClientSecret" v-model="newAuthSettings.authOpenIDClientSecret" :disabled="savingSettings" :label="'Client Secret'" class="mb-2" />
<ui-dropdown v-if="openIdSigningAlgorithmsSupportedByIssuer.length" v-model="newAuthSettings.authOpenIDTokenSigningAlgorithm" :items="openIdSigningAlgorithmsSupportedByIssuer" :label="'Signing Algorithm'" :disabled="savingSettings" class="mb-2" />
<ui-text-input-with-label v-else ref="openidTokenSigningAlgorithm" v-model="newAuthSettings.authOpenIDTokenSigningAlgorithm" :disabled="savingSettings" :label="'Signing Algorithm'" class="mb-2" />
<ui-multi-select ref="redirectUris" v-model="newAuthSettings.authOpenIDMobileRedirectURIs" :items="newAuthSettings.authOpenIDMobileRedirectURIs" :label="$strings.LabelMobileRedirectURIs" class="mb-2" :menuDisabled="true" :disabled="savingSettings" />
<p class="pl-4 text-sm text-gray-300 mb-2" v-html="$strings.LabelMobileRedirectURIsDescription" />
<p class="sm:pl-4 text-sm text-gray-300 mb-2" v-html="$strings.LabelMobileRedirectURIsDescription" />
<ui-text-input-with-label ref="buttonTextInput" v-model="newAuthSettings.authOpenIDButtonText" :disabled="savingSettings" :label="$strings.LabelButtonText" class="mb-2" />
<div class="flex items-center pt-1 mb-2">
<div class="flex sm:items-center flex-col sm:flex-row pt-1 mb-2">
<div class="w-44">
<ui-dropdown v-model="newAuthSettings.authOpenIDMatchExistingBy" small :items="matchingExistingOptions" :label="$strings.LabelMatchExistingUsersBy" :disabled="savingSettings" />
</div>
<p class="pl-4 text-sm text-gray-300 mt-5">{{ $strings.LabelMatchExistingUsersByDescription }}</p>
<p class="sm:pl-4 text-sm text-gray-300 mt-2 sm:mt-5">{{ $strings.LabelMatchExistingUsersByDescription }}</p>
</div>
<div class="flex items-center py-4 px-1">
<div class="flex items-center py-4 px-1 w-full">
<ui-toggle-switch labeledBy="auto-redirect-toggle" v-model="newAuthSettings.authOpenIDAutoLaunch" :disabled="savingSettings" />
<p id="auto-redirect-toggle" class="pl-4 whitespace-nowrap">{{ $strings.LabelAutoLaunch }}</p>
<p class="pl-4 text-sm text-gray-300" v-html="$strings.LabelAutoLaunchDescription" />
</div>
<div class="flex items-center py-4 px-1">
<div class="flex items-center py-4 px-1 w-full">
<ui-toggle-switch labeledBy="auto-register-toggle" v-model="newAuthSettings.authOpenIDAutoRegister" :disabled="savingSettings" />
<p id="auto-register-toggle" class="pl-4 whitespace-nowrap">{{ $strings.LabelAutoRegister }}</p>
<p class="pl-4 text-sm text-gray-300">{{ $strings.LabelAutoRegisterDescription }}</p>
</div>
<p class="pt-6 mb-4 px-1">{{ $strings.LabelOpenIDClaims }}</p>
<div class="flex flex-col sm:flex-row mb-4">
<div class="w-44 min-w-44">
<ui-text-input-with-label ref="openidGroupClaim" v-model="newAuthSettings.authOpenIDGroupClaim" :disabled="savingSettings" :placeholder="'groups'" :label="'Group Claim'" />
</div>
<p class="sm:pl-4 pt-2 sm:pt-0 text-sm text-gray-300" v-html="$strings.LabelOpenIDGroupClaimDescription"></p>
</div>
<div class="flex flex-col sm:flex-row mb-4">
<div class="w-44 min-w-44">
<ui-text-input-with-label ref="openidAdvancedPermsClaim" v-model="newAuthSettings.authOpenIDAdvancedPermsClaim" :disabled="savingSettings" :placeholder="'abspermissions'" :label="'Advanced Permission Claim'" />
</div>
<div class="sm:pl-4 pt-2 sm:pt-0 text-sm text-gray-300">
<p v-html="$strings.LabelOpenIDAdvancedPermsClaimDescription"></p>
<pre class="text-pre-wrap mt-2"
>{{ newAuthSettings.authOpenIDSamplePermissions }}
</pre>
</div>
</div>
</div>
</transition>
</div>
@@ -117,6 +141,7 @@ export default {
enableOpenIDAuth: false,
showCustomLoginMessage: false,
savingSettings: false,
openIdSigningAlgorithmsSupportedByIssuer: [],
newAuthSettings: {}
}
},
@@ -157,6 +182,22 @@ export default {
this.newAuthSettings.authOpenIDIssuerURL = this.newAuthSettings.authOpenIDIssuerURL.replace('/.well-known/openid-configuration', '')
}
const setSupportedSigningAlgorithms = (algorithms) => {
if (!algorithms?.length || !Array.isArray(algorithms)) {
console.warn('Invalid id_token_signing_alg_values_supported from openid-configuration', algorithms)
this.openIdSigningAlgorithmsSupportedByIssuer = []
return
}
this.openIdSigningAlgorithmsSupportedByIssuer = algorithms
// If a signing algorithm is already selected, then keep it, when it is still supported.
// But if it is not supported, then select one of the supported ones.
let currentAlgorithm = this.newAuthSettings.authOpenIDTokenSigningAlgorithm
if (!algorithms.includes(currentAlgorithm)) {
this.newAuthSettings.authOpenIDTokenSigningAlgorithm = algorithms[0]
}
}
this.$axios
.$get(`/auth/openid/config?issuer=${issuerUrl}`)
.then((data) => {
@@ -166,6 +207,7 @@ export default {
if (data.userinfo_endpoint) this.newAuthSettings.authOpenIDUserInfoURL = data.userinfo_endpoint
if (data.end_session_endpoint) this.newAuthSettings.authOpenIDLogoutURL = data.end_session_endpoint
if (data.jwks_uri) this.newAuthSettings.authOpenIDJwksURL = data.jwks_uri
if (data.id_token_signing_alg_values_supported) setSupportedSigningAlgorithms(data.id_token_signing_alg_values_supported)
})
.catch((error) => {
console.error('Failed to receive data', error)
@@ -203,6 +245,10 @@ export default {
this.$toast.error('Client Secret required')
isValid = false
}
if (!this.newAuthSettings.authOpenIDTokenSigningAlgorithm) {
this.$toast.error('Signing Algorithm required')
isValid = false
}
function isValidRedirectURI(uri) {
// Check for somestring://someother/string
@@ -222,6 +268,22 @@ export default {
}
})
}
function isValidClaim(claim) {
if (claim === '') return true
const pattern = new RegExp('^[a-zA-Z][a-zA-Z0-9_-]*$', 'i')
return pattern.test(claim)
}
if (!isValidClaim(this.newAuthSettings.authOpenIDGroupClaim)) {
this.$toast.error('Group Claim: Invalid claim name')
isValid = false
}
if (!isValidClaim(this.newAuthSettings.authOpenIDAdvancedPermsClaim)) {
this.$toast.error('Advanced Permission Claim: Invalid claim name')
isValid = false
}
return isValid
},
async saveSettings() {
+5 -4
View File
@@ -64,8 +64,8 @@
<td class="hidden md:table-cell w-26 min-w-26">
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }}</p>
</td>
<td class="hidden sm:table-cell w-32 min-w-32">
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
<td class="hidden sm:table-cell max-w-32 min-w-32">
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
</td>
<td class="text-center w-24 min-w-24 sm:w-32 sm:min-w-32">
<p class="text-xs font-mono">{{ $elapsedPretty(session.timeListening) }}</p>
@@ -127,8 +127,8 @@
<td class="hidden md:table-cell">
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }}</p>
</td>
<td class="hidden sm:table-cell">
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
<td class="hidden sm:table-cell max-w-32 min-w-32">
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
</td>
<td class="text-center">
<p class="text-xs font-mono">{{ $elapsedPretty(session.timeListening) }}</p>
@@ -394,6 +394,7 @@ export default {
getDeviceInfoString(deviceInfo) {
if (!deviceInfo) return ''
var lines = []
if (deviceInfo.clientName) lines.push(`${deviceInfo.clientName} ${deviceInfo.clientVersion || ''}`)
if (deviceInfo.osName) lines.push(`${deviceInfo.osName} ${deviceInfo.osVersion}`)
if (deviceInfo.browserName) lines.push(deviceInfo.browserName)
+3 -2
View File
@@ -36,8 +36,8 @@
<td class="hidden md:table-cell">
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }}</p>
</td>
<td class="hidden sm:table-cell">
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
<td class="hidden sm:table-cell min-w-32 max-w-32">
<p class="text-xs truncate" v-html="getDeviceInfoString(session.deviceInfo)" />
</td>
<td class="text-center">
<p class="text-xs font-mono">{{ $elapsedPretty(session.timeListening) }}</p>
@@ -193,6 +193,7 @@ export default {
getDeviceInfoString(deviceInfo) {
if (!deviceInfo) return ''
var lines = []
if (deviceInfo.clientName) lines.push(`${deviceInfo.clientName} ${deviceInfo.clientVersion || ''}`)
if (deviceInfo.osName) lines.push(`${deviceInfo.osName} ${deviceInfo.osVersion}`)
if (deviceInfo.browserName) lines.push(deviceInfo.browserName)
+13 -14
View File
@@ -34,7 +34,10 @@
<p v-if="bookSubtitle" class="text-gray-200 text-xl md:text-2xl">{{ bookSubtitle }}</p>
<nuxt-link v-for="_series in seriesList" :key="_series.id" :to="`/library/${libraryId}/series/${_series.id}`" class="hover:underline font-sans text-gray-300 text-lg leading-7"> {{ _series.text }}</nuxt-link>
<template v-for="(_series, index) in seriesList">
<nuxt-link :key="_series.id" :to="`/library/${libraryId}/series/${_series.id}`" class="hover:underline font-sans text-gray-300 text-lg leading-7">{{ _series.text }}</nuxt-link
><span :key="index" v-if="index < seriesList.length - 1">, </span>
</template>
<template v-if="!isVideo">
<p v-if="isPodcast" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl">by {{ podcastAuthor || 'Unknown' }}</p>
@@ -125,24 +128,18 @@
</div>
<div class="my-4 w-full">
<p ref="description" id="item-description" class="text-base text-gray-100 whitespace-pre-line mb-1" :class="{ 'show-full': showFullDescription }">{{ description }}</p>
<p ref="description" id="item-description" dir="auto" class="text-base text-gray-100 whitespace-pre-line mb-1" :class="{ 'show-full': showFullDescription }">{{ description }}</p>
<button v-if="isDescriptionClamped" class="py-0.5 flex items-center text-slate-300 hover:text-white" @click="showFullDescription = !showFullDescription">
{{ showFullDescription ? 'Read less' : 'Read more' }} <span class="material-icons text-xl pl-1">{{ showFullDescription ? 'expand_less' : 'expand_more' }}</span>
</button>
</div>
<div v-if="invalidAudioFiles.length" class="bg-error border-red-800 shadow-md p-4">
<p class="text-sm mb-2">Invalid audio files</p>
<tables-chapters-table v-if="chapters.length" :library-item="libraryItem" class="mt-6" />
<p v-for="audioFile in invalidAudioFiles" :key="audioFile.id" class="text-xs pl-2">- {{ audioFile.metadata.filename }} ({{ audioFile.error }})</p>
</div>
<widgets-audiobook-data v-if="tracks.length" :library-item-id="libraryItemId" :is-file="isFile" :media="media" />
<tables-tracks-table v-if="tracks.length" :title="$strings.LabelStatsAudioTracks" :tracks="tracksWithAudioFile" :is-file="isFile" :library-item-id="libraryItemId" class="mt-6" />
<tables-podcast-lazy-episodes-table v-if="isPodcast" :library-item="libraryItem" />
<tables-chapters-table v-if="chapters.length" :library-item="libraryItem" class="mt-6" />
<tables-ebook-files-table v-if="ebookFiles.length" :library-item="libraryItem" class="mt-6" />
<tables-library-files-table v-if="libraryFiles.length" :library-item="libraryItem" class="mt-6" />
@@ -239,10 +236,6 @@ export default {
isAbridged() {
return !!this.mediaMetadata.abridged
},
invalidAudioFiles() {
if (!this.isBook) return []
return this.libraryItem.media.audioFiles.filter((af) => af.invalid)
},
showPlayButton() {
if (this.isMissing || this.isInvalid) return false
if (this.isMusic) return !!this.audioFile
@@ -275,6 +268,12 @@ export default {
tracks() {
return this.media.tracks || []
},
tracksWithAudioFile() {
return this.tracks.map((track) => {
track.audioFile = this.media.audioFiles?.find((af) => af.metadata.path === track.metadata.path)
return track
})
},
podcastEpisodes() {
return this.media.episodes || []
},
@@ -40,12 +40,12 @@
<div v-if="episode.episode">{{ episode.episode }}</div>
</div>
<div class="flex items-center mb-2">
<div dir="auto" class="flex items-center mb-2">
<div class="font-semibold text-sm md:text-base">{{ episode.title }}</div>
<widgets-podcast-type-indicator :type="episode.episodeType" />
</div>
<p class="text-sm text-gray-200 mb-4 line-clamp-4" v-html="episode.subtitle || episode.description" />
<p dir="auto" class="text-sm text-gray-200 mb-4 line-clamp-4" v-html="episode.subtitle || episode.description" />
<div class="flex items-center">
<button class="h-8 px-4 border border-white border-opacity-20 hover:bg-white hover:bg-opacity-10 rounded-full flex items-center justify-center cursor-pointer focus:outline-none" :class="episode.progress && episode.progress.isFinished ? 'text-white text-opacity-40' : ''" @click.stop="playClick(episode)">
+22 -6
View File
@@ -5,6 +5,7 @@ import { supplant } from './utils'
const defaultCode = 'en-us'
const languageCodeMap = {
'bn': { label: 'বাংলা', dateFnsLocale: 'bn' },
'cs': { label: 'Čeština', dateFnsLocale: 'cs' },
'da': { label: 'Dansk', dateFnsLocale: 'da' },
'de': { label: 'Deutsch', dateFnsLocale: 'de' },
@@ -12,6 +13,7 @@ const languageCodeMap = {
'es': { label: 'Español', dateFnsLocale: 'es' },
'et': { label: 'Eesti', dateFnsLocale: 'et' },
'fr': { label: 'Français', dateFnsLocale: 'fr' },
'he': { label: 'עברית', dateFnsLocale: 'he' },
'hr': { label: 'Hrvatski', dateFnsLocale: 'hr' },
'it': { label: 'Italiano', dateFnsLocale: 'it' },
'lt': { label: 'Lietuvių', dateFnsLocale: 'lt' },
@@ -22,9 +24,10 @@ const languageCodeMap = {
'pt-br': { label: 'Português (Brasil)', dateFnsLocale: 'ptBR' },
'ru': { label: 'Русский', dateFnsLocale: 'ru' },
'sv': { label: 'Svenska', dateFnsLocale: 'sv' },
'uk': { label: 'Українська', dateFnsLocale: 'uk' },
'vi-vn': { label: 'Tiếng Việt', dateFnsLocale: 'vi' },
'zh-cn': { label: '简体中文 (Simplified Chinese)', dateFnsLocale: 'zhCN' },
'zh-tw': { label: '正體中文 (Traditional Chinese)', dateFnsLocale: 'zhTW' },
'zh-tw': { label: '正體中文 (Traditional Chinese)', dateFnsLocale: 'zhTW' }
}
Vue.prototype.$languageCodeOptions = Object.keys(languageCodeMap).map(code => {
return {
@@ -35,6 +38,7 @@ Vue.prototype.$languageCodeOptions = Object.keys(languageCodeMap).map(code => {
// iTunes search API uses ISO 3166 country codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
const podcastSearchRegionMap = {
'ua': { label: 'Україна' },
'us': { label: 'United States' },
'cn': { label: '中国' }
}
@@ -46,14 +50,22 @@ Vue.prototype.$podcastSearchRegionOptions = Object.keys(podcastSearchRegionMap).
})
Vue.prototype.$languageCodes = {
default: defaultCode,
current: defaultCode,
local: null,
server: null
default: defaultCode, // en-us
current: defaultCode, // Current language code in use
local: null, // Language code set at user level
server: null // Language code set at server level
}
// Currently loaded strings (default enUS)
Vue.prototype.$strings = { ...enUsStrings }
/**
* Get string and substitute
*
* @param {string} key
* @param {string[]} subs
* @returns {string}
*/
Vue.prototype.$getString = (key, subs) => {
if (!Vue.prototype.$strings[key]) return ''
if (subs?.length && Array.isArray(subs)) {
@@ -62,7 +74,11 @@ Vue.prototype.$getString = (key, subs) => {
return Vue.prototype.$strings[key]
}
var translations = {
Vue.prototype.$formatNumber = (num) => {
return Intl.NumberFormat(Vue.prototype.$languageCodes.current).format(num)
}
const translations = {
[defaultCode]: enUsStrings
}
+782
View File
@@ -0,0 +1,782 @@
{
"ButtonAdd": "যোগ করুন",
"ButtonAddChapters": "অধ্যায় যোগ করুন",
"ButtonAddDevice": "ডিভাইস যোগ করুন",
"ButtonAddLibrary": "লাইব্রেরি যোগ করুন",
"ButtonAddPodcasts": "পডকাস্ট যোগ করুন",
"ButtonAddUser": "ব্যবহারকারী যোগ করুন",
"ButtonAddYourFirstLibrary": "আপনার প্রথম লাইব্রেরি যোগ করুন",
"ButtonApply": "প্রয়োগ করুন",
"ButtonApplyChapters": "অধ্যায় প্রয়োগ করুন",
"ButtonAuthors": "লেখক",
"ButtonBrowseForFolder": "ফোল্ডারের জন্য ব্রাউজ করুন",
"ButtonCancel": "বাতিল করুন",
"ButtonCancelEncode": "এনকোড বাতিল করুন",
"ButtonChangeRootPassword": "রুট পাসওয়ার্ড পরিবর্তন করুন",
"ButtonCheckAndDownloadNewEpisodes": "নতুন পর্বগুলি পরীক্ষা এবং ডাউনলোড করুন",
"ButtonChooseAFolder": "একটি ফোল্ডার চয়ন করুন",
"ButtonChooseFiles": "ফাইল চয়ন করুন",
"ButtonClearFilter": "ফিল্টার পরিষ্কার করুন",
"ButtonCloseFeed": "ফিড বন্ধ করুন",
"ButtonCollections": "সংগ্রহ",
"ButtonConfigureScanner": "স্ক্যানার কনফিগার করুন",
"ButtonCreate": "তৈরি করুন",
"ButtonCreateBackup": "ব্যাকআপ তৈরি করুন",
"ButtonDelete": "মুছুন",
"ButtonDownloadQueue": "সারি",
"ButtonEdit": "সম্পাদনা করুন",
"ButtonEditChapters": "অধ্যায় সম্পাদনা করুন",
"ButtonEditPodcast": "পডকাস্ট সম্পাদনা করুন",
"ButtonForceReScan": "জোরপূর্বক পুনরায় স্ক্যান করুন",
"ButtonFullPath": "সম্পূর্ণ পথ",
"ButtonHide": "লুকান",
"ButtonHome": "নীড়",
"ButtonIssues": "ইস্যু",
"ButtonJumpBackward": "পিছনে লাফ দিন",
"ButtonJumpForward": "সামনে লাফ দিন",
"ButtonLatest": "সর্বশেষ",
"ButtonLibrary": "লাইব্রেরি",
"ButtonLogout": "লগআউট",
"ButtonLookup": "সন্ধান",
"ButtonManageTracks": "ট্র্যাকগুলি পরিচালনা করুন",
"ButtonMapChapterTitles": "অধ্যায়ের শিরোনাম ম্যাপ করুন",
"ButtonMatchAllAuthors": "সমস্ত লেখকের সাথে মিল করুন",
"ButtonMatchBooks": "বইগুলো মিল করুন",
"ButtonNevermind": "কিছু মনে করবেন না",
"ButtonNext": "পরবর্তী",
"ButtonNextChapter": "পরবর্তী অধ্যায়",
"ButtonOk": "ঠিক আছে",
"ButtonOpenFeed": "ফিড খুলুন",
"ButtonOpenManager": "ম্যানেজার খুলুন",
"ButtonPause": "বিরতি",
"ButtonPlay": "বাজান",
"ButtonPlaying": "বাজছে",
"ButtonPlaylists": "প্লেলিস্ট",
"ButtonPrevious": "পূর্ববর্তী",
"ButtonPreviousChapter": "আগের অধ্যায়",
"ButtonPurgeAllCache": "সমস্ত ক্যাশে পরিষ্কার করুন",
"ButtonPurgeItemsCache": "আইটেম ক্যাশে পরিষ্কার করুন",
"ButtonPurgeMediaProgress": "মিডিয়া ক্যাশে পরিষ্কার করুন",
"ButtonQueueAddItem": "সারিতে যোগ করুন",
"ButtonQueueRemoveItem": "সারি থেকে মুছে ফেলুন",
"ButtonQuickMatch": "দ্রুত ম্যাচ",
"ButtonRead": "পড়ুন",
"ButtonRefresh": "রিফ্রেশ",
"ButtonRemove": "মুছে ফেলুন",
"ButtonRemoveAll": "সব মুছে ফেলুন",
"ButtonRemoveAllLibraryItems": "সমস্ত লাইব্রেরি আইটেম মুছে ফেলুন",
"ButtonRemoveFromContinueListening": "শোনা চালিয়ে যাওয়া থেকে মুছে ফেলুন",
"ButtonRemoveFromContinueReading": "পঠন চালিয়ে যান থেকে মুছে ফেলুন",
"ButtonRemoveSeriesFromContinueSeries": "কন্টিনিউ সিরিজ থেকে সিরিজ মুছে ফেলুন",
"ButtonReScan": "পুনরায় স্ক্যান",
"ButtonReset": "রিসেট",
"ButtonResetToDefault": "ডিফল্টে পুনরায় সেট করুন",
"ButtonRestore": "পুনরুদ্ধার করুন",
"ButtonSave": "সংরক্ষণ করুন",
"ButtonSaveAndClose": "সংরক্ষণ এবং বন্ধ করুন",
"ButtonSaveTracklist": "ট্র্যাকলিস্ট সংরক্ষণ করুন",
"ButtonScan": "স্ক্যান",
"ButtonScanLibrary": "স্ক্যান লাইব্রেরি",
"ButtonSearch": "অনুসন্ধান",
"ButtonSelectFolderPath": "ফোল্ডারের পথ নির্বাচন করুন",
"ButtonSeries": "সিরিজ",
"ButtonSetChaptersFromTracks": "ট্র্যাক থেকে অধ্যায় সেট করুন",
"ButtonShare": "শেয়ার করুন",
"ButtonShiftTimes": "সময় শিফট করুন",
"ButtonShow": "দেখান",
"ButtonStartM4BEncode": "M4B এনকোড শুরু করুন",
"ButtonStartMetadataEmbed": "মেটাডেটা এম্বেড শুরু করুন",
"ButtonSubmit": "জমা দিন",
"ButtonTest": "পরীক্ষা",
"ButtonUpload": "আপলোড",
"ButtonUploadBackup": "আপলোড ব্যাকআপ",
"ButtonUploadCover": "কভার আপলোড করুন",
"ButtonUploadOPMLFile": "OPML ফাইল আপলোড করুন",
"ButtonUserDelete": "ব্যবহারকারী {0} মুছুন",
"ButtonUserEdit": "ব্যবহারকারী {0} সম্পাদনা করুন",
"ButtonViewAll": "সমস্ত দেখুন",
"ButtonYes": "হ্যাঁ",
"ErrorUploadFetchMetadataAPI": "মেটাডেটা আনতে ত্রুটি হচ্ছে",
"ErrorUploadFetchMetadataNoResults": "মেটাডেটা আনা যায়নি - শিরোনাম এবং/অথবা লেখক আপডেট করার চেষ্টা করুন",
"ErrorUploadLacksTitle": "একটি শিরোনাম থাকতে হবে",
"HeaderAccount": "অ্যাকাউন্ট",
"HeaderAdvanced": "অ্যাডভান্সড",
"HeaderAppriseNotificationSettings": "বিজ্ঞপ্তি সেটিংস অবহিত করুন",
"HeaderAudiobookTools": "অডিওবই ফাইল ম্যানেজমেন্ট টুলস",
"HeaderAudioTracks": "অডিও ট্র্যাকস",
"HeaderAuthentication": "প্রমাণীকরণ",
"HeaderBackups": "ব্যাকআপ",
"HeaderChangePassword": "পাসওয়ার্ড পরিবর্তন করুন",
"HeaderChapters": "অধ্যায়",
"HeaderChooseAFolder": "একটি ফোল্ডার চয়ন করুন",
"HeaderCollection": "সংগ্রহ",
"HeaderCollectionItems": "সংগ্রহ আইটেম",
"HeaderCover": "কভার",
"HeaderCurrentDownloads": "বর্তমান ডাউনলোডগুলি",
"HeaderCustomMetadataProviders": "কাস্টম মেটাডেটা প্রদানকারী",
"HeaderDetails": "বিস্তারিত",
"HeaderDownloadQueue": "ডাউনলোড সারি",
"HeaderEbookFiles": "ই-বই ফাইল",
"HeaderEmail": "ইমেইল",
"HeaderEmailSettings": "ইমেল সেটিংস",
"HeaderEpisodes": "পর্ব",
"HeaderEreaderDevices": "ই-রিডার ডিভাইস",
"HeaderEreaderSettings": "ই-রিডার সেটিংস",
"HeaderFiles": "ফাইল",
"HeaderFindChapters": "অধ্যায় খুঁজুন",
"HeaderIgnoredFiles": "উপেক্ষিত ফাইল",
"HeaderItemFiles": "আইটেম ফাইল",
"HeaderItemMetadataUtils": "আইটেম মেটাডেটা ইউটিলস",
"HeaderLastListeningSession": "শেষ শোনার অধিবেশন",
"HeaderLatestEpisodes": "সর্বশেষ পর্ব",
"HeaderLibraries": "লাইব্রেরি",
"HeaderLibraryFiles": "লাইব্রেরি ফাইল",
"HeaderLibraryStats": "লাইব্রেরি পরিসংখ্যান",
"HeaderListeningSessions": "শোনার সেশন",
"HeaderListeningStats": "শোনার পরিসংখ্যান",
"HeaderLogin": "লগইন",
"HeaderLogs": "লগস",
"HeaderManageGenres": "ঘরানাগুলো পরিচালনা করুন",
"HeaderManageTags": "ট্যাগগুলো পরিচালনা করুন",
"HeaderMapDetails": "মানচিত্রের বিবরণ",
"HeaderMatch": "ম্যাচ",
"HeaderMetadataOrderOfPrecedence": "মেটাডেটা অগ্রাধিকারের ক্রম",
"HeaderMetadataToEmbed": "এম্বেড করার জন্য মেটাডেটা",
"HeaderNewAccount": "নতুন অ্যাকাউন্ট",
"HeaderNewLibrary": "নতুন লাইব্রেরি",
"HeaderNotifications": "বিজ্ঞপ্তি",
"HeaderOpenIDConnectAuthentication": "ওপেনআইডি সংযোগ প্রমাণীকরণ",
"HeaderOpenRSSFeed": "আরএসএস ফিড খুলুন",
"HeaderOtherFiles": "অন্যান্য ফাইল",
"HeaderPasswordAuthentication": "পাসওয়ার্ড প্রমাণীকরণ",
"HeaderPermissions": "অনুমতি",
"HeaderPlayerQueue": "প্লেয়ার সারি",
"HeaderPlaylist": "প্লেলিস্ট",
"HeaderPlaylistItems": "প্লেলিস্ট আইটেম",
"HeaderPodcastsToAdd": "যোগ করার জন্য পডকাস্ট",
"HeaderPreviewCover": "কভার ্দেখুন",
"HeaderRemoveEpisode": "পর্বটি সরান",
"HeaderRemoveEpisodes": "{0}টি পর্ব সরান",
"HeaderRSSFeedGeneral": "আরএসএস বিবরণ",
"HeaderRSSFeedIsOpen": "আরএসএস ফিড খোলা আছে",
"HeaderRSSFeeds": "আরএসএস ফিড",
"HeaderSavedMediaProgress": "মিডিয়া সংরক্ষণের অগ্রগতি",
"HeaderSchedule": "সময়সূচী",
"HeaderScheduleLibraryScans": "স্বয়ংক্রিয় লাইব্রেরি স্ক্যানের সময়সূচী",
"HeaderSession": "সেশন",
"HeaderSetBackupSchedule": "ব্যাকআপ সময়সূচী সেট করুন",
"HeaderSettings": "সেটিংস",
"HeaderSettingsDisplay": "প্রদর্শন",
"HeaderSettingsExperimental": "পরীক্ষামূলক ফিচার",
"HeaderSettingsGeneral": "সাধারণ",
"HeaderSettingsScanner": "স্ক্যানার",
"HeaderSleepTimer": "স্লিপ টাইমার",
"HeaderStatsLargestItems": "সবচেয়ে বড় আইটেম",
"HeaderStatsLongestItems": "দীর্ঘতম আইটেম (ঘন্টা)",
"HeaderStatsMinutesListeningChart": "মিনিট শ্রবণ (গত ৭ দিন)",
"HeaderStatsRecentSessions": "সাম্প্রতিক সেশন",
"HeaderStatsTop10Authors": "শীর্ষ ১০ জন লেখক",
"HeaderStatsTop5Genres": "শীর্ষ ৫ টি ঘরানা",
"HeaderTableOfContents": "বিষয়বস্তুর সারণী",
"HeaderTools": "টুলস",
"HeaderUpdateAccount": "অ্যাকাউন্ট আপডেট করুন",
"HeaderUpdateAuthor": "লেখক আপডেট করুন",
"HeaderUpdateDetails": "বিশদ আপডেট করুন",
"HeaderUpdateLibrary": "লাইব্রেরি আপডেট করুন",
"HeaderUsers": "ব্যবহারকারীরা",
"HeaderYearReview": "বাৎসরিক পর্যালোচনা {0}",
"HeaderYourStats": "আপনার পরিসংখ্যান",
"LabelAbridged": "সংক্ষিপ্ত",
"LabelAccountType": "অ্যাকাউন্টের প্রকার",
"LabelAccountTypeAdmin": "প্রশাসন",
"LabelAccountTypeGuest": "অতিথি",
"LabelAccountTypeUser": "ব্যবহারকারী",
"LabelActivity": "ক্রিয়াকলাপ",
"LabelAdded": "যোগ করা হয়েছে",
"LabelAddedAt": "এতে যোগ করা হয়েছে",
"LabelAddToCollection": "সংগ্রহে যোগ করুন",
"LabelAddToCollectionBatch": "সংগ্রহে {0}টি বই যোগ করুন",
"LabelAddToPlaylist": "প্লেলিস্টে যোগ করুন",
"LabelAddToPlaylistBatch": "প্লেলিস্টে {0}টি আইটেম যোগ করুন",
"LabelAdminUsersOnly": "শুধু অ্যাডমিন ব্যবহারকারী",
"LabelAll": "সব",
"LabelAllUsers": "সমস্ত ব্যবহারকারী",
"LabelAllUsersExcludingGuests": "অতিথি ব্যতীত সকল ব্যবহারকারী",
"LabelAllUsersIncludingGuests": "অতিথি সহ সকল ব্যবহারকারী",
"LabelAlreadyInYourLibrary": "ইতিমধ্যেই আপনার লাইব্রেরিতে রয়েছে",
"LabelAppend": "সংযোজন",
"LabelAuthor": "লেখক",
"LabelAuthorFirstLast": "লেখক (প্রথম শেষ)",
"LabelAuthorLastFirst": "লেখক (শেষ, প্রথম)",
"LabelAuthors": "লেখকগণ",
"LabelAutoDownloadEpisodes": "স্বয়ংক্রিয় ডাউনলোড পর্ব",
"LabelAutoFetchMetadata": "স্বয়ংক্রিয় ফেচ মেটাডেটা",
"LabelAutoFetchMetadataHelp": "আপলোডিং স্ট্রিমলাইন করার জন্য শিরোনাম, লেখক এবং সিরিজের জন্য মেটাডেটা খুঁজুন। আপলোড করার পরে অতিরিক্ত মেটাডেটা মিলতে হতে পারে।",
"LabelAutoLaunch": "স্বয়ংক্রিয় আরম্ভ",
"LabelAutoLaunchDescription": "লগইন পৃষ্ঠায় নেভিগেট করার সময় স্বয়ংক্রিয়ভাবে অনুমোদন প্রদানকারীর কাছে পুনঃনির্দেশ করুন (হস্তকৃত ওভাররাইড পথ <code>/login?autoLaunch=0</code>)",
"LabelAutoRegister": "স্বয়ংক্রিয় নিবন্ধন",
"LabelAutoRegisterDescription": "লগ ইন করার পর স্বয়ংক্রিয়ভাবে নতুন ব্যবহারকারী তৈরি করুন",
"LabelBackToUser": "ব্যবহারকারীর কাছে ফিরে যান",
"LabelBackupLocation": "ব্যাকআপ অবস্থান",
"LabelBackupsEnableAutomaticBackups": "স্বয়ংক্রিয় ব্যাকআপ সক্ষম করুন",
"LabelBackupsEnableAutomaticBackupsHelp": "ব্যাকআপগুলি /মেটাডাটা/ব্যাকআপে সংরক্ষিত",
"LabelBackupsMaxBackupSize": "সর্বোচ্চ ব্যাকআপ আকার (GB-তে)",
"LabelBackupsMaxBackupSizeHelp": "ভুল কনফিগারেশনের বিরুদ্ধে সুরক্ষা হিসেবে ব্যাকআপগুলি ব্যর্থ হবে যদি তারা কনফিগার করা আকার অতিক্রম করে।",
"LabelBackupsNumberToKeep": "ব্যাকআপের সংখ্যা রাখুন",
"LabelBackupsNumberToKeepHelp": "এক সময়ে শুধুমাত্র ১ টি ব্যাকআপ সরানো হবে তাই যদি আপনার কাছে ইতিমধ্যে এর চেয়ে বেশি ব্যাকআপ থাকে তাহলে আপনাকে ম্যানুয়ালি সেগুলি সরিয়ে ফেলতে হবে।",
"LabelBitrate": "বিটরেট",
"LabelBooks": "বইগুলো",
"LabelButtonText": "ঘর পাঠ্য",
"LabelChangePassword": "পাসওয়ার্ড পরিবর্তন করুন",
"LabelChannels": "চ্যানেল",
"LabelChapters": "অধ্যায়",
"LabelChaptersFound": "অধ্যায় পাওয়া গেছে",
"LabelChapterTitle": "অধ্যায়ের শিরোনাম",
"LabelClickForMoreInfo": "আরো তথ্যের জন্য ক্লিক করুন",
"LabelClosePlayer": "প্লেয়ার বন্ধ করুন",
"LabelCodec": "কোডেক",
"LabelCollapseSeries": "সিরিজ সঙ্কুচিত করুন",
"LabelCollection": "সংগ্রহ",
"LabelCollections": "সংগ্রহ",
"LabelComplete": "সম্পূর্ণ",
"LabelConfirmPassword": "পাসওয়ার্ড নিশ্চিত করুন",
"LabelContinueListening": "শোনা চালিয়ে যান",
"LabelContinueReading": "পড়া চালিয়ে যান",
"LabelContinueSeries": "সিরিজ চালিয়ে যান",
"LabelCover": "কভার",
"LabelCoverImageURL": "ছবির কভারের URL",
"LabelCreatedAt": "তৈরি করা হয়েছে",
"LabelCronExpression": "Cron এক্সপ্রেশন",
"LabelCurrent": "বর্তমান",
"LabelCurrently": "বর্তমানে:",
"LabelCustomCronExpression": "কাস্টম Cron এক্সপ্রেশন:",
"LabelDatetime": "তারিখ সময়",
"LabelDeleteFromFileSystemCheckbox": "ফাইল সিস্টেম থেকে মুছে ফেলুন (শুধু ডাটাবেস থেকে সরাতে টিক চিহ্ন মুক্ত করুন)",
"LabelDescription": "বিবরণ",
"LabelDeselectAll": "সমস্ত অনির্বাচিত করুন",
"LabelDevice": "ডিভাইস",
"LabelDeviceInfo": "ডিভাইস তথ্য",
"LabelDeviceIsAvailableTo": "ডিভাইস এর জন্য উপলব্ধ...",
"LabelDirectory": "ডিরেক্টরি",
"LabelDiscFromFilename": "ফাইলের নাম থেকে ডিস্ক",
"LabelDiscFromMetadata": "মেটাডেটা থেকে ডিস্ক",
"LabelDiscover": "আবিষ্কার",
"LabelDownload": "ডাউনলোড করুন",
"LabelDownloadNEpisodes": "{0}টি পর্ব ডাউনলোড করুন",
"LabelDuration": "সময়কাল",
"LabelDurationFound": "সময়কাল পাওয়া গেছে:",
"LabelEbook": "ই-বই",
"LabelEbooks": "ই-বইগুলো",
"LabelEdit": "সম্পাদনা করুন",
"LabelEmail": "ইমেইল",
"LabelEmailSettingsFromAddress": "ঠিকানা থেকে",
"LabelEmailSettingsSecure": "নিরাপদ",
"LabelEmailSettingsSecureHelp": "যদি সত্য হয় সার্ভারের সাথে সংযোগ করার সময় সংযোগটি TLS ব্যবহার করবে। মিথ্যা হলে TLS ব্যবহার করা হবে যদি সার্ভার STARTTLS এক্সটেনশন সমর্থন করে। বেশিরভাগ ক্ষেত্রে এই মানটিকে সত্য হিসাবে সেট করুন যদি আপনি পোর্ট 465-এর সাথে সংযোগ করছেন। পোর্ট 587 বা পোর্টের জন্য 25 এটি মিথ্যা রাখুন। (nodemailer.com/smtp/#authentication থেকে)",
"LabelEmailSettingsTestAddress": "পরীক্ষার ঠিকানা",
"LabelEmbeddedCover": "এম্বেডেড কভার",
"LabelEnable": "সক্ষম করুন",
"LabelEnd": "সমাপ্ত",
"LabelEpisode": "পর্ব",
"LabelEpisodeTitle": "পর্বের শিরোনাম",
"LabelEpisodeType": "পর্বের ধরন",
"LabelExample": "উদাহরণ",
"LabelExplicit": "বিশদ",
"LabelFeedURL": "ফিড ইউআরএল",
"LabelFetchingMetadata": "মেটাডেটা আনা হচ্ছে",
"LabelFile": "ফাইল",
"LabelFileBirthtime": "ফাইল জন্মের সময়",
"LabelFileModified": "ফাইল পরিবর্তিত",
"LabelFilename": "ফাইলের নাম",
"LabelFilterByUser": "ব্যবহারকারী দ্বারা ফিল্টারকৃত",
"LabelFindEpisodes": "পর্বগুলো খুঁজুন",
"LabelFinished": "সমাপ্ত",
"LabelFolder": "ফোল্ডার",
"LabelFolders": "ফোল্ডারগুলো",
"LabelFontBold": "বোল্ড",
"LabelFontFamily": "ফন্ট পরিবার",
"LabelFontItalic": "ইটালিক",
"LabelFontScale": "ফন্ট স্কেল",
"LabelFontStrikethrough": "অবচ্ছেদন রেখা",
"LabelFormat": "ফরম্যাট",
"LabelGenre": "ঘরানা",
"LabelGenres": "ঘরানাগুলো",
"LabelHardDeleteFile": "জোরপূর্বক ফাইল মুছে ফেলুন",
"LabelHasEbook": "ই-বই আছে",
"LabelHasSupplementaryEbook": "পরিপূরক ই-বই আছে",
"LabelHighestPriority": "সর্বোচ্চ অগ্রাধিকার",
"LabelHost": "নিমন্ত্রণকর্তা",
"LabelHour": "ঘন্টা",
"LabelIcon": "আইকন",
"LabelImageURLFromTheWeb": "ওয়েব থেকে ছবির ইউআরএল",
"LabelIncludeInTracklist": "ট্র্যাকলিস্টে অন্তর্ভুক্ত করুন",
"LabelIncomplete": "অসম্পূর্ণ",
"LabelInProgress": "প্রগতিতে আছে",
"LabelInterval": "বিরতি",
"LabelIntervalCustomDailyWeekly": "কাস্টম দৈনিক/সাপ্তাহিক",
"LabelIntervalEvery12Hours": "প্রতি ১২ ঘন্টায়",
"LabelIntervalEvery15Minutes": "প্রতি ১৫ মিনিটে",
"LabelIntervalEvery2Hours": "প্রতি ২ ঘন্টায়",
"LabelIntervalEvery30Minutes": "প্রতি ৩০ মিনিটে",
"LabelIntervalEvery6Hours": "প্রতি ৬ ঘন্টায়",
"LabelIntervalEveryDay": "প্রতিদিন",
"LabelIntervalEveryHour": "প্রতি ঘন্টা",
"LabelInvert": "উল্টানো",
"LabelItem": "আইটেম",
"LabelLanguage": "ভাষা",
"LabelLanguageDefaultServer": "সার্ভারের ডিফল্ট ভাষা",
"LabelLastBookAdded": "শেষ বই যোগ করা হয়েছে",
"LabelLastBookUpdated": "শেষ বই আপডেট করা হয়েছে",
"LabelLastSeen": "শেষ দেখা",
"LabelLastTime": "শেষ বার",
"LabelLastUpdate": "শেষ আপডেট",
"LabelLayout": "লেআউট",
"LabelLayoutSinglePage": "একক পৃষ্ঠা",
"LabelLayoutSplitPage": "বিভক্ত পৃষ্ঠা",
"LabelLess": "কম",
"LabelLibrariesAccessibleToUser": "ব্যবহারকারীর কাছে অ্যাক্সেসযোগ্য লাইব্রেরি",
"LabelLibrary": "লাইব্রেরি",
"LabelLibraryItem": "লাইব্রেরি আইটেম",
"LabelLibraryName": "লাইব্রেরির নাম",
"LabelLimit": "সীমা",
"LabelLineSpacing": "লাইন স্পেসিং",
"LabelListenAgain": "আবার শুনুন",
"LabelLogLevelDebug": "ডিবাগ",
"LabelLogLevelInfo": "তথ্য",
"LabelLogLevelWarn": "সতর্ক",
"LabelLookForNewEpisodesAfterDate": "এই তারিখের পরে নতুন পর্বগুলি সন্ধান করুন",
"LabelLowestPriority": "সর্বনিম্ন অগ্রাধিকার",
"LabelMatchExistingUsersBy": "বিদ্যমান ব্যবহারকারীদের দ্বারা মিলিত করুন",
"LabelMatchExistingUsersByDescription": "বিদ্যমান ব্যবহারকারীদের সংযোগ করার জন্য ব্যবহৃত হয়। একবার সংযুক্ত হলে, ব্যবহারকারীদের আপনার SSO প্রদানকারীর থেকে একটি অনন্য আইডি দ্বারা মিলিত হবে",
"LabelMediaPlayer": "মিডিয়া প্লেয়ার",
"LabelMediaType": "মিডিয়ার ধরন",
"LabelMetadataOrderOfPrecedenceDescription": "উচ্চ অগ্রাধিকারের মেটাডেটার উৎসগুলো নিম্ন অগ্রাধিকারের মেটাডেটা উৎসগুলোকে ওভাররাইড করবে",
"LabelMetadataProvider": "মেটাডেটা প্রদানকারী",
"LabelMetaTag": "মেটা ট্যাগ",
"LabelMetaTags": "মেটা ট্যাগগুলো",
"LabelMinute": "মিনিট",
"LabelMissing": "নিখোঁজ",
"LabelMissingEbook": "কোনও ই-বই নেই",
"LabelMissingSupplementaryEbook": "কোনও সম্পূরক ই-বই নেই",
"LabelMobileRedirectURIs": "অনুমোদিত মোবাইল রিডাইরেক্ট URIs",
"LabelMobileRedirectURIsDescription": "এটি মোবাইল অ্যাপের জন্য বৈধ পুনঃনির্দেশিত URI-এর একটি সাদা তালিকা। ডিফল্টটি হল <code>audiobookshelf://oauth</code>, যা আপনি তৃতীয় পক্ষের অ্যাপ ইন্টিগ্রেশনের জন্য অতিরিক্ত URI-এর সাথে সরাতে বা সম্পূরক করতে পারেন। একটি তারকাচিহ্ন (<code>*</code>) ব্যবহার করে একমাত্র এন্ট্রি যেকোন ইউআরআইকে অনুমতি দেয়।",
"LabelMore": "আরো",
"LabelMoreInfo": "আরো তথ্য",
"LabelName": "নাম",
"LabelNarrator": "কথক",
"LabelNarrators": "কথক",
"LabelNew": "নতুন",
"LabelNewestAuthors": "নতুন লেখক",
"LabelNewestEpisodes": "নতুনতম পর্ব",
"LabelNewPassword": "নতুন পাসওয়ার্ড",
"LabelNextBackupDate": "পরবর্তী ব্যাকআপ তারিখ",
"LabelNextScheduledRun": "পরবর্তী নির্ধারিত দৌড়",
"LabelNoEpisodesSelected": "কোন পর্ব নির্বাচন করা হয়নি",
"LabelNotes": "নোটস",
"LabelNotFinished": "সমাপ্ত হয়নি",
"LabelNotificationAppriseURL": "অবহিত URL(গুলি)",
"LabelNotificationAvailableVariables": "ব্যবহারযোগ্য ভেরিয়েবল",
"LabelNotificationBodyTemplate": "বডি টেমপ্লেট",
"LabelNotificationEvent": "ইভেন্ট বিজ্ঞপ্তি",
"LabelNotificationsMaxFailedAttempts": "সর্বোচ্চ ব্যর্থ প্রচেষ্টা",
"LabelNotificationsMaxFailedAttemptsHelp": "এটি বারবার পাঠাতে ব্যর্থ হলে বিজ্ঞপ্তি অক্ষম করা হবে",
"LabelNotificationsMaxQueueSize": "বিজ্ঞপ্তি ইভেন্টের জন্য সর্বোচ্চ সারির আকার",
"LabelNotificationsMaxQueueSizeHelp": "ইভেন্টগুলি প্রতি সেকেন্ডে ১ বার ইন্ধন করার মধ্যে সীমাবদ্ধ। সারি সর্বাধিক আকারে থাকলে ইভেন্টগুলি উপেক্ষা করা হবে। এটি বিজ্ঞপ্তি স্প্যামিং প্রতিরোধ করে।",
"LabelNotificationTitleTemplate": "শিরোনাম টেমপ্লেট",
"LabelNotStarted": "শুরু হয়নি",
"LabelNumberOfBooks": "বইয়ের সংখ্যা",
"LabelNumberOfEpisodes": "# টি পর্ব",
"LabelOpenIDAdvancedPermsClaimDescription": "ওপেনআইডি দাবির নাম যাতে অ্যাপ্লিকেশনের মধ্যে ব্যবহারকারীর ক্রিয়াকলাপের জন্য উন্নত অনুমতি রয়েছে যা অ-প্রশাসক ভূমিকাগুলিতে প্রযোজ্য হবে (<b>যদি কনফিগার করা হয়</b>)। প্রতিক্রিয়া থেকে দাবিটি অনুপস্থিত থাকলে, অ্যাক্সেস করুন ABS-তে অস্বীকার করা হবে। যদি একটি একক বিকল্প অনুপস্থিত থাকে, তাহলে এটিকে <code>false</code> হিসাবে গণ্য করা হবে। নিশ্চিত করুন যে পরিচয় প্রদানকারীর দাবি প্রত্যাশিত কাঠামোর সাথে মেলে:",
"LabelOpenIDClaims": "অ্যাডভান্সড গ্রুপ এবং পারমিশন অ্যাসাইনমেন্ট নিষ্ক্রিয় করতে নিম্নলিখিত বিকল্পগুলিকে খালি ছেড়ে দিন, তারপর স্বয়ংক্রিয়ভাবে 'ব্যবহারকারী' গ্রুপকে বরাদ্দ করা হবে।",
"LabelOpenIDGroupClaimDescription": "ওপেনআইডি দাবির নাম যাতে ব্যবহারকারীর গোষ্ঠীর একটি তালিকা থাকে। সাধারণত <code>গ্রুপ</code> হিসাবে উল্লেখ করা হয়। <b>কনফিগার করা থাকলে</b>, অ্যাপ্লিকেশনটি স্বয়ংক্রিয়ভাবে এর উপর ভিত্তি করে ব্যবহারকারীর গোষ্ঠীর সদস্যপদ নির্ধারণ করবে, শর্ত এই যে এই গোষ্ঠীগুলি কেস-অসংবেদনশীলভাবে দাবিতে 'অ্যাডমিন', 'ব্যবহারকারী' বা 'অতিথি' নাম দেওয়া হয়৷ দাবিতে একটি তালিকা থাকা উচিত এবং যদি একজন ব্যবহারকারী একাধিক গোষ্ঠীর অন্তর্গত হয় তবে অ্যাপ্লিকেশনটি বরাদ্দ করবে সর্বোচ্চ স্তরের অ্যাক্সেসের সাথে সঙ্গতিপূর্ণ ভূমিকা৷ যদি কোনও গোষ্ঠীর সাথে মেলে না, তবে অ্যাক্সেস অস্বীকার করা হবে।",
"LabelOpenRSSFeed": "আরএসএস ফিড খুলুন",
"LabelOverwrite": "পুনঃলিখিত",
"LabelPassword": "পাসওয়ার্ড",
"LabelPath": "পথ",
"LabelPermissionsAccessAllLibraries": "সমস্ত লাইব্রেরি অ্যাক্সেস করতে পারবে",
"LabelPermissionsAccessAllTags": "সমস্ত ট্যাগ অ্যাক্সেস করতে পারবে",
"LabelPermissionsAccessExplicitContent": "স্পষ্ট বিষয়বস্তু অ্যাক্সেস করতে পারে",
"LabelPermissionsDelete": "মুছে দিতে পারবে",
"LabelPermissionsDownload": "ডাউনলোড করতে পারবে",
"LabelPermissionsUpdate": "আপডেট করতে পারবে",
"LabelPermissionsUpload": "আপলোড করতে পারবে",
"LabelPersonalYearReview": "আপনার বছরের পর্যালোচনা ({0})",
"LabelPhotoPathURL": "ছবি পথ/ইউআরএল",
"LabelPlaylists": "প্লেলিস্ট",
"LabelPlayMethod": "প্লে পদ্ধতি",
"LabelPodcast": "পডকাস্ট",
"LabelPodcasts": "পডকাস্টগুলো",
"LabelPodcastSearchRegion": "পডকাস্ট অনুসন্ধান অঞ্চল",
"LabelPodcastType": "পডকাস্টের ধরন",
"LabelPort": "পোর্ট",
"LabelPrefixesToIgnore": "উপেক্ষা করার উপসর্গ (কেস সংবেদনশীল)",
"LabelPreventIndexing": "আইটিউনস এবং গুগল পডকাস্ট ডিরেক্টরি দ্বারা আপনার ফিডকে ইন্ডেক্স করা থেকে বিরত রাখুন",
"LabelPrimaryEbook": "প্রাথমিক ই-বই",
"LabelProgress": "প্রগতি",
"LabelProvider": "প্রদানকারী",
"LabelPubDate": "প্রকাশের তারিখ",
"LabelPublisher": "প্রকাশক",
"LabelPublishYear": "প্রকাশের বছর",
"LabelRead": "পড়ুন",
"LabelReadAgain": "আবার পড়ুন",
"LabelReadEbookWithoutProgress": "প্রগতি না রেখে ই-বই পড়ুন",
"LabelRecentlyAdded": "সম্প্রতি যোগ করা হয়েছে",
"LabelRecentSeries": "সাম্প্রতিক সিরিজ",
"LabelRecommended": "সুপারিশকৃত",
"LabelRedo": "পুনরায় করুন",
"LabelRegion": "অঞ্চল",
"LabelReleaseDate": "উন্মোচনের তারিখ",
"LabelRemoveCover": "কভার সরান",
"LabelRowsPerPage": "প্রতি পৃষ্ঠায় সারি",
"LabelRSSFeedCustomOwnerEmail": "কাস্টম মালিকের ইমেইল",
"LabelRSSFeedCustomOwnerName": "কাস্টম মালিকের নাম",
"LabelRSSFeedOpen": "আরএসএস ফিড খুলুন",
"LabelRSSFeedPreventIndexing": "সূচীকরণ প্রতিরোধ করুন",
"LabelRSSFeedSlug": "আরএসএস ফিড স্লাগ",
"LabelRSSFeedURL": "আরএসএস ফিড ইউআরএল",
"LabelSearchTerm": "অনুসন্ধান শব্দ",
"LabelSearchTitle": "অনুসন্ধান শিরোনাম",
"LabelSearchTitleOrASIN": "অনুসন্ধান শিরোনাম বা ASIN",
"LabelSeason": "সেশন",
"LabelSelectAllEpisodes": "সমস্ত পর্ব নির্বাচন করুন",
"LabelSelectEpisodesShowing": "দেখানো {0}টি পর্ব নির্বাচন করুন",
"LabelSelectUsers": "ব্যবহারকারী নির্বাচন করুন",
"LabelSendEbookToDevice": "ই-বই পাঠান...",
"LabelSequence": "ক্রম",
"LabelSeries": "সিরিজ",
"LabelSeriesName": "সিরিজের নাম",
"LabelSeriesProgress": "সিরিজের অগ্রগতি",
"LabelServerYearReview": "সার্ভারের বাৎসরিক পর্যালোচনা ({0})",
"LabelSetEbookAsPrimary": "প্রাথমিক হিসাবে সেট করুন",
"LabelSetEbookAsSupplementary": "পরিপূরক হিসেবে সেট করুন",
"LabelSettingsAudiobooksOnly": "শুধুমাত্র অডিও বই",
"LabelSettingsAudiobooksOnlyHelp": "এই সেটিংটি সক্ষম করা ই-বই ফাইলগুলিকে উপেক্ষা করবে যদি না সেগুলি একটি অডিওবই ফোল্ডারের মধ্যে থাকে যে ক্ষেত্রে সেগুলিকে সম্পূরক ই-বই হিসাবে সেট করা হবে",
"LabelSettingsBookshelfViewHelp": "কাঠের তাক সহ স্কুমরফিক ডিজাইন",
"LabelSettingsChromecastSupport": "ক্রোমকাস্ট সমর্থন",
"LabelSettingsDateFormat": "তারিখ বিন্যাস",
"LabelSettingsDisableWatcher": "প্রহরী নিষ্ক্রিয় করুন",
"LabelSettingsDisableWatcherForLibrary": "লাইব্রেরির জন্য ফোল্ডার প্রহরী নিষ্ক্রিয় করুন",
"LabelSettingsDisableWatcherHelp": "ফাইলের পরিবর্তন শনাক্ত হলে স্বয়ংক্রিয়ভাবে আইটেম যোগ/আপডেট করা অক্ষম করবে। *সার্ভার পুনরায় চালু করতে হবে",
"LabelSettingsEnableWatcher": "প্রহরী সক্ষম করুন",
"LabelSettingsEnableWatcherForLibrary": "লাইব্রেরির জন্য ফোল্ডার প্রহরী সক্ষম করুন",
"LabelSettingsEnableWatcherHelp": "ফাইলের পরিবর্তন শনাক্ত হলে আইটেমগুলির স্বয়ংক্রিয় যোগ/আপডেট সক্ষম করবে। *সার্ভার পুনরায় চালু করতে হবে",
"LabelSettingsExperimentalFeatures": "পরীক্ষামূলক বৈশিষ্ট্য",
"LabelSettingsExperimentalFeaturesHelp": "ফিচারের বৈশিষ্ট্য যা আপনার প্রতিক্রিয়া ব্যবহার করতে পারে এবং পরীক্ষায় সহায়তা করতে পারে। গিটহাব আলোচনা খুলতে ক্লিক করুন।",
"LabelSettingsFindCovers": "কভার খুঁজুন",
"LabelSettingsFindCoversHelp": "যদি আপনার অডিওবইয়ের ফোল্ডারের ভিতরে একটি এমবেডেড কভার বা কভার ইমেজ না থাকে, তাহলে স্ক্যানার একটি কভার খোঁজার চেষ্টা করবে৷<br>দ্রষ্টব্য: এটি স্ক্যানের সময় বাড়িয়ে দেবে",
"LabelSettingsHideSingleBookSeries": "একক বই সিরিজ লুকান",
"LabelSettingsHideSingleBookSeriesHelp": "যে সিরিজগুলোতে একটি বই আছে সেগুলো সিরিজের পাতা এবং নীড় পেজের তাক থেকে লুকিয়ে রাখা হবে।",
"LabelSettingsHomePageBookshelfView": "নীড় পেজে বুকশেলফ ভিউ ব্যবহার করুন",
"LabelSettingsLibraryBookshelfView": "লাইব্রেরি বুকশেলফ ভিউ ব্যবহার করুন",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "কন্টিনিউ সিরিজে আগের বইগুলো এড়িয়ে যান",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "কন্টিনিউ সিরিজের হোম পেজ শেল্ফ প্রথম বইটি দেখায় যেটি সিরিজে শুরু হয়নি যেটিতে অন্তত একটি বই শেষ হয়েছে এবং কোনো বই চলছে না। এই সেটিংটি সক্ষম করা হলে তা শুরু না হওয়া প্রথম বইটির পরিবর্তে সবচেয়ে দূরের সম্পূর্ণ বই থেকে সিরিজ চালিয়ে যাবে। ",
"LabelSettingsParseSubtitles": "সাবটাইটেল পার্স করুন",
"LabelSettingsParseSubtitlesHelp": "অডিওবুক ফোল্ডারের নাম থেকে সাবটাইটেল বের করুন৷<br>সাবটাইটেল অবশ্যই \" - \"<br>অর্থাৎ \"বুকের শিরোনাম - এখানে একটি সাবটাইটেল\" এর সাবটাইটেল আছে \"এখানে একটি সাবটাইটেল\"",
"LabelSettingsPreferMatchedMetadata": "মিলিত মেটাডেটা পছন্দ করুন",
"LabelSettingsPreferMatchedMetadataHelp": "দ্রুত ম্যাচ ব্যবহার করার সময় মিলে যাওয়া ডেটা আইটেমের বিবরণকে ওভাররাইড করবে। ডিফল্টরূপে দ্রুত ম্যাচ শুধুমাত্র অনুপস্থিত বিশদগুলি পূরণ করবে।",
"LabelSettingsSkipMatchingBooksWithASIN": "এমন বইগুলি এড়িয়ে যান যেগুলির মধ্যে ইতিমধ্যে একটি ASIN আছে",
"LabelSettingsSkipMatchingBooksWithISBN": "ইতিমধ্যে একটি ISBN আছে এমন মেলা বইগুলি এড়িয়ে যান",
"LabelSettingsSortingIgnorePrefixes": "বাছাই করার সময় উপসর্গ উপেক্ষা করুন",
"LabelSettingsSortingIgnorePrefixesHelp": "অর্থাৎ \"বইয়ের শিরোনাম\" বইয়ের শিরোনাম \"বইয়ের শিরোনাম, \" হিসাবে সাজানো হবে উপসর্গের জন্য",
"LabelSettingsSquareBookCovers": "বর্গাকার বইয়ের কভার ব্যবহার করুন",
"LabelSettingsSquareBookCoversHelp": "প্রমাণ ১.৬:১ বইয়ের কভারের চেয়ে বর্গাকার কভার ব্যবহার করতে পছন্দ করুন",
"LabelSettingsStoreCoversWithItem": "আইটেম সহ কভার সংরক্ষণ",
"LabelSettingsStoreCoversWithItemHelp": "ডিফল্টভাবে কভারগুলি /মেটাডাটা/আইটেমগুলিতে সংরক্ষণ করা হয়, এই সেটিংটি সক্ষম করলে আপনার লাইব্রেরি আইটেম ফোল্ডারে কভারগুলি সংরক্ষণ করা হবে৷ \"কভার\" নামে শুধুমাত্র একটি ফাইল রাখা হবে",
"LabelSettingsStoreMetadataWithItem": "আইটেমের সাথে মেটাডেটা সংরক্ষণ করুন",
"LabelSettingsStoreMetadataWithItemHelp": "ডিফল্টরূপে মেটাডেটা ফাইলগুলি /মেটাডাটা/আইটেমগুলি -এ সংরক্ষণ করা হয়, এই সেটিংটি সক্ষম করলে মেটাডেটা ফাইলগুলি আপনার লাইব্রেরি আইটেম ফোল্ডারে সংরক্ষণ করা হবে",
"LabelSettingsTimeFormat": "সময় বিন্যাস",
"LabelShowAll": "সব দেখান",
"LabelSize": "আকার",
"LabelSleepTimer": "স্লিপ টাইমার",
"LabelSlug": "স্লাগ",
"LabelStart": "শুরু",
"LabelStarted": "শুরু হয়েছে",
"LabelStartedAt": "এতে শুরু হয়েছে",
"LabelStartTime": "শুরু করার সময়",
"LabelStatsAudioTracks": "অডিও ট্র্যাক",
"LabelStatsAuthors": "লেখক",
"LabelStatsBestDay": "সেরা দিন",
"LabelStatsDailyAverage": "দৈনিক গড়",
"LabelStatsDays": "দিন",
"LabelStatsDaysListened": "যেদিন শোনা হয়েছে",
"LabelStatsHours": "ঘন্টা",
"LabelStatsInARow": "এক সারিতে",
"LabelStatsItemsFinished": "আইটেম সমাপ্ত",
"LabelStatsItemsInLibrary": "লাইব্রেরির আইটেম",
"LabelStatsMinutes": "মিনিট",
"LabelStatsMinutesListening": "মিনিট শুনছেন",
"LabelStatsOverallDays": "সামগ্রিক দিন",
"LabelStatsOverallHours": "সামগ্রিক ঘন্টা",
"LabelStatsWeekListening": "সপ্তাহ শোনা",
"LabelSubtitle": "সাবটাইটেল",
"LabelSupportedFileTypes": "সমর্থিত ফাইল প্রকার",
"LabelTag": "ট্যাগ",
"LabelTags": "ট্যাগগুলো",
"LabelTagsAccessibleToUser": "ব্যবহারকারীর কাছে অ্যাক্সেসযোগ্য ট্যাগ",
"LabelTagsNotAccessibleToUser": "ট্যাগগুলি ব্যবহারকারীর কাছে অ্যাক্সেসযোগ্য নয়",
"LabelTasks": "কাজ চলছে",
"LabelTextEditorBulletedList": "বুলেটেড তালিকা",
"LabelTextEditorLink": "লিঙ্ক",
"LabelTextEditorNumberedList": "সংখ্যাযুক্ত তালিকা",
"LabelTextEditorUnlink": "বিচ্ছিন্ন",
"LabelTheme": "থিম",
"LabelThemeDark": "অন্ধকার",
"LabelThemeLight": "আলো",
"LabelTimeBase": "সময় বেস",
"LabelTimeListened": "সময় শোনা হয়েছে",
"LabelTimeListenedToday": "আজ শোনার সময়",
"LabelTimeRemaining": "{0}টি অবশিষ্ট",
"LabelTimeToShift": "সেকেন্ডে স্থানান্তরের সময়",
"LabelTitle": "শিরোনাম",
"LabelToolsEmbedMetadata": "মেটাডেটা এম্বেড করুন",
"LabelToolsEmbedMetadataDescription": "কভার ইমেজ এবং অধ্যায় সহ অডিও ফাইলগুলিতে মেটাডেটা এম্বেড করুন।",
"LabelToolsMakeM4b": "M4B অডিওবুক ফাইল তৈরি করুন",
"LabelToolsMakeM4bDescription": "এমবেডেড মেটাডেটা, কভার ইমেজ এবং অধ্যায় সহ একটি .M4B অডিওবুক ফাইল তৈরি করুন।",
"LabelToolsSplitM4b": "M4B কে MP3 তে বিভক্ত করুন",
"LabelToolsSplitM4bDescription": "এমবেডেড মেটাডেটা, কভার ইমেজ এবং অধ্যায় সহ অধ্যায় দ্বারা একটি M4B বিভক্ত থেকে MP3 তৈরি করুন।",
"LabelTotalDuration": "মোট সময়কাল",
"LabelTotalTimeListened": "মোট সময় শোনা",
"LabelTrackFromFilename": "ফাইলের নাম থেকে ট্র্যাক করুন",
"LabelTrackFromMetadata": "মেটাডেটা থেকে ট্র্যাক করুন",
"LabelTracks": "ট্র্যাকস",
"LabelTracksMultiTrack": "মাল্টি-ট্র্যাক",
"LabelTracksNone": "কোন ট্র্যাক নেই",
"LabelTracksSingleTrack": "একক-ট্র্যাক",
"LabelType": "টাইপ",
"LabelUnabridged": "অসংলগ্ন",
"LabelUndo": "পূর্বাবস্থা",
"LabelUnknown": "অজানা",
"LabelUpdateCover": "কভার আপডেট করুন",
"LabelUpdateCoverHelp": "একটি মিল থাকা অবস্থায় নির্বাচিত বইগুলির বিদ্যমান কভারগুলি ওভাররাইট করার অনুমতি দিন",
"LabelUpdatedAt": "আপডেট করা হয়েছে",
"LabelUpdateDetails": "বিশদ আপডেট করুন",
"LabelUpdateDetailsHelp": "একটি মিল থাকা অবস্থায় নির্বাচিত বইগুলির বিদ্যমান বিবরণ ওভাররাইট করার অনুমতি দিন",
"LabelUploaderDragAndDrop": "ফাইল বা ফোল্ডার টেনে আনুন এবং ফেলে দিন",
"LabelUploaderDropFiles": "ফাইলগুলো ফেলে দিন",
"LabelUploaderItemFetchMetadataHelp": "স্বয়ংক্রিয়ভাবে শিরোনাম, লেখক এবং সিরিজ আনুন",
"LabelUseChapterTrack": "অধ্যায় ট্র্যাক ব্যবহার করুন",
"LabelUseFullTrack": "সম্পূর্ণ ট্র্যাক ব্যবহার করুন",
"LabelUser": "ব্যবহারকারী",
"LabelUsername": "ব্যবহারকারীর নাম",
"LabelValue": "মান",
"LabelVersion": "সংস্করণ",
"LabelViewBookmarks": "বুকমার্ক দেখুন",
"LabelViewChapters": "অধ্যায় দেখুন",
"LabelViewQueue": "প্লেয়ার সারি দেখুন",
"LabelVolume": "ভলিউম",
"LabelWeekdaysToRun": "চলতে হবে সপ্তাহের দিন",
"LabelYearReviewHide": "পর্যালোচনার বছর লুকান",
"LabelYearReviewShow": "পর্যালোচনার বছর দেখুন",
"LabelYourAudiobookDuration": "আপনার অডিওবুকের সময়কাল",
"LabelYourBookmarks": "আপনার বুকমার্কস",
"LabelYourPlaylists": "আপনার প্লেলিস্ট",
"LabelYourProgress": "আপনার অগ্রগতি",
"MessageAddToPlayerQueue": "প্লেয়ার সারিতে যোগ করুন",
"MessageAppriseDescription": "এই বৈশিষ্ট্যটি ব্যবহার করার জন্য আপনাকে <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</-এর একটি উদাহরণ থাকতে হবে a> চলমান বা একটি এপিআই যা সেই একই অনুরোধগুলি পরিচালনা করবে৷ <br /> বিজ্ঞপ্তি পাঠানোর জন্য Apprise API Url সম্পূর্ণ URL পাথ হওয়া উচিত, যেমন, যদি আপনার API উদাহরণ <code>http://192.168 এ পরিবেশিত হয়৷ 1.1:8337</code> তারপর আপনি <code>http://192.168.1.1:8337/notify</code> লিখবেন।",
"MessageBackupsDescription": "ব্যাকআপের মধ্যে রয়েছে ব্যবহারকারী, ব্যবহারকারীর অগ্রগতি, লাইব্রেরি আইটেমের বিবরণ, সার্ভার সেটিংস এবং <code>/metadata/items</code> & <code>/metadata/authors</code>-এ সংরক্ষিত ছবি। ব্যাকআপগুলি <strong> আপনার লাইব্রেরি ফোল্ডারে সঞ্চিত কোনো ফাইল >অন্তর্ভুক্ত করবেন না</strong>।",
"MessageBatchQuickMatchDescription": "কুইক ম্যাচ নির্বাচিত আইটেমগুলির জন্য অনুপস্থিত কভার এবং মেটাডেটা যোগ করার চেষ্টা করবে। বিদ্যমান কভার এবং/অথবা মেটাডেটা ওভাররাইট করার জন্য দ্রুত ম্যাচকে অনুমতি দিতে নীচের বিকল্পগুলি সক্ষম করুন।",
"MessageBookshelfNoCollections": "আপনি এখনও কোনো সংগ্রহ করেননি",
"MessageBookshelfNoResultsForFilter": "ফিল্টার \"{0}: {1}\" এর জন্য কোন ফলাফল নেই",
"MessageBookshelfNoRSSFeeds": "কোনও RSS ফিড খোলা নেই",
"MessageBookshelfNoSeries": "আপনার কোনো সিরিজ নেই",
"MessageChapterEndIsAfter": "অধ্যায়ের সমাপ্তি আপনার অডিওবুকের শেষে",
"MessageChapterErrorFirstNotZero": "প্রথম অধ্যায় 0 এ শুরু হতে হবে",
"MessageChapterErrorStartGteDuration": "অবৈধ শুরুর সময় অবশ্যই অডিওবুকের সময়কালের কম হতে হবে",
"MessageChapterErrorStartLtPrev": "অবৈধ শুরুর সময় অবশ্যই আগের অধ্যায় শুরুর সময়ের চেয়ে বেশি বা সমান হতে হবে",
"MessageChapterStartIsAfter": "আপনার অডিওবুক শেষ হওয়ার পরে অধ্যায় শুরু হয়",
"MessageCheckingCron": "ক্রন পরীক্ষা করা হচ্ছে...",
"MessageConfirmCloseFeed": "আপনি কি নিশ্চিত যে আপনি এই ফিডটি বন্ধ করতে চান?",
"MessageConfirmDeleteBackup": "আপনি কি নিশ্চিত যে আপনি {0} এর ব্যাকআপ মুছে ফেলতে চান?",
"MessageConfirmDeleteFile": "এটি আপনার ফাইল সিস্টেম থেকে ফাইলটি মুছে দেবে। আপনি কি নিশ্চিত?",
"MessageConfirmDeleteLibrary": "আপনি কি নিশ্চিত যে আপনি স্থায়ীভাবে লাইব্রেরি \"{0}\" মুছে ফেলতে চান?",
"MessageConfirmDeleteLibraryItem": "এটি ডাটাবেস এবং আপনার ফাইল সিস্টেম থেকে লাইব্রেরি আইটেমটি মুছে ফেলবে। আপনি কি নিশ্চিত?",
"MessageConfirmDeleteLibraryItems": "এটি ডাটাবেস এবং আপনার ফাইল সিস্টেম থেকে {0}টি লাইব্রেরি আইটেম মুছে ফেলবে। আপনি কি নিশ্চিত?",
"MessageConfirmDeleteSession": "আপনি কি নিশ্চিত আপনি এই অধিবেশন মুছে দিতে চান?",
"MessageConfirmForceReScan": "আপনি কি নিশ্চিত যে আপনি জোর করে পুনরায় স্ক্যান করতে চান?",
"MessageConfirmMarkAllEpisodesFinished": "আপনি কি নিশ্চিত যে আপনি সমস্ত পর্ব সমাপ্ত হিসাবে চিহ্নিত করতে চান?",
"MessageConfirmMarkAllEpisodesNotFinished": "আপনি কি নিশ্চিত যে আপনি সমস্ত পর্বকে শেষ হয়নি বলে চিহ্নিত করতে চান?",
"MessageConfirmMarkSeriesFinished": "আপনি কি নিশ্চিত যে আপনি এই সিরিজের সমস্ত বইকে সমাপ্ত হিসাবে চিহ্নিত করতে চান?",
"MessageConfirmMarkSeriesNotFinished": "আপনি কি নিশ্চিত যে আপনি এই সিরিজের সমস্ত বইকে শেষ হয়নি বলে চিহ্নিত করতে চান?",
"MessageConfirmQuickEmbed": "সতর্কতা! দ্রুত এম্বেড আপনার অডিও ফাইলের ব্যাকআপ করবে না। নিশ্চিত করুন যে আপনার অডিও ফাইলগুলির একটি ব্যাকআপ আছে। <br><br>আপনি কি চালিয়ে যেতে চান?",
"MessageConfirmRemoveAllChapters": "আপনি কি নিশ্চিত যে আপনি সমস্ত অধ্যায় সরাতে চান?",
"MessageConfirmRemoveAuthor": "আপনি কি নিশ্চিত যে আপনি লেখক \"{0}\" অপসারণ করতে চান?",
"MessageConfirmRemoveCollection": "আপনি কি নিশ্চিত যে আপনি সংগ্রহ \"{0}\" সরাতে চান?",
"MessageConfirmRemoveEpisode": "আপনি কি নিশ্চিত আপনি \"{0}\" পর্বটি সরাতে চান?",
"MessageConfirmRemoveEpisodes": "আপনি কি নিশ্চিত যে আপনি {0}টি পর্ব সরাতে চান?",
"MessageConfirmRemoveListeningSessions": "আপনি কি নিশ্চিত যে আপনি {0}টি শোনার সেশন সরাতে চান?",
"MessageConfirmRemoveNarrator": "আপনি কি \"{0}\" বর্ণনাকারীকে সরানোর বিষয়ে নিশ্চিত?",
"MessageConfirmRemovePlaylist": "আপনি কি নিশ্চিত যে আপনি আপনার প্লেলিস্ট \"{0}\" সরাতে চান?",
"MessageConfirmRenameGenre": "আপনি কি নিশ্চিত যে আপনি সমস্ত আইটেমের জন্য \"{0}\" ধারার নাম পরিবর্তন করে \"{1}\" করতে চান?",
"MessageConfirmRenameGenreMergeNote": "দ্রষ্টব্য: এই ধারাটি আগে থেকেই বিদ্যমান তাই সেগুলিকে একত্রিত করা হবে।",
"MessageConfirmRenameGenreWarning": "সতর্কতা! একটি ভিন্ন কেসিং সহ একটি অনুরূপ ধারা ইতিমধ্যেই বিদ্যমান \"{0}\"।",
"MessageConfirmRenameTag": "আপনি কি সব আইটেমের জন্য \"{0}\" ট্যাগের নাম পরিবর্তন করে \"{1}\" করার বিষয়ে নিশ্চিত?",
"MessageConfirmRenameTagMergeNote": "দ্রষ্টব্য: এই ট্যাগটি আগে থেকেই বিদ্যমান তাই সেগুলিকে একত্র করা হবে।",
"MessageConfirmRenameTagWarning": "সতর্কতা! একটি ভিন্ন কেসিং সহ একটি অনুরূপ ট্যাগ ইতিমধ্যেই বিদ্যমান \"{0}\"।",
"MessageConfirmReScanLibraryItems": "আপনি কি নিশ্চিত যে আপনি {0}টি আইটেম পুনরায় স্ক্যান করতে চান?",
"MessageConfirmSendEbookToDevice": "আপনি কি নিশ্চিত যে আপনি \"{2}\" ডিভাইসে {0} ইবুক \"{1}\" পাঠাতে চান?",
"MessageDownloadingEpisode": "ডাউনলোডিং পর্ব",
"MessageDragFilesIntoTrackOrder": "সঠিক ট্র্যাক অর্ডারে ফাইল টেনে আনুন",
"MessageEmbedFinished": "এম্বেড করা শেষ!",
"MessageEpisodesQueuedForDownload": "{0} পর্ব(গুলি) ডাউনলোডের জন্য সারিবদ্ধ",
"MessageFeedURLWillBe": "ফিড URL হবে {0}",
"MessageFetching": "আনয় হচ্ছে...",
"MessageForceReScanDescription": "সকল ফাইল আবার নতুন স্ক্যানের মত স্ক্যান করবে। অডিও ফাইল ID3 ট্যাগ, OPF ফাইল, এবং টেক্সট ফাইলগুলি নতুন হিসাবে স্ক্যান করা হবে।",
"MessageImportantNotice": "গুরুত্বপূর্ণ বিজ্ঞপ্তি!",
"MessageInsertChapterBelow": "নীচে অধ্যায় ঢোকান",
"MessageItemsSelected": "{0}টি আইটেম নির্বাচিত",
"MessageItemsUpdated": "{0}টি আইটেম আপডেট করা হয়েছে",
"MessageJoinUsOn": "আমাদের সাথে যোগ দিন",
"MessageListeningSessionsInTheLastYear": "গত বছরে {0}টি শোনার সেশন",
"MessageLoading": "লোড হচ্ছে...",
"MessageLoadingFolders": "ফোল্ডার লোড হচ্ছে...",
"MessageM4BFailed": "M4B ব্যর্থ!",
"MessageM4BFinished": "M4B সমাপ্ত!",
"MessageMapChapterTitles": "টাইমস্ট্যাম্প সামঞ্জস্য না করে আপনার বিদ্যমান অডিওবুক অধ্যায়গুলিতে অধ্যায়ের শিরোনাম ম্যাপ করুন",
"MessageMarkAllEpisodesFinished": "সমস্ত পর্ব সমাপ্ত চিহ্নিত করুন",
"MessageMarkAllEpisodesNotFinished": "সমস্ত পর্ব শেষ হয়নি চিহ্নিত করুন",
"MessageMarkAsFinished": "সমাপ্ত হিসাবে চিহ্নিত করুন",
"MessageMarkAsNotFinished": "সমাপ্ত হয়নি হিসাবে চিহ্নিত করুন",
"MessageMatchBooksDescription": "নির্বাচিত অনুসন্ধান প্রদানকারীর একটি বইয়ের সাথে লাইব্রেরিতে বই মেলানোর চেষ্টা করবে এবং খালি বিবরণ এবং কভার আর্ট পূরণ করবে। বিস্তারিত ওভাররাইট করে না।",
"MessageNoAudioTracks": "কোন অডিও ট্র্যাক নেই",
"MessageNoAuthors": "কোন লেখক নেই",
"MessageNoBackups": "কোন ব্যাকআপ নেই",
"MessageNoBookmarks": "কোন বুকমার্ক নেই",
"MessageNoChapters": "কোনও অধ্যায় নেই",
"MessageNoCollections": "কোন সংগ্রহ নেই",
"MessageNoCoversFound": "কোন কভার পাওয়া যায়নি",
"MessageNoDescription": "কোন বর্ণনা নেই",
"MessageNoDownloadsInProgress": "বর্তমানে কোনো ডাউনলোড চলছে না",
"MessageNoDownloadsQueued": "কোনও ডাউনলোড সারি নেই",
"MessageNoEpisodeMatchesFound": "কোন পর্বের মিল পাওয়া যায়নি",
"MessageNoEpisodes": "কোন পর্ব নেই",
"MessageNoFoldersAvailable": "কোন ফোল্ডার উপলব্ধ নেই",
"MessageNoGenres": "কোন ধরন নেই",
"MessageNoIssues": "কোন সমস্যা নেই",
"MessageNoItems": "কোন আইটেম নেই",
"MessageNoItemsFound": "কোন আইটেম পাওয়া যায়নি",
"MessageNoListeningSessions": "কোনও শোনার সেশন নেই",
"MessageNoLogs": "কোনও লগ নেই",
"MessageNoMediaProgress": "মিডিয়া অগ্রগতি নেই",
"MessageNoNotifications": "কোনো বিজ্ঞপ্তি নেই",
"MessageNoPodcastsFound": "কোন পডকাস্ট পাওয়া যায়নি",
"MessageNoResults": "কোন ফলাফল নেই",
"MessageNoSearchResultsFor": "\"{0}\" এর জন্য কোন অনুসন্ধান ফলাফল নেই",
"MessageNoSeries": "কোন সিরিজ নেই",
"MessageNoTags": "কোন ট্যাগ নেই",
"MessageNoTasksRunning": "কোন টাস্ক চলছে না",
"MessageNotYetImplemented": "এখনও বাস্তবায়িত হয়নি",
"MessageNoUpdateNecessary": "কোন আপডেটের প্রয়োজন নেই",
"MessageNoUpdatesWereNecessary": "কোন আপডেটের প্রয়োজন ছিল না",
"MessageNoUserPlaylists": "আপনার কোনো প্লেলিস্ট নেই",
"MessageOr": "বা",
"MessagePauseChapter": "পজ অধ্যায় প্লেব্যাক",
"MessagePlayChapter": "অধ্যায়ের শুরুতে শুনুন",
"MessagePlaylistCreateFromCollection": "সংগ্রহ থেকে প্লেলিস্ট তৈরি করুন",
"MessagePodcastHasNoRSSFeedForMatching": "পডকাস্টের সাথে মিলের জন্য ব্যবহার করার জন্য কোন RSS ফিড ইউআরএল নেই",
"MessageQuickMatchDescription": "খালি আইটেমের বিশদ বিবরণ এবং '{0}' থেকে প্রথম ম্যাচের ফলাফলের সাথে কভার করুন। সার্ভার সেটিং সক্ষম না থাকলে বিশদ ওভাররাইট করে না।",
"MessageRemoveChapter": "অধ্যায় সরান",
"MessageRemoveEpisodes": "{0}টি পর্ব(গুলি) সরান",
"MessageRemoveFromPlayerQueue": "প্লেয়ার সারি থেকে সরান",
"MessageRemoveUserWarning": "আপনি কি নিশ্চিত আপনি স্থায়ীভাবে ব্যবহারকারী \"{0}\" মুছে ফেলতে চান?",
"MessageReportBugsAndContribute": "বাগ রিপোর্ট করুন, বৈশিষ্ট্যের অনুরোধ করুন এবং এতে অবদান রাখুন",
"MessageResetChaptersConfirm": "আপনি কি নিশ্চিত যে আপনি অধ্যায়গুলি পুনরায় সেট করতে চান এবং আপনার করা পরিবর্তনগুলি পূর্বাবস্থায় ফেরাতে চান?",
"MessageRestoreBackupConfirm": "আপনি কি নিশ্চিত যে আপনি তৈরি করা ব্যাকআপ পুনরুদ্ধার করতে চান",
"MessageRestoreBackupWarning": "একটি ব্যাকআপ পুনরুদ্ধার করা হলে তা /config-এ অবস্থিত সমগ্র ডাটাবেস ওভাররাইট করবে এবং /metadata/items & /metadata/authors-এ থাকা ছবিগুলিকে কভার করবে৷<br /><br />ব্যাকআপগুলি আপনার লাইব্রেরি ফোল্ডারে কোনো ফাইল পরিবর্তন করে না৷ আপনি যদি আপনার লাইব্রেরি ফোল্ডারে কভার আর্ট এবং মেটাডেটা সংরক্ষণ করতে সার্ভার সেটিংস সক্ষম করে থাকেন তবে সেগুলি ব্যাক আপ বা ওভাররাইট করা হয় না৷<br /><br />আপনার সার্ভার ব্যবহারকারী সমস্ত ক্লায়েন্ট স্বয়ংক্রিয়ভাবে রিফ্রেশ হবে৷",
"MessageSearchResultsFor": "এর জন্য অনুসন্ধান ফলাফল",
"MessageSelected": "{0}টি নির্বাচিত",
"MessageServerCouldNotBeReached": "সার্ভারে পৌঁছানো যায়নি",
"MessageSetChaptersFromTracksDescription": "প্রতিটি অডিও ফাইলকে অধ্যায় হিসেবে ব্যবহার করে অধ্যায় সেট করুন এবং অডিও ফাইলের নাম হিসেবে অধ্যায়ের শিরোনাম করুন",
"MessageStartPlaybackAtTime": "\"{0}\" এর জন্য {1} এ প্লেব্যাক শুরু করবেন?",
"MessageThinking": "চিন্তা করছি...",
"MessageUploaderItemFailed": "আপলোড করতে ব্যর্থ",
"MessageUploaderItemSuccess": "সফলভাবে আপলোড হয়েছে!",
"MessageUploading": "আপলোড হচ্ছে...",
"MessageValidCronExpression": "বৈধ ক্রোন এক্সপ্রেশন",
"MessageWatcherIsDisabledGlobally": "সার্ভার সেটিংসে বিশ্বব্যাপী প্রহরী অক্ষম করা হয়েছে",
"MessageXLibraryIsEmpty": "{0} লাইব্রেরি খালি!",
"MessageYourAudiobookDurationIsLonger": "আপনার অডিওবুকের সময়কাল পাওয়া সময়ের চেয়ে বেশি",
"MessageYourAudiobookDurationIsShorter": "আপনার অডিওবুকের সময়কাল পাওয়া সময়ের চেয়ে কম",
"NoteChangeRootPassword": "রুট ব্যবহারকারীই একমাত্র ব্যবহারকারী যার একটি খালি পাসওয়ার্ড থাকতে পারে",
"NoteChapterEditorTimes": "দ্রষ্টব্য: প্রথম অধ্যায়ের শুরুর সময় অবশ্যই 0:00 এ থাকতে হবে এবং শেষ অধ্যায়ের শুরুর সময়টি এই অডিওবুকের সময়কাল অতিক্রম করতে পারবে না।",
"NoteFolderPicker": "দ্রষ্টব্য: ইতিমধ্যে ম্যাপ করা ফোল্ডারগুলি দেখানো হবে না",
"NoteRSSFeedPodcastAppsHttps": "সতর্কতা: বেশিরভাগ পডকাস্ট অ্যাপের জন্য প্রয়োজন হবে RSS ফিড URL যেটি HTTPS ব্যবহার করছে",
"NoteRSSFeedPodcastAppsPubDate": "সতর্কতা: আপনার 1 বা তার বেশি পর্বের একটি পাব তারিখ নেই। কিছু পডকাস্ট অ্যাপের এটি প্রয়োজন।",
"NoteUploaderFoldersWithMediaFiles": "মিডিয়া ফাইল সহ ফোল্ডারগুলি আলাদা লাইব্রেরি আইটেম হিসাবে পরিচালনা করা হবে।",
"NoteUploaderOnlyAudioFiles": "যদি শুধুমাত্র অডিও ফাইল আপলোড করা হয় তবে প্রতিটি অডিও ফাইল একটি পৃথক অডিওবুক হিসাবে পরিচালনা করা হবে।",
"NoteUploaderUnsupportedFiles": "অসমর্থিত ফাইলগুলি উপেক্ষা করা হয়। একটি ফোল্ডার বেছে নেওয়া বা ফেলে দেওয়ার সময়, আইটেম ফোল্ডারে নেই এমন অন্যান্য ফাইলগুলি উপেক্ষা করা হয়।",
"PlaceholderNewCollection": "নতুন সংগ্রহের নাম",
"PlaceholderNewFolderPath": "নতুন ফোল্ডার পথ",
"PlaceholderNewPlaylist": "নতুন প্লেলিস্টের নাম",
"PlaceholderSearch": "অনুসন্ধান..",
"PlaceholderSearchEpisode": "অনুসন্ধান পর্ব..",
"ToastAccountUpdateFailed": "অ্যাকাউন্ট আপডেট করতে ব্যর্থ",
"ToastAccountUpdateSuccess": "অ্যাকাউন্ট আপডেট করা হয়েছে",
"ToastAuthorImageRemoveFailed": "ছবি সরাতে ব্যর্থ",
"ToastAuthorImageRemoveSuccess": "লেখকের ছবি সরানো হয়েছে",
"ToastAuthorUpdateFailed": "লেখক আপডেট করতে ব্যর্থ",
"ToastAuthorUpdateMerged": "লেখক একত্রিত হয়েছে",
"ToastAuthorUpdateSuccess": "লেখক আপডেট করেছেন",
"ToastAuthorUpdateSuccessNoImageFound": "লেখক আপডেট করেছেন (কোন ছবি পাওয়া যায়নি)",
"ToastBackupCreateFailed": "ব্যাকআপ তৈরি করতে ব্যর্থ",
"ToastBackupCreateSuccess": "ব্যাকআপ তৈরি করা হয়েছে",
"ToastBackupDeleteFailed": "ব্যাকআপ মুছে ফেলতে ব্যর্থ",
"ToastBackupDeleteSuccess": "ব্যাকআপ মুছে ফেলা হয়েছে",
"ToastBackupRestoreFailed": "ব্যাকআপ পুনরুদ্ধার করতে ব্যর্থ",
"ToastBackupUploadFailed": "ব্যাকআপ আপলোড করতে ব্যর্থ",
"ToastBackupUploadSuccess": "ব্যাকআপ আপলোড হয়েছে",
"ToastBatchUpdateFailed": "ব্যাচ আপডেট ব্যর্থ হয়েছে",
"ToastBatchUpdateSuccess": "ব্যাচ আপডেট সাফল্য",
"ToastBookmarkCreateFailed": "বুকমার্ক তৈরি করতে ব্যর্থ",
"ToastBookmarkCreateSuccess": "বুকমার্ক যোগ করা হয়েছে",
"ToastBookmarkRemoveFailed": "বুকমার্ক সরাতে ব্যর্থ",
"ToastBookmarkRemoveSuccess": "বুকমার্ক সরানো হয়েছে",
"ToastBookmarkUpdateFailed": "বুকমার্ক আপডেট করতে ব্যর্থ",
"ToastBookmarkUpdateSuccess": "বুকমার্ক আপডেট করা হয়েছে",
"ToastChaptersHaveErrors": "অধ্যায়ে ত্রুটি আছে",
"ToastChaptersMustHaveTitles": "অধ্যায়ের শিরোনাম থাকতে হবে",
"ToastCollectionItemsRemoveFailed": "সংগ্রহ থেকে আইটেম(গুলি) সরাতে ব্যর্থ",
"ToastCollectionItemsRemoveSuccess": "আইটেম(গুলি) সংগ্রহ থেকে সরানো হয়েছে",
"ToastCollectionRemoveFailed": "সংগ্রহ সরাতে ব্যর্থ",
"ToastCollectionRemoveSuccess": "সংগ্রহ সরানো হয়েছে",
"ToastCollectionUpdateFailed": "সংগ্রহ আপডেট করতে ব্যর্থ",
"ToastCollectionUpdateSuccess": "সংগ্রহ আপডেট করা হয়েছে",
"ToastItemCoverUpdateFailed": "আইটেম কভার আপডেট করতে ব্যর্থ হয়েছে",
"ToastItemCoverUpdateSuccess": "আইটেম কভার আপডেট করা হয়েছে",
"ToastItemDetailsUpdateFailed": "আইটেমের বিবরণ আপডেট করতে ব্যর্থ",
"ToastItemDetailsUpdateSuccess": "আইটেমের বিবরণ আপডেট করা হয়েছে",
"ToastItemDetailsUpdateUnneeded": "আইটেমের বিবরণের জন্য কোন আপডেটের প্রয়োজন নেই",
"ToastItemMarkedAsFinishedFailed": "সমাপ্ত হিসাবে চিহ্নিত করতে ব্যর্থ",
"ToastItemMarkedAsFinishedSuccess": "আইটেম সমাপ্ত হিসাবে চিহ্নিত",
"ToastItemMarkedAsNotFinishedFailed": "সমাপ্ত হয়নি হিসাবে চিহ্নিত করতে ব্যর্থ",
"ToastItemMarkedAsNotFinishedSuccess": "আইটেম সমাপ্ত হয়নি বলে চিহ্নিত",
"ToastLibraryCreateFailed": "লাইব্রেরি তৈরি করতে ব্যর্থ",
"ToastLibraryCreateSuccess": "লাইব্রেরি \"{0}\" তৈরি করা হয়েছে",
"ToastLibraryDeleteFailed": "লাইব্রেরি মুছে ফেলতে ব্যর্থ",
"ToastLibraryDeleteSuccess": "লাইব্রেরি মুছে ফেলা হয়েছে",
"ToastLibraryScanFailedToStart": "স্ক্যান শুরু করতে ব্যর্থ",
"ToastLibraryScanStarted": "লাইব্রেরি স্ক্যান শুরু হয়েছে",
"ToastLibraryUpdateFailed": "লাইব্রেরি আপডেট করতে ব্যর্থ",
"ToastLibraryUpdateSuccess": "লাইব্রেরি \"{0}\" আপডেট করা হয়েছে",
"ToastPlaylistCreateFailed": "প্লেলিস্ট তৈরি করতে ব্যর্থ",
"ToastPlaylistCreateSuccess": "প্লেলিস্ট তৈরি করা হয়েছে",
"ToastPlaylistRemoveFailed": "প্লেলিস্ট সরাতে ব্যর্থ",
"ToastPlaylistRemoveSuccess": "প্লেলিস্ট সরানো হয়েছে",
"ToastPlaylistUpdateFailed": "প্লেলিস্ট আপডেট করতে ব্যর্থ",
"ToastPlaylistUpdateSuccess": "প্লেলিস্ট আপডেট করা হয়েছে",
"ToastPodcastCreateFailed": "পডকাস্ট তৈরি করতে ব্যর্থ",
"ToastPodcastCreateSuccess": "পডকাস্ট সফলভাবে তৈরি করা হয়েছে",
"ToastRemoveItemFromCollectionFailed": "সংগ্রহ থেকে আইটেম সরাতে ব্যর্থ",
"ToastRemoveItemFromCollectionSuccess": "সংগ্রহ থেকে আইটেম সরানো হয়েছে",
"ToastRSSFeedCloseFailed": "RSS ফিড বন্ধ করতে ব্যর্থ",
"ToastRSSFeedCloseSuccess": "RSS ফিড বন্ধ",
"ToastSendEbookToDeviceFailed": "ডিভাইসে ইবুক পাঠাতে ব্যর্থ",
"ToastSendEbookToDeviceSuccess": "ইবুক \"{0}\" ডিভাইসে পাঠানো হয়েছে",
"ToastSeriesUpdateFailed": "সিরিজ আপডেট ব্যর্থ হয়েছে",
"ToastSeriesUpdateSuccess": "সিরিজ আপডেট সাফল্য",
"ToastSessionDeleteFailed": "সেশন মুছে ফেলতে ব্যর্থ",
"ToastSessionDeleteSuccess": "সেশন মুছে ফেলা হয়েছে",
"ToastSocketConnected": "সকেট সংযুক্ত",
"ToastSocketDisconnected": "সকেট সংযোগ বিচ্ছিন্ন",
"ToastSocketFailedToConnect": "সকেট সংযোগ করতে ব্যর্থ হয়েছে",
"ToastUserDeleteFailed": "ব্যবহারকারী মুছতে ব্যর্থ",
"ToastUserDeleteSuccess": "ব্যবহারকারী মুছে ফেলা হয়েছে"
}
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Každých 6 hodin",
"LabelIntervalEveryDay": "Každý den",
"LabelIntervalEveryHour": "Každou hodinu",
"LabelInvalidParts": "Neplatné části",
"LabelInvert": "Invertovat",
"LabelItem": "Položka",
"LabelLanguage": "Jazyk",
@@ -357,7 +356,6 @@
"LabelMinute": "Minuta",
"LabelMissing": "Chybějící",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Chybějící díly",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Nezahájeno",
"LabelNumberOfBooks": "Počet knih",
"LabelNumberOfEpisodes": "Počet epizod",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Otevřít RSS kanál",
"LabelOverwrite": "Přepsat",
"LabelPassword": "Heslo",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Hver 6. time",
"LabelIntervalEveryDay": "Hver dag",
"LabelIntervalEveryHour": "Hver time",
"LabelInvalidParts": "Ugyldige dele",
"LabelInvert": "Inverter",
"LabelItem": "Element",
"LabelLanguage": "Sprog",
@@ -357,7 +356,6 @@
"LabelMinute": "Minut",
"LabelMissing": "Mangler",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Manglende dele",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Ikke påbegyndt",
"LabelNumberOfBooks": "Antal bøger",
"LabelNumberOfEpisodes": "Antal episoder",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Åbn RSS-feed",
"LabelOverwrite": "Overskriv",
"LabelPassword": "Kodeord",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Alle 6 Stunden",
"LabelIntervalEveryDay": "Jeden Tag",
"LabelIntervalEveryHour": "Jede Stunde",
"LabelInvalidParts": "Ungültige Teile",
"LabelInvert": "Umkehren",
"LabelItem": "Medium",
"LabelLanguage": "Sprache",
@@ -357,7 +356,6 @@
"LabelMinute": "Minute",
"LabelMissing": "Fehlend",
"LabelMissingEbook": "E-Book fehlt",
"LabelMissingParts": "Fehlende Teile",
"LabelMissingSupplementaryEbook": "Ergänzendes E-Book fehlt",
"LabelMobileRedirectURIs": "Erlaubte Weiterleitungs-URIs für die mobile App",
"LabelMobileRedirectURIsDescription": "Dies ist eine Whitelist gültiger Umleitungs-URIs für mobile Apps. Der Standardwert ist <code>audiobookshelf://oauth</code>, den du entfernen oder durch zusätzliche URIs für die Integration von Drittanbieter-Apps ergänzen kannst. Die Verwendung eines Sternchens (<code>*</code>) als alleiniger Eintrag erlaubt jede URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Nicht begonnen",
"LabelNumberOfBooks": "Anzahl der Hörbücher",
"LabelNumberOfEpisodes": "Anzahl der Episoden",
"LabelOpenIDAdvancedPermsClaimDescription": "Name des OpenID-Claims, der erweiterte Berechtigungen für Benutzeraktionen innerhalb der Anwendung enthält, die auf Nicht-Admin-Rollen angewendet werden (<b>wenn konfiguriert</b>). Wenn der Claim in der Antwort fehlt, wird der Zugang zu ABS verweigert. Fehlt eine einzelne Option, wird sie als <code>false</code> behandelt. Stelle sicher, dass der Claim des Identitätsanbieters der erwarteten Struktur entspricht:",
"LabelOpenIDClaims": "Lass die folgenden Optionen leer, um die erweiterte Zuweisung von Gruppen und Berechtigungen zu deaktivieren und automatisch die 'User'-Gruppe zuzuweisen.",
"LabelOpenIDGroupClaimDescription": "Name des OpenID-Claims, der eine Liste der Benutzergruppen enthält. Wird häufig als <code>groups</code> bezeichnet. <b>Wenn konfiguriert</b>, wird die Anwendung automatisch Rollen basierend auf den Gruppenmitgliedschaften des Benutzers zuweisen, vorausgesetzt, dass diese Gruppen im Claim als 'admin', 'user' oder 'guest' benannt sind (Groß/Kleinschreibung ist irrelevant). Der Claim eine Liste sein, und wenn ein Benutzer mehreren Gruppen angehört, wird die Anwendung die Rolle zuordnen, die dem höchsten Zugriffslevel entspricht. Wenn keine Gruppe übereinstimmt, wird der Zugang verweigert.",
"LabelOpenRSSFeed": "Öffne RSS-Feed",
"LabelOverwrite": "Überschreiben",
"LabelPassword": "Passwort",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Every 6 hours",
"LabelIntervalEveryDay": "Every day",
"LabelIntervalEveryHour": "Every hour",
"LabelInvalidParts": "Invalid Parts",
"LabelInvert": "Invert",
"LabelItem": "Item",
"LabelLanguage": "Language",
@@ -357,7 +356,6 @@
"LabelMinute": "Minute",
"LabelMissing": "Missing",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Missing Parts",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Not Started",
"LabelNumberOfBooks": "Number of Books",
"LabelNumberOfEpisodes": "# of Episodes",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Open RSS Feed",
"LabelOverwrite": "Overwrite",
"LabelPassword": "Password",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Cada 6 Horas",
"LabelIntervalEveryDay": "Cada Día",
"LabelIntervalEveryHour": "Cada Hora",
"LabelInvalidParts": "Partes Inválidas",
"LabelInvert": "Invertir",
"LabelItem": "Elemento",
"LabelLanguage": "Lenguaje",
@@ -357,7 +356,6 @@
"LabelMinute": "Minuto",
"LabelMissing": "Ausente",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Partes Ausentes",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "URIs de redirección a móviles permitidos",
"LabelMobileRedirectURIsDescription": "Esta es una lista de URIs válidos para redireccionamiento de apps móviles. La URI por defecto es <code>audiobookshelf://oauth</code>, la cual puedes remover or corroborar con URIs adicionales para la integración con apps de terceros. Utilizando un asterisco (<code>*</code>) como el único punto de entrada permite cualquier URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Sin Iniciar",
"LabelNumberOfBooks": "Numero de Libros",
"LabelNumberOfEpisodes": "# de Episodios",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Abrir Fuente RSS",
"LabelOverwrite": "Sobrescribir",
"LabelPassword": "Contraseña",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Iga 6 tunni tagant",
"LabelIntervalEveryDay": "Iga päev",
"LabelIntervalEveryHour": "Iga tunni tagant",
"LabelInvalidParts": "Vigased osad",
"LabelInvert": "Pööra ümber",
"LabelItem": "Kirje",
"LabelLanguage": "Keel",
@@ -357,7 +356,6 @@
"LabelMinute": "Minut",
"LabelMissing": "Puudub",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Puuduvad osad",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Lubatud mobiilile suunamise URI-d",
"LabelMobileRedirectURIsDescription": "See on mobiilirakenduste jaoks kehtivate suunamise URI-de lubatud nimekiri. Vaikimisi on selleks <code>audiobookshelf://oauth</code>, mida saate eemaldada või täiendada täiendavate URI-dega kolmanda osapoole rakenduste integreerimiseks. Tärni (<code>*</code>) ainukese kirjena kasutamine võimaldab mis tahes URI-d.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Pole alustatud",
"LabelNumberOfBooks": "Raamatute arv",
"LabelNumberOfEpisodes": "Episoodide arv",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Ava RSS voog",
"LabelOverwrite": "Kirjuta üle",
"LabelPassword": "Parool",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Toutes les 6 heures",
"LabelIntervalEveryDay": "Tous les jours",
"LabelIntervalEveryHour": "Toutes les heures",
"LabelInvalidParts": "Parties invalides",
"LabelInvert": "Inverser",
"LabelItem": "Article",
"LabelLanguage": "Langue",
@@ -357,7 +356,6 @@
"LabelMinute": "Minute",
"LabelMissing": "Manquant",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Parties manquantes",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "URI de redirection mobile autorisés",
"LabelMobileRedirectURIsDescription": "Il s'agit d'une liste blanche dURI de redirection valides pour les applications mobiles. Celui par défaut est <code>audiobookshelf://oauth</code>, que vous pouvez supprimer ou compléter avec des URIs supplémentaires pour l'intégration d'applications tierces. Lutilisation dun astérisque (<code>*</code>) comme seule entrée autorise nimporte quel URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Pas commencé",
"LabelNumberOfBooks": "Nombre de livres",
"LabelNumberOfEpisodes": "Nombre d’épisodes",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Ouvrir le flux RSS",
"LabelOverwrite": "Écraser",
"LabelPassword": "Mot de passe",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Every 6 hours",
"LabelIntervalEveryDay": "Every day",
"LabelIntervalEveryHour": "Every hour",
"LabelInvalidParts": "Invalid Parts",
"LabelInvert": "Invert",
"LabelItem": "Item",
"LabelLanguage": "Language",
@@ -357,7 +356,6 @@
"LabelMinute": "Minute",
"LabelMissing": "Missing",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Missing Parts",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Not Started",
"LabelNumberOfBooks": "Number of Books",
"LabelNumberOfEpisodes": "# of Episodes",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Open RSS Feed",
"LabelOverwrite": "Overwrite",
"LabelPassword": "Password",
+782
View File
@@ -0,0 +1,782 @@
{
"ButtonAdd": "הוסף",
"ButtonAddChapters": "הוסף פרקים",
"ButtonAddDevice": "הוסף התקן",
"ButtonAddLibrary": "הוסף ספרייה",
"ButtonAddPodcasts": "הוסף פודקאסטים",
"ButtonAddUser": "הוסף משתמש",
"ButtonAddYourFirstLibrary": "הוסף את הספרייה הראשונה שלך",
"ButtonApply": "החל",
"ButtonApplyChapters": "החל פרקים",
"ButtonAuthors": "יוצרים",
"ButtonBrowseForFolder": "עיין בתיקייה",
"ButtonCancel": "בטל",
"ButtonCancelEncode": "בטל קידוד",
"ButtonChangeRootPassword": "שנה סיסמת root",
"ButtonCheckAndDownloadNewEpisodes": "בדוק והורד פרקים חדשים",
"ButtonChooseAFolder": "בחר תיקייה",
"ButtonChooseFiles": "בחר קבצים",
"ButtonClearFilter": "נקה סינון",
"ButtonCloseFeed": "סגור פיד",
"ButtonCollections": "אוספים",
"ButtonConfigureScanner": "הגדר סורק",
"ButtonCreate": "צור",
"ButtonCreateBackup": "צור גיבוי",
"ButtonDelete": "מחק",
"ButtonDownloadQueue": "תור הורדה",
"ButtonEdit": "ערוך",
"ButtonEditChapters": "ערוך פרקים",
"ButtonEditPodcast": "ערוך פודקאסט",
"ButtonForceReScan": "סרוק מחדש בכוח",
"ButtonFullPath": "נתיב מלא",
"ButtonHide": "הסתר",
"ButtonHome": "בית",
"ButtonIssues": "תקלות",
"ButtonJumpBackward": "דלג אחורה",
"ButtonJumpForward": "דלג קדימה",
"ButtonLatest": "חדש ביותר",
"ButtonLibrary": "ספרייה",
"ButtonLogout": "התנתק",
"ButtonLookup": "חפש",
"ButtonManageTracks": "נהל רצועות",
"ButtonMapChapterTitles": "מפה כותרות פרקים",
"ButtonMatchAllAuthors": "התאם את כל היוצרים",
"ButtonMatchBooks": "התאם ספרים",
"ButtonNevermind": "לא משנה",
"ButtonNext": "הבא",
"ButtonNextChapter": "פרק הבא",
"ButtonOk": "אישור",
"ButtonOpenFeed": "פתח פיד",
"ButtonOpenManager": "פתח מנהל",
"ButtonPause": "השהה",
"ButtonPlay": "נגן",
"ButtonPlaying": "מנגן",
"ButtonPlaylists": "רשימות השמעה",
"ButtonPrevious": "קודם",
"ButtonPreviousChapter": "פרק קודם",
"ButtonPurgeAllCache": "נקה את כל המטמון",
"ButtonPurgeItemsCache": "נקה את מטמון הפריטים",
"ButtonPurgeMediaProgress": "נקה את ההתקדמות במדיה",
"ButtonQueueAddItem": "הוסף לתור",
"ButtonQueueRemoveItem": "הסר מהתור",
"ButtonQuickMatch": "התאמה מהירה",
"ButtonRead": "קרא",
"ButtonRefresh": "רענן",
"ButtonRemove": "הסר",
"ButtonRemoveAll": "הסר הכל",
"ButtonRemoveAllLibraryItems": "הסר את כל פריטי הספרייה",
"ButtonRemoveFromContinueListening": "הסר מ- המשך האזנה",
"ButtonRemoveFromContinueReading": "הסר מ- המשך קריאה",
"ButtonRemoveSeriesFromContinueSeries": "הסר סדרה מ- המשך סדרה",
"ButtonReScan": "סרוק מחדש",
"ButtonReset": "איפוס",
"ButtonResetToDefault": "איפוס לברירת המחדל",
"ButtonRestore": "שחזר",
"ButtonSave": "שמור",
"ButtonSaveAndClose": "שמור וסגור",
"ButtonSaveTracklist": "שמור רשימת רצועות",
"ButtonScan": "סרוק",
"ButtonScanLibrary": "סרוק ספרייה",
"ButtonSearch": "חפש",
"ButtonSelectFolderPath": "בחר נתיב לתיקייה",
"ButtonSeries": "סדרה",
"ButtonSetChaptersFromTracks": "קבע פרקים לפי הרצועות",
"ButtonShare": "שיתוף",
"ButtonShiftTimes": "הזז זמנים",
"ButtonShow": "הצג",
"ButtonStartM4BEncode": "התחל קידוד M4B",
"ButtonStartMetadataEmbed": "התחל הטמעת מטא-נתונים",
"ButtonSubmit": "שלח",
"ButtonTest": "בדיקה",
"ButtonUpload": "העלה",
"ButtonUploadBackup": "העלה גיבוי",
"ButtonUploadCover": "העלה כריכה",
"ButtonUploadOPMLFile": "העלה קובץ OPML",
"ButtonUserDelete": "מחק משתמש {0}",
"ButtonUserEdit": "ערוך משתמש {0}",
"ButtonViewAll": "הצג הכול",
"ButtonYes": "כן",
"ErrorUploadFetchMetadataAPI": "שגיאה בשליפת מטא-נתונים",
"ErrorUploadFetchMetadataNoResults": "לא ניתן לשלוף מטא-נתונים - נסה לעדכן כותרת ו/או יוצר",
"ErrorUploadLacksTitle": "חובה לתת כותרת",
"HeaderAccount": "חשבון",
"HeaderAdvanced": "מתקדם",
"HeaderAppriseNotificationSettings": "הגדרות התראות של Apprise",
"HeaderAudiobookTools": "כלים לניהול קבצי ספרים קוליים",
"HeaderAudioTracks": "רצועות קול",
"HeaderAuthentication": "אימות",
"HeaderBackups": "גיבויים",
"HeaderChangePassword": "שנה סיסמה",
"HeaderChapters": "פרקים",
"HeaderChooseAFolder": "בחר תיקייה",
"HeaderCollection": "אוסף",
"HeaderCollectionItems": "פריטי אוסף",
"HeaderCover": "כריכה",
"HeaderCurrentDownloads": "הורדות נוכחיות",
"HeaderCustomMetadataProviders": "ספקי מטא-נתונים מותאמים אישית",
"HeaderDetails": "פרטים",
"HeaderDownloadQueue": "תור הורדה",
"HeaderEbookFiles": "קבצי ספר אלקטרוני",
"HeaderEmail": "אימייל",
"HeaderEmailSettings": "הגדרות אימייל",
"HeaderEpisodes": "פרקים",
"HeaderEreaderDevices": "התקני קריאה דיגיטליים",
"HeaderEreaderSettings": "הגדרות התקני קריאה דיגיטליים",
"HeaderFiles": "קבצים",
"HeaderFindChapters": "מצא פרקים",
"HeaderIgnoredFiles": "קבצים שנתעלמו",
"HeaderItemFiles": "קבצי פריט",
"HeaderItemMetadataUtils": "כלי מטא-נתונים",
"HeaderLastListeningSession": "הפעלת האזנה אחרונה",
"HeaderLatestEpisodes": "הפרקים העדכניים ביותר",
"HeaderLibraries": "ספריות",
"HeaderLibraryFiles": "קבצי ספרייה",
"HeaderLibraryStats": "סטטיסטיקות ספרייה",
"HeaderListeningSessions": "הפעלות האזנה",
"HeaderListeningStats": "סטטיסטיקות האזנה",
"HeaderLogin": "התחברות",
"HeaderLogs": "לוגים",
"HeaderManageGenres": "נהל ז'אנרים",
"HeaderManageTags": "נהל תגיות",
"HeaderMapDetails": "מפה פרטים",
"HeaderMatch": "התאם",
"HeaderMetadataOrderOfPrecedence": "סדר העדפת מטא-נתונים",
"HeaderMetadataToEmbed": "מטא-נתונים להטמעה",
"HeaderNewAccount": "חשבון חדש",
"HeaderNewLibrary": "ספרייה חדשה",
"HeaderNotifications": "התראות",
"HeaderOpenIDConnectAuthentication": "אימות OpenID Connect",
"HeaderOpenRSSFeed": "פתח ערוץ RSS",
"HeaderOtherFiles": "קבצים אחרים",
"HeaderPasswordAuthentication": "אימות סיסמה",
"HeaderPermissions": "הרשאות",
"HeaderPlayerQueue": "תור ניגון",
"HeaderPlaylist": "רשימת השמעה",
"HeaderPlaylistItems": "פריטי רשימת השמעה",
"HeaderPodcastsToAdd": "פודקאסטים להוספה",
"HeaderPreviewCover": "תצוגה מקדימה של כריכה",
"HeaderRemoveEpisode": "הסר פרק",
"HeaderRemoveEpisodes": "הסר {0} פרקים",
"HeaderRSSFeedGeneral": "פרטי ערוץ RSS",
"HeaderRSSFeedIsOpen": "ערוץ RSS פתוח",
"HeaderRSSFeeds": "ערוצי RSS",
"HeaderSavedMediaProgress": "התקדמות מדיה שמורה",
"HeaderSchedule": "תיזמון",
"HeaderScheduleLibraryScans": "קבע סריקות ספרייה אוטומטיות",
"HeaderSession": "הפעלה",
"HeaderSetBackupSchedule": "קבע לוח זמנים לגיבוי",
"HeaderSettings": "הגדרות",
"HeaderSettingsDisplay": "תצוגה",
"HeaderSettingsExperimental": "תכונות ניסיוניות",
"HeaderSettingsGeneral": "כללי",
"HeaderSettingsScanner": "סורק",
"HeaderSleepTimer": "טיימר שינה",
"HeaderStatsLargestItems": "הפריטים הגדולים ביותר",
"HeaderStatsLongestItems": "הפריטים הארוכים ביותר (בשעות)",
"HeaderStatsMinutesListeningChart": "דקות האזנה (בימים האחרונים)",
"HeaderStatsRecentSessions": "הפעלות אחרונות",
"HeaderStatsTop10Authors": "10 היוצרים המובילים",
"HeaderStatsTop5Genres": "הז'אנרים המובילים 5",
"HeaderTableOfContents": "תוכן העניינים",
"HeaderTools": "כלים",
"HeaderUpdateAccount": "עדכן חשבון",
"HeaderUpdateAuthor": "עדכן יוצר",
"HeaderUpdateDetails": "עדכן פרטים",
"HeaderUpdateLibrary": "עדכן ספרייה",
"HeaderUsers": "משתמשים",
"HeaderYearReview": "שנת {0} בסקירה",
"HeaderYourStats": "הסטטיסטיקות שלך",
"LabelAbridged": "מקוצר",
"LabelAccountType": "סוג חשבון",
"LabelAccountTypeAdmin": "מנהל",
"LabelAccountTypeGuest": "אורח",
"LabelAccountTypeUser": "משתמש",
"LabelActivity": "פעילות",
"LabelAdded": "נוסף",
"LabelAddedAt": "נוסף בתאריך",
"LabelAddToCollection": "הוסף לאוסף",
"LabelAddToCollectionBatch": "הוסף {0} ספרים לאוסף",
"LabelAddToPlaylist": "הוסף לרשימת השמעה",
"LabelAddToPlaylistBatch": "הוסף {0} פריטים לרשימת השמעה",
"LabelAdminUsersOnly": "רק מנהלים",
"LabelAll": "הכל",
"LabelAllUsers": "כל המשתמשים",
"LabelAllUsersExcludingGuests": "כל המשתמשים, ללא אורחים",
"LabelAllUsersIncludingGuests": "כל המשתמשים כולל אורחים",
"LabelAlreadyInYourLibrary": "כבר קיים בספרייה שלך",
"LabelAppend": "הוסף לסוף",
"LabelAuthor": "יוצר",
"LabelAuthorFirstLast": "יוצר (שם פרטי שם משפחה)",
"LabelAuthorLastFirst": "יוצר (שם משפחה, שם פרטי)",
"LabelAuthors": "יוצרים",
"LabelAutoDownloadEpisodes": "הורד פרקים באופן אוטומטי",
"LabelAutoFetchMetadata": "חפש והורד מטא-נתונים באופן אוטומטי",
"LabelAutoFetchMetadataHelp": "מחפש ומוריד מטא-נתונים לשדות כותרת, יוצר וסדרה כדי לשפר את תהליך ההעלאה. ייתכן שיהיה צורך להתאים מטא-נתונים נוסף לאחר ההעלאה.",
"LabelAutoLaunch": "הפעלה אוטומטית",
"LabelAutoLaunchDescription": "הפניה אוטומטית לספק האימות כאשר מגיעים לדף ההתחברות (ניתן להפעיל ידנית בכתובת <code>/login?autoLaunch=0</code>)",
"LabelAutoRegister": "הרשמה אוטומטית",
"LabelAutoRegisterDescription": "יצירת משתמשים חדשים אוטומטית לאחר התחברות",
"LabelBackToUser": "חזרה למשתמש",
"LabelBackupLocation": "מיקום גיבוי",
"LabelBackupsEnableAutomaticBackups": "הפעל גיבויים אוטומטיים",
"LabelBackupsEnableAutomaticBackupsHelp": "גיבויים שמורים ב /metadata/backups",
"LabelBackupsMaxBackupSize": "גודל הגיבוי המרבי (בג'יגה-בייט)",
"LabelBackupsMaxBackupSizeHelp": "כהגנה על עצמך מפני תצורה שגויה, הגיבויים ייכשלו אם הם יעברו את הגודל שהוגדר.",
"LabelBackupsNumberToKeep": "מספר הגיבויים לשמירה",
"LabelBackupsNumberToKeepHelp": "רק גיבוי אחד יוסר בכל פעם, לכן אם יש לך כבר יותר מגיבוי אחד יש להסיר אותם באופן ידני.",
"LabelBitrate": "קצב סיביות",
"LabelBooks": "ספרים",
"LabelButtonText": "טקסט לחצן",
"LabelChangePassword": "שינוי סיסמה",
"LabelChannels": "ערוצים",
"LabelChapters": "פרקים",
"LabelChaptersFound": "פרקים שנמצאו",
"LabelChapterTitle": "כותרת הפרק",
"LabelClickForMoreInfo": "לחץ למידע נוסף",
"LabelClosePlayer": "סגור נגן",
"LabelCodec": "Codec",
"LabelCollapseSeries": "צמצום סדרה",
"LabelCollection": "אוסף",
"LabelCollections": "אוספים",
"LabelComplete": "מלא",
"LabelConfirmPassword": "אישור סיסמה",
"LabelContinueListening": "המשך האזנה",
"LabelContinueReading": "המשך קריאה",
"LabelContinueSeries": "המשך סדרה",
"LabelCover": "כריכה",
"LabelCoverImageURL": "כתובת התמונה ברשת",
"LabelCreatedAt": "נוצר בתאריך",
"LabelCronExpression": "Cron Expression",
"LabelCurrent": "נוכחי",
"LabelCurrently": "כעת:",
"LabelCustomCronExpression": "Custom Cron Expression:",
"LabelDatetime": "Datetime",
"LabelDeleteFromFileSystemCheckbox": "מחיקה מהמערכת הקבצים (הסר סימון למחיקה רק ממסד הנתונים)",
"LabelDescription": "תיאור",
"LabelDeselectAll": "הסר בחירת כל הפריטים",
"LabelDevice": "התקן",
"LabelDeviceInfo": "מידע על התקן",
"LabelDeviceIsAvailableTo": "התקן זמין ל...",
"LabelDirectory": "תיקייה",
"LabelDiscFromFilename": "דיסק משם הקובץ",
"LabelDiscFromMetadata": "דיסק מהמטא-נתונים",
"LabelDiscover": "גלה",
"LabelDownload": "הורד",
"LabelDownloadNEpisodes": "הורד {0} פרקים",
"LabelDuration": "משך",
"LabelDurationFound": "משך נמצא:",
"LabelEbook": "ספר אלקטרוני",
"LabelEbooks": "ספרים אלקטרוניים",
"LabelEdit": "עריכה",
"LabelEmail": "דואר אלקטרוני",
"LabelEmailSettingsFromAddress": "מאת",
"LabelEmailSettingsSecure": "מאובטח",
"LabelEmailSettingsSecureHelp": "אם מופעל, החיבור ישתמש ב-TLS בעת ההתחברות לשרת. אם לא, אז TLS יהיה בשימוש אם השרת תומך בהרחבת STARTTLS. ברוב המקרים מומלץ להפעיל את הגדרה זו אם אתה מתחבר לפורט 465. לפורט 587 או 25, השאר כבוי. (from nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "כתובת לבדיקה",
"LabelEmbeddedCover": "כריכה מוטמעת",
"LabelEnable": "הפעל",
"LabelEnd": "סיום",
"LabelEpisode": "פרק",
"LabelEpisodeTitle": "כותרת הפרק",
"LabelEpisodeType": "סוג הפרק",
"LabelExample": "דוגמה",
"LabelExplicit": "בוטה",
"LabelFeedURL": "כתובת ערוץ",
"LabelFetchingMetadata": "מושך מטא-נתונים",
"LabelFile": "קובץ",
"LabelFileBirthtime": "זמן יצירת הקובץ",
"LabelFileModified": "הקובץ שונה",
"LabelFilename": "שם הקובץ",
"LabelFilterByUser": "סינון לפי משתמש",
"LabelFindEpisodes": "מצא פרקים",
"LabelFinished": "הושלם",
"LabelFolder": "תיקייה",
"LabelFolders": "תיקיות",
"LabelFontBold": "מודגש",
"LabelFontFamily": "משפחת הפונטים",
"LabelFontItalic": "נטוי",
"LabelFontScale": "קנה מידה של הפונט",
"LabelFontStrikethrough": "קו חוצה",
"LabelFormat": "תבנית",
"LabelGenre": "ז'אנר",
"LabelGenres": "ז'אנרים",
"LabelHardDeleteFile": "מחיקה חזקה של הקובץ",
"LabelHasEbook": "ספר אלקטרוני קיים",
"LabelHasSupplementaryEbook": "קיים ספר אלקטרוני נלווה",
"LabelHighestPriority": "העדיפות הגבוהה ביותר",
"LabelHost": "מארח",
"LabelHour": "שעה",
"LabelIcon": "סמל",
"LabelImageURLFromTheWeb": "כתובת התמונה מהרשת",
"LabelIncludeInTracklist": "כלול ברשימת השמעה",
"LabelIncomplete": "לא הושלם",
"LabelInProgress": "בתהליך",
"LabelInterval": "מרווח",
"LabelIntervalCustomDailyWeekly": "מותאם אישית יומי/שבועי",
"LabelIntervalEvery12Hours": "כל 12 שעות",
"LabelIntervalEvery15Minutes": "כל 15 דקות",
"LabelIntervalEvery2Hours": "כל שעתיים",
"LabelIntervalEvery30Minutes": "כל 30 דקות",
"LabelIntervalEvery6Hours": "כל 6 שעות",
"LabelIntervalEveryDay": "כל יום",
"LabelIntervalEveryHour": "כל שעה",
"LabelInvert": "הפוך",
"LabelItem": "פריט",
"LabelLanguage": "שפה",
"LabelLanguageDefaultServer": "שפת ברירת המחדל של השרת",
"LabelLastBookAdded": "הספר האחרון שנוסף",
"LabelLastBookUpdated": "הספר האחרון שעודכן",
"LabelLastSeen": "נראה לאחרונה",
"LabelLastTime": "הזמן האחרון",
"LabelLastUpdate": "עדכון אחרון",
"LabelLayout": "פריסה",
"LabelLayoutSinglePage": "דף בודד",
"LabelLayoutSplitPage": "פיצול הדף",
"LabelLess": "פחות",
"LabelLibrariesAccessibleToUser": "ספריות נגישות למשתמש",
"LabelLibrary": "ספרייה",
"LabelLibraryItem": "פריט ספרייה",
"LabelLibraryName": "שם הספרייה",
"LabelLimit": "מגבלה",
"LabelLineSpacing": "ריווח שורות",
"LabelListenAgain": "האזן שוב",
"LabelLogLevelDebug": "דיבוג",
"LabelLogLevelInfo": "מידע",
"LabelLogLevelWarn": "אזהרה",
"LabelLookForNewEpisodesAfterDate": "חפש פרקים חדשים לאחר תאריך זה",
"LabelLowestPriority": "העדיפות הנמוכה ביותר",
"LabelMatchExistingUsersBy": "התאם משתמשים קיימים לפי",
"LabelMatchExistingUsersByDescription": "משמש לחיבור משתמשים קיימים. לאחר החיבור, המשתמשים יותאמו לפי זיהוי ייחודי מספק ה-SSO שלך",
"LabelMediaPlayer": "נגן מדיה",
"LabelMediaType": "סוג מדיה",
"LabelMetadataOrderOfPrecedenceDescription": "מקורות המטא-נתונים עם עדיפות גבוהה יחליפו מקורות עם עדיפות נמוכה יותר",
"LabelMetadataProvider": "ספק מטא-נתונים",
"LabelMetaTag": "תג מטא",
"LabelMetaTags": "תגי מטא",
"LabelMinute": "דקה",
"LabelMissing": "חסר",
"LabelMissingEbook": "אין ספר אלקטרוני",
"LabelMissingSupplementaryEbook": "אין ספר אלקטרוני נלווה",
"LabelMobileRedirectURIs": "כתובות משדר ניידות מורשות",
"LabelMobileRedirectURIsDescription": "זהו רשימה לבניה של כתובות ה-URI הנתמכות להפניות עבור אפליקציות ניידות. הברירת מחדל היא <code>audiobookshelf://oauth</code>, שניתן להסיר או להוסיף לה כתובות נוספות לאינטגרציה עם אפליקציות צד שלישי. שימוש בכוכבית (<code>*</code>) כקלט בודד מאפשר כל URI.",
"LabelMore": "עוד",
"LabelMoreInfo": "מידע נוסף",
"LabelName": "שם",
"LabelNarrator": "מספר",
"LabelNarrators": "מספרים",
"LabelNew": "חדש",
"LabelNewestAuthors": "הסופרים החדשים ביותר",
"LabelNewestEpisodes": "הפרקים החדשים ביותר",
"LabelNewPassword": "סיסמה חדשה",
"LabelNextBackupDate": "תאריך הגיבוי הבא",
"LabelNextScheduledRun": "הרצה מתוזמנת הבאה",
"LabelNoEpisodesSelected": "לא נבחרו פרקים",
"LabelNotes": "הערות",
"LabelNotFinished": "לא הושלם",
"LabelNotificationAppriseURL": "כתובות Apprise",
"LabelNotificationAvailableVariables": "משתנים זמינים",
"LabelNotificationBodyTemplate": "תבנית גוף",
"LabelNotificationEvent": "אירוע התראה",
"LabelNotificationsMaxFailedAttempts": "מספר הניסיונות הנכשלים המרבי",
"LabelNotificationsMaxFailedAttemptsHelp": "ההתראות מושבתות לאחר שהן נכשלות לשלוח מספר פעמים זה",
"LabelNotificationsMaxQueueSize": "גודל התור המרבי לאירועי התראה",
"LabelNotificationsMaxQueueSizeHelp": "האירועים מוגבלים לשליחה אחת לשנייה. האירועים יתעלמו אם התור מלא. הגדרה זו נועדה למנוע ספאם התראות.",
"LabelNotificationTitleTemplate": "תבנית כותרת",
"LabelNotStarted": "לא התחיל",
"LabelNumberOfBooks": "מספר הספרים",
"LabelNumberOfEpisodes": "מספר הפרקים",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "פתח ערוץ RSS",
"LabelOverwrite": "לשכפל",
"LabelPassword": "סיסמה",
"LabelPath": "נתיב",
"LabelPermissionsAccessAllLibraries": "ניתן לגשת לכל הספריות",
"LabelPermissionsAccessAllTags": "ניתן לגשת לכל התגיות",
"LabelPermissionsAccessExplicitContent": "ניתן לגשת לתוכן בוטה",
"LabelPermissionsDelete": "מותר למחוק",
"LabelPermissionsDownload": "מותר להוריד",
"LabelPermissionsUpdate": "מותר לעדכן",
"LabelPermissionsUpload": "מותר להעלות",
"LabelPersonalYearReview": "השנה שלך בסקירה ({0})",
"LabelPhotoPathURL": "נתיב/URL לתמונה",
"LabelPlaylists": "רשימות השמעה",
"LabelPlayMethod": "שיטת הפעלה",
"LabelPodcast": "פודקאסט",
"LabelPodcasts": "פודקאסטים",
"LabelPodcastSearchRegion": "אזור חיפוש פודקאסט",
"LabelPodcastType": "סוג פודקאסט",
"LabelPort": "פורט",
"LabelPrefixesToIgnore": "קידומות להתעלמות (מתעלם מאותיות גדולות/קטנות)",
"LabelPreventIndexing": "מנע רישום של הערוץ שלך על ידי ספריות אייטונס וגוגל פודקאסט",
"LabelPrimaryEbook": "ספר אלקטרוני ראשי",
"LabelProgress": "התקדמות",
"LabelProvider": "ספק",
"LabelPubDate": "תאריך פרסום",
"LabelPublisher": "מוציא לאור",
"LabelPublishYear": "שנת הפרסום",
"LabelRead": "קריאה",
"LabelReadAgain": "קרא שוב",
"LabelReadEbookWithoutProgress": "קרא/י ספר אלקטרוני ללא שמירת התקדמות",
"LabelRecentlyAdded": "נוסף לאחרונה",
"LabelRecentSeries": "סדרות אחרונות",
"LabelRecommended": "מומלץ",
"LabelRedo": "עשה שוב",
"LabelRegion": "אזור",
"LabelReleaseDate": "תאריך הוצאה לאור",
"LabelRemoveCover": "הסר כריכה",
"LabelRowsPerPage": "שורות לעמוד",
"LabelRSSFeedCustomOwnerEmail": "אימייל בעלים מותאם אישית",
"LabelRSSFeedCustomOwnerName": "שם בעלים מותאם אישית",
"LabelRSSFeedOpen": "פתח ערוץ RSS",
"LabelRSSFeedPreventIndexing": "מנע רישום",
"LabelRSSFeedSlug": "Slug של ערוץ ה-RSS",
"LabelRSSFeedURL": "כתובת ערוץ ה-RSS",
"LabelSearchTerm": "מונח חיפוש",
"LabelSearchTitle": "כותרת חיפוש",
"LabelSearchTitleOrASIN": "כותרת חיפוש או ASIN",
"LabelSeason": "עונה",
"LabelSelectAllEpisodes": "בחר את כל הפרקים",
"LabelSelectEpisodesShowing": "בחר {0} פרקים המוצגים",
"LabelSelectUsers": "בחר משתמשים",
"LabelSendEbookToDevice": "שלח ספר אלקטרוני ל...",
"LabelSequence": "רצף",
"LabelSeries": "סדרה",
"LabelSeriesName": "שם הסדרה",
"LabelSeriesProgress": "התקדמות בסדרה",
"LabelServerYearReview": "השנה בסקירה של השרת ({0})",
"LabelSetEbookAsPrimary": "קבע כראשי",
"LabelSetEbookAsSupplementary": "קבע כספר אלקטרוני נלווה",
"LabelSettingsAudiobooksOnly": "רק ספרי קול",
"LabelSettingsAudiobooksOnlyHelp": "הפעלת ההגדרה הזו תתעלם מקבצי ספרים אלקטרוניים אלא אם כן הם נמצאים בתיקיית ספרי קול, שבמקרה זה יקבעו כספרים אלקטרוניים נלווים",
"LabelSettingsBookshelfViewHelp": "עיצוב סקאומורפי עם מדפי עץ",
"LabelSettingsChromecastSupport": "תמיכה ב-Chromecast",
"LabelSettingsDateFormat": "פורמט תאריך",
"LabelSettingsDisableWatcher": "השבת עוקב",
"LabelSettingsDisableWatcherForLibrary": "השבת עוקב תיקייה עבור ספרייה",
"LabelSettingsDisableWatcherHelp": "מבטל את הוספת/עדכון אוטומטי של פריטים כאשר שינויי קבצים זוהים. *דורש איתחול שרת",
"LabelSettingsEnableWatcher": "הפעל עוקב",
"LabelSettingsEnableWatcherForLibrary": "הפעל עוקב תיקייה עבור ספרייה",
"LabelSettingsEnableWatcherHelp": "מאפשר הוספת/עדכון אוטומטי של פריטים כאשר שינויי קבצים זוהים. *דורש איתחול שרת",
"LabelSettingsExperimentalFeatures": "תכונות ניסיוניות",
"LabelSettingsExperimentalFeaturesHelp": "תכונות בפיתוח שדורשות משובך ובדיקה. לחץ לפתיחת דיון ב-GitHub.",
"LabelSettingsFindCovers": "מצא כריכות",
"LabelSettingsFindCoversHelp": "אם לספר הקולי שלך אין כריכה מוטמעת או תמונת כריכה בתיקייה, הסורק ינסה למצוא תמונת כריכה.<br>שים לב: זה יאריך את זמן הסריקה",
"LabelSettingsHideSingleBookSeries": "הסתר סדרות עם ספר אחד",
"LabelSettingsHideSingleBookSeriesHelp": "סדרות הכוללות ספר אחד יוסתרו מדף הסדרות ומדף הבית.",
"LabelSettingsHomePageBookshelfView": "השתמש בתצוגת מדף בדף הבית",
"LabelSettingsLibraryBookshelfView": "השתמש בתצוגת מדף בספרייה",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "דלג על ספרים קודמים ב-המשך סדרה",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "מדף המשך סדרות מציג את הספר הראשון שלא הושמע בסדרה שיש בה לפחות ספר אחד שהושלם ואין ספרים שכבר באמצע שמיעה. הפעלת הגדרה זו תמשיך סדרות מהספר שהושלם הכי מתקדם בסדרה במקום מהספר הראשון שלא הושמע.",
"LabelSettingsParseSubtitles": "פענח כתוביות",
"LabelSettingsParseSubtitlesHelp": "העתק כותרת משנה משם תיקיית הספר.<br>כותרת המשנה חייבת להיות מופרדת עם התו ״-״<br>לדוגמא, כותרת המשנה לספר ״שם הספר - כותרת משנה״, היא ״כותרת משנה״",
"LabelSettingsPreferMatchedMetadata": "העדף מטה-נתונים מותאמים",
"LabelSettingsPreferMatchedMetadataHelp": "נתונים מותאמים יועדפו על פני פרטים שכבר מוטמעים בפריט כאשר התאמה מהירה בשימוש. כברירת מחדל, התאמה מהירה תמלא פרטים חסרים בלבד.",
"LabelSettingsSkipMatchingBooksWithASIN": "דלג על ספרים שכבר יש להם ASIN",
"LabelSettingsSkipMatchingBooksWithISBN": "דלג על ספרים שכבר יש להם ISBN",
"LabelSettingsSortingIgnorePrefixes": "התעלם מקידומות במיון",
"LabelSettingsSortingIgnorePrefixesHelp": "לדוגמא, לקידומת ״ה״ שם הספר, שם הספר ימוין בתור ״שם הספר״, ״ה״",
"LabelSettingsSquareBookCovers": "השתמש בכריכות מרובעות לספרים",
"LabelSettingsSquareBookCoversHelp": "השתמש בכריכות מרובעות על פני בכריכות סטנדרטיות ביחס 1.6:1",
"LabelSettingsStoreCoversWithItem": "אחסן תמונת כריכה עם הפריט",
"LabelSettingsStoreCoversWithItemHelp": "כברירת מחדל, צילומי כריכות נשמרים בתיקיית /metadata/items, לאחר הפעלת הגדרה זו צילומי כריכות יישמרו בתיקיית הספר, רק קובץ אחד בשם ״cover״ יישמר",
"LabelSettingsStoreMetadataWithItem": "אחסן מטה-נתונים עם הפריט",
"LabelSettingsStoreMetadataWithItemHelp": "כברירת מחדל, קבצי מטה-נתונים מאוחסנים ב- /metadata/items, הפעלת ההגדרה תאחסן קבצי מטה-נתונים בתיקיית פריט שלך בספרייה",
"LabelSettingsTimeFormat": "פורמט זמן",
"LabelShowAll": "הצג הכל",
"LabelSize": "גודל",
"LabelSleepTimer": "טיימר שינה",
"LabelSlug": "Slug",
"LabelStart": "התחלה",
"LabelStarted": "התחיל",
"LabelStartedAt": "התחיל ב",
"LabelStartTime": "זמן התחלה",
"LabelStatsAudioTracks": "רצועות שמע",
"LabelStatsAuthors": "מחברים",
"LabelStatsBestDay": "היום הטוב ביותר",
"LabelStatsDailyAverage": "ממוצע יומי",
"LabelStatsDays": "ימים",
"LabelStatsDaysListened": "מספר ימים בהם נשמע ספר",
"LabelStatsHours": "שעות",
"LabelStatsInARow": "ברצף",
"LabelStatsItemsFinished": "פריטים שסיימת",
"LabelStatsItemsInLibrary": "פריטים בספרייה",
"LabelStatsMinutes": "דקות",
"LabelStatsMinutesListening": "דקות האזנה",
"LabelStatsOverallDays": "ימים כולל",
"LabelStatsOverallHours": "שעות כולל",
"LabelStatsWeekListening": "האזנה שבועית",
"LabelSubtitle": "כותרת משנה",
"LabelSupportedFileTypes": "סוגי קבצים נתמכים",
"LabelTag": "תג",
"LabelTags": "תגיות",
"LabelTagsAccessibleToUser": "תגיות נגישות למשתמש",
"LabelTagsNotAccessibleToUser": "תגיות לא נגישות למשתמש",
"LabelTasks": "משימות פעילות",
"LabelTextEditorBulletedList": "רשימת נקודות",
"LabelTextEditorLink": "קישור",
"LabelTextEditorNumberedList": "רשימה ממוספרת",
"LabelTextEditorUnlink": "ביטול קישור",
"LabelTheme": "ערכת נושא",
"LabelThemeDark": "כהה",
"LabelThemeLight": "בהיר",
"LabelTimeBase": "בסיס זמן",
"LabelTimeListened": "זמן האזנה",
"LabelTimeListenedToday": "זמן האזנה היום",
"LabelTimeRemaining": "{0} נותרו",
"LabelTimeToShift": "זמן להיסט בשניות",
"LabelTitle": "כותרת",
"LabelToolsEmbedMetadata": "הטמעת מטה-נתונים",
"LabelToolsEmbedMetadataDescription": "הטמעת מטה-נתונים לקבצי שמע כולל תמונות כריכה ופרקים.",
"LabelToolsMakeM4b": "יצירת קובץ אודיו M4B",
"LabelToolsMakeM4bDescription": "יצירת קובץ אודיו .M4B עם מטה-נתונים מוטמעים, תמונת שער ופרקים.",
"LabelToolsSplitM4b": "פיצול M4B ל-MP3",
"LabelToolsSplitM4bDescription": "יצירת קבצי MP3 מ-M4B מפוצל לפי פרקים עם מטה-נתונים מוטמעים, תמונת שער ופרקים.",
"LabelTotalDuration": "משך כולל",
"LabelTotalTimeListened": "סך הזמן שהקשבת",
"LabelTrackFromFilename": "רצועות משמות קבצים",
"LabelTrackFromMetadata": "רצועות ממטה-נתונים",
"LabelTracks": "רצועות",
"LabelTracksMultiTrack": "רב-ערוצי",
"LabelTracksNone": "אין ערוצים",
"LabelTracksSingleTrack": "רצועה יחידה",
"LabelType": "סוג",
"LabelUnabridged": "לא מקוצר",
"LabelUndo": "בטל",
"LabelUnknown": "לא ידוע",
"LabelUpdateCover": "עדכן כריכה",
"LabelUpdateCoverHelp": "אפשר החלפה של כריכות קיימות עבור הספרים הנבחרים כאשר נמצאה התאמה",
"LabelUpdatedAt": "עודכן ב-",
"LabelUpdateDetails": "עדכון פרטים",
"LabelUpdateDetailsHelp": "אפשר החלפה של פרטים קיימים עבור הספרים הנבחרים כאשר נמצאה התאמה",
"LabelUploaderDragAndDrop": "גרור ושחרר קבצים או תיקיות",
"LabelUploaderDropFiles": "שחרר קבצים",
"LabelUploaderItemFetchMetadataHelp": "משיכת כותרת, סופר וסדרה באופן אוטומטי",
"LabelUseChapterTrack": "השתמש ברצועות הפרקים",
"LabelUseFullTrack": "השתמש ברצועה המלאה",
"LabelUser": "משתמש",
"LabelUsername": "שם משתמש",
"LabelValue": "ערך",
"LabelVersion": "גרסה",
"LabelViewBookmarks": "הצג סימניות",
"LabelViewChapters": "הצג פרקים",
"LabelViewQueue": "הצג תור נגן",
"LabelVolume": "עוצמת קול",
"LabelWeekdaysToRun": "ימי השבוע להרצה",
"LabelYearReviewHide": "הסתר שנת סקירה",
"LabelYearReviewShow": "הצג שנת סקירה",
"LabelYourAudiobookDuration": "משך הספר הקולי שלך",
"LabelYourBookmarks": "הסימניות שלך",
"LabelYourPlaylists": "הפלייליסטים שלך",
"LabelYourProgress": "ההתקדמות שלך",
"MessageAddToPlayerQueue": "הוסף לתור הנגן",
"MessageAppriseDescription": "כדי להשתמש בתכונה זו יש לך להריץ מופע של <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">ממשק התכנית האפליקציה</a> או API שיטפל בבקשות אלו. <br /> כתובת URL של ממשק ה-Apprise API צריכה להיות הנתיב המלא לשליחת ההתראה, לדוגמה, אם המופע של ה-API שלך מוצע ב-<code>http://192.168.1.1:8337</code> אז עליך לשים <code>http://192.168.1.1:8337/notify</code>.",
"MessageBackupsDescription": "גיבויים כוללים משתמשים, התקדמות משתמש, פרטי פריטי ספרייה, הגדרות שרת ותמונות השמורות ב-<code>/metadata/items</code> & <code>/metadata/authors</code>. גיבויים <strong>לא</strong> כוללים קבצים שמורים בתיקיות הספרייה שלך.",
"MessageBatchQuickMatchDescription": "התאמה מהירה תנסה להוסיף כריכות ומטה-נתונים חסרים עבור הפריטים הנבחרים. הפעל את האפשרויות למטה כדי לאפשר להתאמה מהירה להחליף כריכות קיימות ו/או מטה-נתונים.",
"MessageBookshelfNoCollections": "עדיין לא יצרת אוספים",
"MessageBookshelfNoResultsForFilter": "אין תוצאות עבור סינון \"{0}: {1}\"",
"MessageBookshelfNoRSSFeeds": "אין ערוצי RSS פתוחים",
"MessageBookshelfNoSeries": "אין לך סדרות",
"MessageChapterEndIsAfter": "זמן סיום הפרק אחרי סיום הספר הקולי שלך",
"MessageChapterErrorFirstNotZero": "הפרק הראשון חייב להתחיל ב-0",
"MessageChapterErrorStartGteDuration": "זמן התחלה לא תקין, חייב להיות פחות ממשך הספר הקולי",
"MessageChapterErrorStartLtPrev": "זמן התחלה לא תקין, חייב להיות גדול או שווה לזמן ההתחלה של הפרק הקודם",
"MessageChapterStartIsAfter": "התחלת הפרק אחרי סיום הספר הקולי שלך",
"MessageCheckingCron": "בודק את תזמון העבודה...",
"MessageConfirmCloseFeed": "האם אתה בטוח שאתה רוצה לסגור את הערוץ הזה?",
"MessageConfirmDeleteBackup": "האם אתה בטוח שברצונך למחוק גיבוי עבור {0}?",
"MessageConfirmDeleteFile": "הקובץ ימחק לצמיתות מהמערכת שלך. האם אתה בטוח?",
"MessageConfirmDeleteLibrary": "האם אתה בטוח שברצונך למחוק לצמיתות את הספרייה \"{0}\"?",
"MessageConfirmDeleteLibraryItem": "פריט הספרייה יימחק לצמיתות ממסד הנתונים ומהמערכת שלך. האם אתה בטוח?",
"MessageConfirmDeleteLibraryItems": "פריטי הספרייה {0} יימחקו ממסד הנתונים ומהמערכת שלך. האם אתה בטוח?",
"MessageConfirmDeleteSession": "האם אתה בטוח שאתה רוצה למחוק את ההפעלה הזו?",
"MessageConfirmForceReScan": "האם אתה בטוח שאתה רוצה להכריח סריקה מחדש?",
"MessageConfirmMarkAllEpisodesFinished": "האם אתה בטוח שברצונך לסמן את כל הפרקים כהסתיימו?",
"MessageConfirmMarkAllEpisodesNotFinished": "האם אתה בטוח שברצונך לסמן את כל הפרקים כלא הסתיימו?",
"MessageConfirmMarkSeriesFinished": "האם אתה בטוח שברצונך לסמן את כל הספרים בסדרה זו כהסתיימו?",
"MessageConfirmMarkSeriesNotFinished": "האם אתה בטוח שברצונך לסמן את כל הספרים בסדרה זו כלא הסתיימו?",
"MessageConfirmQuickEmbed": "אזהרה! הטמעה מהירה לא תגבה גיבוי של קבצי האודיו שלך. וודא שיש לך גיבוי של קבצי האודיו שלך. <br><br>האם ברצונך להמשיך?",
"MessageConfirmRemoveAllChapters": "האם אתה בטוח שברצונך להסיר את כל הפרקים?",
"MessageConfirmRemoveAuthor": "האם אתה בטוח שברצונך להסיר את המחבר \"{0}\"?",
"MessageConfirmRemoveCollection": "האם אתה בטוח שברצונך להסיר אוסף \"{0}\"?",
"MessageConfirmRemoveEpisode": "האם אתה בטוח שברצונך להסיר פרק \"{0}\"?",
"MessageConfirmRemoveEpisodes": "האם אתה בטוח שברצונך להסיר {0} פרקים?",
"MessageConfirmRemoveListeningSessions": "האם אתה בטוח שברצונך להסיר {0} הפעלות האזנה?",
"MessageConfirmRemoveNarrator": "האם אתה בטוח שברצונך להסיר מקריא \"{0}\"?",
"MessageConfirmRemovePlaylist": "האם אתה בטוח שברצונך להסיר את רשימת ההשמעה שלך \"{0}\"?",
"MessageConfirmRenameGenre": "האם אתה בטוח שברצונך לשנות את שם הז'אנר \"{0}\" ל \"{1}\" עבור כל הפריטים?",
"MessageConfirmRenameGenreMergeNote": "הערה: ז'אנר זה כבר קיים ולכן הם יתמזגו.",
"MessageConfirmRenameGenreWarning": "אזהרה! יש ז'אנר דומה עם רישום שונה שכבר קיים \"{0}\".",
"MessageConfirmRenameTag": "האם אתה בטוח שברצונך לשנות את שם התג \"{0}\" ל \"{1}\" עבור כל הפריטים?",
"MessageConfirmRenameTagMergeNote": "הערה: התג זה כבר קיים ולכן הם יתמזגו.",
"MessageConfirmRenameTagWarning": "אזהרה! יש תג דומה עם רישום שונה שכבר קיים \"{0}\".",
"MessageConfirmReScanLibraryItems": "האם אתה בטוח שברצונך לסרוק מחדש {0} פריטים?",
"MessageConfirmSendEbookToDevice": "האם אתה בטוח שברצונך לשלוח {0} את הספר האלקטרוני \"{1}\" למכשיר \"{2}\"?",
"MessageDownloadingEpisode": "מוריד פרק",
"MessageDragFilesIntoTrackOrder": "גרור קבצים לסדר ההשמעה נכון",
"MessageEmbedFinished": "ההטמעה הושלמה!",
"MessageEpisodesQueuedForDownload": "{0} פרקים בתור להורדה",
"MessageFeedURLWillBe": "כתובת URL של העדכון תהיה {0}",
"MessageFetching": "מושך...",
"MessageForceReScanDescription": "תבוצע סריקה מחדש כמו סריקה חדש מאפס, תגי ID3 של קבצי קול, קבצי OPF, וקבצי טקסט ייסרקו כחדשים.",
"MessageImportantNotice": "הודעה חשובה!",
"MessageInsertChapterBelow": "הוסף פרק מתחת",
"MessageItemsSelected": "{0} פריטים נבחרו",
"MessageItemsUpdated": "{0} פריטים עודכנו",
"MessageJoinUsOn": "הצטרף אלינו ב-",
"MessageListeningSessionsInTheLastYear": "{0} מפגשי האזנה בשנה האחרונה",
"MessageLoading": "טוען...",
"MessageLoadingFolders": "טוען תיקיות...",
"MessageM4BFailed": "M4B נכשל!",
"MessageM4BFinished": "M4B הושלם!",
"MessageMapChapterTitles": "מפה שמות פרקים לפרקי הספר השמורים שלך ללא שינוי תגי זמן",
"MessageMarkAllEpisodesFinished": "סמן את כל הפרקים כהסתיימו",
"MessageMarkAllEpisodesNotFinished": "סמן את כל הפרקים כלא הסתיימו",
"MessageMarkAsFinished": "סמן כהסתיים",
"MessageMarkAsNotFinished": "סמן כלא הסתיים",
"MessageMatchBooksDescription": "ינסה להתאים ספרים בספריית הספרים שלך עם ספר מספק החיפוש הנבחר וימלא פרטים ריקים ותמונות כריכה. לא יחליף פרטים קיימים.",
"MessageNoAudioTracks": "אין רצועות שמע",
"MessageNoAuthors": "אין סופרים",
"MessageNoBackups": "אין גיבויים",
"MessageNoBookmarks": "אין סימניות",
"MessageNoChapters": "אין פרקים",
"MessageNoCollections": "אין אוספים",
"MessageNoCoversFound": "לא נמצאו כריכות",
"MessageNoDescription": "אין תיאור",
"MessageNoDownloadsInProgress": "אין הורדות פעילות כרגע",
"MessageNoDownloadsQueued": "אין הורדות בתור",
"MessageNoEpisodeMatchesFound": "לא נמצאו התאמות לפרק",
"MessageNoEpisodes": "אין פרקים",
"MessageNoFoldersAvailable": "אין תיקיות זמינות",
"MessageNoGenres": "אין ז'אנרים",
"MessageNoIssues": "אין תקלות",
"MessageNoItems": "אין פריטים",
"MessageNoItemsFound": "לא נמצאו פריטים",
"MessageNoListeningSessions": "אין הפעלות האזנה",
"MessageNoLogs": "אין לוגים",
"MessageNoMediaProgress": "אין התקדמות במדיה",
"MessageNoNotifications": "אין התראות",
"MessageNoPodcastsFound": "לא נמצאו פודקאסטים",
"MessageNoResults": "אין תוצאות",
"MessageNoSearchResultsFor": "אין תוצאות חיפוש עבור \"{0}\"",
"MessageNoSeries": "אין סדרות",
"MessageNoTags": "אין תגיות",
"MessageNoTasksRunning": "אין משימות פעילות",
"MessageNotYetImplemented": "עדיין לא מיושם",
"MessageNoUpdateNecessary": "לא נדרש עדכון",
"MessageNoUpdatesWereNecessary": "לא היה צורך בעדכונים",
"MessageNoUserPlaylists": "אין לך רשימות השמעה",
"MessageOr": "או",
"MessagePauseChapter": "השהה השמעת הפרק",
"MessagePlayChapter": "הקשב לתחילת הפרק",
"MessagePlaylistCreateFromCollection": "צור רשימת השמעה מאוסף",
"MessagePodcastHasNoRSSFeedForMatching": "לפודקאסט אין כתובת URL של ערוץ RSS להתאמה",
"MessageQuickMatchDescription": "ממלא פרטים ריקים וכריכות עם התוצאה הראשונה מ '{0}'. לא ימחק פרטים אלא אם הגדרת השרת 'העדף מטה-נתונים מותאמים' מופעלת.",
"MessageRemoveChapter": "הסר פרק",
"MessageRemoveEpisodes": "הסר {0} פרקים",
"MessageRemoveFromPlayerQueue": "הסר מתור ההשמעה של הנגן",
"MessageRemoveUserWarning": "האם אתה בטוח שברצונך למחוק לצמיתות את המשתמש \"{0}\"?",
"MessageReportBugsAndContribute": "דווח על באגים, בקש תכונות חדשות, ותרום ב-",
"MessageResetChaptersConfirm": "האם אתה בטוח שברצונך לאפס את הפרקים ולבטל את השינויים שביצעת?",
"MessageRestoreBackupConfirm": "האם אתה בטוח שברצונך לשחזר את הגיבוי שנוצר ב",
"MessageRestoreBackupWarning": "שחזור גיבוי ימחק את כל מסד הנתונים הנוכחי השוכן ב /config ואת תמונות הכריכה ב- /metadata/items & /metadata/authors.<br /><br />גיבויים אינם משנים קבצים בתיקיות הספרייה שלך. אם הגדרות השרת לאחסן תמונות כריכה ומטא-נתונים בתיקיות הספרייה שלך מופעלות אז אלה לא יגובו או ימחקו.<br /><br />כל האפליקציות המשתמשות בשרת שלך יתעדכנו באופן אוטומטי.",
"MessageSearchResultsFor": "תוצאות חיפוש עבור",
"MessageSelected": "{0} נבחרו",
"MessageServerCouldNotBeReached": "לא ניתן להגיע אל השרת",
"MessageSetChaptersFromTracksDescription": "קבע פרקים באמצעות כל קובץ שמע כפרק וכותרת פרק כשם הקובץ שמע",
"MessageStartPlaybackAtTime": "להתחיל השמעה עבור \"{0}\" ב-{1}?",
"MessageThinking": "חושב...",
"MessageUploaderItemFailed": "העלאת הפריט נכשלה",
"MessageUploaderItemSuccess": "העלאה הצליחה!",
"MessageUploading": "מעלה...",
"MessageValidCronExpression": "ביטוי Cron חוקי",
"MessageWatcherIsDisabledGlobally": "עוקב מנוטרל באופן גלובלי בהגדרות השרת",
"MessageXLibraryIsEmpty": "ספריית {0} ריקה!",
"MessageYourAudiobookDurationIsLonger": "הזמן של הספר הקולי שלך ארוך יותר מהזמן שנמצא",
"MessageYourAudiobookDurationIsShorter": "הזמן של הספר הקולי שלך קצר יותר מהזמן שנמצא",
"NoteChangeRootPassword": "המשתמש root הוא המשתמש היחיד שיכולה להיות לו סיסמה ריקה",
"NoteChapterEditorTimes": "הערה: זמן ההתחלה של הפרק הראשון חייב להישאר 0:00 וזמן ההתחלה של הפרק האחרון לא יכול לחרוג מהזמן של ספר השמע.",
"NoteFolderPicker": "הערה: תיקיות שכבר מופו לא יוצגו",
"NoteRSSFeedPodcastAppsHttps": "אזהרה: רוב יישומי הפודקאסט דורשים שכתובת ה-URL ערוץ ה-RSS תשתמש ב-HTTPS",
"NoteRSSFeedPodcastAppsPubDate": "אזהרה: פרק אחד או יותר לא מכילים תאריך פרסום. חלק מיישומי הפודקאסט דורשים זאת.",
"NoteUploaderFoldersWithMediaFiles": "תיקיות עם קבצי מדיה יעובדו כפריטי ספריה נפרדים.",
"NoteUploaderOnlyAudioFiles": "אם מועלים רק קבצי שמע, כל קובץ שמע יעובד כספר שמע נפרד.",
"NoteUploaderUnsupportedFiles": "מתעלם מקבצים לא נתמכים. בעת בחירת תיקייה או גרירה לדף, מתעלם מקבצים אחרים שאינם בתיקיית פריט.",
"PlaceholderNewCollection": "שם אוסף חדש",
"PlaceholderNewFolderPath": "נתיב תיקייה חדשה",
"PlaceholderNewPlaylist": "שם רשימת השמעה חדשה",
"PlaceholderSearch": "חיפוש..",
"PlaceholderSearchEpisode": "חיפוש פרק..",
"ToastAccountUpdateFailed": "עדכון חשבון נכשל",
"ToastAccountUpdateSuccess": "חשבון עודכן בהצלחה",
"ToastAuthorImageRemoveFailed": "הסרת התמונה של המחבר נכשלה",
"ToastAuthorImageRemoveSuccess": "תמונת המחבר הוסרה בהצלחה",
"ToastAuthorUpdateFailed": "עדכון המחבר נכשל",
"ToastAuthorUpdateMerged": "המחבר מוזג",
"ToastAuthorUpdateSuccess": "המחבר עודכן בהצלחה",
"ToastAuthorUpdateSuccessNoImageFound": "המחבר עודכן (תמונה לא נמצאה)",
"ToastBackupCreateFailed": "יצירת גיבוי נכשלה",
"ToastBackupCreateSuccess": "גיבוי נוצר בהצלחה",
"ToastBackupDeleteFailed": "מחיקת הגיבוי נכשלה",
"ToastBackupDeleteSuccess": "הגיבוי נמחק בהצלחה",
"ToastBackupRestoreFailed": "שחזור הגיבוי נכשל",
"ToastBackupUploadFailed": "העלאת הגיבוי נכשלה",
"ToastBackupUploadSuccess": "הגיבוי הועלה בהצלחה",
"ToastBatchUpdateFailed": "עדכון קבוצתי נכשל",
"ToastBatchUpdateSuccess": "עדכון קבוצתי הצליח",
"ToastBookmarkCreateFailed": "יצירת סימניה נכשלה",
"ToastBookmarkCreateSuccess": "הסימניה נוספה בהצלחה",
"ToastBookmarkRemoveFailed": "הסרת הסימניה נכשלה",
"ToastBookmarkRemoveSuccess": "הסימניה הוסרה בהצלחה",
"ToastBookmarkUpdateFailed": "עדכון הסימניה נכשל",
"ToastBookmarkUpdateSuccess": "הסימניה עודכנה בהצלחה",
"ToastChaptersHaveErrors": "פרקים מכילים שגיאות",
"ToastChaptersMustHaveTitles": "פרקים חייבים לכלול כותרות",
"ToastCollectionItemsRemoveFailed": "הסרת הפריט(ים) מהאוסף נכשלה",
"ToastCollectionItemsRemoveSuccess": "הפריט(ים) הוסרו מהאוסף בהצלחה",
"ToastCollectionRemoveFailed": "מחיקת האוסף נכשלה",
"ToastCollectionRemoveSuccess": "האוסף הוסר בהצלחה",
"ToastCollectionUpdateFailed": "עדכון האוסף נכשל",
"ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה",
"ToastItemCoverUpdateFailed": "עדכון כריכת הפריט נכשל",
"ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה",
"ToastItemDetailsUpdateFailed": "עדכון פרטי הפריט נכשל",
"ToastItemDetailsUpdateSuccess": "פרטי הפריט עודכנו בהצלחה",
"ToastItemDetailsUpdateUnneeded": "לא נדרשים עדכונים לפרטי הפריט",
"ToastItemMarkedAsFinishedFailed": "סימון כפריט כהושלם נכשל",
"ToastItemMarkedAsFinishedSuccess": "הפריט סומן כהושלם בהצלחה",
"ToastItemMarkedAsNotFinishedFailed": "סימון כפריט שלא הושלם נכשל",
"ToastItemMarkedAsNotFinishedSuccess": "הפריט סומן כלא הושלם בהצלחה",
"ToastLibraryCreateFailed": "יצירת הספרייה נכשלה",
"ToastLibraryCreateSuccess": "הספרייה \"{0}\" נוצרה בהצלחה",
"ToastLibraryDeleteFailed": "מחיקת הספרייה נכשלה",
"ToastLibraryDeleteSuccess": "הספרייה נמחקה בהצלחה",
"ToastLibraryScanFailedToStart": "הפעלת הסריקה נכשלה",
"ToastLibraryScanStarted": "הסריקה של הספרייה החלה",
"ToastLibraryUpdateFailed": "עדכון הספרייה נכשל",
"ToastLibraryUpdateSuccess": "הספרייה \"{0}\" עודכנה בהצלחה",
"ToastPlaylistCreateFailed": "יצירת רשימת השמעה נכשלה",
"ToastPlaylistCreateSuccess": "רשימת השמעה נוצרה בהצלחה",
"ToastPlaylistRemoveFailed": "הסרת רשימת השמעה נכשלה",
"ToastPlaylistRemoveSuccess": "רשימת השמעה הוסרה בהצלחה",
"ToastPlaylistUpdateFailed": "עדכון רשימת השמעה נכשל",
"ToastPlaylistUpdateSuccess": "רשימת השמעה עודכנה בהצלחה",
"ToastPodcastCreateFailed": "יצירת הפודקאסט נכשלה",
"ToastPodcastCreateSuccess": "הפודקאסט נוצר בהצלחה",
"ToastRemoveItemFromCollectionFailed": "הסרת הפריט מהאוסף נכשלה",
"ToastRemoveItemFromCollectionSuccess": "הפריט הוסר מהאוסף בהצלחה",
"ToastRSSFeedCloseFailed": "סגירת ערוץ ה-RSS נכשלה",
"ToastRSSFeedCloseSuccess": "ערוץ ה-RSS נסגר בהצלחה",
"ToastSendEbookToDeviceFailed": "שליחת הספר אל המכשיר נכשלה",
"ToastSendEbookToDeviceSuccess": "הספר נשלח אל המכשיר \"{0}\"",
"ToastSeriesUpdateFailed": "עדכון הסדרה נכשל",
"ToastSeriesUpdateSuccess": "הסדרה עודכנה בהצלחה",
"ToastSessionDeleteFailed": "מחיקת הפעולה נכשלה",
"ToastSessionDeleteSuccess": "הפעולה נמחקה בהצלחה",
"ToastSocketConnected": "קצה תקשורת חובר",
"ToastSocketDisconnected": "קצה תקשורת נותק",
"ToastSocketFailedToConnect": "התחברות קצה התקשורת נכשלה",
"ToastUserDeleteFailed": "מחיקת המשתמש נכשלה",
"ToastUserDeleteSuccess": "המשתמש נמחק בהצלחה"
}
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Every 6 hours",
"LabelIntervalEveryDay": "Every day",
"LabelIntervalEveryHour": "Every hour",
"LabelInvalidParts": "Invalid Parts",
"LabelInvert": "Invert",
"LabelItem": "Item",
"LabelLanguage": "Language",
@@ -357,7 +356,6 @@
"LabelMinute": "Minute",
"LabelMissing": "Missing",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Missing Parts",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Not Started",
"LabelNumberOfBooks": "Number of Books",
"LabelNumberOfEpisodes": "# of Episodes",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Open RSS Feed",
"LabelOverwrite": "Overwrite",
"LabelPassword": "Password",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Every 6 hours",
"LabelIntervalEveryDay": "Every day",
"LabelIntervalEveryHour": "Every hour",
"LabelInvalidParts": "Nevaljajuči dijelovi",
"LabelInvert": "Invert",
"LabelItem": "Stavka",
"LabelLanguage": "Jezik",
@@ -357,7 +356,6 @@
"LabelMinute": "Minuta",
"LabelMissing": "Nedostaje",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Nedostajali dijelovi",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Not Started",
"LabelNumberOfBooks": "Number of Books",
"LabelNumberOfEpisodes": "# of Episodes",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Otvori RSS Feed",
"LabelOverwrite": "Overwrite",
"LabelPassword": "Lozinka",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Minden 6 órában",
"LabelIntervalEveryDay": "Minden nap",
"LabelIntervalEveryHour": "Minden órában",
"LabelInvalidParts": "Érvénytelen részek",
"LabelInvert": "Megfordítás",
"LabelItem": "Elem",
"LabelLanguage": "Nyelv",
@@ -357,7 +356,6 @@
"LabelMinute": "Perc",
"LabelMissing": "Hiányzó",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Hiányzó részek",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Engedélyezett mobil átirányítási URI-k",
"LabelMobileRedirectURIsDescription": "Ez egy fehérlista az érvényes mobilalkalmazás-átirányítási URI-k számára. Az alapértelmezett <code>audiobookshelf://oauth</code>, amely eltávolítható vagy kiegészíthető további URI-kkal harmadik féltől származó alkalmazásintegráció érdekében. Ha az egyetlen bejegyzés egy csillag (<code>*</code>), akkor bármely URI engedélyezett.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Nem indult el",
"LabelNumberOfBooks": "Könyvek száma",
"LabelNumberOfEpisodes": "Epizódok száma",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "RSS hírcsatorna megnyitása",
"LabelOverwrite": "Felülírás",
"LabelPassword": "Jelszó",
+10 -9
View File
@@ -43,7 +43,7 @@
"ButtonMatchAllAuthors": "Aggiungi metadata agli Autori",
"ButtonMatchBooks": "Aggiungi metadata della Libreria",
"ButtonNevermind": "Nevermind",
"ButtonNext": "Next",
"ButtonNext": "Prossimo",
"ButtonNextChapter": "Prossimo Capitolo",
"ButtonOk": "Ok",
"ButtonOpenFeed": "Apri Feed",
@@ -52,7 +52,7 @@
"ButtonPlay": "Play",
"ButtonPlaying": "In Riproduzione",
"ButtonPlaylists": "Playlists",
"ButtonPrevious": "Previous",
"ButtonPrevious": "Precendente",
"ButtonPreviousChapter": "Capitolo Precendente",
"ButtonPurgeAllCache": "Elimina tutta la Cache",
"ButtonPurgeItemsCache": "Elimina la Cache selezionata",
@@ -113,7 +113,7 @@
"HeaderCollectionItems": "Elementi della Raccolta",
"HeaderCover": "Cover",
"HeaderCurrentDownloads": "Download Correnti",
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
"HeaderCustomMetadataProviders": " Metadata Providers Personalizzato",
"HeaderDetails": "Dettagli",
"HeaderDownloadQueue": "Download coda",
"HeaderEbookFiles": "Ebook File",
@@ -184,7 +184,7 @@
"HeaderUpdateDetails": "Aggiorna Dettagli",
"HeaderUpdateLibrary": "Aggiorna Libreria",
"HeaderUsers": "Utenti",
"HeaderYearReview": "Year {0} in Review",
"HeaderYearReview": "Anno {0} in Sintesi",
"HeaderYourStats": "Statistiche Personali",
"LabelAbridged": "Abbreviato",
"LabelAccountType": "Tipo di Account",
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Ogni 6 ore",
"LabelIntervalEveryDay": "Ogni Giorno",
"LabelIntervalEveryHour": "Ogni ora",
"LabelInvalidParts": "Parti Invalide",
"LabelInvert": "Inverti",
"LabelItem": "Oggetti",
"LabelLanguage": "Lingua",
@@ -356,9 +355,8 @@
"LabelMetaTags": "Meta Tags",
"LabelMinute": "Minuto",
"LabelMissing": "Altro",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Parti rimanenti",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMissingEbook": "Non ha ebook",
"LabelMissingSupplementaryEbook": "Non ha ebook supplementare",
"LabelMobileRedirectURIs": "URI di reindirizzamento mobile consentiti",
"LabelMobileRedirectURIsDescription": "Questa è una lista bianca di URI di reindirizzamento validi per le app mobili. Quello predefinito è <code>audiobookshelf://oauth</code>, che puoi rimuovere o integrare con URI aggiuntivi per l'integrazione di app di terze parti. Utilizzando un asterisco (<code>*</code>) poiché l'unica voce consente qualsiasi URI.",
"LabelMore": "Molto",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Non iniziato",
"LabelNumberOfBooks": "Numero di libri",
"LabelNumberOfEpisodes": "# degli episodi",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Apri RSS Feed",
"LabelOverwrite": "Sovrascrivi",
"LabelPassword": "Password",
@@ -444,7 +445,7 @@
"LabelSeries": "Serie",
"LabelSeriesName": "Nome Serie",
"LabelSeriesProgress": "Cominciato",
"LabelServerYearReview": "Server Year in Review ({0})",
"LabelServerYearReview": "Anno del server in sintesi({0})",
"LabelSetEbookAsPrimary": "Immposta come Primario",
"LabelSetEbookAsSupplementary": "Imposta come Suplementare",
"LabelSettingsAudiobooksOnly": "Solo Audiolibri",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Kas 6 valandas",
"LabelIntervalEveryDay": "Kasdien",
"LabelIntervalEveryHour": "Kiekvieną valandą",
"LabelInvalidParts": "Netinkamos dalys",
"LabelInvert": "Apversti",
"LabelItem": "Elementas",
"LabelLanguage": "Kalba",
@@ -357,7 +356,6 @@
"LabelMinute": "Minutė",
"LabelMissing": "Trūksta",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Trūkstamos dalys",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Nepasileista",
"LabelNumberOfBooks": "Knygų skaičius",
"LabelNumberOfEpisodes": "Epizodų skaičius",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Atidaryti RSS srautą",
"LabelOverwrite": "Perrašyti",
"LabelPassword": "Slaptažodis",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Iedere 6 uur",
"LabelIntervalEveryDay": "Iedere dag",
"LabelIntervalEveryHour": "Ieder uur",
"LabelInvalidParts": "Ongeldige delen",
"LabelInvert": "Omdraaien",
"LabelItem": "Onderdeel",
"LabelLanguage": "Taal",
@@ -357,7 +356,6 @@
"LabelMinute": "Minuut",
"LabelMissing": "Ontbrekend",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Ontbrekende delen",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Niet Gestart",
"LabelNumberOfBooks": "Aantal Boeken",
"LabelNumberOfEpisodes": "# afleveringen",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Open RSS-feed",
"LabelOverwrite": "Overschrijf",
"LabelPassword": "Wachtwoord",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Hver 6. timer",
"LabelIntervalEveryDay": "Hver dag",
"LabelIntervalEveryHour": "Hver time",
"LabelInvalidParts": "Ugyldige deler",
"LabelInvert": "Inverter",
"LabelItem": "Enhet",
"LabelLanguage": "Språk",
@@ -357,7 +356,6 @@
"LabelMinute": "Minutt",
"LabelMissing": "Mangler",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Manglende deler",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Ikke startet",
"LabelNumberOfBooks": "Antall bøker",
"LabelNumberOfEpisodes": "Antall episoder",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Åpne RSS Feed",
"LabelOverwrite": "Overskriv",
"LabelPassword": "Passord",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Co 6 godzin",
"LabelIntervalEveryDay": "Każdego dnia",
"LabelIntervalEveryHour": "Każdej godziny",
"LabelInvalidParts": "Nieprawidłowe części",
"LabelInvert": "Invert",
"LabelItem": "Pozycja",
"LabelLanguage": "Język",
@@ -357,7 +356,6 @@
"LabelMinute": "Minuta",
"LabelMissing": "Brakujący",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Brakujące cześci",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Nie rozpoęczto",
"LabelNumberOfBooks": "Liczba książek",
"LabelNumberOfEpisodes": "# odcinków",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Otwórz kanał RSS",
"LabelOverwrite": "Overwrite",
"LabelPassword": "Hasło",
+5 -4
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "A cada 6 horas",
"LabelIntervalEveryDay": "Todo dia",
"LabelIntervalEveryHour": "Toda hora",
"LabelInvalidParts": "Partes Inválidas",
"LabelInvert": "Inverter",
"LabelItem": "Item",
"LabelLanguage": "Idioma",
@@ -357,7 +356,6 @@
"LabelMinute": "Minuto",
"LabelMissing": "Ausente",
"LabelMissingEbook": "Ebook não existe",
"LabelMissingParts": "Partes Ausentes",
"LabelMissingSupplementaryEbook": "Ebook complementar não existe",
"LabelMobileRedirectURIs": "URIs de redirecionamento móveis permitidas",
"LabelMobileRedirectURIsDescription": "Essa é uma lista de permissionamento para URIs válidas para o redirecionamento de aplicativos móveis. A padrão é <code>audiobookshelf://oauth</code>, que pode ser removida ou acrescentada com novas URIs para integração com apps de terceiros. Usando um asterisco (<code>*</code>) como um item único dará permissão para qualquer URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Não iniciado",
"LabelNumberOfBooks": "Número de Livros",
"LabelNumberOfEpisodes": "# de Episódios",
"LabelOpenIDAdvancedPermsClaimDescription": "Nome do claim OpenID contendo as permissões avançadas para ações do usuário na aplicação para serem aplicadas aos perfis não-administradores (<b>se configurados</b>). Se o claim não estiver presente na resposta, acesso ao ABS será negado. Se apenas uma opção estiver ausente, ela será tratada como <code>false</code>. Garanta que o claim do provedor de identidade segue a estrutura esperada:",
"LabelOpenIDClaims": "Deixe as opções a seguir em branco para desativar a atribuição de grupos e permissões avançadas; nesse caso, o grupo 'Usuário' será atribuído automaticamente.",
"LabelOpenIDGroupClaimDescription": "Nome do claim OpenID contendo a lista de grupos do usuário, normalmente chamada de <code>groups</code>. <b>Se configurada</b>, a aplicação atribuirá automaticamente os perfis com base na participação do usuário nos grupos, contanto que os nomes desses grupos no claim, sem distinção entre maiúsculas e minúsculas, sejam 'admin', 'user' ou 'guest'. O claim deve conter uma lista e, se o usuário pertencer a múltiplos grupos, a aplicação atribuirá o perfil correspondendo ao maior nível de acesso. Se não houver correspondência a qualquer grupo, o acesso será negado.",
"LabelOpenRSSFeed": "Abrir Feed RSS",
"LabelOverwrite": "Sobrescrever",
"LabelPassword": "Senha",
@@ -466,8 +467,8 @@
"LabelSettingsHideSingleBookSeriesHelp": "Séries com um só livro serão ocultadas na página de séries e na prateleira de séries na página principal.",
"LabelSettingsHomePageBookshelfView": "Usar visão estante na página principal",
"LabelSettingsLibraryBookshelfView": "Usar visão estante na página da biblioteca",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Skip earlier books in Continue Series",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "The Continue Series home page shelf shows the first book not started in series that have at least one book finished and no books in progress. Enabling this setting will continue series from the furthest completed book instead of the first book not started.",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Pular livros anteriores em Continuar Série",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "A prateleira Continuar Série na página principal de exibe o primeiro livro não iniciado em uma série que tem pelo menos um livro concluído e nenhum livro em andamento. Ativar essa configuração irá continuar a série a partir do livro mais recentemente concluído ao invés do primeiro livro não iniciado.",
"LabelSettingsParseSubtitles": "Analisar subtítulos",
"LabelSettingsParseSubtitlesHelp": "Extrair subtítulos do nome da pasta do audiobook.<br>Subtítulo deve estar separado por \" - \"<br>ex: \"Título do Livro - Um Subtítulo Aqui\" tem o subtítulo \"Um Subtítulo Aqui\"",
"LabelSettingsPreferMatchedMetadata": "Preferir metadados consultados",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Каждые 6 часов",
"LabelIntervalEveryDay": "Каждый день",
"LabelIntervalEveryHour": "Каждый час",
"LabelInvalidParts": "Неверные части",
"LabelInvert": "Инвертировать",
"LabelItem": "Элемент",
"LabelLanguage": "Язык",
@@ -357,7 +356,6 @@
"LabelMinute": "Минуты",
"LabelMissing": "Потеряно",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Потерянные части",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Разрешенные URI перенаправления с мобильных устройств",
"LabelMobileRedirectURIsDescription": "Это белый список допустимых URI перенаправления для мобильных приложений. По умолчанию используется <code>audiobookshelf://oauth</code>, который можно удалить или дополнить дополнительными URI для интеграции со сторонними приложениями. Использование звездочки (<code>*</code>) в качестве единственной записи разрешает любой URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Не запущено",
"LabelNumberOfBooks": "Количество книг",
"LabelNumberOfEpisodes": "# Эпизодов",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Открыть RSS-канал",
"LabelOverwrite": "Перезаписать",
"LabelPassword": "Пароль",
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Var 6:e timme",
"LabelIntervalEveryDay": "Varje dag",
"LabelIntervalEveryHour": "Varje timme",
"LabelInvalidParts": "Ogiltiga delar",
"LabelInvert": "Invertera",
"LabelItem": "Objekt",
"LabelLanguage": "Språk",
@@ -357,7 +356,6 @@
"LabelMinute": "Minut",
"LabelMissing": "Saknad",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "Saknade delar",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "Allowed Mobile Redirect URIs",
"LabelMobileRedirectURIsDescription": "This is a whitelist of valid redirect URIs for mobile apps. The default one is <code>audiobookshelf://oauth</code>, which you can remove or supplement with additional URIs for third-party app integration. Using an asterisk (<code>*</code>) as the sole entry permits any URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Inte påbörjad",
"LabelNumberOfBooks": "Antal böcker",
"LabelNumberOfEpisodes": "Antal avsnitt",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Öppna RSS-flöde",
"LabelOverwrite": "Skriv över",
"LabelPassword": "Lösenord",
+782
View File
@@ -0,0 +1,782 @@
{
"ButtonAdd": "Додати",
"ButtonAddChapters": "Додати глави",
"ButtonAddDevice": "Додати пристрій",
"ButtonAddLibrary": "Додати бібліотеку",
"ButtonAddPodcasts": "Додати подкаст",
"ButtonAddUser": "Додати користувача",
"ButtonAddYourFirstLibrary": "Додайте вашу першу бібліотеку",
"ButtonApply": "Застосувати",
"ButtonApplyChapters": "Зберегти глави",
"ButtonAuthors": "Автори",
"ButtonBrowseForFolder": "Огляд тек",
"ButtonCancel": "Скасувати",
"ButtonCancelEncode": "Скасувати кодування",
"ButtonChangeRootPassword": "Змінити кореневий пароль",
"ButtonCheckAndDownloadNewEpisodes": "Перевірити та завантажити нові епізоди",
"ButtonChooseAFolder": "Обрати теку",
"ButtonChooseFiles": "Обрати файли",
"ButtonClearFilter": "Очистити фільтр",
"ButtonCloseFeed": "Закрити стрічку",
"ButtonCollections": "Колекції",
"ButtonConfigureScanner": "Налаштувати сканер",
"ButtonCreate": "Створити",
"ButtonCreateBackup": "Створити резервну копію",
"ButtonDelete": "Видалити",
"ButtonDownloadQueue": "Черга",
"ButtonEdit": "Редагувати",
"ButtonEditChapters": "Редагувати глави",
"ButtonEditPodcast": "Редагувати подкаст",
"ButtonForceReScan": "Примусово сканувати",
"ButtonFullPath": "Повний шлях",
"ButtonHide": "Приховати",
"ButtonHome": "Головна",
"ButtonIssues": "Проблеми",
"ButtonJumpBackward": "Перейти назад",
"ButtonJumpForward": "Перейти вперед",
"ButtonLatest": "Останній",
"ButtonLibrary": "Бібліотека",
"ButtonLogout": "Вийти",
"ButtonLookup": "Пошук",
"ButtonManageTracks": "Керувати доріжками",
"ButtonMapChapterTitles": "Призначити назви глав",
"ButtonMatchAllAuthors": "Віднайти усіх авторів",
"ButtonMatchBooks": "Віднайти книги",
"ButtonNevermind": "Скасувати",
"ButtonNext": "Наступний",
"ButtonNextChapter": "Наступна глава",
"ButtonOk": "Гаразд",
"ButtonOpenFeed": "Відкрити стрічку",
"ButtonOpenManager": "Відкрити менеджер",
"ButtonPause": "Пауза",
"ButtonPlay": "Грати",
"ButtonPlaying": "Відтворюється",
"ButtonPlaylists": "Списки відтворення",
"ButtonPrevious": "Попередній",
"ButtonPreviousChapter": "Попередня глава",
"ButtonPurgeAllCache": "Очистити весь кеш",
"ButtonPurgeItemsCache": "Очистити кеш елементів",
"ButtonPurgeMediaProgress": "Очистити прогрес",
"ButtonQueueAddItem": "Додати до черги",
"ButtonQueueRemoveItem": "Вилучити з черги",
"ButtonQuickMatch": "Швидкий пошук",
"ButtonRead": "Читати",
"ButtonRefresh": "Оновити",
"ButtonRemove": "Видалити",
"ButtonRemoveAll": "Видалити все",
"ButtonRemoveAllLibraryItems": "Видалити всі елементи бібліотеки",
"ButtonRemoveFromContinueListening": "Видалити з Продовжити слухати",
"ButtonRemoveFromContinueReading": "Видалити з Продовжити читання",
"ButtonRemoveSeriesFromContinueSeries": "Видалити серію з Продовжити серії",
"ButtonReScan": "Пересканувати",
"ButtonReset": "Скинути",
"ButtonResetToDefault": "Скинути до стандартних",
"ButtonRestore": "Відновити",
"ButtonSave": "Зберегти",
"ButtonSaveAndClose": "Зберегти та закрити",
"ButtonSaveTracklist": "Зберегти порядок",
"ButtonScan": "Сканувати",
"ButtonScanLibrary": "Сканувати бібліотеку",
"ButtonSearch": "Пошук",
"ButtonSelectFolderPath": "Обрати шлях до теки",
"ButtonSeries": "Серії",
"ButtonSetChaptersFromTracks": "Встановити глави за доріжками",
"ButtonShare": "Поширити",
"ButtonShiftTimes": "Зсунути час",
"ButtonShow": "Показати",
"ButtonStartM4BEncode": "Почати кодування у M4B",
"ButtonStartMetadataEmbed": "Почати вбудування метаданих",
"ButtonSubmit": "Надіслати",
"ButtonTest": "Перевірити",
"ButtonUpload": "Завантажити",
"ButtonUploadBackup": "Завантажити резервну копію",
"ButtonUploadCover": "Завантажити обкладинку",
"ButtonUploadOPMLFile": "Завантажити OPML-файл",
"ButtonUserDelete": "Видалити користувача {0}",
"ButtonUserEdit": "Редагувати користувача {0}",
"ButtonViewAll": "Переглянути все",
"ButtonYes": "Так",
"ErrorUploadFetchMetadataAPI": "Помилка при отриманні метаданих",
"ErrorUploadFetchMetadataNoResults": "Не вдалося отримати метадані — спробуйте оновити заголовок та/або автора",
"ErrorUploadLacksTitle": "Назва обов'язкова",
"HeaderAccount": "Профіль",
"HeaderAdvanced": "Розширені",
"HeaderAppriseNotificationSettings": "Налаштування сповіщень Apprise",
"HeaderAudiobookTools": "Інструменти керування файлами книг",
"HeaderAudioTracks": "Аудіодоріжки",
"HeaderAuthentication": "Автентифікація",
"HeaderBackups": "Резервні копії",
"HeaderChangePassword": "Змінити пароль",
"HeaderChapters": "Глави",
"HeaderChooseAFolder": "Обрати теку",
"HeaderCollection": "Колекція",
"HeaderCollectionItems": "Елементи колекції",
"HeaderCover": "Обкладинка",
"HeaderCurrentDownloads": "Поточні завантаження",
"HeaderCustomMetadataProviders": "Постачальники метаданих",
"HeaderDetails": "Подробиці",
"HeaderDownloadQueue": "Черга завантажень",
"HeaderEbookFiles": "Файли електронних книг",
"HeaderEmail": "Електронна пошта",
"HeaderEmailSettings": "Налаштування електронної пошти",
"HeaderEpisodes": "Епізоди",
"HeaderEreaderDevices": "Пристрої для читання",
"HeaderEreaderSettings": "Налаштування пристрою для читання",
"HeaderFiles": "Файли",
"HeaderFindChapters": "Пошук глав",
"HeaderIgnoredFiles": "Ігноровані файли",
"HeaderItemFiles": "Файли елементів",
"HeaderItemMetadataUtils": "Інструменти для метаданих",
"HeaderLastListeningSession": "Останній сеанс прослуховування",
"HeaderLatestEpisodes": "Останні епізоди",
"HeaderLibraries": "Бібліотеки",
"HeaderLibraryFiles": "Файли бібліотеки",
"HeaderLibraryStats": "Статистика бібліотеки",
"HeaderListeningSessions": "Сеанси прослуховування",
"HeaderListeningStats": "Статистика відтворення",
"HeaderLogin": "Вхід",
"HeaderLogs": "Журнал",
"HeaderManageGenres": "Керувати жанрами",
"HeaderManageTags": "Керувати мітками",
"HeaderMapDetails": "Призначити подробиці",
"HeaderMatch": "Пошук",
"HeaderMetadataOrderOfPrecedence": "Порядок метаданих",
"HeaderMetadataToEmbed": "Вбудувати метадані",
"HeaderNewAccount": "Новий профіль",
"HeaderNewLibrary": "Нова бібліотека",
"HeaderNotifications": "Сповіщення",
"HeaderOpenIDConnectAuthentication": "Автентифікація OpenID Connect",
"HeaderOpenRSSFeed": "Відкрити RSS-канал",
"HeaderOtherFiles": "Інші файли",
"HeaderPasswordAuthentication": "Автентифікація за паролем",
"HeaderPermissions": "Дозволи",
"HeaderPlayerQueue": "Черга відтворення",
"HeaderPlaylist": "Список відтворення",
"HeaderPlaylistItems": "Елементи списку відтворення",
"HeaderPodcastsToAdd": "Додати подкасти",
"HeaderPreviewCover": "Попередній перегляд",
"HeaderRemoveEpisode": "Видалити епізод",
"HeaderRemoveEpisodes": "Видалити епізодів: {0}",
"HeaderRSSFeedGeneral": "Подробиці RSS",
"HeaderRSSFeedIsOpen": "RSS-канал відкрито",
"HeaderRSSFeeds": "RSS-канали",
"HeaderSavedMediaProgress": "Збережений прогрес медіа",
"HeaderSchedule": "Розклад",
"HeaderScheduleLibraryScans": "Розклад автосканування бібліотеки",
"HeaderSession": "Сеанс",
"HeaderSetBackupSchedule": "Встановити розклад резервного копіювання",
"HeaderSettings": "Налаштування",
"HeaderSettingsDisplay": "Відображення",
"HeaderSettingsExperimental": "Експериментальні функції",
"HeaderSettingsGeneral": "Основне",
"HeaderSettingsScanner": "Сканер",
"HeaderSleepTimer": "Таймер вимкнення",
"HeaderStatsLargestItems": "Найбільші елементи",
"HeaderStatsLongestItems": "Найдовші елементи (год)",
"HeaderStatsMinutesListeningChart": "Хвилин прослухано (останні 7 днів)",
"HeaderStatsRecentSessions": "Останні сеанси",
"HeaderStatsTop10Authors": "10 улюблених авторів",
"HeaderStatsTop5Genres": "5 улюблених жанрів",
"HeaderTableOfContents": "Зміст",
"HeaderTools": "Інструменти",
"HeaderUpdateAccount": "Оновити профіль",
"HeaderUpdateAuthor": "Оновити автора",
"HeaderUpdateDetails": "Оновити подробиці",
"HeaderUpdateLibrary": "Оновити бібліотеку",
"HeaderUsers": "Користувачі",
"HeaderYearReview": "Підсумки {0} року",
"HeaderYourStats": "Ваша статистика",
"LabelAbridged": "Скорочена",
"LabelAccountType": "Тип профілю",
"LabelAccountTypeAdmin": "Адміністратор",
"LabelAccountTypeGuest": "Гість",
"LabelAccountTypeUser": "Користувач",
"LabelActivity": "Активність",
"LabelAdded": "Додано",
"LabelAddedAt": "Дата додавання",
"LabelAddToCollection": "Додати у добірку",
"LabelAddToCollectionBatch": "Додати книги до добірки: {0}",
"LabelAddToPlaylist": "Додати до списку відтворення",
"LabelAddToPlaylistBatch": "Додано елементів у список відтворення: {0}",
"LabelAdminUsersOnly": "Тільки для адміністраторів",
"LabelAll": "Усе",
"LabelAllUsers": "Усі користувачі",
"LabelAllUsersExcludingGuests": "Усі, крім гостей",
"LabelAllUsersIncludingGuests": "Усі, включно з гостями",
"LabelAlreadyInYourLibrary": "Вже у вашій бібліотеці",
"LabelAppend": "Додати",
"LabelAuthor": "Автор",
"LabelAuthorFirstLast": "Автор (за ім'ям)",
"LabelAuthorLastFirst": "Автор (за прізвищем)",
"LabelAuthors": "Автори",
"LabelAutoDownloadEpisodes": "Автозавантаження епізодів",
"LabelAutoFetchMetadata": "Автозавантаження метаданих",
"LabelAutoFetchMetadataHelp": "Отримує метадані про назву, автора та серію під час послідового завантаження. Після завантаження може знадобитися пошук додаткових метаданих.",
"LabelAutoLaunch": "Автозапуск",
"LabelAutoLaunchDescription": "Автоматично перенаправляти зі сторінки входу до сервісу автентифікації (ручний перезапис шляху <code>/login?autoLaunch=0</code>)",
"LabelAutoRegister": "Автореєстрація",
"LabelAutoRegisterDescription": "Автоматично створювати нових користувачів після входу",
"LabelBackToUser": "Повернутися до користувача",
"LabelBackupLocation": "Розташування резервних копій",
"LabelBackupsEnableAutomaticBackups": "Автоматичне резервне копіювання",
"LabelBackupsEnableAutomaticBackupsHelp": "Резервні копії збережено у /metadata/backups",
"LabelBackupsMaxBackupSize": "Максимальний розмір резервної копії (у ГБ)",
"LabelBackupsMaxBackupSizeHelp": "У якості захисту від неправильного налаштування, резервну копію не буде збережено, якщо її розмір перевищуватиме вказаний.",
"LabelBackupsNumberToKeep": "Кількість резервних копій",
"LabelBackupsNumberToKeepHelp": "Лиш 1 резервну копію буде видалено за раз, тож якщо їх багато, то вам варто видалити їх вручну.",
"LabelBitrate": "Бітрейт",
"LabelBooks": "Книги",
"LabelButtonText": "Текст кнопки",
"LabelChangePassword": "Змінити пароль",
"LabelChannels": "Канали",
"LabelChapters": "Глави",
"LabelChaptersFound": "глав знайдено",
"LabelChapterTitle": "Назва глави",
"LabelClickForMoreInfo": "Натисніть, щоб дізнатися більше",
"LabelClosePlayer": "Закрити програвач",
"LabelCodec": "Кодек",
"LabelCollapseSeries": "Згорнути серії",
"LabelCollection": "Добірка",
"LabelCollections": "Добірки",
"LabelComplete": "Завершити",
"LabelConfirmPassword": "Підтвердити пароль",
"LabelContinueListening": "Слухати далі",
"LabelContinueReading": "Читати далі",
"LabelContinueSeries": "Продовжити серії",
"LabelCover": "Обкладинка",
"LabelCoverImageURL": "URL-адреса обкладинки",
"LabelCreatedAt": "Дата створення",
"LabelCronExpression": "Команда cron",
"LabelCurrent": "Поточне",
"LabelCurrently": "Поточний:",
"LabelCustomCronExpression": "Спецільна команда cron:",
"LabelDatetime": "Дата й час",
"LabelDeleteFromFileSystemCheckbox": "Видалити з файлової системи (зніміть прапорець, щоб видалити лише з бази даних)",
"LabelDescription": "Опис",
"LabelDeselectAll": "Скасувати вибір",
"LabelDevice": "Пристрій",
"LabelDeviceInfo": "Про пристрій",
"LabelDeviceIsAvailableTo": "Пристрій доступний для...",
"LabelDirectory": "Каталог",
"LabelDiscFromFilename": "Диск за назвою файлу",
"LabelDiscFromMetadata": "Диск за метаданими",
"LabelDiscover": "Огляд",
"LabelDownload": "Завантажити",
"LabelDownloadNEpisodes": "Завантажити епізодів: {0}",
"LabelDuration": "Тривалість",
"LabelDurationFound": "Виявлена тривалість:",
"LabelEbook": "Електронна книга",
"LabelEbooks": "Електронні книги",
"LabelEdit": "Редагувати",
"LabelEmail": "Електронна пошта",
"LabelEmailSettingsFromAddress": "Адреса відправника",
"LabelEmailSettingsSecure": "Безпечне",
"LabelEmailSettingsSecureHelp": "Увімкніть, аби використовувати TLS при підключенні до сервера. Якщо вимкнути, то TLS буде використано, якщо сервер підтримує STARTTLS. Увімкніть, якщо ви підключаєтеся до порту 465. Вимкніть для портів 587 або 25. (з nodemailer.com/smtp/#authentication)",
"LabelEmailSettingsTestAddress": "Тестова адреса",
"LabelEmbeddedCover": "Вбудована обкладинка",
"LabelEnable": "Увімкнути",
"LabelEnd": "Кінець",
"LabelEpisode": "Епізод",
"LabelEpisodeTitle": "Назва епізоду",
"LabelEpisodeType": "Тип епізоду",
"LabelExample": "Приклад",
"LabelExplicit": "Відверта",
"LabelFeedURL": "Адреса стрічки",
"LabelFetchingMetadata": "Отримання метаданих",
"LabelFile": "Файл",
"LabelFileBirthtime": "Дата створення",
"LabelFileModified": "Дата змінення",
"LabelFilename": "Ім'я файлу",
"LabelFilterByUser": "Фільтрувати за користувачем",
"LabelFindEpisodes": "Знайти епізоди",
"LabelFinished": "Завершено",
"LabelFolder": "Тека",
"LabelFolders": "Теки",
"LabelFontBold": "Жирний",
"LabelFontFamily": "Гарнітура",
"LabelFontItalic": "Курсив",
"LabelFontScale": "Розмір шрифту",
"LabelFontStrikethrough": "Закреслений",
"LabelFormat": "Формат",
"LabelGenre": "Жанр",
"LabelGenres": "Жанри",
"LabelHardDeleteFile": "Остаточно видалити файл",
"LabelHasEbook": "Має електронну книгу",
"LabelHasSupplementaryEbook": "Має додаткову електронну книгу",
"LabelHighestPriority": "Найвищий пріоритет",
"LabelHost": "Гост",
"LabelHour": "Година",
"LabelIcon": "Іконка",
"LabelImageURLFromTheWeb": "URL зображення з мережі",
"LabelIncludeInTracklist": "Включити у список",
"LabelIncomplete": "Не завершено",
"LabelInProgress": "У процесі",
"LabelInterval": "Частота",
"LabelIntervalCustomDailyWeekly": "Налаштувати щодня/щотижня",
"LabelIntervalEvery12Hours": "Кожні 12 годин",
"LabelIntervalEvery15Minutes": "Кожні 15 хвилин",
"LabelIntervalEvery2Hours": "Кожні 2 години",
"LabelIntervalEvery30Minutes": "Кожні 30 хвилин",
"LabelIntervalEvery6Hours": "Кожні 6 годин",
"LabelIntervalEveryDay": "Щодня",
"LabelIntervalEveryHour": "Щогодини",
"LabelInvert": "Інвертувати",
"LabelItem": "Елемент",
"LabelLanguage": "Мова",
"LabelLanguageDefaultServer": "Типова мова сервера",
"LabelLastBookAdded": "Останню книгу додано",
"LabelLastBookUpdated": "Останню книгу оновлено",
"LabelLastSeen": "Активність",
"LabelLastTime": "Останній час",
"LabelLastUpdate": "Останнє оновлення",
"LabelLayout": "Вигляд",
"LabelLayoutSinglePage": "Одна сторінка",
"LabelLayoutSplitPage": "Розділити сторінку",
"LabelLess": "Менше",
"LabelLibrariesAccessibleToUser": "Бібліотеки, доступні користувачу",
"LabelLibrary": "Бібліотека",
"LabelLibraryItem": "Елемент бібліотеки",
"LabelLibraryName": "Назва бібліотеки",
"LabelLimit": "Обмеження",
"LabelLineSpacing": "Відстань між рядками",
"LabelListenAgain": "Слухати знову",
"LabelLogLevelDebug": "Зневадження",
"LabelLogLevelInfo": "Відомості",
"LabelLogLevelWarn": "Увага",
"LabelLookForNewEpisodesAfterDate": "Шукати нові епізоди після вказаної дати",
"LabelLowestPriority": "Найнижчий пріоритет",
"LabelMatchExistingUsersBy": "Шукати наявних користувачів за",
"LabelMatchExistingUsersByDescription": "Використовується для підключення наявних користувачів. Після підключення користувач отримає унікальний id від вашого сервісу SSO",
"LabelMediaPlayer": "Програвач медіа",
"LabelMediaType": "Тип медіа",
"LabelMetadataOrderOfPrecedenceDescription": "Пріоритетніші джерела метаданих перезапишуть менш пріоритетні метадані",
"LabelMetadataProvider": "Джерело метаданих",
"LabelMetaTag": "Метатег",
"LabelMetaTags": "Метатеги",
"LabelMinute": "Хвилина",
"LabelMissing": "Бракує",
"LabelMissingEbook": "Без електронної книги",
"LabelMissingSupplementaryEbook": "Без додаткової електронної книги",
"LabelMobileRedirectURIs": "Дозволені адреси перенаправлення",
"LabelMobileRedirectURIsDescription": "Це білий список наявних URI, що перенаправляють у мобільний додаток. За замовчуванням це <code>audiobookshelf://oauth</code>, який ви можете видалити або ж додати інші адреси для сторонніх інтеграцій. Використайте зірочку (<code>*</code>), аби дозволити будь-яке URI.",
"LabelMore": "Більше",
"LabelMoreInfo": "Докладніше",
"LabelName": "Назва",
"LabelNarrator": "Читець",
"LabelNarrators": "Читці",
"LabelNew": "Нове",
"LabelNewestAuthors": "Нові автори",
"LabelNewestEpisodes": "Нові епізоди",
"LabelNewPassword": "Новий пароль",
"LabelNextBackupDate": "Дата наступного резервного копіювання",
"LabelNextScheduledRun": "Наступний запланований запуск",
"LabelNoEpisodesSelected": "Не вибрано жодного епізоду",
"LabelNotes": "Примітки",
"LabelNotFinished": "Незавершені",
"LabelNotificationAppriseURL": "URL Apprise",
"LabelNotificationAvailableVariables": "Доступні змінні",
"LabelNotificationBodyTemplate": "Шаблон сповіщення",
"LabelNotificationEvent": "Сповіщення про події",
"LabelNotificationsMaxFailedAttempts": "Ліміт невдалих спроб",
"LabelNotificationsMaxFailedAttemptsHelp": "Сповіщення буде вимкнено після багатьох невдалих надсилань",
"LabelNotificationsMaxQueueSize": "Ліміт розміру черги сповіщень",
"LabelNotificationsMaxQueueSizeHelp": "Події обмежені до 1 на секунду. Події буде проігноровано, якщо ліміт черги досягнуто. Це запобігає спаму сповіщеннями.",
"LabelNotificationTitleTemplate": "Шаблон заголовку",
"LabelNotStarted": "Не розпочато",
"LabelNumberOfBooks": "Кількість книг",
"LabelNumberOfEpisodes": "Кількість епізодів",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Відкрити RSS-канал",
"LabelOverwrite": "Перезаписати",
"LabelPassword": "Пароль",
"LabelPath": "Шлях",
"LabelPermissionsAccessAllLibraries": "Доступ до усіх бібліотек",
"LabelPermissionsAccessAllTags": "Доступ до усіх міток",
"LabelPermissionsAccessExplicitContent": "Доступ до відвертого вмісту",
"LabelPermissionsDelete": "Може видаляти",
"LabelPermissionsDownload": "Може завантажувати",
"LabelPermissionsUpdate": "Може оновлювати",
"LabelPermissionsUpload": "Може завантажувати",
"LabelPersonalYearReview": "Ваші підсумки року ({0})",
"LabelPhotoPathURL": "Шлях/URL фото",
"LabelPlaylists": "Списки відтворення",
"LabelPlayMethod": "Метод відтворення",
"LabelPodcast": "Подкаст",
"LabelPodcasts": "Подкасти",
"LabelPodcastSearchRegion": "Регіон пошуку подкасту",
"LabelPodcastType": "Тип подкасту",
"LabelPort": "Порт",
"LabelPrefixesToIgnore": "Ігнорувати префікси (з урахуванням регістру)",
"LabelPreventIndexing": "Заборонити індексування вашого каналу каталогами подкастів iTunes та Google",
"LabelPrimaryEbook": "Основна електронна книга",
"LabelProgress": "Прогрес",
"LabelProvider": "Джерело",
"LabelPubDate": "Дата публікації",
"LabelPublisher": "Видавець",
"LabelPublishYear": "Рік публікації",
"LabelRead": "Читати",
"LabelReadAgain": "Читати знову",
"LabelReadEbookWithoutProgress": "Читати книгу без збереження прогресу",
"LabelRecentlyAdded": "Нещодавно додані",
"LabelRecentSeries": "Останні серії",
"LabelRecommended": "Рекомендовані",
"LabelRedo": "Повторити",
"LabelRegion": "Регіон",
"LabelReleaseDate": "Дата публікації",
"LabelRemoveCover": "Видалити обкладинку",
"LabelRowsPerPage": "Рядків на сторінку",
"LabelRSSFeedCustomOwnerEmail": "Користувацька електронна адреса власника",
"LabelRSSFeedCustomOwnerName": "Користувацьке ім'я власника",
"LabelRSSFeedOpen": "RSS-канал відкрито",
"LabelRSSFeedPreventIndexing": "Запобігати індексації",
"LabelRSSFeedSlug": "Назва RSS-каналу",
"LabelRSSFeedURL": "Адреса RSS-каналу",
"LabelSearchTerm": "Пошуковий запит",
"LabelSearchTitle": "Пошук за назвою",
"LabelSearchTitleOrASIN": "Пошук назви або ASIN",
"LabelSeason": "Сезон",
"LabelSelectAllEpisodes": "Вибрати всі серії",
"LabelSelectEpisodesShowing": "Обрати показані епізоди: {0}",
"LabelSelectUsers": "Вибрати користувачів",
"LabelSendEbookToDevice": "Надіслати електронну книгу на...",
"LabelSequence": "Послідовність",
"LabelSeries": "Серії",
"LabelSeriesName": "Назва серії",
"LabelSeriesProgress": "Прогрес серії",
"LabelServerYearReview": "Підсумки року сервера ({0})",
"LabelSetEbookAsPrimary": "Зробити основною",
"LabelSetEbookAsSupplementary": "Зробити додатковою",
"LabelSettingsAudiobooksOnly": "Лише аудіокниги",
"LabelSettingsAudiobooksOnlyHelp": "Увімкніть цей параметр, щоб ігнорувати файли електронних книг, якщо вони не знаходяться у теці аудіокниги, тоді вони будуть встановлені як додаткові електронні книги",
"LabelSettingsBookshelfViewHelp": "Імітує вигляд дерев'яних полиць",
"LabelSettingsChromecastSupport": "Підтримка Chromecast",
"LabelSettingsDateFormat": "Формат дати",
"LabelSettingsDisableWatcher": "Вимкнути спостерігача",
"LabelSettingsDisableWatcherForLibrary": "Вимкнути спостерігання тек бібліотеки",
"LabelSettingsDisableWatcherHelp": "Вимикає автоматичне додавання/оновлення елементів, коли спостерігаються зміни файлів. *Потребує перезавантаження сервера",
"LabelSettingsEnableWatcher": "Увімкнути спостерігача",
"LabelSettingsEnableWatcherForLibrary": "Увімкнути спостерігання тек бібліотеки",
"LabelSettingsEnableWatcherHelp": "Вмикає автоматичне додавання/оновлення елементів, коли спостерігаються зміни файлів. *Потребує перезавантаження сервера",
"LabelSettingsExperimentalFeatures": "Експериментальні функції",
"LabelSettingsExperimentalFeaturesHelp": "Функції в розробці, які потребують вашого відгуку та допомоги в тестуванні. Натисніть, щоб відкрити обговорення на Github.",
"LabelSettingsFindCovers": "Пошук обкладинок",
"LabelSettingsFindCoversHelp": "Якщо ваша аудіокнига не містить вбудованої обкладинки або зображення у теці, сканувальник спробує знайти обкладинку.<br>Примітка: Це збільшить час сканування",
"LabelSettingsHideSingleBookSeries": "Сховати серії з однією книгою",
"LabelSettingsHideSingleBookSeriesHelp": "Серії, що містять одну книгу, будуть приховані зі сторінки серій та полиць головної сторінки.",
"LabelSettingsHomePageBookshelfView": "Полиці на головній сторінці",
"LabelSettingsLibraryBookshelfView": "Показувати полиці у бібліотеці",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Пропускати попередні книги у Продовжити серії",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Полиця Продовжити серії на головній сторінці показує найпершу непочату книгу з тих серій, у яких ви завершили хоча б одну книгу та не маєте книг у процесі. Якщо увімкнути це налаштування, то серії продовжуватимуться з останньої завершеної книги, а не з першої непочатої.",
"LabelSettingsParseSubtitles": "Дістати підзаголовки",
"LabelSettingsParseSubtitlesHelp": "Дістати підзаголовки з назв тек аудіокниг.<br>Підзаголовок мусить йти після \" - \"<br>Наприклад, \"Назва книги - Це підзаголовок\" має підзаголовок \"Це підзаголовок\"",
"LabelSettingsPreferMatchedMetadata": "Надавати перевагу віднайденим метаданим",
"LabelSettingsPreferMatchedMetadataHelp": "Подробиці буде перезаписано віднайденими даними Швидкого пошуку. Без цього Швидкий пошук заповнить лише подробиці, яких бракує.",
"LabelSettingsSkipMatchingBooksWithASIN": "Не шукати книги, що мають ASIN",
"LabelSettingsSkipMatchingBooksWithISBN": "Не шукати книги, що мають ISBN",
"LabelSettingsSortingIgnorePrefixes": "Ігнорувати префікси при сортуванні",
"LabelSettingsSortingIgnorePrefixesHelp": "Наприклад, для префіксу \"1.\" назва книги \"1. Назва книги\" буде визначена як \"Назва книги, 1.\"",
"LabelSettingsSquareBookCovers": "Квадратні обкладинки",
"LabelSettingsSquareBookCoversHelp": "Надавати перевагу квадратним обкладинкам замість формату 1,6:1",
"LabelSettingsStoreCoversWithItem": "Зберігати обкладинки з елементом",
"LabelSettingsStoreCoversWithItemHelp": "За замовчуванням обкладинки зберігаються у /metadata/items. Цей параметр увімкне збереження обкладинок у теці елемента бібліотеки. Буде збережено лише один файл \"cover\"",
"LabelSettingsStoreMetadataWithItem": "Зберігати метадані з елементом",
"LabelSettingsStoreMetadataWithItemHelp": "За замовчуванням файли метаданих зберігаються у /metadata/items. Цей параметр увімкне збереження метаданих у теці елемента бібліотеки",
"LabelSettingsTimeFormat": "Формат часу",
"LabelShowAll": "Показати все",
"LabelSize": "Розмір",
"LabelSleepTimer": "Таймер вимкнення",
"LabelSlug": "Назва",
"LabelStart": "Початок",
"LabelStarted": "Почато",
"LabelStartedAt": "Почато",
"LabelStartTime": "Час початку",
"LabelStatsAudioTracks": "Аудіодоріжки",
"LabelStatsAuthors": "Автори",
"LabelStatsBestDay": "Найкращий день",
"LabelStatsDailyAverage": "В середньому за добу",
"LabelStatsDays": "Днів",
"LabelStatsDaysListened": "Днів прослухано",
"LabelStatsHours": "Годин",
"LabelStatsInARow": "поспіль",
"LabelStatsItemsFinished": "Елементів завершено",
"LabelStatsItemsInLibrary": "Елементів у бібліотеці",
"LabelStatsMinutes": "хвилин",
"LabelStatsMinutesListening": "Хвилин прослухано",
"LabelStatsOverallDays": "Днів загалом",
"LabelStatsOverallHours": "Годин загалом",
"LabelStatsWeekListening": "Прослухано за тиждень",
"LabelSubtitle": "Підзаголовок",
"LabelSupportedFileTypes": "Підтримувані типи файлів",
"LabelTag": "Мітка",
"LabelTags": "Мітки",
"LabelTagsAccessibleToUser": "Мітки, доступні користувачу",
"LabelTagsNotAccessibleToUser": "Мітки, недоступні користувачу",
"LabelTasks": "Запущені завдання",
"LabelTextEditorBulletedList": "Маркований список",
"LabelTextEditorLink": "Посилання",
"LabelTextEditorNumberedList": "Нумерований список",
"LabelTextEditorUnlink": "Прибрати посилання",
"LabelTheme": "Тема",
"LabelThemeDark": "Темна",
"LabelThemeLight": "Світла",
"LabelTimeBase": "Шкала часу",
"LabelTimeListened": "Часу прослухано",
"LabelTimeListenedToday": "Сьогодні прослухано",
"LabelTimeRemaining": "Залишилося: {0}",
"LabelTimeToShift": "На скільки секунд зсунути",
"LabelTitle": "Назва",
"LabelToolsEmbedMetadata": "Вбудувати метадані",
"LabelToolsEmbedMetadataDescription": "Вбудувати метадані в аудіофайли, включно з обкладинками та главами",
"LabelToolsMakeM4b": "Створити M4B-файл аудіокниги",
"LabelToolsMakeM4bDescription": "Створити .M4B-аудіокнигу з вбудованими метаданими, обкладинкою та главами.",
"LabelToolsSplitM4b": "Розділити M4B на MP3",
"LabelToolsSplitM4bDescription": "Створення MP3 з розділеного за главами M4B з вбудованими метаданими, обкладинкою та главами.",
"LabelTotalDuration": "Загальна тривалість",
"LabelTotalTimeListened": "Усього прослухано",
"LabelTrackFromFilename": "Доріжка за назвою файлу",
"LabelTrackFromMetadata": "Доріжка за метаданими",
"LabelTracks": "Доріжки",
"LabelTracksMultiTrack": "Декілька доріжок",
"LabelTracksNone": "Доріжки відсутні",
"LabelTracksSingleTrack": "Одна доріжка",
"LabelType": "Тип",
"LabelUnabridged": "Повна",
"LabelUndo": "Скасувати",
"LabelUnknown": "Невідомо",
"LabelUpdateCover": "Оновити обкладинку",
"LabelUpdateCoverHelp": "Дозволити перезапис наявних обкладинок обраних книг після віднайдення",
"LabelUpdatedAt": "Оновлення",
"LabelUpdateDetails": "Оновити подробиці",
"LabelUpdateDetailsHelp": "Дозволити перезапис наявних подробиць обраних книг після віднайдення",
"LabelUploaderDragAndDrop": "Перетягніть файли або теки",
"LabelUploaderDropFiles": "Перетягніть файли",
"LabelUploaderItemFetchMetadataHelp": "Автоматично шукати назву, автора та серію",
"LabelUseChapterTrack": "Прогрес глави",
"LabelUseFullTrack": "Використовувати доріжку повністю",
"LabelUser": "Користувач",
"LabelUsername": "Ім’я користувача",
"LabelValue": "Значення",
"LabelVersion": "Версія",
"LabelViewBookmarks": "Переглянути закладки",
"LabelViewChapters": "Переглянути глави",
"LabelViewQueue": "Переглянути чергу відтворення",
"LabelVolume": "Гучність",
"LabelWeekdaysToRun": "Виконувати у дні",
"LabelYearReviewHide": "Сховати підсумки року",
"LabelYearReviewShow": "Переглянути підсумки року",
"LabelYourAudiobookDuration": "Тривалість вашої аудіокниги",
"LabelYourBookmarks": "Ваші закладки",
"LabelYourPlaylists": "Ваші списки відтворення",
"LabelYourProgress": "Ваш прогрес",
"MessageAddToPlayerQueue": "Додати до черги відтворення",
"MessageAppriseDescription": "Щоб скористатися цією функцією, вам потрібно мати запущену <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> або API, що оброблятиме ті ж запити. <br />Аби надсилати сповіщення, URL-адреса API Apprise мусить бути повною, наприклад, якщо ваш API розміщено за адресою <code>http://192.168.1.1:8337</code>, то необхідно вказати адресу <code>http://192.168.1.1:8337/notify</code>.",
"MessageBackupsDescription": "Резервні копії містять користувачів, прогрес, подробиці елементів бібліотеки, налаштування сервера та зображення з <code>/metadata/items</code> та <code>/metadata/authors</code>. Резервні копії <strong>не</strong> містять жодних файлів з тек бібліотеки.",
"MessageBatchQuickMatchDescription": "Швидкий пошук спробує знайти відсутні обкладинки та метадані обраних елементів. Увімкніть налаштування нижче, аби дозволити заміну наявних обкладинок та/або метаданих під час швидкого пошуку.",
"MessageBookshelfNoCollections": "Ви не створили жодної добірки",
"MessageBookshelfNoResultsForFilter": "Немає результатів з фільтром \"{0}: {1}\"",
"MessageBookshelfNoRSSFeeds": "Немає відкритих RSS-каналів",
"MessageBookshelfNoSeries": "Серії відсутні",
"MessageChapterEndIsAfter": "Кінець глави знаходиться після закінчення книги",
"MessageChapterErrorFirstNotZero": "Перша глава мусить починатися з 0",
"MessageChapterErrorStartGteDuration": "Час початку мусить бути меншим за тривалість аудіокниги",
"MessageChapterErrorStartLtPrev": "Неприпустимий час початку, має бути більшим за час початку попередньої глави",
"MessageChapterStartIsAfter": "Початок глави знаходиться після закінчення книги",
"MessageCheckingCron": "Перевірка планувальника...",
"MessageConfirmCloseFeed": "Ви дійсно бажаєте закрити цей канал?",
"MessageConfirmDeleteBackup": "Ви дійсно бажаєте видалити резервну копію за {0}?",
"MessageConfirmDeleteFile": "Файл буде видалено з вашої файлової системи. Ви впевнені?",
"MessageConfirmDeleteLibrary": "Ви дійсно бажаєте назавжди видалити бібліотеку \"{0}\"?",
"MessageConfirmDeleteLibraryItem": "Елемент бібліотеки буде видалено з бази даних та вашої файлової системи. Ви впевнені?",
"MessageConfirmDeleteLibraryItems": "З бази даних та вашої файлової системи будуть видалені елементи бібліотеки: {0}. Ви впевнені?",
"MessageConfirmDeleteSession": "Ви дійсно бажаєте видалити цей сеанс?",
"MessageConfirmForceReScan": "Ви дійсно бажаєте примусово пересканувати?",
"MessageConfirmMarkAllEpisodesFinished": "Ви дійсно бажаєте позначити усі епізоди завершеними?",
"MessageConfirmMarkAllEpisodesNotFinished": "Ви дійсно бажаєте позначити усі епізоди незавершеними?",
"MessageConfirmMarkSeriesFinished": "Ви дійсно бажаєте позначити усі книги серії завершеними?",
"MessageConfirmMarkSeriesNotFinished": "Ви дійсно бажаєте позначити всі книги серії незавершеними?",
"MessageConfirmQuickEmbed": "Увага! Швидке вбудування не створює резервних копій ваших аудіо. Переконайтеся, що маєте копію ваших файлів.<br><br>Продовжити?",
"MessageConfirmRemoveAllChapters": "Ви дійсно бажаєте видалити усі глави?",
"MessageConfirmRemoveAuthor": "Ви дійсно бажаєте видалити автора \"{0}\"?",
"MessageConfirmRemoveCollection": "Ви дійсно бажаєте видалити добірку \"{0}\"?",
"MessageConfirmRemoveEpisode": "Ви дійсно бажаєте видалити епізод \"{0}\"?",
"MessageConfirmRemoveEpisodes": "Ви дійсно бажаєте видалити епізодів: {0}?",
"MessageConfirmRemoveListeningSessions": "Ви дійсно бажаєте видалити сеанси прослуховування: {0}?",
"MessageConfirmRemoveNarrator": "Ви дійсно бажаєте видалити читця \"{0}\"?",
"MessageConfirmRemovePlaylist": "Ви дійсно бажаєте видалити список відтворення \"{0}\"?",
"MessageConfirmRenameGenre": "Ви дійсно бажаєте замінити жанр \"{0}\" на \"{1}\" для усіх елементів?",
"MessageConfirmRenameGenreMergeNote": "Примітка: такий жанр вже існує, тож їх буде об'єднано.",
"MessageConfirmRenameGenreWarning": "Увага! Вже існує схожий жанр у іншому регістрі \"{0}\".",
"MessageConfirmRenameTag": "Ви дійсно бажаєте замінити мітку \"{0}\" на \"{1}\" для усіх елементів?",
"MessageConfirmRenameTagMergeNote": "Примітка: така мітка вже існує, тож їх буде об'єднано.",
"MessageConfirmRenameTagWarning": "Увага! Вже існує схожа мітка у іншому регістрі \"{0}\".",
"MessageConfirmReScanLibraryItems": "Ви дійсно бажаєте пересканувати елементи: {0}?",
"MessageConfirmSendEbookToDevice": "Ви дійсно хочете відправити на пристрій \"{2}\" електроні книги: {0}, \"{1}\"?",
"MessageDownloadingEpisode": "Завантаження епізоду",
"MessageDragFilesIntoTrackOrder": "Перетягніть файли до правильного порядку",
"MessageEmbedFinished": "Вбудовано!",
"MessageEpisodesQueuedForDownload": "Епізодів у черзі завантаження: {0}",
"MessageFeedURLWillBe": "URL-адреса каналу буде {0}",
"MessageFetching": "Отримання...",
"MessageForceReScanDescription": "Просканує усі файли заново, неначе вперше. ID3-мітки, файли OPF та текстові файли будуть проскановані як нові.",
"MessageImportantNotice": "Важливе повідомлення!",
"MessageInsertChapterBelow": "Введіть главу нижче",
"MessageItemsSelected": "Обрано елементів: {0}",
"MessageItemsUpdated": "Оновлено елементів: {0}",
"MessageJoinUsOn": "Приєднуйтесь до",
"MessageListeningSessionsInTheLastYear": "Сесій прослуховування минулого року: {0}",
"MessageLoading": "Завантаження...",
"MessageLoadingFolders": "Завантаження тек...",
"MessageM4BFailed": "Помилка M4B!",
"MessageM4BFinished": "M4B створено!",
"MessageMapChapterTitles": "Встановіть назви глав вашої аудіокниги без визначення налаштувань тривалості",
"MessageMarkAllEpisodesFinished": "Позначити всі епізоди завершеними",
"MessageMarkAllEpisodesNotFinished": "Позначити всі епізоди незавершеними",
"MessageMarkAsFinished": "Позначити завершеним",
"MessageMarkAsNotFinished": "Позначити незавершеним",
"MessageMatchBooksDescription": "Спробує віднайти книгу у вказаному джерелі пошуку та встановити подробиці та обкладинку, яких бракує. Не перезаписує подробиці.",
"MessageNoAudioTracks": "Аудіодоріжки відсутні",
"MessageNoAuthors": "Автори відсутні",
"MessageNoBackups": "Резервні копії відсутні",
"MessageNoBookmarks": "Немає закладок",
"MessageNoChapters": "Глави відсутні",
"MessageNoCollections": "Добірки відсутні",
"MessageNoCoversFound": "Обкладинок не знайдено",
"MessageNoDescription": "Без опису",
"MessageNoDownloadsInProgress": "Немає активних завантажень",
"MessageNoDownloadsQueued": "Немає завантажень у черзі",
"MessageNoEpisodeMatchesFound": "Відповідних епізодів не знайдено",
"MessageNoEpisodes": "Епізоди відсутні",
"MessageNoFoldersAvailable": "Немає доступних тек",
"MessageNoGenres": "Без жанру",
"MessageNoIssues": "Немає проблем",
"MessageNoItems": "Елементи відсутні",
"MessageNoItemsFound": "Елементів не знайдено",
"MessageNoListeningSessions": "Сеанси прослуховування відсутні",
"MessageNoLogs": "Журнал порожній",
"MessageNoMediaProgress": "Прогрес відсутній",
"MessageNoNotifications": "Сповіщення відсутні",
"MessageNoPodcastsFound": "Подкастів не знайдено",
"MessageNoResults": "Немає результатів",
"MessageNoSearchResultsFor": "Немає результатів пошуку для \"{0}\"",
"MessageNoSeries": "Без серії",
"MessageNoTags": "Без міток",
"MessageNoTasksRunning": "Немає активних завдань",
"MessageNotYetImplemented": "Ще не реалізовано",
"MessageNoUpdateNecessary": "Оновлення не потрібно",
"MessageNoUpdatesWereNecessary": "Оновлень не потрібно",
"MessageNoUserPlaylists": "У вас немає списків відтворення",
"MessageOr": "або",
"MessagePauseChapter": "Призупинити відтворення глави",
"MessagePlayChapter": "Слухати початок глави",
"MessagePlaylistCreateFromCollection": "Створити список відтворення з добірки",
"MessagePodcastHasNoRSSFeedForMatching": "Подкаст не має RSS-каналу для пошуку",
"MessageQuickMatchDescription": "Заповнити відсутні подробиці та обкладинку першим результатом пошуку '{0}'. Не перезаписує подробиці, якщо не увімкнено параметр \"Надавати перевагу віднайденим метаданим\".",
"MessageRemoveChapter": "Видалити главу",
"MessageRemoveEpisodes": "Видалити епізодів: {0}",
"MessageRemoveFromPlayerQueue": "Вилучити з черги відтворення",
"MessageRemoveUserWarning": "Ви дійсно бажаєте назавжди видалити користувача \"{0}\"?",
"MessageReportBugsAndContribute": "Повідомляйте про помилки, пропонуйте функції та долучайтеся на",
"MessageResetChaptersConfirm": "Ви дійсно бажаєте скинути глави та скасувати внесені зміни?",
"MessageRestoreBackupConfirm": "Ви дійсно бажаєте відновити резервну копію від",
"MessageRestoreBackupWarning": "Відновлення резервної копії перезапише всю базу даних, розташовану в /config, і зображення обкладинок в /metadata/items та /metadata/authors.<br /><br />Резервні копії не змінюють жодних файлів у теках бібліотеки. Якщо у налаштуваннях сервера увімкнено збереження обкладинок і метаданих у теках бібліотеки, вони не створюються під час резервного копіювання і не перезаписуються..<br /><br />Всі клієнти, що користуються вашим сервером, будуть автоматично оновлені.",
"MessageSearchResultsFor": "Результати пошуку для",
"MessageSelected": "Вибрано: {0}",
"MessageServerCouldNotBeReached": "Не вдалося підключитися до сервера",
"MessageSetChaptersFromTracksDescription": "Створити глави з аудіодоріжок, встановивши назви файлів за заголовки",
"MessageStartPlaybackAtTime": "Почати відтворення \"{0}\" з {1}?",
"MessageThinking": "Думаю…",
"MessageUploaderItemFailed": "Не вдалося завантажити",
"MessageUploaderItemSuccess": "Успішно завантажено!",
"MessageUploading": "Завантаження...",
"MessageValidCronExpression": "Допустима команда cron",
"MessageWatcherIsDisabledGlobally": "Спостерігача вимкнено в налаштуваннях сервера",
"MessageXLibraryIsEmpty": "Бібліотека {0} порожня!",
"MessageYourAudiobookDurationIsLonger": "Тривалість вашої аудіокниги довша за віднайдену",
"MessageYourAudiobookDurationIsShorter": "Тривалість вашої аудіокниги коротша за віднайдену",
"NoteChangeRootPassword": "Кореневий користувач — єдиний, хто може мати порожній пароль",
"NoteChapterEditorTimes": "Примітка: Перша глава мусить починатися з 0:00, а час початку останньої глави не може бути більшим за зазначену тривалість аудіокниги.",
"NoteFolderPicker": "Примітка: вже обрані теки не буде показано",
"NoteRSSFeedPodcastAppsHttps": "Попередження: Більшість додатків подкастів вимагатимуть використання протоколу HTTPS від RSS-каналу",
"NoteRSSFeedPodcastAppsPubDate": "Попередження: 1 або більше ваших епізодів не мають дати публікації. Деякі додатки подкастів вимагають це.",
"NoteUploaderFoldersWithMediaFiles": "Теки з медіафайлами буде оброблено як окремі елементи бібліотеки.",
"NoteUploaderOnlyAudioFiles": "Якщо завантажувати лише аудіофайли, то кожен файл буде оброблено як окрему книгу.",
"NoteUploaderUnsupportedFiles": "Непідтримувані файли пропущено. Під час вибору або перетягування теки, файли, що знаходяться поза текою, пропускаються.",
"PlaceholderNewCollection": "Нова назва добірки",
"PlaceholderNewFolderPath": "Новий шлях до теки",
"PlaceholderNewPlaylist": "Нова назва списку",
"PlaceholderSearch": "Пошук...",
"PlaceholderSearchEpisode": "Шукати епізод...",
"ToastAccountUpdateFailed": "Не вдалося оновити профіль",
"ToastAccountUpdateSuccess": "Профіль оновлено",
"ToastAuthorImageRemoveFailed": "Не вдалося видалити зображення",
"ToastAuthorImageRemoveSuccess": "Фото автора видалено",
"ToastAuthorUpdateFailed": "Не вдалося оновити автора",
"ToastAuthorUpdateMerged": "Автора об'єднано",
"ToastAuthorUpdateSuccess": "Автора оновлено",
"ToastAuthorUpdateSuccessNoImageFound": "Автора оновлено (фото не знайдено)",
"ToastBackupCreateFailed": "Не вдалося створити резервну копію",
"ToastBackupCreateSuccess": "Резервну копію створено",
"ToastBackupDeleteFailed": "Не вдалося видалити резервну копію",
"ToastBackupDeleteSuccess": "Резервну копію видалено",
"ToastBackupRestoreFailed": "Не вдалося відновити резервну копію",
"ToastBackupUploadFailed": "Не вдалося завантажити резервну копію",
"ToastBackupUploadSuccess": "Резервну копію завантажено",
"ToastBatchUpdateFailed": "Не вдалося оновити обрані",
"ToastBatchUpdateSuccess": "Обрані успішно оновлено",
"ToastBookmarkCreateFailed": "Не вдалося створити закладку",
"ToastBookmarkCreateSuccess": "Закладку додано",
"ToastBookmarkRemoveFailed": "Не вдалося видалити закладку",
"ToastBookmarkRemoveSuccess": "Закладку видалено",
"ToastBookmarkUpdateFailed": "Не вдалося оновити закладку",
"ToastBookmarkUpdateSuccess": "Закладку оновлено",
"ToastChaptersHaveErrors": "Глави містять помилки",
"ToastChaptersMustHaveTitles": "Глави повинні мати назви",
"ToastCollectionItemsRemoveFailed": "Не вдалося видалити елемент(и) з добірки",
"ToastCollectionItemsRemoveSuccess": "Елемент(и) видалено з добірки",
"ToastCollectionRemoveFailed": "Не вдалося видалити добірку",
"ToastCollectionRemoveSuccess": "Добірку видалено",
"ToastCollectionUpdateFailed": "Не вдалося оновити добірку",
"ToastCollectionUpdateSuccess": "Добірку оновлено",
"ToastItemCoverUpdateFailed": "Не вдалося оновити обкладинку",
"ToastItemCoverUpdateSuccess": "Обкладинку елемента оновлено",
"ToastItemDetailsUpdateFailed": "Не вдалося оновити подробиці елемента",
"ToastItemDetailsUpdateSuccess": "Подробиці про елемент оновлено",
"ToastItemDetailsUpdateUnneeded": "Оновлення подробиць непотрібне",
"ToastItemMarkedAsFinishedFailed": "Не вдалося позначити як завершене",
"ToastItemMarkedAsFinishedSuccess": "Елемент позначено як завершений",
"ToastItemMarkedAsNotFinishedFailed": "Не вдалося позначити незавершеним",
"ToastItemMarkedAsNotFinishedSuccess": "Елемент позначено незавершеним",
"ToastLibraryCreateFailed": "Не вдалося створити бібліотеку",
"ToastLibraryCreateSuccess": "Бібліотеку \"{0}\" створено",
"ToastLibraryDeleteFailed": "Не вдалося видалити бібліотеку",
"ToastLibraryDeleteSuccess": "Бібліотеку видалено",
"ToastLibraryScanFailedToStart": "Не вдалося розпочати сканування",
"ToastLibraryScanStarted": "Почалося сканування бібліотеки",
"ToastLibraryUpdateFailed": "Не вдалося оновити бібліотеку",
"ToastLibraryUpdateSuccess": "Бібліотеку \"{0}\" оновлено",
"ToastPlaylistCreateFailed": "Не вдалося створити список",
"ToastPlaylistCreateSuccess": "Список відтворення створено",
"ToastPlaylistRemoveFailed": "Не вдалося видалити список",
"ToastPlaylistRemoveSuccess": "Список відтворення видалено",
"ToastPlaylistUpdateFailed": "Не вдалося оновити список",
"ToastPlaylistUpdateSuccess": "Список відтворення оновлено",
"ToastPodcastCreateFailed": "Не вдалося створити подкаст",
"ToastPodcastCreateSuccess": "Подкаст успішно створено",
"ToastRemoveItemFromCollectionFailed": "Не вдалося видалити елемент із добірки",
"ToastRemoveItemFromCollectionSuccess": "Елемент видалено з добірки",
"ToastRSSFeedCloseFailed": "Не вдалося закрити RSS-канал",
"ToastRSSFeedCloseSuccess": "RSS-канал закрито",
"ToastSendEbookToDeviceFailed": "Не вдалося надіслати електронну книгу на пристрій",
"ToastSendEbookToDeviceSuccess": "Електронну книгу надіслано на пристрій \"{0}\"",
"ToastSeriesUpdateFailed": "Не вдалося оновити серію",
"ToastSeriesUpdateSuccess": "Серію успішно оновлено",
"ToastSessionDeleteFailed": "Не вдалося видалити сесію",
"ToastSessionDeleteSuccess": "Сесію видалено",
"ToastSocketConnected": "Сокет під'єднано",
"ToastSocketDisconnected": "Сокет від'єднано",
"ToastSocketFailedToConnect": "Не вдалося під'єднатися до сокета",
"ToastUserDeleteFailed": "Не вдалося видалити користувача",
"ToastUserDeleteSuccess": "Користувача видалено"
}
+3 -2
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "Mỗi 6 giờ",
"LabelIntervalEveryDay": "Mỗi ngày",
"LabelIntervalEveryHour": "Mỗi giờ",
"LabelInvalidParts": "Phần không hợp lệ",
"LabelInvert": "Nghịch đảo",
"LabelItem": "Mục",
"LabelLanguage": "Ngôn ngữ",
@@ -357,7 +356,6 @@
"LabelMinute": "Phút",
"LabelMissing": "Thiếu",
"LabelMissingEbook": "Không có ebook",
"LabelMissingParts": "Các phần thiếu",
"LabelMissingSupplementaryEbook": "Không có ebook bổ sung",
"LabelMobileRedirectURIs": "URI chuyển hướng di động được cho phép",
"LabelMobileRedirectURIsDescription": "Đây là danh sách trắng các URI chuyển hướng hợp lệ cho ứng dụng di động. Mặc định là <code>audiobookshelf://oauth</code>, bạn có thể loại bỏ hoặc bổ sung thêm các URI cho tích hợp ứng dụng bên thứ ba. Sử dụng dấu hoa thị (<code>*</code>) như một mục duy nhất cho phép bất kỳ URI nào.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "Chưa bắt đầu",
"LabelNumberOfBooks": "Số lượng Sách",
"LabelNumberOfEpisodes": "# của Tập",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Mở RSS Feed",
"LabelOverwrite": "Ghi đè",
"LabelPassword": "Mật khẩu",
+29 -28
View File
@@ -32,8 +32,8 @@
"ButtonHide": "隐藏",
"ButtonHome": "首页",
"ButtonIssues": "问题",
"ButtonJumpBackward": "Jump Backward",
"ButtonJumpForward": "Jump Forward",
"ButtonJumpBackward": "向后跳转",
"ButtonJumpForward": "向前跳转",
"ButtonLatest": "最新",
"ButtonLibrary": "媒体库",
"ButtonLogout": "注销",
@@ -43,8 +43,8 @@
"ButtonMatchAllAuthors": "匹配所有作者",
"ButtonMatchBooks": "匹配图书",
"ButtonNevermind": "没有关系",
"ButtonNext": "Next",
"ButtonNextChapter": "Next Chapter",
"ButtonNext": "下一个",
"ButtonNextChapter": "下一章节",
"ButtonOk": "确定",
"ButtonOpenFeed": "打开源",
"ButtonOpenManager": "打开管理器",
@@ -52,8 +52,8 @@
"ButtonPlay": "播放",
"ButtonPlaying": "正在播放",
"ButtonPlaylists": "播放列表",
"ButtonPrevious": "Previous",
"ButtonPreviousChapter": "Previous Chapter",
"ButtonPrevious": "上一个",
"ButtonPreviousChapter": "上一章节",
"ButtonPurgeAllCache": "清理所有缓存",
"ButtonPurgeItemsCache": "清理项目缓存",
"ButtonPurgeMediaProgress": "清理媒体进度",
@@ -61,7 +61,7 @@
"ButtonQueueRemoveItem": "从队列中移除",
"ButtonQuickMatch": "快速匹配",
"ButtonRead": "读取",
"ButtonRefresh": "Refresh",
"ButtonRefresh": "刷新",
"ButtonRemove": "移除",
"ButtonRemoveAll": "移除所有",
"ButtonRemoveAllLibraryItems": "移除所有媒体库项目",
@@ -113,7 +113,7 @@
"HeaderCollectionItems": "收藏项目",
"HeaderCover": "封面",
"HeaderCurrentDownloads": "当前下载",
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
"HeaderCustomMetadataProviders": "自定义元数据提供者",
"HeaderDetails": "详情",
"HeaderDownloadQueue": "下载队列",
"HeaderEbookFiles": "电子书文件",
@@ -184,7 +184,7 @@
"HeaderUpdateDetails": "更新详情",
"HeaderUpdateLibrary": "更新媒体库",
"HeaderUsers": "用户",
"HeaderYearReview": "Year {0} in Review",
"HeaderYearReview": "{0} 年回顾",
"HeaderYourStats": "你的统计数据",
"LabelAbridged": "概要",
"LabelAccountType": "帐户类型",
@@ -294,9 +294,9 @@
"LabelFolders": "文件夹",
"LabelFontBold": "Bold",
"LabelFontFamily": "字体系列",
"LabelFontItalic": "Italic",
"LabelFontItalic": "斜体",
"LabelFontScale": "字体比例",
"LabelFontStrikethrough": "Strikethrough",
"LabelFontStrikethrough": "删除线",
"LabelFormat": "编码格式",
"LabelGenre": "流派",
"LabelGenres": "流派",
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "每 6 小时",
"LabelIntervalEveryDay": "每天",
"LabelIntervalEveryHour": "每小时",
"LabelInvalidParts": "无效部件",
"LabelInvert": "倒转",
"LabelItem": "项目",
"LabelLanguage": "语言",
@@ -356,9 +355,8 @@
"LabelMetaTags": "元标签",
"LabelMinute": "分钟",
"LabelMissing": "丢失",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "丢失的部分",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMissingEbook": "没有电子书",
"LabelMissingSupplementaryEbook": "没有补充电子书",
"LabelMobileRedirectURIs": "允许移动应用重定向 URI",
"LabelMobileRedirectURIsDescription": "这是移动应用程序的有效重定向 URI 白名单. 默认值为 <code>audiobookshelf://oauth</code>,您可以删除它或添加其他 URI 以进行第三方应用集成. 使用星号 (<code>*</code>) 作为唯一条目允许任何 URI.",
"LabelMore": "更多",
@@ -387,6 +385,9 @@
"LabelNotStarted": "未开始",
"LabelNumberOfBooks": "图书数量",
"LabelNumberOfEpisodes": "# 集",
"LabelOpenIDAdvancedPermsClaimDescription": "OpenID 声明的名称, 该声明包含应用程序内用户操作的高级权限, 该权限将应用于非管理员角色(<b>如果已配置</b>). 如果响应中缺少声明, 获取 ABS 的权限将被拒绝. 如果缺少单个选项, 它将被视为 <code>禁用</code>. 确保身份提供者的声明与预期结构匹配:",
"LabelOpenIDClaims": "将以下选项留空以禁用高级组和权限分配, 然后自动分配 'User' 组.",
"LabelOpenIDGroupClaimDescription": "OpenID 声明的名称, 该声明包含用户组的列表. 通常称为<code>组</code><b>如果已配置</b>, 应用程序将根据用户的组成员身份自动分配角色, 前提是这些组在声明中以不区分大小写的方式命名为 'Admin', 'User' 或 'Guest'. 声明应包含一个列表, 如果用户属于多个组, 则应用程序将分配与最高访问级别相对应的角色. 如果没有组匹配, 访问将被拒绝.",
"LabelOpenRSSFeed": "打开 RSS 源",
"LabelOverwrite": "覆盖",
"LabelPassword": "密码",
@@ -398,7 +399,7 @@
"LabelPermissionsDownload": "可以下载",
"LabelPermissionsUpdate": "可以更新",
"LabelPermissionsUpload": "可以上传",
"LabelPersonalYearReview": "Your Year in Review ({0})",
"LabelPersonalYearReview": "你的年度回顾 ({0})",
"LabelPhotoPathURL": "图片路径或 URL",
"LabelPlaylists": "播放列表",
"LabelPlayMethod": "播放方法",
@@ -425,7 +426,7 @@
"LabelRegion": "区域",
"LabelReleaseDate": "发布日期",
"LabelRemoveCover": "移除封面",
"LabelRowsPerPage": "Rows per page",
"LabelRowsPerPage": "每页行数",
"LabelRSSFeedCustomOwnerEmail": "自定义所有者电子邮件",
"LabelRSSFeedCustomOwnerName": "自定义所有者名称",
"LabelRSSFeedOpen": "打开 RSS 源",
@@ -438,13 +439,13 @@
"LabelSeason": "季",
"LabelSelectAllEpisodes": "选择所有剧集",
"LabelSelectEpisodesShowing": "选择正在播放的 {0} 剧集",
"LabelSelectUsers": "Select users",
"LabelSelectUsers": "选择用户",
"LabelSendEbookToDevice": "发送电子书到...",
"LabelSequence": "序列",
"LabelSeries": "系列",
"LabelSeriesName": "系列名称",
"LabelSeriesProgress": "系列进度",
"LabelServerYearReview": "Server Year in Review ({0})",
"LabelServerYearReview": "服务器年度回顾 ({0})",
"LabelSetEbookAsPrimary": "设置为主",
"LabelSetEbookAsSupplementary": "设置为补充",
"LabelSettingsAudiobooksOnly": "只有有声读物",
@@ -466,8 +467,8 @@
"LabelSettingsHideSingleBookSeriesHelp": "只有一本书的系列将从系列页面和主页书架中隐藏.",
"LabelSettingsHomePageBookshelfView": "首页使用书架视图",
"LabelSettingsLibraryBookshelfView": "媒体库使用书架视图",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Skip earlier books in Continue Series",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "The Continue Series home page shelf shows the first book not started in series that have at least one book finished and no books in progress. Enabling this setting will continue series from the furthest completed book instead of the first book not started.",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "跳过继续系列中的早期书籍",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "继续系列主页书架显示系列中未开始的第一本书, 该系列至少有一本书已完成且没有正在进行的书. 启用此设置将从最远完成的书开始系列, 而不是从第一本书开始.",
"LabelSettingsParseSubtitles": "解析副标题",
"LabelSettingsParseSubtitlesHelp": "从有声读物文件夹中提取副标题.<br>副标题必须用 \" - \" 分隔.<br>例: \"书名 - 这里是副标题\" 则显示副标题 \"这里是副标题\"",
"LabelSettingsPreferMatchedMetadata": "首选匹配的元数据",
@@ -513,10 +514,10 @@
"LabelTagsAccessibleToUser": "用户可访问的标签",
"LabelTagsNotAccessibleToUser": "用户无法访问标签",
"LabelTasks": "正在运行的任务",
"LabelTextEditorBulletedList": "Bulleted list",
"LabelTextEditorLink": "Link",
"LabelTextEditorNumberedList": "Numbered list",
"LabelTextEditorUnlink": "Unlink",
"LabelTextEditorBulletedList": "项目符号列表",
"LabelTextEditorLink": "链接",
"LabelTextEditorNumberedList": "编号列表",
"LabelTextEditorUnlink": "取消链接",
"LabelTheme": "主题",
"LabelThemeDark": "黑暗",
"LabelThemeLight": "明亮",
@@ -563,8 +564,8 @@
"LabelViewQueue": "查看播放列表",
"LabelVolume": "音量",
"LabelWeekdaysToRun": "工作日运行",
"LabelYearReviewHide": "Hide Year in Review",
"LabelYearReviewShow": "See Year in Review",
"LabelYearReviewHide": "隐藏年度回顾",
"LabelYearReviewShow": "查看年度回顾",
"LabelYourAudiobookDuration": "你的有声读物持续时间",
"LabelYourBookmarks": "你的书签",
"LabelYourPlaylists": "你的播放列表",
@@ -601,7 +602,7 @@
"MessageConfirmRemoveCollection": "你确定要移除收藏 \"{0}\"?",
"MessageConfirmRemoveEpisode": "你确定要移除剧集 \"{0}\"?",
"MessageConfirmRemoveEpisodes": "你确定要移除 {0} 剧集?",
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
"MessageConfirmRemoveListeningSessions": "你确定要移除 {0} 收听会话吗?",
"MessageConfirmRemoveNarrator": "你确定要删除演播者 \"{0}\"?",
"MessageConfirmRemovePlaylist": "你确定要移除播放列表 \"{0}\"?",
"MessageConfirmRenameGenre": "你确定要将所有项目流派 \"{0}\" 重命名到 \"{1}\"?",
+6 -3
View File
@@ -320,7 +320,6 @@
"LabelIntervalEvery6Hours": "每 6 小時",
"LabelIntervalEveryDay": "每天",
"LabelIntervalEveryHour": "每小時",
"LabelInvalidParts": "無效部件",
"LabelInvert": "倒轉",
"LabelItem": "項目",
"LabelLanguage": "語言",
@@ -357,7 +356,6 @@
"LabelMinute": "分鐘",
"LabelMissing": "丟失",
"LabelMissingEbook": "Has no ebook",
"LabelMissingParts": "丟失的部分",
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
"LabelMobileRedirectURIs": "允許移動應用重定向 URI",
"LabelMobileRedirectURIsDescription": "這是移動應用程序的有效重定向 URI 白名單. 預設值為 <code>audiobookshelf://oauth</code>,您可以刪除它或加入其他 URI 以進行第三方應用集成. 使用星號 (<code>*</code>) 作為唯一條目允許任何 URI.",
@@ -387,6 +385,9 @@
"LabelNotStarted": "未開始",
"LabelNumberOfBooks": "圖書數量",
"LabelNumberOfEpisodes": "# 集",
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "打開 RSS 源",
"LabelOverwrite": "覆蓋",
"LabelPassword": "密碼",
@@ -466,6 +467,8 @@
"LabelSettingsHideSingleBookSeriesHelp": "只有一本書的系列將從系列頁面和主頁書架中隱藏.",
"LabelSettingsHomePageBookshelfView": "首頁使用書架視圖",
"LabelSettingsLibraryBookshelfView": "媒體庫使用書架視圖",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Skip earlier books in Continue Series",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "The Continue Series home page shelf shows the first book not started in series that have at least one book finished and no books in progress. Enabling this setting will continue series from the furthest completed book instead of the first book not started.",
"LabelSettingsParseSubtitles": "解析副標題",
"LabelSettingsParseSubtitlesHelp": "從有聲書資料夾中提取副標題.<br>副標題必須用 \" - \" 分隔.<br>例: \"書名 - 這裡是副標題\" 則顯示副標題 \"這裡是副標題\"",
"LabelSettingsPreferMatchedMetadata": "首選匹配的元數據",
@@ -776,4 +779,4 @@
"ToastSocketFailedToConnect": "網路連接失敗",
"ToastUserDeleteFailed": "刪除使用者失敗",
"ToastUserDeleteSuccess": "使用者已刪除"
}
}
+2 -1
View File
@@ -86,7 +86,8 @@ module.exports = {
fontSize: {
xxs: '0.625rem',
'1.5xl': '1.375rem',
'2.5xl': '1.6875rem'
'2.5xl': '1.6875rem',
'4.5xl': '2.625rem'
},
zIndex: {
'50': 50,
+30
View File
@@ -0,0 +1,30 @@
# OpenAPI specification
This directory includes the OpenAPI spec for the ABS server.
The spec is made up of a number of individual `yaml` files located here and in the subfolders, with `root.yaml` being the file that references all of the others.
The files are organized to have the same hierarchy as the server source files.
The full spec is bundled into one file in `openapi.json`.
The spec is linted and bundled by the [`vacuum` tool](https://quobix.com/vacuum/).
The spec can also be tested with a real server using the [`wiretap` tool](https://pb33f.io/wiretap/).
Both of these tools are created by [pb33f](https://pb33f.io/).
### Bundling the spec
The command to bundle the spec into a `yaml` file is `vacuum bundle root.yaml openapi.yaml`.
The current version of `vacuum` cannot convert input `yaml` files to `json` files.
To convert the spec to `json`, you can use the `yq` tool or another tool.
The command to convert the spec using `yq` is `yq -p yaml -o json openapi.yaml > openapi.json`.
### Viewing report
To generate an HTML report, you can use `vacuum html-report [file]` to generate `report.html` and view the report in your browser.
### Putting it all together
The full command that I run to bundle the spec and generate the report is:
```
vacuum bundle root.yaml openapi.yaml && \
yq -p yaml -o json openapi.yaml > openapi.json && \
vacuum html-report openapi.json
```
+139
View File
@@ -0,0 +1,139 @@
components:
schemas:
authorUpdated:
description: Whether the author was updated without errors. Will not exist if author was merged.
type: boolean
nullable: true
parameters:
authorId:
name: id
in: path
description: Author ID
required: true
schema:
$ref: '../objects/entities/Author.yaml#/components/schemas/authorId'
authorInclude:
name: include
in: query
description: A comma separated list of what to include with the author. The options are `items` and `series`. `series` will only have an effect if `items` is included.
required: false
schema:
type: string
example: "items"
examples:
empty:
summary: Do not return library items
value: ""
itemOnly:
summary: Only return library items
value: "items"
itemsAndSeries:
summary: Return library items and series
value: "items,series"
authorLibraryId:
name: library
in: query
description: The ID of the library to to include filter included items from.
required: false
schema:
$ref: '../objects/Library.yaml#/components/schemas/libraryId'
asin:
name: asin
in: query
description: The Audible Identifier (ASIN).
required: false
schema:
$ref: '../objects/entities/Author.yaml#/components/schemas/authorAsin'
authorSearchName:
name: q
in: query
description: The name of the author to use for searching.
required: false
schema:
type: string
example: Terry Goodkind
authorName:
name: name
in: query
description: The new name of the author.
required: false
schema:
$ref: '../objects/entities/Author.yaml#/components/schemas/authorName'
authorDescription:
name: description
in: query
description: The new description of the author.
required: false
schema:
type: string
nullable: true
example: Terry Goodkind is a #1 New York Times Bestselling Author and creator of the critically acclaimed masterwork, The Sword of Truth. He has written 30+ major, bestselling novels, has been published in more than 20 languages world-wide, and has sold more than 26 Million books. The Sword of Truth is a revered literary tour de force, comprised of 17 volumes, borne from over 25 years of dedicated writing.
authorImagePath:
name: imagePath
in: query
description: The new absolute path for the author image.
required: false
schema:
type: string
nullable: true
example: /metadata/authors/aut_z3leimgybl7uf3y4ab.jpg
imageUrl:
name: url
in: query
description: The URL of the image to add to the server
required: true
schema:
type: string
format: uri
example: https://images-na.ssl-images-amazon.com/images/I/51NoQTm33OL.__01_SX120_CR0,0,120,120__.jpg
imageWidth:
name: width
in: query
description: The requested width of image in pixels.
schema:
type: integer
default: 400
example: 400
example: 400
imageHeight:
name: height
in: query
description: The requested height of image in pixels. If `null`, the height is scaled to maintain aspect ratio based on the requested width.
schema:
type: integer
nullable: true
default: null
example: 600
examples:
scaleHeight:
summary: Scale height with width
value: null
fixedHeight:
summary: Force height of image
value: 600
imageFormat:
name: format
in: query
description: The requested output format.
schema:
type: string
default: jpeg
example: webp
imageRaw:
name: raw
in: query
description: Return the raw image without scaling if true.
schema:
type: boolean
default: false
responses:
author404:
description: Author not found.
content:
text/html:
schema:
type: string
example: Not found
tags:
- name: Authors
description: Author endpoints
+21
View File
@@ -0,0 +1,21 @@
components:
schemas:
folderId:
type: string
description: The ID of the folder.
format: uuid
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
folder:
type: object
description: Folder used in library
properties:
id:
$ref: '#/components/schemas/folderId'
fullPath:
description: The path on the server for the folder. (Read Only)
type: string
example: /podcasts
libraryId:
$ref: './Library.yaml#/components/schemas/libraryId'
addedAt:
$ref: '../schemas.yaml#/components/schemas/addedAt'
+12
View File
@@ -0,0 +1,12 @@
components:
schemas:
oldLibraryId:
type: string
description: The ID of the libraries created on server version 2.2.23 and before.
format: "lib_[a-z0-9]{18}"
example: lib_o78uaoeuh78h6aoeif
libraryId:
type: string
description: The ID of the library.
format: uuid
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
+66
View File
@@ -0,0 +1,66 @@
components:
schemas:
oldLibraryItemId:
description: The ID of library items on server version 2.2.23 and before.
type: string
nullable: true
format: "li_[a-z0-9]{18}"
example: li_o78uaoeuh78h6aoeif
libraryItemId:
type: string
description: The ID of library items after 2.3.0.
format: uuid
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
libraryItemBase:
type: object
description: Base library item schema
properties:
id:
$ref: '#/components/schemas/libraryItemId'
oldLibraryItemId:
$ref: '#/components/schemas/oldLibraryItemId'
ino:
$ref: '../schemas.yaml#/components/schemas/inode'
libraryId:
$ref: './Library.yaml#/components/schemas/libraryId'
folderId:
$ref: './Folder.yaml#/components/schemas/folderId'
path:
description: The path of the library item on the server.
type: string
relPath:
description: The path, relative to the library folder, of the library item.
type: string
isFile:
description: Whether the library item is a single file in the root of the library folder.
type: boolean
mtimeMs:
description: The time (in ms since POSIX epoch) when the library item was last modified on disk.
type: integer
ctimeMs:
description: The time (in ms since POSIX epoch) when the library item status was changed on disk.
type: integer
birthtimeMs:
description: The time (in ms since POSIX epoch) when the library item was created on disk. Will be 0 if unknown.
type: integer
addedAt:
$ref: '../schemas.yaml#/components/schemas/addedAt'
updatedAt:
$ref: '../schemas.yaml#/components/schemas/updatedAt'
isMissing:
description: Whether the library item was scanned and no longer exists.
type: boolean
isInvalid:
description: Whether the library item was scanned and no longer has media files.
type: boolean
mediaType:
$ref: './mediaTypes/media.yaml#/components/schemas/mediaType'
libraryItemMinified:
type: object
description: A single item on the server, like a book or podcast. Minified media format.
allOf:
- $ref : '#/components/schemas/libraryItemBase'
- type: object
properties:
media:
$ref: './mediaTypes/media.yaml#/components/schemas/mediaMinified'
+104
View File
@@ -0,0 +1,104 @@
components:
schemas:
authorId:
type: string
description: The ID of the author.
format: uuid
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
authorAsin:
type: string
description: The Audible identifier (ASIN) of the author. Will be null if unknown. Not the Amazon identifier.
nullable: true
example: B000APZOQA
authorName:
description: The name of the author.
type: string
example: Terry Goodkind
authorSeries:
type: object
description: Series and the included library items that an author has written.
properties:
id:
$ref: './Series.yaml#/components/schemas/seriesId'
name:
$ref: './Series.yaml#/components/schemas/seriesName'
items:
description: The items in the series. Each library item's media's metadata will have a `series` attribute, a `Series Sequence`, which is the matching series.
type: array
items:
ref: '../LibraryItem.yaml#/components/schemas/libraryItemMinified'
author:
description: An author object which includes a description and image path.
type: object
properties:
id:
$ref: '#/components/schemas/authorId'
asin:
$ref: '#/components/schemas/authorAsin'
name:
$ref: '#/components/schemas/authorName'
description:
description: A description of the author. Will be null if there is none.
type: string
nullable: true
example: |
Terry Goodkind is a #1 New York Times Bestselling Author and creator of the critically acclaimed masterwork,
The Sword of Truth. He has written 30+ major, bestselling novels, has been published in more than 20
languages world-wide, and has sold more than 26 Million books. The Sword of Truth is a revered literary
tour de force, comprised of 17 volumes, borne from over 25 years of dedicated writing. Terry Goodkind's
brilliant books are character-driven stories, with a focus on the complexity of the human psyche. Goodkind
has an uncanny grasp for crafting compelling stories about people like you and me, trapped in terrifying
situations.
imagePath:
description: The absolute path for the author image located in the `metadata/` directory. Will be null if there is no image.
type: string
nullable: true
example: /metadata/authors/aut_bxxbyjiptmgb56yzoz.jpg
addedAt:
$ref: '../../schemas.yaml#/components/schemas/addedAt'
updatedAt:
$ref: '../../schemas.yaml#/components/schemas/updatedAt'
authorWithItems:
type: object
description: The author schema with an array of items they are associated with.
allOf:
- $ref: '#/components/schemas/author'
- type: object
properties:
libraryItems:
description: The items associated with the author
type: string
type: array
items:
$ref: '../LibraryItem.yaml#/components/schemas/libraryItemMinified'
authorWithSeries:
type: object
description: The author schema with an array of items and series they are associated with.
allOf:
- $ref: '#/components/schemas/authorWithItems'
- type: object
properties:
series:
description: The series associated with the author
type: array
items:
$ref: '#/components/schemas/authorSeries'
authorMinified:
type: object
description: Minified author object which only contains the author name and ID.
properties:
id:
$ref: '#/components/schemas/authorId'
name:
$ref: '#/components/schemas/authorName'
authorExpanded:
type: object
description: The author schema with the total number of books in the library.
allOf:
- $ref: '#/components/schemas/author'
- type: object
properties:
numBooks:
description: The number of books associated with the author in the library.
type: integer
example: 1
+11
View File
@@ -0,0 +1,11 @@
components:
schemas:
seriesId:
type: string
description: The ID of the series.
format: uuid
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
seriesName:
description: The name of the series.
type: string
example: Sword of Truth
+94
View File
@@ -0,0 +1,94 @@
components:
schemas:
audioFile:
type: object
description: An audio file for a book. Includes audio metadata and track numbers.
properties:
index:
description: The index of the audio file.
type: integer
example: 1
ino:
$ref: '../../schemas.yaml#/components/schemas/inode'
metadata:
$ref: '../metadata/FileMetadata.yaml#/components/schemas/fileMetadata'
addedAt:
$ref: '../../schemas.yaml#/components/schemas/addedAt'
updatedAt:
$ref: '../../schemas.yaml#/components/schemas/updatedAt'
trackNumFromMeta:
description: The track number of the audio file as pulled from the file's metadata. Will be null if unknown.
type: integer
nullable: true
example: 1
discNumFromMeta:
description: The disc number of the audio file as pulled from the file's metadata. Will be null if unknown.
type: string
nullable: true
trackNumFromFilename:
description: The track number of the audio file as determined from the file's name. Will be null if unknown.
type: integer
nullable: true
example: 1
discNumFromFilename:
description: The disc number of the audio file as determined from the file's name. Will be null if unknown.
type: string
nullable: true
manuallyVerified:
description: Whether the audio file has been manually verified by a user.
type: boolean
invalid:
description: Whether the audio file is missing from the server.
type: boolean
exclude:
description: Whether the audio file has been marked for exclusion.
type: boolean
error:
description: Any error with the audio file. Will be null if there is none.
type: string
nullable: true
format:
description: The format of the audio file.
type: string
example: MP2/3 (MPEG audio layer 2/3)
duration:
$ref: '#/components/schemas/durationSec'
bitRate:
description: The bit rate (in bit/s) of the audio file.
type: integer
example: 64000
language:
description: The language of the audio file.
type: string
nullable: true
codec:
description: The codec of the audio file.
type: string
example: mp3
timeBase:
description: The time base of the audio file.
type: string
example: 1/14112000
channels:
description: The number of channels the audio file has.
type: integer
example: 2
channelLayout:
description: The layout of the audio file's channels.
type: string
example: stereo
chapters:
description: If the audio file is part of an audiobook, the chapters the file contains.
type: array
items:
$ref: '../metadata/BookMetadata.yaml#/components/schemas/bookChapter'
embeddedCoverArt:
description: The type of embedded cover art in the audio file. Will be null if none exists.
type: string
nullable: true
metaTags:
$ref: '../metadata/AudioMetaTags.yaml#/components/schemas/audioMetaTags'
mimeType:
description: The MIME type of the audio file.
type: string
example: audio/mpeg
+70
View File
@@ -0,0 +1,70 @@
components:
schemas:
bookCoverPath:
description: The absolute path on the server of the cover file. Will be null if there is no cover.
type: string
nullable: true
example: /audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/cover.jpg
bookBase:
type: object
description: Base book schema
properties:
libraryItemId:
$ref: '../LibraryItem.yaml#/components/schemas/libraryItemId'
coverPath:
$ref: '#/components/schemas/bookCoverPath'
tags:
$ref: '../../schemas.yaml#/components/schemas/tags'
audioFiles:
type: array
items:
$ref: '#/components/schemas/audioFile'
chapters:
type: array
items:
$ref: '#/components/schemas/bookChapter'
missingParts:
description: Any parts missing from the book by track index.
type: array
items:
type: integer
ebookFile:
$ref: '#/components/schemas/ebookFile'
bookMinified:
type: object
description: Minified book schema. Does not depend on `bookBase` because there's pretty much no overlap.
properties:
metadata:
$ref: '../metadata/BookMetadata.yaml#/components/schemas/bookMetadataMinified'
coverPath:
$ref: '#/components/schemas/bookCoverPath'
tags:
$ref: '../../schemas.yaml#/components/schemas/tags'
numTracks:
description: The number of tracks the book's audio files have.
type: integer
example: 1
numAudioFiles:
description: The number of audio files the book has.
type: integer
example: 1
numChapters:
description: The number of chapters the book has.
type: integer
example: 1
numMissingParts:
description: The total number of missing parts the book has.
type: integer
example: 0
numInvalidAudioFiles:
description: The number of invalid audio files the book has.
type: integer
example: 0
duration:
$ref: '../../schemas.yaml#/components/schemas/durationSec'
size:
$ref: '../../schemas.yaml#/components/schemas/size'
ebookFormat:
description: The format of ebook of the book. Will be null if the book is an audiobook.
type: string
nullable: true
+10
View File
@@ -0,0 +1,10 @@
components:
schemas:
mediaType:
type: string
description: The type of media, will be book or podcast.
enum: [book, podcast]
mediaMinified:
description: The minified media of the library item.
oneOf:
- $ref: './Book.yaml#/components/schemas/bookMinified'
+103
View File
@@ -0,0 +1,103 @@
components:
schemas:
audioMetaTags:
description: ID3 metadata tags pulled from the audio file on import. Only non-null tags will be returned in requests.
type: object
properties:
tagAlbum:
type: string
nullable: true
example: SOT Bk01
tagArtist:
type: string
nullable: true
example: Terry Goodkind
tagGenre:
type: string
nullable: true
example: Audiobook Fantasy
tagTitle:
type: string
nullable: true
example: Wizards First Rule 01
tagSeries:
type: string
nullable: true
tagSeriesPart:
type: string
nullable: true
tagTrack:
type: string
nullable: true
example: 01/20
tagDisc:
type: string
nullable: true
tagSubtitle:
type: string
nullable: true
tagAlbumArtist:
type: string
nullable: true
example: Terry Goodkind
tagDate:
type: string
nullable: true
tagComposer:
type: string
nullable: true
example: Terry Goodkind
tagPublisher:
type: string
nullable: true
tagComment:
type: string
nullable: true
tagDescription:
type: string
nullable: true
tagEncoder:
type: string
nullable: true
tagEncodedBy:
type: string
nullable: true
tagIsbn:
type: string
nullable: true
tagLanguage:
type: string
nullable: true
tagASIN:
type: string
nullable: true
tagOverdriveMediaMarker:
type: string
nullable: true
tagOriginalYear:
type: string
nullable: true
tagReleaseCountry:
type: string
nullable: true
tagReleaseType:
type: string
nullable: true
tagReleaseStatus:
type: string
nullable: true
tagISRC:
type: string
nullable: true
tagMusicBrainzTrackId:
type: string
nullable: true
tagMusicBrainzAlbumId:
type: string
nullable: true
tagMusicBrainzAlbumArtistId:
type: string
nullable: true
tagMusicBrainzArtistId:
type: string
nullable: true
+126
View File
@@ -0,0 +1,126 @@
components:
schemas:
narrators:
description: The narrators of the audiobook.
type: array
items:
type: string
example: Sam Tsoutsouvas
bookMetadataBase:
type: object
description: The base book metadata object for minified, normal, and extended schemas to inherit from.
properties:
title:
description: The title of the book. Will be null if unknown.
type: string
nullable: true
example: Wizards First Rule
subtitle:
description: The subtitle of the book. Will be null if there is no subtitle.
type: string
nullable: true
genres:
description: The genres of the book.
type: array
items:
type: string
example: ["Fantasy", "Sci-Fi", "Nonfiction: History"]
publishedYear:
description: The year the book was published. Will be null if unknown.
type: string
nullable: true
example: '2008'
publishedDate:
description: The date the book was published. Will be null if unknown.
type: string
nullable: true
publisher:
description: The publisher of the book. Will be null if unknown.
type: string
nullable: true
example: Brilliance Audio
description:
description: A description for the book. Will be null if empty.
type: string
nullable: true
example: >-
The masterpiece that started Terry Goodkind's New York Times bestselling
epic Sword of Truth In the aftermath of the brutal murder of his father,
a mysterious woman, Kahlan Amnell, appears in Richard Cypher's forest
sanctuary seeking help...and more. His world, his very beliefs, are
shattered when ancient debts come due with thundering violence. In a
dark age it takes courage to live, and more than mere courage to
challenge those who hold dominion, Richard and Kahlan must take up that
challenge or become the next victims. Beyond awaits a bewitching land
where even the best of their hearts could betray them. Yet, Richard
fears nothing so much as what secrets his sword might reveal about his
own soul. Falling in love would destroy them - for reasons Richard can't
imagine and Kahlan dare not say. In their darkest hour, hunted
relentlessly, tormented by treachery and loss, Kahlan calls upon Richard
to reach beyond his sword - to invoke within himself something more
noble. Neither knows that the rules of battle have just changed...or
that their time has run out. Wizard's First Rule is the beginning. One
book. One Rule. Witness the birth of a legend.
isbn:
description: The ISBN of the book. Will be null if unknown.
type: string
nullable: true
asin:
description: The ASIN of the book. Will be null if unknown.
type: string
nullable: true
example: B002V0QK4C
language:
description: The language of the book. Will be null if unknown.
type: string
nullable: true
explicit:
description: Whether the book has been marked as explicit.
type: boolean
example: false
bookMetadataMinified:
type: object
description: The minified metadata for a book in the database.
allOf:
- $ref : '#/components/schemas/bookMetadataBase'
- type: object
properties:
titleIgnorePrefix:
description: The title of the book with any prefix moved to the end.
type: string
authorName:
description: The name of the book's author(s).
type: string
example: Terry Goodkind
authorNameLF:
description: The name of the book's author(s) with last names first.
type: string
example: Goodkind, Terry
narratorName:
description: The name of the audiobook's narrator(s).
type: string
example: Sam Tsoutsouvas
seriesName:
description: The name of the book's series.
type: string
example: Sword of Truth
bookChapter:
type: object
description: A book chapter. Includes the title and timestamps.
properties:
id:
description: The ID of the book chapter.
type: integer
example: 0
start:
description: When in the book (in seconds) the chapter starts.
type: integer
example: 0
end:
description: When in the book (in seconds) the chapter ends.
type: number
example: 6004.6675
title:
description: The title of the chapter.
type: string
example: Wizards First Rule 01 Chapter 1
+39
View File
@@ -0,0 +1,39 @@
components:
schemas:
fileMetadata:
type: object
description: The metadata for a file, including the path, size, and unix timestamps of the file.
nullable: true
properties:
filename:
description: The filename of the file.
type: string
example: Wizards First Rule 01.mp3
ext:
description: The file extension of the file.
type: string
example: .mp3
path:
description: The absolute path on the server of the file.
type: string
example: >-
/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry
Goodkind - SOT Bk01 - Wizards First Rule 01.mp3
relPath:
description: The path of the file, relative to the book's or podcast's folder.
type: string
example: Wizards First Rule 01.mp3
size:
$ref: '../../schemas.yaml#/components/schemas/size'
mtimeMs:
description: The time (in ms since POSIX epoch) when the file was last modified on disk.
type: integer
example: 1632223180278
ctimeMs:
description: The time (in ms since POSIX epoch) when the file status was changed on disk.
type: integer
example: 1645978261001
birthtimeMs:
description: The time (in ms since POSIX epoch) when the file was created on disk. Will be 0 if unknown.
type: integer
example: 0
BIN
View File
Binary file not shown.
+157
View File
@@ -0,0 +1,157 @@
openapi: 3.0.0
info:
title: Audiobookshelf API
version: 0.1.0
description: Audiobookshelf API with autogenerated OpenAPI doc
servers:
- url: http://localhost:3000
description: Development server
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
responses:
ok200:
description: OK
security:
- BearerAuth: []
paths:
/api/authors/{id}:
get:
operationId: getAuthorById
summary: Get a single author by ID on server
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorInclude'
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorLibraryId'
responses:
200:
description: getAuthorById OK
content:
application/json:
schema:
oneOf:
- $ref: './objects/entities/Author.yaml#/components/schemas/author'
- $ref: './objects/entities/Author.yaml#/components/schemas/authorWithItems'
- $ref: './objects/entities/Author.yaml#/components/schemas/authorWithSeries'
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
patch:
operationId: updateAuthorById
summary: Update a single author by ID on server. This endpoint will merge two authors if the new author name matches another author in the database.
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
- $ref: './controllers/AuthorController.yaml#/components/parameters/asin'
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorName'
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorDescription'
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorImagePath'
responses:
200:
description: updateAuthorById OK
content:
application/json:
schema:
allOf:
- $ref: './objects/entities/Author.yaml#/components/schemas/author'
- $ref: './controllers/AuthorController.yaml#/components/schemas/authorUpdated'
- type: object
properties:
merged:
description: Will only exist and be `true` if the author was merged with another author
type: boolean
nullable: true
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
delete:
operationId: deleteAuthorById
summary: Delete a single author by ID on server and remove author from all books.
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
responses:
200:
$ref: '#/components/responses/ok200'
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
/api/authors/{id}/image:
post:
operationId: setAuthorImageById
summary: Set an author image using a provided URL.
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
- $ref: './controllers/AuthorController.yaml#/components/parameters/imageUrl'
responses:
200:
description: setAuthorImageById OK
content:
application/json:
schema:
oneOf:
- $ref: './objects/entities/Author.yaml#/components/schemas/author'
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
delete:
operationId: deleteAuthorImageById
summary: Delete an author image from the server and remove the image from the database.
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
responses:
200:
$ref: '#/components/responses/ok200'
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
patch:
operationId: getAuthorImageById
summary: Return the author image by author ID.
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
- $ref: './controllers/AuthorController.yaml#/components/parameters/imageWidth'
- $ref: './controllers/AuthorController.yaml#/components/parameters/imageHeight'
- $ref: './controllers/AuthorController.yaml#/components/parameters/imageFormat'
- $ref: './controllers/AuthorController.yaml#/components/parameters/imageRaw'
responses:
200:
description: getAuthorImageById OK
content:
image/*:
schema:
type: string
format: binary
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
/api/authors/{id}/match:
post:
operationId: matchAuthorById
summary: Match the author against Audible using quick match. Quick match updates the author's description and image (if no image already existed) with information from audible. Either `asin` or `q` must be provided, with `asin` taking priority if both are provided.
tags:
- Authors
parameters:
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorId'
- $ref: './controllers/AuthorController.yaml#/components/parameters/asin'
- $ref: './controllers/AuthorController.yaml#/components/parameters/authorSearchName'
responses:
200:
description: matchAuthorById OK
content:
application/json:
schema:
allOf:
- $ref: './objects/entities/Author.yaml#/components/schemas/author'
- $ref: './controllers/AuthorController.yaml#/components/schemas/authorUpdated'
404:
$ref: './controllers/AuthorController.yaml#/components/responses/author404'
tags:
- name: Authors
description: Author endpoints
+33
View File
@@ -0,0 +1,33 @@
components:
schemas:
addedAt:
type: integer
description: The time (in ms since POSIX epoch) when added to the server.
example: 1633522963509
createdAt:
type: integer
description: The time (in ms since POSIX epoch) when was created.
example: 1633522963509
updatedAt:
type: integer
description: The time (in ms since POSIX epoch) when last updated.
example: 1633522963509
size:
description: The total size (in bytes) of the item or file.
type: integer
example: 268824228
durationSec:
description: The total length (in seconds) of the item or file.
type: number
example: 33854.905
tags:
description: Tags applied to items.
type: array
items:
type: string
example: ["To Be Read", "Genre: Nonfiction"]
inode:
description: The inode of the item in the file system.
type: string
format: "[0-9]*"
example: '649644248522215260'
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf",
"version": "2.8.1",
"version": "2.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf",
"version": "2.8.1",
"version": "2.9.0",
"license": "GPL-3.0",
"dependencies": {
"axios": "^0.27.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
"version": "2.8.1",
"version": "2.9.0",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
+2
View File
@@ -336,6 +336,8 @@ Need to do one of following:
This application is built using [NodeJs](https://nodejs.org/).
Information on helping with translations of the web client [here](https://www.audiobookshelf.org/faq#how-do-i-help-with-translations).
### Dev Container Setup
The easiest way to begin developing this project is to use a dev container. An introduction to dev containers in VSCode can be found [here](https://code.visualstudio.com/docs/devcontainers/containers).
+192 -54
View File
@@ -89,7 +89,8 @@ class Auth {
}).Client
const openIdClient = new openIdIssuerClient({
client_id: global.ServerSettings.authOpenIDClientID,
client_secret: global.ServerSettings.authOpenIDClientSecret
client_secret: global.ServerSettings.authOpenIDClientSecret,
id_token_signed_response_alg: global.ServerSettings.authOpenIDTokenSigningAlgorithm
})
passport.use('openid-client', new OpenIDClient.Strategy({
client: openIdClient,
@@ -98,71 +99,198 @@ class Auth {
scope: 'openid profile email'
}
}, async (tokenset, userinfo, done) => {
Logger.debug(`[Auth] openid callback userinfo=`, userinfo)
try {
Logger.debug(`[Auth] openid callback userinfo=`, JSON.stringify(userinfo, null, 2))
let failureMessage = 'Unauthorized'
if (!userinfo.sub) {
Logger.error(`[Auth] openid callback invalid userinfo, no sub`)
return done(null, null, failureMessage)
if (!userinfo.sub) {
throw new Error('Invalid userinfo, no sub')
}
if (!this.validateGroupClaim(userinfo)) {
throw new Error(`Group claim ${Database.serverSettings.authOpenIDGroupClaim} not found or empty in userinfo`)
}
let user = await this.findOrCreateUser(userinfo)
if (!user?.isActive) {
throw new Error('User not active or not found')
}
await this.setUserGroup(user, userinfo)
await this.updateUserPermissions(user, userinfo)
// We also have to save the id_token for later (used for logout) because we cannot set cookies here
user.openid_id_token = tokenset.id_token
return done(null, user)
} catch (error) {
Logger.error(`[Auth] openid callback error: ${error?.message}\n${error?.stack}`)
return done(null, null, 'Unauthorized')
}
}))
}
// First check for matching user by sub
let user = await Database.userModel.getUserByOpenIDSub(userinfo.sub)
if (!user) {
// Optionally match existing by email or username based on server setting "authOpenIDMatchExistingBy"
if (Database.serverSettings.authOpenIDMatchExistingBy === 'email' && userinfo.email && userinfo.email_verified) {
/**
* Finds an existing user by OpenID subject identifier, or by email/username based on server settings,
* or creates a new user if configured to do so.
*/
async findOrCreateUser(userinfo) {
let user = await Database.userModel.getUserByOpenIDSub(userinfo.sub)
// Matched by sub
if (user) {
Logger.debug(`[Auth] openid: User found by sub`)
return user
}
// Match existing user by email
if (Database.serverSettings.authOpenIDMatchExistingBy === 'email') {
if (userinfo.email) {
// Only disallow when email_verified explicitly set to false (allow both if not set or true)
if (userinfo.email_verified === false) {
Logger.warn(`[Auth] openid: User not found and email "${userinfo.email}" is not verified`)
return null
} else {
Logger.info(`[Auth] openid: User not found, checking existing with email "${userinfo.email}"`)
user = await Database.userModel.getUserByEmail(userinfo.email)
// Check that user is not already matched
if (user?.authOpenIDSub) {
Logger.warn(`[Auth] openid: User found with email "${userinfo.email}" but is already matched with sub "${user.authOpenIDSub}"`)
// TODO: Message isn't actually returned to the user yet. Need to override the passport authenticated callback
failureMessage = 'A matching user was found but is already matched with another user from your auth provider'
user = null
}
} else if (Database.serverSettings.authOpenIDMatchExistingBy === 'username' && userinfo.preferred_username) {
Logger.info(`[Auth] openid: User not found, checking existing with username "${userinfo.preferred_username}"`)
user = await Database.userModel.getUserByUsername(userinfo.preferred_username)
// Check that user is not already matched
if (user?.authOpenIDSub) {
Logger.warn(`[Auth] openid: User found with username "${userinfo.preferred_username}" but is already matched with sub "${user.authOpenIDSub}"`)
// TODO: Message isn't actually returned to the user yet. Need to override the passport authenticated callback
failureMessage = 'A matching user was found but is already matched with another user from your auth provider'
user = null
return null // User is linked to a different OpenID subject; do not proceed.
}
}
} else {
Logger.warn(`[Auth] openid: User not found and no email in userinfo`)
// We deny login, because if the admin whishes to match email, it makes sense to require it
return null
}
}
// Match existing user by username
else if (Database.serverSettings.authOpenIDMatchExistingBy === 'username') {
let username
// If existing user was matched and isActive then save sub to user
if (user?.isActive) {
Logger.info(`[Auth] openid: New user found matching existing user "${user.username}"`)
user.authOpenIDSub = userinfo.sub
await Database.userModel.updateFromOld(user)
} else if (user && !user.isActive) {
Logger.warn(`[Auth] openid: New user found matching existing user "${user.username}" but that user is deactivated`)
}
if (userinfo.preferred_username) {
Logger.info(`[Auth] openid: User not found, checking existing with userinfo.preferred_username "${userinfo.preferred_username}"`)
username = userinfo.preferred_username
} else if (userinfo.username) {
Logger.info(`[Auth] openid: User not found, checking existing with userinfo.username "${userinfo.username}"`)
username = userinfo.username
} else {
Logger.warn(`[Auth] openid: User not found and neither preferred_username nor username in userinfo`)
return null
}
// Optionally auto register the user
if (!user && Database.serverSettings.authOpenIDAutoRegister) {
Logger.info(`[Auth] openid: Auto-registering user with sub "${userinfo.sub}"`, userinfo)
user = await Database.userModel.createUserFromOpenIdUserInfo(userinfo, this)
user = await Database.userModel.getUserByUsername(username)
if (user?.authOpenIDSub) {
Logger.warn(`[Auth] openid: User found with username "${username}" but is already matched with sub "${user.authOpenIDSub}"`)
return null // User is linked to a different OpenID subject; do not proceed.
}
}
// Found existing user via email or username
if (user) {
if (!user.isActive) {
Logger.warn(`[Auth] openid: User found but is not active`)
return null
}
user.authOpenIDSub = userinfo.sub
await Database.userModel.updateFromOld(user)
Logger.debug(`[Auth] openid: User found by email/username`)
return user
}
// If no existing user was matched, auto-register if configured
if (Database.serverSettings.authOpenIDAutoRegister) {
Logger.info(`[Auth] openid: Auto-registering user with sub "${userinfo.sub}"`, userinfo)
user = await Database.userModel.createUserFromOpenIdUserInfo(userinfo, this)
return user
}
Logger.warn(`[Auth] openid: User not found and auto-register is disabled`)
return null
}
/**
* Validates the presence and content of the group claim in userinfo.
*/
validateGroupClaim(userinfo) {
const groupClaimName = Database.serverSettings.authOpenIDGroupClaim
if (!groupClaimName) // Allow no group claim when configured like this
return true
// If configured it must exist in userinfo
if (!userinfo[groupClaimName]) {
return false
}
return true
}
/**
* Sets the user group based on group claim in userinfo.
*
* @param {import('./objects/user/User')} user
* @param {Object} userinfo
*/
async setUserGroup(user, userinfo) {
const groupClaimName = Database.serverSettings.authOpenIDGroupClaim
if (!groupClaimName) // No group claim configured, don't set anything
return
if (!userinfo[groupClaimName])
throw new Error(`Group claim ${groupClaimName} not found in userinfo`)
const groupsList = userinfo[groupClaimName].map(group => group.toLowerCase())
const rolesInOrderOfPriority = ['admin', 'user', 'guest']
let userType = rolesInOrderOfPriority.find(role => groupsList.includes(role))
if (userType) {
if (user.type === 'root') {
// Check OpenID Group
if (userType !== 'admin') {
throw new Error(`Root user "${user.username}" cannot be downgraded to ${userType}. Denying login.`)
} else {
// If root user is logging in via OpenID, we will not change the type
return
}
}
if (!user?.isActive) {
if (user && !user.isActive) {
failureMessage = 'Unauthorized'
}
// deny login
done(null, null, failureMessage)
return
if (user.type !== userType) {
Logger.info(`[Auth] openid callback: Updating user "${user.username}" type to "${userType}" from "${user.type}"`)
user.type = userType
await Database.userModel.updateFromOld(user)
}
} else {
throw new Error(`No valid group found in userinfo: ${JSON.stringify(userinfo[groupClaimName], null, 2)}`)
}
}
// We also have to save the id_token for later (used for logout) because we cannot set cookies here
user.openid_id_token = tokenset.id_token
/**
* Updates user permissions based on the advanced permissions claim.
*
* @param {import('./objects/user/User')} user
* @param {Object} userinfo
*/
async updateUserPermissions(user, userinfo) {
const absPermissionsClaim = Database.serverSettings.authOpenIDAdvancedPermsClaim
if (!absPermissionsClaim) // No advanced permissions claim configured, don't set anything
return
// permit login
return done(null, user)
}))
if (user.type === 'admin' || user.type === 'root')
return
const absPermissions = userinfo[absPermissionsClaim]
if (!absPermissions)
throw new Error(`Advanced permissions claim ${absPermissionsClaim} not found in userinfo`)
if (user.updatePermissionsFromExternalJSON(absPermissions)) {
Logger.info(`[Auth] openid callback: Updating advanced perms for user "${user.username}" using "${JSON.stringify(absPermissions)}"`)
await Database.userModel.updateFromOld(user)
}
}
/**
@@ -334,10 +462,19 @@ class Auth {
sso_redirect_uri: oidcStrategy._params.redirect_uri // Save the redirect_uri (for the SSO Provider) for the callback
}
var scope = 'openid profile email'
if (global.ServerSettings.authOpenIDGroupClaim) {
scope += ' ' + global.ServerSettings.authOpenIDGroupClaim
}
if (global.ServerSettings.authOpenIDAdvancedPermsClaim) {
scope += ' ' + global.ServerSettings.authOpenIDAdvancedPermsClaim
}
const authorizationUrl = client.authorizationUrl({
...oidcStrategy._params,
state: state,
response_type: 'code',
scope: scope,
code_challenge,
code_challenge_method
})
@@ -346,7 +483,7 @@ class Auth {
res.redirect(authorizationUrl)
} catch (error) {
Logger.error(`[Auth] Error in /auth/openid route: ${error}`)
Logger.error(`[Auth] Error in /auth/openid route: ${error}\n${error?.stack}`)
res.status(500).send('Internal Server Error')
}
@@ -402,7 +539,7 @@ class Auth {
// Redirect to the overwrite URI saved in the map
res.redirect(redirectUri)
} catch (error) {
Logger.error(`[Auth] Error in /auth/openid/mobile-redirect route: ${error}`)
Logger.error(`[Auth] Error in /auth/openid/mobile-redirect route: ${error}\n${error?.stack}`)
res.status(500).send('Internal Server Error')
}
})
@@ -424,12 +561,12 @@ class Auth {
}
function handleAuthError(isMobile, errorCode, errorMessage, logMessage, response) {
Logger.error(logMessage)
Logger.error(JSON.stringify(logMessage, null, 2))
if (response) {
// Depending on the error, it can also have a body
// We also log the request header the passport plugin sents for the URL
const header = response.req?._header.replace(/Authorization: [^\r\n]*/i, 'Authorization: REDACTED')
Logger.debug(header + '\n' + response.body?.toString() + '\n' + JSON.stringify(response.body, null, 2))
Logger.debug(header + '\n' + JSON.stringify(response.body, null, 2))
}
if (isMobile) {
@@ -514,7 +651,8 @@ class Auth {
token_endpoint: data.token_endpoint,
userinfo_endpoint: data.userinfo_endpoint,
end_session_endpoint: data.end_session_endpoint,
jwks_uri: data.jwks_uri
jwks_uri: data.jwks_uri,
id_token_signing_alg_values_supported: data.id_token_signing_alg_values_supported
})
}).catch((error) => {
Logger.error(`[Auth] Failed to get openid configuration at "${configUrl}"`, error)
+1 -1
View File
@@ -93,7 +93,7 @@ class Logger {
// Save log to file
if (level >= this.logLevel) {
await this.logManager.logToFile(logObj)
await this.logManager?.logToFile(logObj)
}
}
+21 -4
View File
@@ -103,15 +103,28 @@ class FolderWatcher extends EventEmitter {
this.buildLibraryWatcher(library)
}
/**
*
* @param {import('./objects/Library')} library
*/
updateLibrary(library) {
if (this.disabled || library.settings.disableWatcher) return
var libwatcher = this.libraryWatchers.find(lib => lib.id === library.id)
if (this.disabled) return
const libwatcher = this.libraryWatchers.find(lib => lib.id === library.id)
if (libwatcher) {
// Library watcher was disabled
if (library.settings.disableWatcher) {
Logger.info(`[Watcher] updateLibrary: Library "${library.name}" watcher disabled`)
libwatcher.watcher.close()
this.libraryWatchers = this.libraryWatchers.filter(lw => lw.id !== libwatcher.id)
return
}
libwatcher.name = library.name
// If any folder paths were added or removed then re-init watcher
var pathsToAdd = library.folderPaths.filter(path => !libwatcher.paths.includes(path))
var pathsRemoved = libwatcher.paths.filter(path => !library.folderPaths.includes(path))
const pathsToAdd = library.folderPaths.filter(path => !libwatcher.paths.includes(path))
const pathsRemoved = libwatcher.paths.filter(path => !library.folderPaths.includes(path))
if (pathsToAdd.length || pathsRemoved.length) {
Logger.info(`[Watcher] Re-Initializing watcher for "${library.name}".`)
@@ -119,6 +132,10 @@ class FolderWatcher extends EventEmitter {
this.libraryWatchers = this.libraryWatchers.filter(lw => lw.id !== libwatcher.id)
this.buildLibraryWatcher(library)
}
} else if (!library.settings.disableWatcher) {
// Library watcher was enabled
Logger.info(`[Watcher] updateLibrary: Library "${library.name}" watcher enabled - initializing`)
this.buildLibraryWatcher(library)
}
}
+7
View File
@@ -128,7 +128,14 @@ class LibraryController {
res.json(libraryDownloadQueueDetails)
}
/**
* PATCH: /api/libraries/:id
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async update(req, res) {
/** @type {import('../objects/Library')} */
const library = req.library
// Validate that the custom provider exists if given any
+8 -4
View File
@@ -117,16 +117,20 @@ class LibraryItemController {
zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res)
}
//
// PATCH: will create new authors & series if in payload
//
/**
* PATCH: /items/:id/media
* Update media for a library item. Will create new authors & series when necessary
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async updateMedia(req, res) {
const libraryItem = req.libraryItem
const mediaPayload = req.body
if (mediaPayload.url) {
await LibraryItemController.prototype.uploadCover.bind(this)(req, res, false)
if (res.writableEnded) return
if (res.writableEnded || res.headersSent) return
}
// Book specific
+5 -1
View File
@@ -284,7 +284,7 @@ class MiscController {
}
res.json({
tags: tags
tags: tags.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
})
}
@@ -329,6 +329,7 @@ class MiscController {
await libraryItem.media.update({
tags: libraryItem.media.tags
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
@@ -370,6 +371,7 @@ class MiscController {
await libraryItem.media.update({
tags: libraryItem.media.tags
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
@@ -462,6 +464,7 @@ class MiscController {
await libraryItem.media.update({
genres: libraryItem.media.genres
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
@@ -503,6 +506,7 @@ class MiscController {
await libraryItem.media.update({
genres: libraryItem.media.genres
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
+1 -1
View File
@@ -158,7 +158,7 @@ class BookFinder {
* @returns {Promise<Object[]>}
*/
async getCustomProviderResults(title, author, isbn, providerSlug) {
const books = await this.customProviderAdapter.search(title, author, providerSlug, 'book')
const books = await this.customProviderAdapter.search(title, author, isbn, providerSlug, 'book')
if (this.verbose) Logger.debug(`Custom provider '${providerSlug}' Search Results: ${books.length || 0}`)
return books
+7 -2
View File
@@ -144,8 +144,13 @@ class PlaybackSessionManager {
session.currentTime = sessionJson.currentTime
session.timeListening = sessionJson.timeListening
session.updatedAt = sessionJson.updatedAt
session.date = date.format(new Date(), 'YYYY-MM-DD')
session.dayOfWeek = date.format(new Date(), 'dddd')
let jsDate = new Date(sessionJson.updatedAt)
if (isNaN(jsDate)) {
jsDate = new Date()
}
session.date = date.format(jsDate, 'YYYY-MM-DD')
session.dayOfWeek = date.format(jsDate, 'dddd')
Logger.debug(`[PlaybackSessionManager] Updated session for "${session.displayTitle}" (${session.id})`)
await Database.updatePlaybackSession(session)
+1 -1
View File
@@ -7,7 +7,7 @@ const Logger = require('../Logger')
* @property {string} ebookFormat
* @property {number} addedAt
* @property {number} updatedAt
* @property {{filename:string, ext:string, path:string, relPath:string, size:number, mtimeMs:number, ctimeMs:number, birthtimeMs:number}} metadata
* @property {{filename:string, ext:string, path:string, relPath:strFing, size:number, mtimeMs:number, ctimeMs:number, birthtimeMs:number}} metadata
*/
/**
+151 -4
View File
@@ -1,8 +1,12 @@
const { DataTypes, Model, WhereOptions } = require('sequelize')
const Path = require('path')
const { DataTypes, Model } = require('sequelize')
const fsExtra = require('../libs/fsExtra')
const Logger = require('../Logger')
const oldLibraryItem = require('../objects/LibraryItem')
const libraryFilters = require('../utils/queries/libraryFilters')
const { areEquivalent } = require('../utils/index')
const { filePathToPOSIX, getFileTimestampsWithIno } = require('../utils/fileUtils')
const LibraryFile = require('../objects/files/LibraryFile')
const Book = require('./Book')
const Podcast = require('./Podcast')
@@ -116,7 +120,7 @@ class LibraryItem extends Model {
/**
* Currently unused because this is too slow and uses too much mem
* @param {[WhereOptions]} where
* @param {import('sequelize').WhereOptions} [where]
* @returns {Array<objects.LibraryItem>} old library items
*/
static async getAllOldLibraryItems(where = null) {
@@ -773,12 +777,14 @@ class LibraryItem extends Model {
/**
*
* @param {WhereOptions} where
* @param {import('sequelize').WhereOptions} where
* @param {import('sequelize').BindOrReplacements} replacements
* @returns {Object} oldLibraryItem
*/
static async findOneOld(where) {
static async findOneOld(where, replacements = {}) {
const libraryItem = await this.findOne({
where,
replacements,
include: [
{
model: this.sequelize.models.book,
@@ -826,6 +832,147 @@ class LibraryItem extends Model {
return this[mixinMethodName](options)
}
/**
*
* @returns {Promise<Book|Podcast>}
*/
getMediaExpanded() {
if (this.mediaType === 'podcast') {
return this.getMedia({
include: [
{
model: this.sequelize.models.podcastEpisode
}
]
})
} else {
return this.getMedia({
include: [
{
model: this.sequelize.models.author,
through: {
attributes: []
}
},
{
model: this.sequelize.models.series,
through: {
attributes: ['sequence']
}
}
],
order: [
[this.sequelize.models.author, this.sequelize.models.bookAuthor, 'createdAt', 'ASC'],
[this.sequelize.models.series, 'bookSeries', 'createdAt', 'ASC']
]
})
}
}
/**
*
* @returns {Promise}
*/
async saveMetadataFile() {
let metadataPath = Path.join(global.MetadataPath, 'items', this.id)
let storeMetadataWithItem = global.ServerSettings.storeMetadataWithItem
if (storeMetadataWithItem && !this.isFile) {
metadataPath = this.path
} else {
// Make sure metadata book dir exists
storeMetadataWithItem = false
await fsExtra.ensureDir(metadataPath)
}
const metadataFilePath = Path.join(metadataPath, `metadata.${global.ServerSettings.metadataFileFormat}`)
// Expanded with series, authors, podcastEpisodes
const mediaExpanded = this.media || await this.getMediaExpanded()
let jsonObject = {}
if (this.mediaType === 'book') {
jsonObject = {
tags: mediaExpanded.tags || [],
chapters: mediaExpanded.chapters?.map(c => ({ ...c })) || [],
title: mediaExpanded.title,
subtitle: mediaExpanded.subtitle,
authors: mediaExpanded.authors.map(a => a.name),
narrators: mediaExpanded.narrators,
series: mediaExpanded.series.map(se => {
const sequence = se.bookSeries?.sequence || ''
if (!sequence) return se.name
return `${se.name} #${sequence}`
}),
genres: mediaExpanded.genres || [],
publishedYear: mediaExpanded.publishedYear,
publishedDate: mediaExpanded.publishedDate,
publisher: mediaExpanded.publisher,
description: mediaExpanded.description,
isbn: mediaExpanded.isbn,
asin: mediaExpanded.asin,
language: mediaExpanded.language,
explicit: !!mediaExpanded.explicit,
abridged: !!mediaExpanded.abridged
}
} else {
jsonObject = {
tags: mediaExpanded.tags || [],
title: mediaExpanded.title,
author: mediaExpanded.author,
description: mediaExpanded.description,
releaseDate: mediaExpanded.releaseDate,
genres: mediaExpanded.genres || [],
feedURL: mediaExpanded.feedURL,
imageURL: mediaExpanded.imageURL,
itunesPageURL: mediaExpanded.itunesPageURL,
itunesId: mediaExpanded.itunesId,
itunesArtistId: mediaExpanded.itunesArtistId,
asin: mediaExpanded.asin,
language: mediaExpanded.language,
explicit: !!mediaExpanded.explicit,
podcastType: mediaExpanded.podcastType
}
}
return fsExtra.writeFile(metadataFilePath, JSON.stringify(jsonObject, null, 2)).then(async () => {
// Add metadata.json to libraryFiles array if it is new
let metadataLibraryFile = this.libraryFiles.find(lf => lf.metadata.path === filePathToPOSIX(metadataFilePath))
if (storeMetadataWithItem) {
if (!metadataLibraryFile) {
const newLibraryFile = new LibraryFile()
await newLibraryFile.setDataFromPath(metadataFilePath, `metadata.json`)
metadataLibraryFile = newLibraryFile.toJSON()
this.libraryFiles.push(metadataLibraryFile)
} else {
const fileTimestamps = await getFileTimestampsWithIno(metadataFilePath)
if (fileTimestamps) {
metadataLibraryFile.metadata.mtimeMs = fileTimestamps.mtimeMs
metadataLibraryFile.metadata.ctimeMs = fileTimestamps.ctimeMs
metadataLibraryFile.metadata.size = fileTimestamps.size
metadataLibraryFile.ino = fileTimestamps.ino
}
}
const libraryItemDirTimestamps = await getFileTimestampsWithIno(this.path)
if (libraryItemDirTimestamps) {
this.mtime = libraryItemDirTimestamps.mtimeMs
this.ctime = libraryItemDirTimestamps.ctimeMs
let size = 0
this.libraryFiles.forEach((lf) => size += (!isNaN(lf.metadata.size) ? Number(lf.metadata.size) : 0))
this.size = size
await this.save()
}
}
Logger.debug(`Success saving abmetadata to "${metadataFilePath}"`)
return metadataLibraryFile
}).catch((error) => {
Logger.error(`Failed to save json file at "${metadataFilePath}"`, error)
return null
})
}
/**
* Initialize model
* @param {import('../Database').sequelize} sequelize

Some files were not shown because too many files have changed in this diff Show More