mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-05 18:22:44 +02:00
Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 166454ef43 | |||
| d5c854d606 | |||
| eace46bf55 | |||
| b9ffce166e | |||
| 9713e94aed | |||
| d71bc89c9d | |||
| a2b2a2d060 | |||
| 88f9533b37 | |||
| 630ece82ad | |||
| 5777184cae | |||
| a76da14fb0 | |||
| 0c612b4836 | |||
| a1af672c7c | |||
| 5fcd23409a | |||
| 99f0799a11 | |||
| 316aeba1b0 | |||
| bfd4a378f3 | |||
| 521db90ae0 | |||
| d02fc2debe | |||
| e6c21c5be1 | |||
| 91248b496e | |||
| f7ae7783bd | |||
| ae395497a5 | |||
| 8826d3af62 | |||
| 65153fae9d | |||
| d4c1bc5dfc | |||
| d6f13513ae | |||
| 2584c3b432 | |||
| b54421412d | |||
| e2451a3281 | |||
| dbf4bd5c3d | |||
| 2a722ab163 | |||
| c83399c7b5 | |||
| a814e45150 | |||
| 29e9216bb1 | |||
| 94d1732b0d | |||
| 7610084627 | |||
| d840905a97 | |||
| 7b1b448795 | |||
| 77559d29bb | |||
| c14f9accaf | |||
| 76a1f48c62 | |||
| ae0a9bcf86 | |||
| 9e44fe5524 | |||
| 727dad7e19 | |||
| 0c2de91097 | |||
| 450fa45360 | |||
| e0dddae2c2 | |||
| daa9fccc14 | |||
| ad45dadc15 | |||
| 0e8148001e | |||
| fa71f9db2e | |||
| 0d9d2fa4be | |||
| c34e9cde05 | |||
| b934a755b5 | |||
| a5772f6b66 | |||
| 153f149d58 | |||
| e50b06183e | |||
| 305689d513 | |||
| 4dd140585d | |||
| cd60d0219f | |||
| 8ec18e8d7b | |||
| 15545654ea | |||
| 8a0fab2b20 | |||
| 6e8c6aa740 | |||
| 5005aabe5e | |||
| abc2d28617 | |||
| 7569a14510 | |||
| b52341dbcf | |||
| b4eed3bad2 | |||
| 4fe672f09d | |||
| 49af7eb7b0 | |||
| c93c863d82 | |||
| 763bb1b829 | |||
| 79d32274aa | |||
| 987842ed04 | |||
| d2b006b909 | |||
| f4a19e48ad | |||
| 38f12f4795 | |||
| 7a4f4b1586 | |||
| 20ec54e085 | |||
| 655bebfec4 | |||
| 71e1abd263 | |||
| 72172dcb33 | |||
| def2988e12 | |||
| b47793c365 | |||
| 3a99cc56b7 | |||
| 24c35dede5 | |||
| 8c4400dff1 | |||
| af8dffaa33 | |||
| 4a36a3c8e6 | |||
| e6735e042e | |||
| c799379a54 | |||
| d8b9f08e5a | |||
| 608b25de45 | |||
| 2db8869908 | |||
| 9500737bbe | |||
| def2b6425b | |||
| 0f4b11494e | |||
| 46448ce1e9 | |||
| fbe12b393f | |||
| ccf59b2c1a | |||
| d7af3b7788 | |||
| 682aca0b2a | |||
| 3328ffe1b9 | |||
| c07b7840e2 | |||
| 9f848b2c64 | |||
| 3d66ec0761 | |||
| f50920be69 | |||
| d31add9d5a | |||
| a4dcb4f92e | |||
| 2c589c1dbd | |||
| 60ea386c6d | |||
| 24be1a0ec5 | |||
| e71a14756b | |||
| 85fecbd1b9 | |||
| 335d39f317 | |||
| 973a18d346 | |||
| a43b93d796 | |||
| acf75abdf1 | |||
| 58598bfcf2 | |||
| 7a570439db | |||
| 6e769d1c20 | |||
| d9e7f5d133 | |||
| a119b05d85 | |||
| 7bf7b6bcf9 | |||
| e47ea98cdd | |||
| bf66e13377 | |||
| d7aba5629e | |||
| a5c200ac79 | |||
| fdc1fc1b2a | |||
| 42a4b762bd | |||
| 180c328ed1 | |||
| 2ec52a7a45 | |||
| aacf37e32b | |||
| 52323b7eb5 | |||
| 5b5613a762 | |||
| de6df0c029 | |||
| e180b3c171 | |||
| 1364b79cbf | |||
| ef96f3102f | |||
| 06ce3b08f7 | |||
| a13217dddf | |||
| ce528d4012 | |||
| 89207b6d2a | |||
| e9591caf81 | |||
| 24f1aae6b6 | |||
| 04fbc9a22b | |||
| 14e31d5690 | |||
| a9e9808183 | |||
| af7cb2432b | |||
| e0c1364916 | |||
| 04d16fc535 | |||
| 44135b3fed | |||
| 6111e8f0da | |||
| 4e3e7b10ce | |||
| ce7f81d676 | |||
| 0cf2f8885e | |||
| ddf4b2646c | |||
| fe1e0749a2 | |||
| 2093468c92 | |||
| 19af7454f2 | |||
| d24427aad8 | |||
| e2bb0cfb7c | |||
| 2ebdb44826 | |||
| 432e25565e | |||
| ebe511404a | |||
| e0a79fb86c | |||
| 295ca3d9a2 | |||
| dbad8bdb96 | |||
| 8c703859a0 | |||
| bedb260b00 | |||
| b49592301f | |||
| c6c67078b8 | |||
| 9e45ad10f1 | |||
| 24da859975 | |||
| 0b6a8a9641 | |||
| e43c4f082e | |||
| 0b334cf957 | |||
| ae387ab397 | |||
| 056e62dce8 | |||
| 47999214bd | |||
| 68473ee345 | |||
| 455f27d443 | |||
| ba996c3b55 | |||
| d43a1109c8 | |||
| c3ba7daa16 | |||
| 82048cd4f3 | |||
| 71b0a5cc81 | |||
| edb5ff1e33 | |||
| d4ed6348ee | |||
| f12ac685e8 | |||
| b9ec4068ee | |||
| 02aabb8f97 | |||
| dcec2154c0 | |||
| bbc1d20396 | |||
| e682213681 | |||
| 0153c0faae | |||
| 87ebf4722b | |||
| 3906dca04e | |||
| 399ba314a3 | |||
| 19e1803633 | |||
| 71048c7ff0 | |||
| 7f350279fa | |||
| 90f4833c9e | |||
| c0cb3a176f | |||
| 7b0fa48e2e | |||
| b51853b3df | |||
| f5545cd3f4 | |||
| e76af3bfc2 | |||
| 6ef4944d89 | |||
| 850397e4c1 | |||
| 3b531144cf | |||
| 6ca684603c | |||
| cf85d66b2f | |||
| e8fa029df7 | |||
| 1a361c91f1 | |||
| 4a76059608 | |||
| da25eff5c1 | |||
| 69e23ef9f2 | |||
| 48a08e9659 | |||
| 4608f91ec6 | |||
| e88c1fa329 | |||
| 935e545caa | |||
| a426da534c | |||
| eaf6bf29cc | |||
| a0eb6bd3dc | |||
| fbe228a4f8 | |||
| 578a59063f | |||
| 81020ff34d | |||
| fea78898a5 | |||
| ffa7cc0d22 | |||
| 4f9969cd9b | |||
| 1be34564f2 | |||
| 56eff7a236 | |||
| 9f909b0d85 | |||
| baa65b8155 | |||
| 12c6a1baa0 | |||
| 5ea423072b | |||
| 08a41e37b4 | |||
| 8027c4a06f | |||
| a1e321b153 | |||
| 8c6a2ac5dd | |||
| b489bf9236 | |||
| aa63aa6cf3 | |||
| 9a2b93fb37 | |||
| e8ea7efc98 | |||
| 81a76593da | |||
| 5336864f7d | |||
| d38058e1d2 | |||
| fececd4651 | |||
| 021adf3104 | |||
| 160c83df4a | |||
| 456bb87a00 | |||
| 707451309c | |||
| 269676e8a5 | |||
| e4effebc19 | |||
| fbbceddba8 | |||
| 9a634e0de5 | |||
| 21d0d43edc | |||
| 3051b963ef | |||
| 0d0bdce337 | |||
| bdb5dc8c28 | |||
| 209847d98a | |||
| 14f42e15d1 | |||
| 7402e4811d | |||
| 6de0465b86 | |||
| cd7c4baaaf | |||
| a2db81bf7d | |||
| b376f89ce5 | |||
| 5633113f25 | |||
| 669415cfbf | |||
| 9f366863a9 | |||
| 0d644fe0c9 | |||
| 72fa6b8200 | |||
| 6d3f1d263a | |||
| 47bf9f7836 | |||
| 2738402aac | |||
| 68d36522b1 | |||
| 24a587b944 | |||
| 76119445a3 | |||
| 46ec59c74e | |||
| 2b7122c744 | |||
| 52f0a5432b | |||
| 7391b4d0ec | |||
| aa7ee3e8ff | |||
| bef0f3709f | |||
| f33b011847 | |||
| 2d8d11d4da | |||
| 10b1784f6d | |||
| f2f2ea161c | |||
| dc67a52000 | |||
| 05820aa820 | |||
| 8966dbbcd1 | |||
| cf32819c01 | |||
| 728496010c | |||
| 0a08f47942 | |||
| 39ceb02500 | |||
| 4336714248 | |||
| 1d41904fc3 | |||
| fae383a045 | |||
| 8f7a420cca | |||
| 9720ba3eed | |||
| d3256d59d5 | |||
| fa5f7ab7a5 | |||
| 6f26fd7238 | |||
| 6abc0819d9 | |||
| b580a23e7e | |||
| f659c3f11c | |||
| 0282a0521b | |||
| 75637e4b94 | |||
| b6c789dee6 | |||
| 8d3d636329 | |||
| 6f6395bad7 | |||
| b8c8d2a02e | |||
| 98104a3c03 | |||
| 8f4c65ec8c | |||
| 341a0452da | |||
| 6afb8de3dd | |||
| 0e62ccc7aa | |||
| 09282a9a62 | |||
| 18b3ab5610 | |||
| 699a658df9 | |||
| b5e255a384 | |||
| 67ccd2c1fb | |||
| 898b072e68 | |||
| 34156af403 | |||
| 61a0126278 | |||
| 1ce1904c89 | |||
| 7c9c278cc4 | |||
| 450507a812 | |||
| c074c835d4 | |||
| 2e989fbe83 | |||
| b1b325d00b | |||
| cf00650c6d | |||
| e6ab28365f | |||
| 80fd2a1a18 | |||
| 84160b2f07 | |||
| fbc2c2b481 | |||
| 57a5005197 | |||
| 9350c5513e | |||
| f59516cc6e | |||
| 88078ff813 | |||
| 281de48ed4 | |||
| 3c6d6bf688 | |||
| 8ac0ce399f | |||
| 80458e24bd | |||
| 6ab966ee2f | |||
| 166477ae27 | |||
| a719065b8d | |||
| 36599a2984 | |||
| d9c9289d65 | |||
| e5579b2c33 | |||
| 618028503b | |||
| 2f6756eddf | |||
| ad53894ea1 | |||
| 8c434703fb | |||
| 3cc900ffbf | |||
| 6d968f9044 | |||
| 23fa9e8d7f | |||
| 59a428d549 |
@@ -1,5 +1,5 @@
|
|||||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
ARG VARIANT=16
|
ARG VARIANT=20
|
||||||
FROM mcr.microsoft.com/devcontainers/javascript-node:0-${VARIANT} as base
|
FROM mcr.microsoft.com/devcontainers/javascript-node:0-${VARIANT} as base
|
||||||
|
|
||||||
# Setup the node environment
|
# Setup the node environment
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ module.exports.config = {
|
|||||||
ConfigPath: Path.resolve('config'),
|
ConfigPath: Path.resolve('config'),
|
||||||
MetadataPath: Path.resolve('metadata'),
|
MetadataPath: Path.resolve('metadata'),
|
||||||
FFmpegPath: '/usr/bin/ffmpeg',
|
FFmpegPath: '/usr/bin/ffmpeg',
|
||||||
FFProbePath: '/usr/bin/ffprobe'
|
FFProbePath: '/usr/bin/ffprobe',
|
||||||
|
SkipBinariesCheck: false
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
// Append -bullseye or -buster to pin to an OS version.
|
// Append -bullseye or -buster to pin to an OS version.
|
||||||
// Use -bullseye variants on local arm64/Apple Silicon.
|
// Use -bullseye variants on local arm64/Apple Silicon.
|
||||||
"args": {
|
"args": {
|
||||||
"VARIANT": "16"
|
"VARIANT": "20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mounts": [
|
"mounts": [
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ body:
|
|||||||
value: "### Mobile app issues report [here](https://github.com/advplyr/audiobookshelf-app/issues/new/choose)."
|
value: "### Mobile app issues report [here](https://github.com/advplyr/audiobookshelf-app/issues/new/choose)."
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "### Join the [discord server](https://discord.gg/pJsjuNCKRq) for questions or if you are not sure about a bug."
|
value: "### Join the [discord server](https://discord.gg/HQgCbd6E75) for questions or if you are not sure about a bug."
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "## Be as descriptive as you can. Include screenshots, error logs, browser, file types, everything you can think of that might be relevant."
|
value: "## Be as descriptive as you can. Include screenshots, error logs, browser, file types, everything you can think of that might be relevant."
|
||||||
@@ -44,6 +44,7 @@ body:
|
|||||||
options:
|
options:
|
||||||
- Docker
|
- Docker
|
||||||
- Debian/PPA
|
- Debian/PPA
|
||||||
|
- Windows Tray App
|
||||||
- Built from source
|
- Built from source
|
||||||
- Other
|
- Other
|
||||||
validations:
|
validations:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Discord
|
- name: Discord
|
||||||
url: https://discord.gg/pJsjuNCKRq
|
url: https://discord.gg/HQgCbd6E75
|
||||||
about: Ask questions, get help troubleshooting, and join the Abs community here.
|
about: Ask questions, get help troubleshooting, and join the Abs community here.
|
||||||
- name: Matrix
|
- name: Matrix
|
||||||
url: https://matrix.to/#/#audiobookshelf:matrix.org
|
url: https://matrix.to/#/#audiobookshelf:matrix.org
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tags: ${{ github.event.inputs.tags || steps.meta.outputs.tags }}
|
tags: ${{ github.event.inputs.tags || steps.meta.outputs.tags }}
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: setup nade
|
- name: setup nade
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 20
|
||||||
|
|
||||||
- name: install pkg
|
- name: install pkg
|
||||||
run: npm install -g pkg
|
run: npm install -g pkg
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
name: Run Unit Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
ref:
|
||||||
|
description: 'Branch/Tag/SHA to test'
|
||||||
|
required: true
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-unit-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event_name != 'workflow_dispatch' && github.ref_name || inputs.ref}}
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm test
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
/deploy/
|
/deploy/
|
||||||
/coverage/
|
/coverage/
|
||||||
/.nyc_output/
|
/.nyc_output/
|
||||||
|
/ffmpeg*
|
||||||
|
/ffprobe*
|
||||||
|
|
||||||
sw.*
|
sw.*
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|||||||
+5
-3
@@ -1,5 +1,5 @@
|
|||||||
### STAGE 0: Build client ###
|
### STAGE 0: Build client ###
|
||||||
FROM node:16-alpine AS build
|
FROM node:20-alpine AS build
|
||||||
WORKDIR /client
|
WORKDIR /client
|
||||||
COPY /client /client
|
COPY /client /client
|
||||||
RUN npm ci && npm cache clean --force
|
RUN npm ci && npm cache clean --force
|
||||||
@@ -7,7 +7,7 @@ RUN npm run generate
|
|||||||
|
|
||||||
### STAGE 1: Build server ###
|
### STAGE 1: Build server ###
|
||||||
FROM sandreas/tone:v0.1.5 AS tone
|
FROM sandreas/tone:v0.1.5 AS tone
|
||||||
FROM node:16-alpine
|
FROM node:20-alpine
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
@@ -18,7 +18,8 @@ RUN apk update && \
|
|||||||
ffmpeg \
|
ffmpeg \
|
||||||
make \
|
make \
|
||||||
python3 \
|
python3 \
|
||||||
g++
|
g++ \
|
||||||
|
tini
|
||||||
|
|
||||||
COPY --from=tone /usr/local/bin/tone /usr/local/bin/
|
COPY --from=tone /usr/local/bin/tone /usr/local/bin/
|
||||||
COPY --from=build /client/dist /client/dist
|
COPY --from=build /client/dist /client/dist
|
||||||
@@ -31,4 +32,5 @@ RUN apk del make python3 g++
|
|||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
|
ENTRYPOINT ["tini", "--"]
|
||||||
CMD ["node", "index.js"]
|
CMD ["node", "index.js"]
|
||||||
|
|||||||
+2
-3
@@ -48,11 +48,10 @@ Description: $DESCRIPTION"
|
|||||||
echo "$controlfile" > dist/debian/DEBIAN/control;
|
echo "$controlfile" > dist/debian/DEBIAN/control;
|
||||||
|
|
||||||
# Package debian
|
# Package debian
|
||||||
pkg -t node16-linux-x64 -o dist/debian/usr/share/audiobookshelf/audiobookshelf .
|
pkg -t node18-linux-x64 -o dist/debian/usr/share/audiobookshelf/audiobookshelf .
|
||||||
|
|
||||||
fakeroot dpkg-deb --build dist/debian
|
fakeroot dpkg-deb -Zxz --build dist/debian
|
||||||
|
|
||||||
mv dist/debian.deb "dist/$OUTPUT_FILE"
|
mv dist/debian.deb "dist/$OUTPUT_FILE"
|
||||||
chmod +x "dist/$OUTPUT_FILE"
|
|
||||||
|
|
||||||
echo "Finished! Filename: $OUTPUT_FILE"
|
echo "Finished! Filename: $OUTPUT_FILE"
|
||||||
|
|||||||
@@ -217,36 +217,6 @@ Bookshelf Label
|
|||||||
filter: blur(20px);
|
filter: blur(20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.episode-subtitle {
|
|
||||||
word-break: break-word;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
line-height: 16px;
|
|
||||||
/* fallback */
|
|
||||||
max-height: 32px;
|
|
||||||
/* fallback */
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
/* number of lines to show */
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.episode-subtitle-long {
|
|
||||||
word-break: break-word;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
line-height: 16px;
|
|
||||||
/* fallback */
|
|
||||||
max-height: 72px;
|
|
||||||
/* fallback */
|
|
||||||
-webkit-line-clamp: 6;
|
|
||||||
/* number of lines to show */
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Padding for toastification toasts in the top right to not cover appbar/toolbar */
|
/* Padding for toastification toasts in the top right to not cover appbar/toolbar */
|
||||||
.app-bar-and-toolbar .Vue-Toastification__container.top-right {
|
.app-bar-and-toolbar .Vue-Toastification__container.top-right {
|
||||||
padding-top: 104px;
|
padding-top: 104px;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="bookshelf" ref="wrapper" class="w-full max-w-full h-full overflow-y-scroll relative">
|
<div id="bookshelf" ref="wrapper" class="w-full max-w-full h-full overflow-y-scroll relative">
|
||||||
<!-- Cover size widget -->
|
<!-- Cover size widget -->
|
||||||
<widgets-cover-size-widget class="fixed bottom-4 right-4 z-50" />
|
<widgets-cover-size-widget class="fixed right-4 z-50" :style="{ bottom: streamLibraryItem ? '181px' : '16px' }" />
|
||||||
|
|
||||||
<div v-if="loaded && !shelves.length && !search" class="w-full flex flex-col items-center justify-center py-12">
|
<div v-if="loaded && !shelves.length && !search" class="w-full flex flex-col items-center justify-center py-12">
|
||||||
<p class="text-center text-2xl mb-4 py-4">{{ libraryName }} Library is empty!</p>
|
<p class="text-center text-2xl mb-4 py-4">{{ libraryName }} Library is empty!</p>
|
||||||
@@ -94,6 +94,9 @@ export default {
|
|||||||
},
|
},
|
||||||
selectedMediaItems() {
|
selectedMediaItems() {
|
||||||
return this.$store.state.globals.selectedMediaItems || []
|
return this.$store.state.globals.selectedMediaItems || []
|
||||||
|
},
|
||||||
|
streamLibraryItem() {
|
||||||
|
return this.$store.state.streamLibraryItem
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -22,6 +22,10 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
|
||||||
</svg>
|
</svg>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
|
<nuxt-link v-if="showPlaylists" :to="`/library/${currentLibraryId}/bookshelf/playlists`" class="flex-grow h-full flex justify-center items-center" :class="isPlaylistsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
|
||||||
|
<p v-if="isPlaylistsPage || isPodcastLibrary" class="text-sm">{{ $strings.ButtonPlaylists }}</p>
|
||||||
|
<span v-else class="material-icons-outlined text-lg">queue_music</span>
|
||||||
|
</nuxt-link>
|
||||||
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/collections`" class="flex-grow h-full flex justify-center items-center" :class="isCollectionsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
|
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/collections`" class="flex-grow h-full flex justify-center items-center" :class="isCollectionsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
|
||||||
<p v-if="isCollectionsPage" class="text-sm">{{ $strings.ButtonCollections }}</p>
|
<p v-if="isCollectionsPage" class="text-sm">{{ $strings.ButtonCollections }}</p>
|
||||||
<span v-else class="material-icons-outlined text-lg">collections_bookmark</span>
|
<span v-else class="material-icons-outlined text-lg">collections_bookmark</span>
|
||||||
@@ -94,6 +98,9 @@
|
|||||||
<template v-else-if="page === 'authors'">
|
<template v-else-if="page === 'authors'">
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<ui-btn v-if="userCanUpdate && authors && authors.length && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
|
<ui-btn v-if="userCanUpdate && authors && authors.length && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
|
||||||
|
|
||||||
|
<!-- author sort select -->
|
||||||
|
<controls-sort-select v-if="authors && authors.length" v-model="settings.authorSortBy" :descending.sync="settings.authorSortDesc" :items="authorSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateAuthorSort" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -179,6 +186,30 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
authorSortItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: this.$strings.LabelAuthorFirstLast,
|
||||||
|
value: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$strings.LabelAuthorLastFirst,
|
||||||
|
value: 'lastFirst'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$strings.LabelNumberOfBooks,
|
||||||
|
value: 'numBooks'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$strings.LabelAddedAt,
|
||||||
|
value: 'addedAt'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$strings.LabelUpdatedAt,
|
||||||
|
value: 'updatedAt'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
userIsAdminOrUp() {
|
userIsAdminOrUp() {
|
||||||
return this.$store.getters['user/getIsAdminOrUp']
|
return this.$store.getters['user/getIsAdminOrUp']
|
||||||
},
|
},
|
||||||
@@ -293,6 +324,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
},
|
||||||
|
showPlaylists() {
|
||||||
|
return this.$store.state.libraries.numUserPlaylists > 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -448,6 +482,9 @@ export default {
|
|||||||
updateCollapseBookSeries() {
|
updateCollapseBookSeries() {
|
||||||
this.saveSettings()
|
this.saveSettings()
|
||||||
},
|
},
|
||||||
|
updateAuthorSort() {
|
||||||
|
this.saveSettings()
|
||||||
|
},
|
||||||
saveSettings() {
|
saveSettings() {
|
||||||
this.$store.dispatch('user/updateUserSettings', this.settings)
|
this.$store.dispatch('user/updateUserSettings', this.settings)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<widgets-cover-size-widget class="fixed bottom-4 right-4 z-50" />
|
<widgets-cover-size-widget class="fixed right-4 z-50" :style="{ bottom: streamLibraryItem ? '181px' : '16px' }" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -205,6 +205,9 @@ export default {
|
|||||||
sizeMultiplier() {
|
sizeMultiplier() {
|
||||||
const baseSize = this.isCoverSquareAspectRatio ? 192 : 120
|
const baseSize = this.isCoverSquareAspectRatio ? 192 : 120
|
||||||
return this.entityWidth / baseSize
|
return this.entityWidth / baseSize
|
||||||
|
},
|
||||||
|
streamLibraryItem() {
|
||||||
|
return this.$store.state.streamLibraryItem
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
+9
-9
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="streamLibraryItem" id="streamContainer" 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 md:h-40 z-50 bg-primary px-2 md:px-4 pb-1 md:pb-4 pt-2">
|
||||||
<div id="videoDock" />
|
<div id="videoDock" />
|
||||||
<div class="absolute left-2 top-2 md:left-4 cursor-pointer">
|
<div class="absolute left-2 top-2 md:left-4 cursor-pointer">
|
||||||
<covers-book-cover expand-on-click :library-item="streamLibraryItem" :width="bookCoverWidth" :book-cover-aspect-ratio="coverAspectRatio" />
|
<covers-book-cover expand-on-click :library-item="streamLibraryItem" :width="bookCoverWidth" :book-cover-aspect-ratio="coverAspectRatio" />
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<ui-tooltip direction="top" :text="$strings.LabelClosePlayer">
|
<ui-tooltip direction="top" :text="$strings.LabelClosePlayer">
|
||||||
<span class="material-icons sm:px-2 py-1 md:p-4 cursor-pointer text-xl sm:text-2xl" @click="closePlayer">close</span>
|
<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>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<player-ui
|
<player-ui
|
||||||
@@ -349,7 +349,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ('mediaSession' in navigator) {
|
if ('mediaSession' in navigator) {
|
||||||
var coverImageSrc = this.$store.getters['globals/getLibraryItemCoverSrc'](this.streamLibraryItem, '/Logo.png')
|
var coverImageSrc = this.$store.getters['globals/getLibraryItemCoverSrc'](this.streamLibraryItem, '/Logo.png', true)
|
||||||
const artwork = [
|
const artwork = [
|
||||||
{
|
{
|
||||||
src: coverImageSrc
|
src: coverImageSrc
|
||||||
@@ -380,7 +380,7 @@ export default {
|
|||||||
if (this.playerHandler.isPlayingLocalItem && this.playerHandler.currentStreamId === data.stream) {
|
if (this.playerHandler.isPlayingLocalItem && this.playerHandler.currentStreamId === data.stream) {
|
||||||
if (!data.numSegments) return
|
if (!data.numSegments) return
|
||||||
var chunks = data.chunks
|
var chunks = data.chunks
|
||||||
console.log(`[StreamContainer] Stream Progress ${data.percent}`)
|
console.log(`[MediaPlayerContainer] Stream Progress ${data.percent}`)
|
||||||
if (this.$refs.audioPlayer) {
|
if (this.$refs.audioPlayer) {
|
||||||
this.$refs.audioPlayer.setChunksReady(chunks, data.numSegments)
|
this.$refs.audioPlayer.setChunksReady(chunks, data.numSegments)
|
||||||
} else {
|
} else {
|
||||||
@@ -397,17 +397,17 @@ export default {
|
|||||||
this.playerHandler.prepareOpenSession(session, this.currentPlaybackRate)
|
this.playerHandler.prepareOpenSession(session, this.currentPlaybackRate)
|
||||||
},
|
},
|
||||||
streamOpen(session) {
|
streamOpen(session) {
|
||||||
console.log(`[StreamContainer] Stream session open`, session)
|
console.log(`[MediaPlayerContainer] Stream session open`, session)
|
||||||
},
|
},
|
||||||
streamClosed(streamId) {
|
streamClosed(streamId) {
|
||||||
// Stream was closed from the server
|
// Stream was closed from the server
|
||||||
if (this.playerHandler.isPlayingLocalItem && this.playerHandler.currentStreamId === streamId) {
|
if (this.playerHandler.isPlayingLocalItem && this.playerHandler.currentStreamId === streamId) {
|
||||||
console.warn('[StreamContainer] Closing stream due to request from server')
|
console.warn('[MediaPlayerContainer] Closing stream due to request from server')
|
||||||
this.playerHandler.closePlayer()
|
this.playerHandler.closePlayer()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
streamReady() {
|
streamReady() {
|
||||||
console.log(`[StreamContainer] Stream Ready`)
|
console.log(`[MediaPlayerContainer] Stream Ready`)
|
||||||
if (this.$refs.audioPlayer) {
|
if (this.$refs.audioPlayer) {
|
||||||
this.$refs.audioPlayer.setStreamReady()
|
this.$refs.audioPlayer.setStreamReady()
|
||||||
} else {
|
} else {
|
||||||
@@ -417,7 +417,7 @@ export default {
|
|||||||
streamError(streamId) {
|
streamError(streamId) {
|
||||||
// Stream had critical error from the server
|
// Stream had critical error from the server
|
||||||
if (this.playerHandler.isPlayingLocalItem && this.playerHandler.currentStreamId === streamId) {
|
if (this.playerHandler.isPlayingLocalItem && this.playerHandler.currentStreamId === streamId) {
|
||||||
console.warn('[StreamContainer] Closing stream due to stream error from server')
|
console.warn('[MediaPlayerContainer] Closing stream due to stream error from server')
|
||||||
this.playerHandler.closePlayer()
|
this.playerHandler.closePlayer()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -496,7 +496,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#streamContainer {
|
#mediaPlayerContainer {
|
||||||
box-shadow: 0px -6px 8px #1111113f;
|
box-shadow: 0px -6px 8px #1111113f;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<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-4 mb-8">
|
||||||
<div class="flex items-center mb-2">
|
<div class="flex items-center mb-2">
|
||||||
|
<slot name="header-prefix"></slot>
|
||||||
<h1 class="text-xl">{{ headerText }}</h1>
|
<h1 class="text-xl">{{ headerText }}</h1>
|
||||||
|
|
||||||
<slot name="header-items"></slot>
|
<slot name="header-items"></slot>
|
||||||
|
|||||||
@@ -15,24 +15,33 @@
|
|||||||
|
|
||||||
<div class="flex my-2 -mx-2">
|
<div class="flex my-2 -mx-2">
|
||||||
<div class="w-1/2 px-2">
|
<div class="w-1/2 px-2">
|
||||||
<ui-text-input-with-label v-model="itemData.title" :disabled="processing" :label="$strings.LabelTitle" @input="titleUpdated" />
|
<ui-text-input-with-label v-model.trim="itemData.title" :disabled="processing" :label="$strings.LabelTitle" @input="titleUpdated" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/2 px-2">
|
<div class="w-1/2 px-2">
|
||||||
<ui-text-input-with-label v-if="!isPodcast" v-model="itemData.author" :disabled="processing" :label="$strings.LabelAuthor" />
|
<div v-if="!isPodcast" class="flex items-end">
|
||||||
|
<ui-text-input-with-label v-model.trim="itemData.author" :disabled="processing" :label="$strings.LabelAuthor" />
|
||||||
|
<ui-tooltip :text="$strings.LabelUploaderItemFetchMetadataHelp">
|
||||||
|
<div
|
||||||
|
class="ml-2 mb-1 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full hover:bg-primary cursor-pointer"
|
||||||
|
@click="fetchMetadata">
|
||||||
|
<span class="text-base text-white text-opacity-80 font-mono material-icons">sync</span>
|
||||||
|
</div>
|
||||||
|
</ui-tooltip>
|
||||||
|
</div>
|
||||||
<div v-else class="w-full">
|
<div v-else class="w-full">
|
||||||
<p class="px-1 text-sm font-semibold">{{ $strings.LabelDirectory }} <em class="font-normal text-xs pl-2">(auto)</em></p>
|
<p class="px-1 text-sm font-semibold">{{ $strings.LabelDirectory }} <em class="font-normal text-xs pl-2">(auto)</em></p>
|
||||||
<ui-text-input :value="directory" disabled class="w-full font-mono text-xs" style="height: 38px" />
|
<ui-text-input :value="directory" disabled class="w-full font-mono text-xs" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isPodcast" class="flex my-2 -mx-2">
|
<div v-if="!isPodcast" class="flex my-2 -mx-2">
|
||||||
<div class="w-1/2 px-2">
|
<div class="w-1/2 px-2">
|
||||||
<ui-text-input-with-label v-model="itemData.series" :disabled="processing" :label="$strings.LabelSeries" note="(optional)" />
|
<ui-text-input-with-label v-model.trim="itemData.series" :disabled="processing" :label="$strings.LabelSeries" note="(optional)" inputClass="h-10" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/2 px-2">
|
<div class="w-1/2 px-2">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<p class="px-1 text-sm font-semibold">{{ $strings.LabelDirectory }} <em class="font-normal text-xs pl-2">(auto)</em></p>
|
<label class="px-1 text-sm font-semibold">{{ $strings.LabelDirectory }} <em class="font-normal text-xs pl-2">(auto)</em></label>
|
||||||
<ui-text-input :value="directory" disabled class="w-full font-mono text-xs" style="height: 38px" />
|
<ui-text-input :value="directory" disabled class="w-full font-mono text-xs h-10" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,8 +57,8 @@
|
|||||||
<p class="text-base">{{ $strings.MessageUploaderItemFailed }}</p>
|
<p class="text-base">{{ $strings.MessageUploaderItemFailed }}</p>
|
||||||
</widgets-alert>
|
</widgets-alert>
|
||||||
|
|
||||||
<div v-if="isUploading" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-20">
|
<div v-if="isNonInteractable" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-20">
|
||||||
<ui-loading-indicator :text="$strings.MessageUploading" />
|
<ui-loading-indicator :text="nonInteractionLabel" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -61,10 +70,11 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => { }
|
||||||
},
|
},
|
||||||
mediaType: String,
|
mediaType: String,
|
||||||
processing: Boolean
|
processing: Boolean,
|
||||||
|
provider: String
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -76,7 +86,8 @@ export default {
|
|||||||
error: '',
|
error: '',
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
uploadFailed: false,
|
uploadFailed: false,
|
||||||
uploadSuccess: false
|
uploadSuccess: false,
|
||||||
|
isFetchingMetadata: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -87,12 +98,19 @@ export default {
|
|||||||
if (!this.itemData.title) return ''
|
if (!this.itemData.title) return ''
|
||||||
if (this.isPodcast) return this.itemData.title
|
if (this.isPodcast) return this.itemData.title
|
||||||
|
|
||||||
if (this.itemData.series && this.itemData.author) {
|
const outputPathParts = [this.itemData.author, this.itemData.series, this.itemData.title]
|
||||||
return Path.join(this.itemData.author, this.itemData.series, this.itemData.title)
|
const cleanedOutputPathParts = outputPathParts.filter(Boolean).map(part => this.$sanitizeFilename(part))
|
||||||
} else if (this.itemData.author) {
|
|
||||||
return Path.join(this.itemData.author, this.itemData.title)
|
return Path.join(...cleanedOutputPathParts)
|
||||||
} else {
|
},
|
||||||
return this.itemData.title
|
isNonInteractable() {
|
||||||
|
return this.isUploading || this.isFetchingMetadata
|
||||||
|
},
|
||||||
|
nonInteractionLabel() {
|
||||||
|
if (this.isUploading) {
|
||||||
|
return this.$strings.MessageUploading
|
||||||
|
} else if (this.isFetchingMetadata) {
|
||||||
|
return this.$strings.LabelFetchingMetadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -105,9 +123,42 @@ export default {
|
|||||||
titleUpdated() {
|
titleUpdated() {
|
||||||
this.error = ''
|
this.error = ''
|
||||||
},
|
},
|
||||||
|
async fetchMetadata() {
|
||||||
|
if (!this.itemData.title.trim().length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isFetchingMetadata = true
|
||||||
|
this.error = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const searchQueryString = new URLSearchParams({
|
||||||
|
title: this.itemData.title,
|
||||||
|
author: this.itemData.author,
|
||||||
|
provider: this.provider
|
||||||
|
})
|
||||||
|
const [bestCandidate, ..._rest] = await this.$axios.$get(`/api/search/books?${searchQueryString}`)
|
||||||
|
|
||||||
|
if (bestCandidate) {
|
||||||
|
this.itemData = {
|
||||||
|
...this.itemData,
|
||||||
|
title: bestCandidate.title,
|
||||||
|
author: bestCandidate.author,
|
||||||
|
series: (bestCandidate.series || [])[0]?.series
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.error = this.$strings.ErrorUploadFetchMetadataNoResults
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed', e)
|
||||||
|
this.error = this.$strings.ErrorUploadFetchMetadataAPI
|
||||||
|
} finally {
|
||||||
|
this.isFetchingMetadata = false
|
||||||
|
}
|
||||||
|
},
|
||||||
getData() {
|
getData() {
|
||||||
if (!this.itemData.title) {
|
if (!this.itemData.title) {
|
||||||
this.error = 'Must have a title'
|
this.error = this.$strings.ErrorUploadLacksTitle
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
this.error = ''
|
this.error = ''
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
<!-- Alternative bookshelf title/author/sort -->
|
<!-- 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" class="absolute left-0 z-50 w-full" :style="{ bottom: `-${titleDisplayBottomOffset}rem` }">
|
||||||
<div :style="{ fontSize: 0.9 * sizeMultiplier + 'rem' }">
|
<div :style="{ fontSize: 0.9 * sizeMultiplier + 'rem' }">
|
||||||
<div class="flex items-center">
|
<ui-tooltip v-if="displayTitle" :text="displayTitle" :disabled="!displayTitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
|
||||||
<span class="truncate">{{ displayTitle }}</span>
|
<p ref="displayTitle" class="truncate">{{ displayTitle }}</p>
|
||||||
<widgets-explicit-indicator :explicit="isExplicit" />
|
<widgets-explicit-indicator :explicit="isExplicit" />
|
||||||
</div>
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displayLineTwo || ' ' }}</p>
|
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displayLineTwo || ' ' }}</p>
|
||||||
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
|
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
|
||||||
@@ -164,6 +164,7 @@ export default {
|
|||||||
imageReady: false,
|
imageReady: false,
|
||||||
selected: false,
|
selected: false,
|
||||||
isSelectionMode: false,
|
isSelectionMode: false,
|
||||||
|
displayTitleTruncated: false,
|
||||||
showCoverBg: false
|
showCoverBg: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -642,6 +643,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.libraryItem = libraryItem
|
this.libraryItem = libraryItem
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.displayTitle) {
|
||||||
|
this.displayTitleTruncated = this.$refs.displayTitle.scrollWidth > this.$refs.displayTitle.clientWidth
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
clickCard(e) {
|
clickCard(e) {
|
||||||
if (this.processing) return
|
if (this.processing) return
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sm:w-80 w-full relative">
|
<div class="">
|
||||||
<form @submit.prevent="submitSearch">
|
<div class="w-full relative sm:w-80">
|
||||||
<ui-text-input ref="input" v-model="search" :placeholder="$strings.PlaceholderSearch" @input="inputUpdate" @focus="focussed" @blur="blurred" class="w-full h-8 text-sm" />
|
<form @submit.prevent="submitSearch">
|
||||||
</form>
|
<ui-text-input ref="input" v-model="search" :placeholder="$strings.PlaceholderSearch" @input="inputUpdate" @focus="focussed" @blur="blurred" class="w-full h-8 text-sm" />
|
||||||
<div class="absolute top-0 right-0 bottom-0 h-full flex items-center px-2 text-gray-400 cursor-pointer" @click="clickClear">
|
</form>
|
||||||
<span v-if="!search" class="material-icons" style="font-size: 1.2rem">search</span>
|
<div class="absolute top-0 right-0 bottom-0 h-full flex items-center px-2 text-gray-400 cursor-pointer" @click="clickClear">
|
||||||
<span v-else class="material-icons" style="font-size: 1.2rem">close</span>
|
<span v-if="!search" class="material-icons" style="font-size: 1.2rem">search</span>
|
||||||
|
<span v-else class="material-icons" style="font-size: 1.2rem">close</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showMenu && (lastSearch || isTyping)" class="absolute z-40 -mt-px w-40 sm:w-full bg-bg border border-black-200 shadow-lg rounded-md py-1 px-2 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm globalSearchMenu">
|
<div v-show="showMenu && (lastSearch || isTyping)" class="absolute z-40 -mt-px w-full max-w-64 sm:max-w-80 sm:w-80 bg-bg border border-black-200 shadow-lg rounded-md py-1 px-2 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm globalSearchMenu">
|
||||||
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
|
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
|
||||||
<li v-if="isTyping" class="py-2 px-2">
|
<li v-if="isTyping" class="py-2 px-2">
|
||||||
<p>{{ $strings.MessageThinking }}</p>
|
<p>{{ $strings.MessageThinking }}</p>
|
||||||
|
|||||||
@@ -368,9 +368,17 @@ export default {
|
|||||||
id: 'ebook',
|
id: 'ebook',
|
||||||
name: this.$strings.LabelHasEbook
|
name: this.$strings.LabelHasEbook
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'no-ebook',
|
||||||
|
name: this.$strings.LabelMissingEbook
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'supplementary',
|
id: 'supplementary',
|
||||||
name: this.$strings.LabelHasSupplementaryEbook
|
name: this.$strings.LabelHasSupplementaryEbook
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'no-supplementary',
|
||||||
|
name: this.$strings.LabelMissingSupplementaryEbook
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative" v-click-outside="clickOutside" @mouseover="mouseover" @mouseleave="mouseleave">
|
<div class="relative" v-click-outside="clickOutside" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||||
<div class="cursor-pointer text-gray-300 hover:text-white" @mousedown.prevent @mouseup.prevent @click="clickVolumeIcon">
|
<button :aria-label="$strings.LabelVolume" class="text-gray-300 hover:text-white" @mousedown.prevent @mouseup.prevent @click="clickVolumeIcon">
|
||||||
<span class="material-icons text-2xl sm:text-3xl">{{ volumeIcon }}</span>
|
<span class="material-icons text-2xl sm:text-3xl">{{ volumeIcon }}</span>
|
||||||
</div>
|
</button>
|
||||||
<transition name="menux">
|
<transition name="menux">
|
||||||
<div v-show="isOpen" class="volumeMenu h-6 absolute bottom-2 w-28 px-2 bg-bg shadow-sm rounded-lg" style="left: -116px">
|
<div v-show="isOpen" class="volumeMenu h-6 absolute bottom-2 w-28 px-2 bg-bg shadow-sm rounded-lg" style="left: -116px">
|
||||||
<div ref="volumeTrack" class="h-1 w-full bg-gray-500 my-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
|
<div ref="volumeTrack" class="h-1 w-full bg-gray-500 my-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
|
||||||
@@ -38,8 +38,8 @@ export default {
|
|||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem("volume", val);
|
localStorage.setItem('volume', val)
|
||||||
} catch(error) {
|
} catch (error) {
|
||||||
console.error('Failed to store volume', err)
|
console.error('Failed to store volume', err)
|
||||||
}
|
}
|
||||||
this.$emit('input', val)
|
this.$emit('input', val)
|
||||||
@@ -146,7 +146,7 @@ export default {
|
|||||||
if (this.value === 0) {
|
if (this.value === 0) {
|
||||||
this.isMute = true
|
this.isMute = true
|
||||||
}
|
}
|
||||||
const storageVolume = localStorage.getItem("volume")
|
const storageVolume = localStorage.getItem('volume')
|
||||||
if (storageVolume) {
|
if (storageVolume) {
|
||||||
this.volume = parseFloat(storageVolume)
|
this.volume = parseFloat(storageVolume)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex pt-4 px-2">
|
<div class="flex pt-4 px-2">
|
||||||
<ui-btn v-if="isEditingRoot" to="/account">{{ $strings.ButtonChangeRootPassword }}</ui-btn>
|
<ui-btn v-if="hasOpenIDLink" small :loading="unlinkingFromOpenID" color="primary" type="button" class="mr-2" @click.stop="unlinkOpenID">Unlink OpenID</ui-btn>
|
||||||
|
<ui-btn v-if="isEditingRoot" small class="flex items-center" to="/account">{{ $strings.ButtonChangeRootPassword }}</ui-btn>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
|
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,7 +137,8 @@ export default {
|
|||||||
newUser: {},
|
newUser: {},
|
||||||
isNew: true,
|
isNew: true,
|
||||||
tags: [],
|
tags: [],
|
||||||
loadingTags: false
|
loadingTags: false,
|
||||||
|
unlinkingFromOpenID: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -180,7 +182,7 @@ export default {
|
|||||||
return this.isNew ? this.$strings.HeaderNewAccount : this.$strings.HeaderUpdateAccount
|
return this.isNew ? this.$strings.HeaderNewAccount : this.$strings.HeaderUpdateAccount
|
||||||
},
|
},
|
||||||
isEditingRoot() {
|
isEditingRoot() {
|
||||||
return this.account && this.account.type === 'root'
|
return this.account?.type === 'root'
|
||||||
},
|
},
|
||||||
libraries() {
|
libraries() {
|
||||||
return this.$store.state.libraries.libraries
|
return this.$store.state.libraries.libraries
|
||||||
@@ -198,6 +200,9 @@ export default {
|
|||||||
},
|
},
|
||||||
tagsSelectionText() {
|
tagsSelectionText() {
|
||||||
return this.newUser.permissions.selectedTagsNotAccessible ? this.$strings.LabelTagsNotAccessibleToUser : this.$strings.LabelTagsAccessibleToUser
|
return this.newUser.permissions.selectedTagsNotAccessible ? this.$strings.LabelTagsNotAccessibleToUser : this.$strings.LabelTagsAccessibleToUser
|
||||||
|
},
|
||||||
|
hasOpenIDLink() {
|
||||||
|
return !!this.account?.hasOpenIDLink
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -205,6 +210,31 @@ export default {
|
|||||||
// Force close when navigating - used in UsersTable
|
// Force close when navigating - used in UsersTable
|
||||||
if (this.$refs.modal) this.$refs.modal.setHide()
|
if (this.$refs.modal) this.$refs.modal.setHide()
|
||||||
},
|
},
|
||||||
|
unlinkOpenID() {
|
||||||
|
const payload = {
|
||||||
|
message: 'Are you sure you want to unlink this user from OpenID?',
|
||||||
|
callback: (confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.unlinkingFromOpenID = true
|
||||||
|
this.$axios
|
||||||
|
.$patch(`/api/users/${this.account.id}/openid-unlink`)
|
||||||
|
.then(() => {
|
||||||
|
this.$toast.success('User unlinked from OpenID')
|
||||||
|
this.show = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to unlink user from OpenID', error)
|
||||||
|
this.$toast.error('Failed to unlink user from OpenID')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.unlinkingFromOpenID = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'yesNo'
|
||||||
|
}
|
||||||
|
this.$store.commit('globals/setConfirmPrompt', payload)
|
||||||
|
},
|
||||||
accessAllTagsToggled(val) {
|
accessAllTagsToggled(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
if (this.newUser.itemTagsSelected?.length) {
|
if (this.newUser.itemTagsSelected?.length) {
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<modals-modal ref="modal" v-model="show" name="custom-metadata-provider" :width="600" :height="'unset'" :processing="processing">
|
||||||
|
<template #outer>
|
||||||
|
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
|
||||||
|
<p class="text-3xl text-white truncate">Add custom metadata provider</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<form @submit.prevent="submitForm">
|
||||||
|
<div class="px-4 w-full flex items-center text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 overflow-y-auto overflow-x-hidden" style="min-height: 400px; max-height: 80vh">
|
||||||
|
<div class="w-full p-8">
|
||||||
|
<div class="flex mb-2">
|
||||||
|
<div class="w-3/4 p-1">
|
||||||
|
<ui-text-input-with-label v-model="newName" :label="$strings.LabelName" />
|
||||||
|
</div>
|
||||||
|
<div class="w-1/4 p-1">
|
||||||
|
<ui-text-input-with-label value="Book" readonly :label="$strings.LabelMediaType" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full mb-2 p-1">
|
||||||
|
<ui-text-input-with-label v-model="newUrl" label="URL" />
|
||||||
|
</div>
|
||||||
|
<div class="w-full mb-2 p-1">
|
||||||
|
<ui-text-input-with-label v-model="newAuthHeaderValue" :label="'Authorization Header Value'" type="password" />
|
||||||
|
</div>
|
||||||
|
<div class="flex px-1 pt-4">
|
||||||
|
<div class="flex-grow" />
|
||||||
|
<ui-btn color="success" type="submit">{{ $strings.ButtonAdd }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</modals-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
processing: false,
|
||||||
|
newName: '',
|
||||||
|
newUrl: '',
|
||||||
|
newAuthHeaderValue: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('input', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm() {
|
||||||
|
if (!this.newName || !this.newUrl) {
|
||||||
|
this.$toast.error('Must add name and url')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processing = true
|
||||||
|
this.$axios
|
||||||
|
.$post('/api/custom-metadata-providers', {
|
||||||
|
name: this.newName,
|
||||||
|
url: this.newUrl,
|
||||||
|
mediaType: 'book', // Currently only supporting book mediaType
|
||||||
|
authHeaderValue: this.newAuthHeaderValue
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
this.$emit('added', data.provider)
|
||||||
|
this.$toast.success('New provider added')
|
||||||
|
this.show = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = error.response?.data || 'Unknown error'
|
||||||
|
console.error('Failed to add provider', error)
|
||||||
|
this.$toast.error('Failed to add provider: ' + errorMsg)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.processing = false
|
||||||
|
this.newName = ''
|
||||||
|
this.newUrl = ''
|
||||||
|
this.newAuthHeaderValue = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -34,11 +34,6 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
value(newVal) {
|
|
||||||
this.$nextTick(this.scrollToChapter)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
show: {
|
show: {
|
||||||
get() {
|
get() {
|
||||||
@@ -53,7 +48,7 @@ export default {
|
|||||||
return this.playbackRate
|
return this.playbackRate
|
||||||
},
|
},
|
||||||
currentChapterId() {
|
currentChapterId() {
|
||||||
return this.currentChapter ? this.currentChapter.id : null
|
return this.currentChapter?.id || null
|
||||||
},
|
},
|
||||||
currentChapterStart() {
|
currentChapterStart() {
|
||||||
return (this.currentChapter?.start || 0) / this._playbackRate
|
return (this.currentChapter?.start || 0) / this._playbackRate
|
||||||
@@ -74,6 +69,11 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
if (this.value) {
|
||||||
|
this.$nextTick(this.scrollToChapter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -49,8 +49,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="media.coverPath">
|
<div v-if="media.coverPath">
|
||||||
<p class="text-center text-gray-200">Current</p>
|
<p class="text-center text-gray-200">Current</p>
|
||||||
<a :href="$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId, null, true)" target="_blank" class="bg-primary">
|
<a :href="$store.getters['globals/getLibraryItemCoverSrc'](libraryItem, null, true)" target="_blank" class="bg-primary">
|
||||||
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId, null, true)" :width="100" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrc'](libraryItem, null, true)" :width="100" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -328,10 +328,22 @@ export default {
|
|||||||
console.error('PersistProvider', error)
|
console.error('PersistProvider', error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getDefaultBookProvider() {
|
||||||
|
let provider = localStorage.getItem('book-provider')
|
||||||
|
if (!provider) return 'google'
|
||||||
|
// Validate book provider
|
||||||
|
if (!this.$store.getters['scanners/checkBookProviderExists'](provider)) {
|
||||||
|
console.error('Stored book provider does not exist', provider)
|
||||||
|
localStorage.removeItem('book-provider')
|
||||||
|
return 'google'
|
||||||
|
}
|
||||||
|
return provider
|
||||||
|
},
|
||||||
getSearchQuery() {
|
getSearchQuery() {
|
||||||
if (this.isPodcast) return `term=${encodeURIComponent(this.searchTitle)}`
|
if (this.isPodcast) return `term=${encodeURIComponent(this.searchTitle)}`
|
||||||
var searchQuery = `provider=${this.provider}&fallbackTitleOnly=1&title=${encodeURIComponent(this.searchTitle)}`
|
var searchQuery = `provider=${this.provider}&fallbackTitleOnly=1&title=${encodeURIComponent(this.searchTitle)}`
|
||||||
if (this.searchAuthor) searchQuery += `&author=${encodeURIComponent(this.searchAuthor)}`
|
if (this.searchAuthor) searchQuery += `&author=${encodeURIComponent(this.searchAuthor)}`
|
||||||
|
if (this.libraryItemId) searchQuery += `&id=${this.libraryItemId}`
|
||||||
return searchQuery
|
return searchQuery
|
||||||
},
|
},
|
||||||
submitSearch() {
|
submitSearch() {
|
||||||
@@ -433,7 +445,9 @@ export default {
|
|||||||
this.searchTitle = this.libraryItem.media.metadata.title
|
this.searchTitle = this.libraryItem.media.metadata.title
|
||||||
this.searchAuthor = this.libraryItem.media.metadata.authorName || ''
|
this.searchAuthor = this.libraryItem.media.metadata.authorName || ''
|
||||||
if (this.isPodcast) this.provider = 'itunes'
|
if (this.isPodcast) this.provider = 'itunes'
|
||||||
else this.provider = localStorage.getItem('book-provider') || 'google'
|
else {
|
||||||
|
this.provider = this.getDefaultBookProvider()
|
||||||
|
}
|
||||||
|
|
||||||
// Prefer using ASIN if set and using audible provider
|
// Prefer using ASIN if set and using audible provider
|
||||||
if (this.provider.startsWith('audible') && this.libraryItem.media.metadata.asin) {
|
if (this.provider.startsWith('audible') && this.libraryItem.media.metadata.asin) {
|
||||||
@@ -532,24 +546,11 @@ export default {
|
|||||||
// Persist in local storage
|
// Persist in local storage
|
||||||
localStorage.setItem('selectedMatchUsage', JSON.stringify(this.selectedMatchUsage))
|
localStorage.setItem('selectedMatchUsage', JSON.stringify(this.selectedMatchUsage))
|
||||||
|
|
||||||
if (updatePayload.metadata.cover) {
|
|
||||||
const coverPayload = {
|
|
||||||
url: updatePayload.metadata.cover
|
|
||||||
}
|
|
||||||
const success = await this.$axios.$post(`/api/items/${this.libraryItemId}/cover`, coverPayload).catch((error) => {
|
|
||||||
console.error('Failed to update', error)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if (success) {
|
|
||||||
this.$toast.success(this.$strings.ToastItemCoverUpdateSuccess)
|
|
||||||
} else {
|
|
||||||
this.$toast.error(this.$strings.ToastItemCoverUpdateFailed)
|
|
||||||
}
|
|
||||||
console.log('Updated cover')
|
|
||||||
delete updatePayload.metadata.cover
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(updatePayload).length) {
|
if (Object.keys(updatePayload).length) {
|
||||||
|
if (updatePayload.metadata.cover) {
|
||||||
|
updatePayload.url = updatePayload.metadata.cover
|
||||||
|
delete updatePayload.metadata.cover
|
||||||
|
}
|
||||||
const mediaUpdatePayload = updatePayload
|
const mediaUpdatePayload = updatePayload
|
||||||
const updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, mediaUpdatePayload).catch((error) => {
|
const updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, mediaUpdatePayload).catch((error) => {
|
||||||
console.error('Failed to update', error)
|
console.error('Failed to update', error)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="enableAutoDownloadEpisodes" class="flex items-center py-2">
|
<div v-if="enableAutoDownloadEpisodes" class="flex items-center py-2">
|
||||||
<ui-text-input ref="maxEpisodesInput" type="number" v-model="newMaxNewEpisodesToDownload" no-spinner :padding-x="1" text-center class="w-10 text-base" @change="updateMaxNewEpisodesToDownload" />
|
<ui-text-input ref="maxEpisodesToDownloadInput" type="number" v-model="newMaxNewEpisodesToDownload" no-spinner :padding-x="1" text-center class="w-10 text-base" @change="updateMaxNewEpisodesToDownload" />
|
||||||
<ui-tooltip text="Value of 0 sets no max limit. When checking for new episodes this is the max number of episodes that will be downloaded.">
|
<ui-tooltip text="Value of 0 sets no max limit. When checking for new episodes this is the max number of episodes that will be downloaded.">
|
||||||
<p class="pl-4 text-base">
|
<p class="pl-4 text-base">
|
||||||
Max new episodes to download per check
|
Max new episodes to download per check
|
||||||
@@ -129,9 +129,12 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.$refs.maxEpisodesInput && this.$refs.maxEpisodesInput.isFocused) {
|
|
||||||
|
if (this.$refs.maxEpisodesInput?.isFocused) {
|
||||||
this.$refs.maxEpisodesInput.blur()
|
this.$refs.maxEpisodesInput.blur()
|
||||||
return
|
}
|
||||||
|
if (this.$refs.maxEpisodesToDownloadInput?.isFocused) {
|
||||||
|
this.$refs.maxEpisodesToDownloadInput.blur()
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePayload = {
|
const updatePayload = {
|
||||||
@@ -140,9 +143,11 @@ export default {
|
|||||||
if (this.enableAutoDownloadEpisodes) {
|
if (this.enableAutoDownloadEpisodes) {
|
||||||
updatePayload.autoDownloadSchedule = this.cronExpression
|
updatePayload.autoDownloadSchedule = this.cronExpression
|
||||||
}
|
}
|
||||||
|
this.newMaxEpisodesToKeep = Number(this.newMaxEpisodesToKeep)
|
||||||
if (this.newMaxEpisodesToKeep !== this.maxEpisodesToKeep) {
|
if (this.newMaxEpisodesToKeep !== this.maxEpisodesToKeep) {
|
||||||
updatePayload.maxEpisodesToKeep = this.newMaxEpisodesToKeep
|
updatePayload.maxEpisodesToKeep = this.newMaxEpisodesToKeep
|
||||||
}
|
}
|
||||||
|
this.newMaxNewEpisodesToDownload = Number(this.newMaxNewEpisodesToDownload)
|
||||||
if (this.newMaxNewEpisodesToDownload !== this.maxNewEpisodesToDownload) {
|
if (this.newMaxNewEpisodesToDownload !== this.maxNewEpisodesToDownload) {
|
||||||
updatePayload.maxNewEpisodesToDownload = this.newMaxNewEpisodesToDownload
|
updatePayload.maxNewEpisodesToDownload = this.newMaxNewEpisodesToDownload
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
<div class="w-full h-full overflow-hidden overflow-y-auto px-4 py-6">
|
<div class="w-full h-full overflow-hidden overflow-y-auto px-4 py-6">
|
||||||
<p class="text-xl font-semibold mb-2">{{ $strings.HeaderAudiobookTools }}</p>
|
<p class="text-xl font-semibold mb-2">{{ $strings.HeaderAudiobookTools }}</p>
|
||||||
|
|
||||||
|
<!-- alert for windows install -->
|
||||||
|
<widgets-alert v-if="isWindowsInstall" type="warning" class="my-8 text-base">Not supported for the Windows install yet</widgets-alert>
|
||||||
|
|
||||||
<!-- Merge to m4b -->
|
<!-- Merge to m4b -->
|
||||||
<div v-if="showM4bDownload" class="w-full border border-black-200 p-4 my-8">
|
<div v-if="showM4bDownload && !isWindowsInstall" class="w-full border border-black-200 p-4 my-8">
|
||||||
<div class="flex flex-wrap items-center">
|
<div class="flex flex-wrap items-center">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-lg">{{ $strings.LabelToolsMakeM4b }}</p>
|
<p class="text-lg">{{ $strings.LabelToolsMakeM4b }}</p>
|
||||||
@@ -19,22 +22,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Split to mp3 -->
|
|
||||||
<!-- <div v-if="showMp3Split" class="w-full border border-black-200 p-4 my-8">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div>
|
|
||||||
<p class="text-lg">{{ $strings.LabelToolsSplitM4b }}</p>
|
|
||||||
<p class="max-w-sm text-sm pt-2 text-gray-300">{{ $strings.LabelToolsSplitM4bDescription }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow" />
|
|
||||||
<div>
|
|
||||||
<ui-btn :disabled="true">{{ $strings.MessageNotYetImplemented }}</ui-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- Embed Metadata -->
|
<!-- Embed Metadata -->
|
||||||
<div v-if="mediaTracks.length" class="w-full border border-black-200 p-4 my-8">
|
<div v-if="mediaTracks.length && !isWindowsInstall" class="w-full border border-black-200 p-4 my-8">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-lg">{{ $strings.LabelToolsEmbedMetadata }}</p>
|
<p class="text-lg">{{ $strings.LabelToolsEmbedMetadata }}</p>
|
||||||
@@ -122,6 +111,12 @@ export default {
|
|||||||
},
|
},
|
||||||
isEncodeTaskRunning() {
|
isEncodeTaskRunning() {
|
||||||
return this.encodeTask && !this.encodeTask?.isFinished
|
return this.encodeTask && !this.encodeTask?.isFinished
|
||||||
|
},
|
||||||
|
isWindowsInstall() {
|
||||||
|
return this.Source == 'windows'
|
||||||
|
},
|
||||||
|
Source() {
|
||||||
|
return this.$store.state.Source
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<ui-btn class="w-full mt-2" color="primary" @click="browseForFolder">{{ $strings.ButtonBrowseForFolder }}</ui-btn>
|
<ui-btn class="w-full mt-2" color="primary" @click="browseForFolder">{{ $strings.ButtonBrowseForFolder }}</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<modals-libraries-folder-chooser v-else :paths="folderPaths" @back="showDirectoryPicker = false" @select="selectFolder" />
|
<modals-libraries-lazy-folder-chooser v-else :paths="folderPaths" @back="showDirectoryPicker = false" @select="selectFolder" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export default {
|
|||||||
skipMatchingMediaWithIsbn: false,
|
skipMatchingMediaWithIsbn: false,
|
||||||
autoScanCronExpression: null,
|
autoScanCronExpression: null,
|
||||||
hideSingleBookSeries: false,
|
hideSingleBookSeries: false,
|
||||||
|
onlyShowLaterBooksInContinueSeries: false,
|
||||||
metadataPrecedence: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata']
|
metadataPrecedence: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-45
@@ -4,35 +4,37 @@
|
|||||||
<span class="material-icons text-3xl cursor-pointer hover:text-gray-300" @click="$emit('back')">arrow_back</span>
|
<span class="material-icons text-3xl cursor-pointer hover:text-gray-300" @click="$emit('back')">arrow_back</span>
|
||||||
<p class="px-4 text-xl">{{ $strings.HeaderChooseAFolder }}</p>
|
<p class="px-4 text-xl">{{ $strings.HeaderChooseAFolder }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="allFolders.length" class="w-full bg-primary bg-opacity-70 py-1 px-4 mb-2">
|
<div v-if="rootDirs.length" class="w-full bg-primary bg-opacity-70 py-1 px-4 mb-2">
|
||||||
<p class="font-mono truncate">{{ selectedPath || '\\' }}</p>
|
<p class="font-mono truncate">{{ selectedPath || '/' }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="allFolders.length" class="flex bg-primary bg-opacity-50 p-4 folder-container">
|
<div v-if="rootDirs.length" class="relative flex bg-primary bg-opacity-50 p-4 folder-container">
|
||||||
<div class="w-1/2 border-r border-bg h-full overflow-y-auto">
|
<div class="w-1/2 border-r border-bg h-full overflow-y-auto">
|
||||||
<div v-if="level > 0" class="w-full p-1 cursor-pointer flex items-center" @click="goBack">
|
<div v-if="level > 0" class="w-full p-1 cursor-pointer flex items-center hover:bg-white/10" @click="goBack">
|
||||||
<span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
<span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
||||||
<p class="text-base font-mono px-2">..</p>
|
<p class="text-base font-mono px-2">..</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="dir in _directories" :key="dir.path" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200" :class="dir.className" @click="selectDir(dir)">
|
<div v-for="dir in _directories" :key="dir.path" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200 hover:bg-white/10" :class="dir.className" @click="selectDir(dir)">
|
||||||
<span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
<span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
||||||
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
|
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
|
||||||
<span v-if="dir.dirs && dir.dirs.length && dir.path === selectedPath" class="material-icons" style="font-size: 1.1rem">arrow_right</span>
|
<span v-if="dir.path === selectedPath" class="material-icons" style="font-size: 1.1rem">arrow_right</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/2 h-full overflow-y-auto">
|
<div class="w-1/2 h-full overflow-y-auto">
|
||||||
<div v-for="dir in _subdirs" :key="dir.path" :class="dir.className" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200" @click="selectSubDir(dir)">
|
<div v-for="dir in _subdirs" :key="dir.path" :class="dir.className" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200 hover:bg-white/10" @click="selectSubDir(dir)">
|
||||||
<span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
<span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
||||||
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
|
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="loadingDirs" class="absolute inset-0 w-full h-full flex items-center justify-center bg-black/10">
|
||||||
|
<ui-loading-indicator />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="loadingFolders" class="py-12 text-center">
|
<div v-else-if="initialLoad" class="py-12 text-center">
|
||||||
<p>{{ $strings.MessageLoadingFolders }}</p>
|
<p>{{ $strings.MessageLoadingFolders }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="py-12 text-center max-w-sm mx-auto">
|
<div v-else class="py-12 text-center max-w-sm mx-auto">
|
||||||
<p class="text-lg mb-2">{{ $strings.MessageNoFoldersAvailable }}</p>
|
<p class="text-lg mb-2">{{ $strings.MessageNoFoldersAvailable }}</p>
|
||||||
<p class="text-gray-300 mb-2">{{ $strings.NoteFolderPicker }}</p>
|
<p class="text-gray-300 mb-2">{{ $strings.NoteFolderPicker }}</p>
|
||||||
<p v-if="isDebian" class="text-red-400">{{ $strings.NoteFolderPickerDebian }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full py-2">
|
<div class="w-full py-2">
|
||||||
@@ -51,11 +53,12 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loadingFolders: false,
|
initialLoad: false,
|
||||||
allFolders: [],
|
loadingDirs: false,
|
||||||
|
isPosix: true,
|
||||||
|
rootDirs: [],
|
||||||
directories: [],
|
directories: [],
|
||||||
selectedPath: '',
|
selectedPath: '',
|
||||||
selectedFullPath: '',
|
|
||||||
subdirs: [],
|
subdirs: [],
|
||||||
level: 0,
|
level: 0,
|
||||||
currentDir: null,
|
currentDir: null,
|
||||||
@@ -89,68 +92,91 @@ export default {
|
|||||||
...d
|
...d
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
|
||||||
isDebian() {
|
|
||||||
return this.Source == 'debian'
|
|
||||||
},
|
|
||||||
Source() {
|
|
||||||
return this.$store.state.Source
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
async goBack() {
|
||||||
var splitPaths = this.selectedPath.split('\\').slice(1)
|
let selPath = this.selectedPath.replace(/^\//, '')
|
||||||
var prev = splitPaths.slice(0, -1).join('\\')
|
var splitPaths = selPath.split('/')
|
||||||
|
|
||||||
var currDirs = this.allFolders
|
let previousPath = ''
|
||||||
for (let i = 0; i < splitPaths.length; i++) {
|
let lookupPath = ''
|
||||||
var _dir = currDirs.find((dir) => dir.dirname === splitPaths[i])
|
|
||||||
if (_dir && _dir.path.slice(1) === prev) {
|
if (splitPaths.length > 2) {
|
||||||
this.directories = currDirs
|
lookupPath = splitPaths.slice(0, -2).join('/')
|
||||||
this.selectDir(_dir)
|
|
||||||
return
|
|
||||||
} else if (_dir) {
|
|
||||||
currDirs = _dir.dirs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
previousPath = splitPaths.slice(0, -1).join('/')
|
||||||
|
|
||||||
|
if (!this.isPosix) {
|
||||||
|
// For windows drives add a trailing slash. e.g. C:/
|
||||||
|
if (!this.isPosix && lookupPath.endsWith(':')) {
|
||||||
|
lookupPath += '/'
|
||||||
|
}
|
||||||
|
if (!this.isPosix && previousPath.endsWith(':')) {
|
||||||
|
previousPath += '/'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add leading slash
|
||||||
|
if (previousPath) previousPath = '/' + previousPath
|
||||||
|
if (lookupPath) lookupPath = '/' + lookupPath
|
||||||
|
}
|
||||||
|
|
||||||
|
this.level--
|
||||||
|
this.subdirs = this.directories
|
||||||
|
this.selectedPath = previousPath
|
||||||
|
this.directories = await this.fetchDirs(lookupPath, this.level)
|
||||||
},
|
},
|
||||||
selectDir(dir) {
|
async selectDir(dir) {
|
||||||
if (dir.isUsed) return
|
if (dir.isUsed) return
|
||||||
this.selectedPath = dir.path
|
this.selectedPath = dir.path
|
||||||
this.selectedFullPath = dir.fullPath
|
|
||||||
this.level = dir.level
|
this.level = dir.level
|
||||||
this.subdirs = dir.dirs
|
this.subdirs = await this.fetchDirs(dir.path, dir.level + 1)
|
||||||
},
|
},
|
||||||
selectSubDir(dir) {
|
async selectSubDir(dir) {
|
||||||
if (dir.isUsed) return
|
if (dir.isUsed) return
|
||||||
this.selectedPath = dir.path
|
this.selectedPath = dir.path
|
||||||
this.selectedFullPath = dir.fullPath
|
|
||||||
this.level = dir.level
|
this.level = dir.level
|
||||||
this.directories = this.subdirs
|
this.directories = this.subdirs
|
||||||
this.subdirs = dir.dirs
|
this.subdirs = await this.fetchDirs(dir.path, dir.level + 1)
|
||||||
},
|
},
|
||||||
selectFolder() {
|
selectFolder() {
|
||||||
if (!this.selectedPath) {
|
if (!this.selectedPath) {
|
||||||
console.error('No Selected path')
|
console.error('No Selected path')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.paths.find((p) => p.startsWith(this.selectedFullPath))) {
|
if (this.paths.find((p) => p.startsWith(this.selectedPath))) {
|
||||||
this.$toast.error(`Oops, you cannot add a parent directory of a folder already added`)
|
this.$toast.error(`Oops, you cannot add a parent directory of a folder already added`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$emit('select', this.selectedFullPath)
|
this.$emit('select', this.selectedPath)
|
||||||
this.selectedPath = ''
|
this.selectedPath = ''
|
||||||
this.selectedFullPath = ''
|
},
|
||||||
|
fetchDirs(path, level) {
|
||||||
|
this.loadingDirs = true
|
||||||
|
return this.$axios
|
||||||
|
.$get(`/api/filesystem?path=${path}&level=${level}`)
|
||||||
|
.then((data) => {
|
||||||
|
console.log('Fetched directories', data.directories)
|
||||||
|
this.isPosix = !!data.posix
|
||||||
|
return data.directories
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to get filesystem paths', error)
|
||||||
|
this.$toast.error('Failed to get filesystem paths')
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadingDirs = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
this.loadingFolders = true
|
this.initialLoad = true
|
||||||
this.allFolders = await this.$store.dispatch('libraries/loadFolders')
|
this.rootDirs = await this.fetchDirs('', 0)
|
||||||
this.loadingFolders = false
|
this.initialLoad = false
|
||||||
|
|
||||||
this.directories = this.allFolders
|
this.directories = this.rootDirs
|
||||||
this.subdirs = []
|
this.subdirs = []
|
||||||
this.selectedPath = ''
|
this.selectedPath = ''
|
||||||
this.selectedFullPath = ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -63,7 +63,7 @@ export default {
|
|||||||
},
|
},
|
||||||
audioMetatags: {
|
audioMetatags: {
|
||||||
id: 'audioMetatags',
|
id: 'audioMetatags',
|
||||||
name: 'Audio file meta tags',
|
name: 'Audio file meta tags OR ebook metadata',
|
||||||
include: true
|
include: true
|
||||||
},
|
},
|
||||||
nfoFile: {
|
nfoFile: {
|
||||||
|
|||||||
@@ -49,6 +49,20 @@
|
|||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="isBookLibrary" class="py-3">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<ui-toggle-switch v-model="onlyShowLaterBooksInContinueSeries" @input="formUpdated" />
|
||||||
|
<ui-tooltip :text="$strings.LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp">
|
||||||
|
<p class="pl-4 text-base">
|
||||||
|
{{ $strings.LabelSettingsOnlyShowLaterBooksInContinueSeries }}
|
||||||
|
<span class="material-icons icon-text text-sm">info_outlined</span>
|
||||||
|
</p>
|
||||||
|
</ui-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="isPodcastLibrary" class="py-3">
|
||||||
|
<ui-dropdown :label="$strings.LabelPodcastSearchRegion" v-model="podcastSearchRegion" :items="$podcastSearchRegionOptions" small class="max-w-52" @input="formUpdated" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -69,7 +83,9 @@ export default {
|
|||||||
skipMatchingMediaWithAsin: false,
|
skipMatchingMediaWithAsin: false,
|
||||||
skipMatchingMediaWithIsbn: false,
|
skipMatchingMediaWithIsbn: false,
|
||||||
audiobooksOnly: false,
|
audiobooksOnly: false,
|
||||||
hideSingleBookSeries: false
|
hideSingleBookSeries: false,
|
||||||
|
onlyShowLaterBooksInContinueSeries: false,
|
||||||
|
podcastSearchRegion: 'us'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -85,6 +101,9 @@ export default {
|
|||||||
isBookLibrary() {
|
isBookLibrary() {
|
||||||
return this.mediaType === 'book'
|
return this.mediaType === 'book'
|
||||||
},
|
},
|
||||||
|
isPodcastLibrary() {
|
||||||
|
return this.mediaType === 'podcast'
|
||||||
|
},
|
||||||
providers() {
|
providers() {
|
||||||
if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
|
if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
|
||||||
return this.$store.state.scanners.providers
|
return this.$store.state.scanners.providers
|
||||||
@@ -99,7 +118,9 @@ export default {
|
|||||||
skipMatchingMediaWithAsin: !!this.skipMatchingMediaWithAsin,
|
skipMatchingMediaWithAsin: !!this.skipMatchingMediaWithAsin,
|
||||||
skipMatchingMediaWithIsbn: !!this.skipMatchingMediaWithIsbn,
|
skipMatchingMediaWithIsbn: !!this.skipMatchingMediaWithIsbn,
|
||||||
audiobooksOnly: !!this.audiobooksOnly,
|
audiobooksOnly: !!this.audiobooksOnly,
|
||||||
hideSingleBookSeries: !!this.hideSingleBookSeries
|
hideSingleBookSeries: !!this.hideSingleBookSeries,
|
||||||
|
onlyShowLaterBooksInContinueSeries: !!this.onlyShowLaterBooksInContinueSeries,
|
||||||
|
podcastSearchRegion: this.podcastSearchRegion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -113,6 +134,8 @@ export default {
|
|||||||
this.skipMatchingMediaWithIsbn = !!this.librarySettings.skipMatchingMediaWithIsbn
|
this.skipMatchingMediaWithIsbn = !!this.librarySettings.skipMatchingMediaWithIsbn
|
||||||
this.audiobooksOnly = !!this.librarySettings.audiobooksOnly
|
this.audiobooksOnly = !!this.librarySettings.audiobooksOnly
|
||||||
this.hideSingleBookSeries = !!this.librarySettings.hideSingleBookSeries
|
this.hideSingleBookSeries = !!this.librarySettings.hideSingleBookSeries
|
||||||
|
this.onlyShowLaterBooksInContinueSeries = !!this.librarySettings.onlyShowLaterBooksInContinueSeries
|
||||||
|
this.podcastSearchRegion = this.librarySettings.podcastSearchRegion || 'us'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<div class="break-words">{{ episode.title }}</div>
|
<div class="break-words">{{ episode.title }}</div>
|
||||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
||||||
</div>
|
</div>
|
||||||
<p v-if="episode.subtitle" class="break-words mb-1 text-sm text-gray-300 episode-subtitle">{{ episode.subtitle }}</p>
|
<p v-if="episode.subtitle" class="mb-1 text-sm text-gray-300 line-clamp-2">{{ episode.subtitle }}</p>
|
||||||
<p class="text-xs text-gray-300">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
|
<p class="text-xs text-gray-300">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +68,9 @@ export default {
|
|||||||
selectAll: false,
|
selectAll: false,
|
||||||
search: null,
|
search: null,
|
||||||
searchTimeout: null,
|
searchTimeout: null,
|
||||||
searchText: null
|
searchText: null,
|
||||||
|
downloadedEpisodeGuidMap: {},
|
||||||
|
downloadedEpisodeUrlMap: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -122,11 +124,13 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getIsEpisodeDownloaded(episode) {
|
getIsEpisodeDownloaded(episode) {
|
||||||
return this.itemEpisodes.some((downloadedEpisode) => {
|
if (episode.guid && !!this.downloadedEpisodeGuidMap[episode.guid]) {
|
||||||
if (episode.guid && downloadedEpisode.guid === episode.guid) return true
|
return true
|
||||||
if (!downloadedEpisode.enclosure?.url) return false
|
}
|
||||||
return this.getCleanEpisodeUrl(downloadedEpisode.enclosure.url) === episode.cleanUrl
|
if (this.downloadedEpisodeUrlMap[episode.cleanUrl]) {
|
||||||
})
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* UPDATE: As of v2.4.5 guid is used for matching existing downloaded episodes if it is found on the RSS feed.
|
* UPDATE: As of v2.4.5 guid is used for matching existing downloaded episodes if it is found on the RSS feed.
|
||||||
@@ -219,6 +223,14 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
|
this.downloadedEpisodeGuidMap = {}
|
||||||
|
this.downloadedEpisodeUrlMap = {}
|
||||||
|
|
||||||
|
this.itemEpisodes.forEach((episode) => {
|
||||||
|
if (episode.guid) this.downloadedEpisodeGuidMap[episode.guid] = episode.id
|
||||||
|
if (episode.enclosure?.url) this.downloadedEpisodeUrlMap[this.getCleanEpisodeUrl(episode.enclosure.url)] = episode.id
|
||||||
|
})
|
||||||
|
|
||||||
this.episodesCleaned = this.episodes
|
this.episodesCleaned = this.episodes
|
||||||
.filter((ep) => ep.enclosure?.url)
|
.filter((ep) => ep.enclosure?.url)
|
||||||
.map((_ep) => {
|
.map((_ep) => {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<div class="w-full p-1">
|
<div class="w-full p-1">
|
||||||
<ui-textarea-with-label v-model="newEpisode.subtitle" :label="$strings.LabelSubtitle" :rows="3" />
|
<ui-textarea-with-label v-model="newEpisode.subtitle" :label="$strings.LabelSubtitle" :rows="3" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full p-1 default-style">
|
<div class="w-full p-1">
|
||||||
<ui-rich-text-editor :label="$strings.LabelDescription" v-model="newEpisode.description" />
|
<ui-rich-text-editor :label="$strings.LabelDescription" v-model="newEpisode.description" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<div v-for="(episode, index) in episodesFound" :key="index" class="w-full py-4 border-b border-white border-opacity-5 hover:bg-gray-300 hover:bg-opacity-10 cursor-pointer px-2" @click.stop="selectEpisode(episode)">
|
<div v-for="(episode, index) in episodesFound" :key="index" class="w-full py-4 border-b border-white border-opacity-5 hover:bg-gray-300 hover:bg-opacity-10 cursor-pointer px-2" @click.stop="selectEpisode(episode)">
|
||||||
<p v-if="episode.episode" class="font-semibold text-gray-200">#{{ episode.episode }}</p>
|
<p v-if="episode.episode" class="font-semibold text-gray-200">#{{ episode.episode }}</p>
|
||||||
<p class="break-words mb-1">{{ episode.title }}</p>
|
<p class="break-words mb-1">{{ episode.title }}</p>
|
||||||
<p v-if="episode.subtitle" class="break-words mb-1 text-sm text-gray-300 episode-subtitle">{{ episode.subtitle }}</p>
|
<p v-if="episode.subtitle" class="mb-1 text-sm text-gray-300 line-clamp-2">{{ episode.subtitle }}</p>
|
||||||
<p class="text-xs text-gray-400">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
|
<p class="text-xs text-gray-400">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
<div class="flex pt-4 pb-2 md:pt-0 md:pb-2">
|
<div class="flex pt-4 pb-2 md:pt-0 md:pb-2">
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<template v-if="!loading">
|
<template v-if="!loading">
|
||||||
<div class="cursor-pointer flex items-center justify-center text-gray-300 mr-4 md:mr-8" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
|
<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>
|
<span class="material-icons text-2xl sm:text-3xl">first_page</span>
|
||||||
</div>
|
</button>
|
||||||
<div class="cursor-pointer flex items-center justify-center text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
|
<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>
|
<span class="material-icons text-2xl sm:text-3xl">replay_10</span>
|
||||||
</div>
|
</button>
|
||||||
<div class="cursor-pointer 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">
|
<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">
|
||||||
<span class="material-icons text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
|
<span class="material-icons text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
|
||||||
</div>
|
</button>
|
||||||
<div class="cursor-pointer flex items-center justify-center text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
|
<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>
|
<span class="material-icons text-2xl sm:text-3xl">forward_10</span>
|
||||||
</div>
|
</button>
|
||||||
<div class="flex items-center justify-center ml-4 md:ml-8" :class="hasNextChapter ? 'text-gray-300 cursor-pointer' : 'text-gray-500'" @mousedown.prevent @mouseup.prevent @click.stop="nextChapter">
|
<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>
|
<span class="material-icons text-2xl sm:text-3xl">last_page</span>
|
||||||
</div>
|
</button>
|
||||||
<controls-playback-speed-control v-model="playbackRateInput" @input="playbackRateUpdated" @change="playbackRateChanged" />
|
<controls-playback-speed-control v-model="playbackRateInput" @input="playbackRateUpdated" @change="playbackRateChanged" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
duration: {
|
duration: {
|
||||||
immediate: true,
|
|
||||||
handler() {
|
handler() {
|
||||||
this.setChapterTicks()
|
this.setChapterTicks()
|
||||||
}
|
}
|
||||||
@@ -205,10 +204,14 @@ export default {
|
|||||||
},
|
},
|
||||||
windowResize() {
|
windowResize() {
|
||||||
this.setTrackWidth()
|
this.setTrackWidth()
|
||||||
|
this.setChapterTicks()
|
||||||
|
this.updatePlayedTrackWidth()
|
||||||
|
this.updateBufferTrack()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setTrackWidth()
|
this.setTrackWidth()
|
||||||
|
this.setChapterTicks()
|
||||||
window.addEventListener('resize', this.windowResize)
|
window.addEventListener('resize', this.windowResize)
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
|||||||
@@ -9,37 +9,37 @@
|
|||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-tooltip direction="top" :text="$strings.LabelSleepTimer">
|
<ui-tooltip direction="top" :text="$strings.LabelSleepTimer">
|
||||||
<div class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showSleepTimer')">
|
<button :aria-label="$strings.LabelSleepTimer" class="text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showSleepTimer')">
|
||||||
<span v-if="!sleepTimerSet" class="material-icons text-2xl">snooze</span>
|
<span v-if="!sleepTimerSet" class="material-icons text-2xl">snooze</span>
|
||||||
<div v-else class="flex items-center">
|
<div v-else class="flex items-center">
|
||||||
<span class="material-icons text-lg text-warning">snooze</span>
|
<span class="material-icons text-lg text-warning">snooze</span>
|
||||||
<p class="text-xl text-warning font-mono font-semibold text-center px-0.5 pb-0.5" style="min-width: 30px">{{ sleepTimerRemainingString }}</p>
|
<p class="text-xl text-warning font-mono font-semibold text-center px-0.5 pb-0.5" style="min-width: 30px">{{ sleepTimerRemainingString }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-tooltip v-if="!isPodcast" direction="top" :text="$strings.LabelViewBookmarks">
|
<ui-tooltip v-if="!isPodcast" direction="top" :text="$strings.LabelViewBookmarks">
|
||||||
<div class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showBookmarks')">
|
<button :aria-label="$strings.LabelViewBookmarks" class="text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showBookmarks')">
|
||||||
<span class="material-icons text-2xl">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span>
|
<span class="material-icons text-2xl">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span>
|
||||||
</div>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-tooltip v-if="chapters.length" direction="top" :text="$strings.LabelViewChapters">
|
<ui-tooltip v-if="chapters.length" direction="top" :text="$strings.LabelViewChapters">
|
||||||
<div class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="showChapters">
|
<button :aria-label="$strings.LabelViewChapters" class="text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="showChapters">
|
||||||
<span class="material-icons text-2xl">format_list_bulleted</span>
|
<span class="material-icons text-2xl">format_list_bulleted</span>
|
||||||
</div>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-tooltip v-if="playerQueueItems.length" direction="top" :text="$strings.LabelViewQueue">
|
<ui-tooltip v-if="playerQueueItems.length" direction="top" :text="$strings.LabelViewQueue">
|
||||||
<button class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')">
|
<button :aria-label="$strings.LabelViewQueue" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')">
|
||||||
<span class="material-icons text-2.5xl sm:text-3xl">playlist_play</span>
|
<span class="material-icons text-2.5xl sm:text-3xl">playlist_play</span>
|
||||||
</button>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack">
|
<ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack">
|
||||||
<div class="cursor-pointer text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="setUseChapterTrack">
|
<button :aria-label="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack" class="text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="setUseChapterTrack">
|
||||||
<span class="material-icons text-2xl sm:text-3xl transform transition-transform" :class="useChapterTrack ? 'rotate-180' : ''">timelapse</span>
|
<span class="material-icons text-2xl sm:text-3xl transform transition-transform" :class="useChapterTrack ? 'rotate-180' : ''">timelapse</span>
|
||||||
</div>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full">
|
<div class="w-full h-full">
|
||||||
<div v-show="showPageMenu" v-click-outside="clickOutside" class="pagemenu absolute top-9 left-8 rounded-md overflow-y-auto bg-bg shadow-lg z-20 border border-gray-400" :style="{ width: pageMenuWidth + 'px' }">
|
<div v-show="showPageMenu" v-click-outside="clickOutside" class="pagemenu absolute top-9 left-8 rounded-md overflow-y-auto bg-bg shadow-lg z-20 border border-gray-400" :style="{ width: pageMenuWidth + 'px' }">
|
||||||
<div v-for="(file, index) in cleanedPageNames" :key="file" class="w-full cursor-pointer hover:bg-black-200 px-2 py-1" :class="page === index ? 'bg-black-200' : ''" @click="setPage(index + 1)">
|
<div v-for="(file, index) in cleanedPageNames" :key="file" class="w-full cursor-pointer hover:bg-black-200 px-2 py-1" :class="page === index + 1 ? 'bg-black-200' : ''" @click="setPage(index + 1)">
|
||||||
<p class="text-sm truncate">{{ file }}</p>
|
<p class="text-sm truncate">{{ file }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -14,34 +14,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a v-if="pages && numPages" :href="mainImg" :download="pages[page - 1]" class="absolute top-0 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" :class="comicMetadata ? 'left-32' : 'left-20'">
|
<div v-if="numPages" class="absolute top-0 left-4 sm:left-8 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowPageMenu">
|
||||||
<span class="material-icons text-xl">download</span>
|
|
||||||
</a>
|
|
||||||
<div v-if="comicMetadata" class="absolute top-0 left-20 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowInfoMenu">
|
|
||||||
<span class="material-icons text-xl">more</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="numPages" class="absolute top-0 left-8 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowPageMenu">
|
|
||||||
<span class="material-icons text-xl">menu</span>
|
<span class="material-icons text-xl">menu</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="numPages" class="absolute top-0 right-16 bg-bg text-gray-100 border-b border-l border-r border-gray-400 rounded-b-md px-2 h-9 flex items-center text-center z-20">
|
<div v-if="comicMetadata" class="absolute top-0 left-16 sm:left-20 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowInfoMenu">
|
||||||
|
<span class="material-icons text-xl">more</span>
|
||||||
|
</div>
|
||||||
|
<a v-if="pages && numPages" :href="mainImg" :download="pages[page - 1]" class="absolute top-0 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" :class="comicMetadata ? 'left-28 sm:left-32' : 'left-16 sm:left-20'">
|
||||||
|
<span class="material-icons text-xl">download</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div v-if="numPages" class="absolute top-0 right-14 sm:right-16 bg-bg text-gray-100 border-b border-l border-r border-gray-400 rounded-b-md px-2 h-9 flex items-center text-center z-20">
|
||||||
<p class="font-mono">{{ page }} / {{ numPages }}</p>
|
<p class="font-mono">{{ page }} / {{ numPages }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="mainImg" class="absolute top-0 right-36 sm:right-40 bg-bg text-gray-100 border-b border-l border-r border-gray-400 rounded-b-md px-2 h-9 flex items-center text-center z-20">
|
||||||
|
<ui-icon-btn icon="zoom_out" :size="8" :disabled="!canScaleDown" borderless class="mr-px" @click="zoomOut" />
|
||||||
|
<ui-icon-btn icon="zoom_in" :size="8" :disabled="!canScaleUp" borderless class="ml-px" @click="zoomIn" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="overflow-hidden w-full h-full relative">
|
<div class="w-full h-full relative">
|
||||||
<div v-show="canGoPrev" class="absolute top-0 left-0 h-full w-1/2 lg:w-1/3 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="prev" @mousedown.prevent>
|
<div v-show="canGoPrev" ref="prevButton" class="absolute top-0 left-0 h-full w-1/2 lg:w-1/3 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="prev" @mousedown.prevent>
|
||||||
<div class="flex items-center justify-center h-full w-1/2">
|
<div class="flex items-center justify-center h-full w-1/2">
|
||||||
<span v-show="loadedFirstPage" class="material-icons text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_back_ios</span>
|
<span v-show="loadedFirstPage" class="material-icons text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_back_ios</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="canGoNext" class="absolute top-0 right-0 h-full w-1/2 lg:w-1/3 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="next" @mousedown.prevent>
|
<div v-show="canGoNext" ref="nextButton" class="absolute top-0 right-0 h-full w-1/2 lg:w-1/3 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="next" @mousedown.prevent>
|
||||||
<div class="flex items-center justify-center h-full w-1/2 ml-auto">
|
<div class="flex items-center justify-center h-full w-1/2 ml-auto">
|
||||||
<span v-show="loadedFirstPage" class="material-icons text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_forward_ios</span>
|
<span v-show="loadedFirstPage" class="material-icons text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_forward_ios</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-full flex justify-center">
|
<div ref="imageContainer" class="w-full h-full relative overflow-auto">
|
||||||
<img v-if="mainImg" :src="mainImg" class="object-contain h-full m-auto" />
|
<div class="h-full flex" :class="scale > 100 ? '' : 'justify-center'">
|
||||||
|
<img v-if="mainImg" :style="{ minWidth: scale + '%', width: scale + '%' }" :src="mainImg" class="object-contain m-auto" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-show="loading" class="w-full h-full absolute top-0 left-0 flex items-center justify-center z-10">
|
<div v-show="loading" class="w-full h-full absolute top-0 left-0 flex items-center justify-center z-10">
|
||||||
<ui-loading-indicator />
|
<ui-loading-indicator />
|
||||||
</div>
|
</div>
|
||||||
@@ -54,6 +60,10 @@ import Path from 'path'
|
|||||||
import { Archive } from 'libarchive.js/main.js'
|
import { Archive } from 'libarchive.js/main.js'
|
||||||
import { CompressedFile } from 'libarchive.js/src/compressed-file'
|
import { CompressedFile } from 'libarchive.js/src/compressed-file'
|
||||||
|
|
||||||
|
// This is % with respect to the screen width
|
||||||
|
const MAX_SCALE = 400
|
||||||
|
const MIN_SCALE = 10
|
||||||
|
|
||||||
Archive.init({
|
Archive.init({
|
||||||
workerUrl: '/libarchive/worker-bundle.js'
|
workerUrl: '/libarchive/worker-bundle.js'
|
||||||
})
|
})
|
||||||
@@ -81,7 +91,8 @@ export default {
|
|||||||
showInfoMenu: false,
|
showInfoMenu: false,
|
||||||
loadTimeout: null,
|
loadTimeout: null,
|
||||||
loadedFirstPage: false,
|
loadedFirstPage: false,
|
||||||
comicMetadata: null
|
comicMetadata: null,
|
||||||
|
scale: 80
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -136,6 +147,12 @@ export default {
|
|||||||
return p
|
return p
|
||||||
}) || []
|
}) || []
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
canScaleUp() {
|
||||||
|
return this.scale < MAX_SCALE
|
||||||
|
},
|
||||||
|
canScaleDown() {
|
||||||
|
return this.scale > MIN_SCALE
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -331,10 +348,37 @@ export default {
|
|||||||
orderedImages = orderedImages.concat(noNumImages.map((i) => i.filename))
|
orderedImages = orderedImages.concat(noNumImages.map((i) => i.filename))
|
||||||
|
|
||||||
this.pages = orderedImages
|
this.pages = orderedImages
|
||||||
|
},
|
||||||
|
zoomIn() {
|
||||||
|
this.scale += 10
|
||||||
|
},
|
||||||
|
zoomOut() {
|
||||||
|
this.scale -= 10
|
||||||
|
},
|
||||||
|
scroll(event) {
|
||||||
|
const imageContainer = this.$refs.imageContainer
|
||||||
|
|
||||||
|
imageContainer.scrollBy({
|
||||||
|
top: event.deltaY,
|
||||||
|
left: event.deltaX,
|
||||||
|
behavior: 'auto'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {},
|
mounted() {
|
||||||
beforeDestroy() {}
|
const prevButton = this.$refs.prevButton
|
||||||
|
const nextButton = this.$refs.nextButton
|
||||||
|
|
||||||
|
prevButton.addEventListener('wheel', this.scroll, { passive: false })
|
||||||
|
nextButton.addEventListener('wheel', this.scroll, { passive: false })
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
const prevButton = this.$refs.prevButton
|
||||||
|
const nextButton = this.$refs.nextButton
|
||||||
|
|
||||||
|
prevButton.removeEventListener('wheel', this.scroll, { passive: false })
|
||||||
|
nextButton.removeEventListener('wheel', this.scroll, { passive: false })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ export default {
|
|||||||
reader.rendition = reader.book.renderTo('viewer', {
|
reader.rendition = reader.book.renderTo('viewer', {
|
||||||
width: this.readerWidth,
|
width: this.readerWidth,
|
||||||
height: this.readerHeight * 0.8,
|
height: this.readerHeight * 0.8,
|
||||||
|
allowScriptedContent: true,
|
||||||
spread: 'auto',
|
spread: 'auto',
|
||||||
snap: true,
|
snap: true,
|
||||||
manager: 'continuous',
|
manager: 'continuous',
|
||||||
|
|||||||
@@ -0,0 +1,285 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="processing" class="max-w-[800px] h-80 md:h-[800px] mx-auto flex items-center justify-center">
|
||||||
|
<widgets-loading-spinner />
|
||||||
|
</div>
|
||||||
|
<img v-else-if="dataUrl" :src="dataUrl" class="mx-auto" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
variant: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
year: Number,
|
||||||
|
processing: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
canvas: null,
|
||||||
|
dataUrl: null,
|
||||||
|
yearStats: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
variant() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initCanvas() {
|
||||||
|
if (!this.yearStats) return
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = 800
|
||||||
|
canvas.height = 800
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
const createRoundedRect = (x, y, w, h) => {
|
||||||
|
const grd1 = ctx.createLinearGradient(x, y, x + w, y + h)
|
||||||
|
grd1.addColorStop(0, '#44444455')
|
||||||
|
grd1.addColorStop(1, '#ffffff11')
|
||||||
|
ctx.fillStyle = grd1
|
||||||
|
ctx.strokeStyle = '#C0C0C088'
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.roundRect(x, y, w, h, [20])
|
||||||
|
ctx.fill()
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
const addText = (text, fontSize, fontWeight, color, letterSpacing, x, y, maxWidth = 0) => {
|
||||||
|
ctx.fillStyle = color
|
||||||
|
ctx.font = `${fontWeight} ${fontSize} Source Sans Pro`
|
||||||
|
ctx.letterSpacing = letterSpacing
|
||||||
|
|
||||||
|
// If maxWidth is specified then continue to remove chars until under maxWidth and add ellipsis
|
||||||
|
if (maxWidth) {
|
||||||
|
let txtWidth = ctx.measureText(text).width
|
||||||
|
while (txtWidth > maxWidth) {
|
||||||
|
console.warn(`Text "${text}" is greater than max width ${maxWidth} (width:${txtWidth})`)
|
||||||
|
if (text.endsWith('...')) text = text.slice(0, -4) // Repeated checks remove 1 char at a time
|
||||||
|
else text = text.slice(0, -3) // First check remove last 3 chars
|
||||||
|
text += '...'
|
||||||
|
txtWidth = ctx.measureText(text).width
|
||||||
|
console.log(`Checking text "${text}" (width:${txtWidth})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillText(text, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addIcon = (icon, color, fontSize, x, y) => {
|
||||||
|
ctx.fillStyle = color
|
||||||
|
ctx.font = `${fontSize} Material Icons Outlined`
|
||||||
|
ctx.fillText(icon, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bg color
|
||||||
|
ctx.fillStyle = '#232323'
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Cover image tiles
|
||||||
|
const bookCovers = this.yearStats.finishedBooksWithCovers
|
||||||
|
bookCovers.push(...this.yearStats.booksWithCovers)
|
||||||
|
|
||||||
|
let finishedBookCoverImgs = {}
|
||||||
|
|
||||||
|
if (bookCovers.length) {
|
||||||
|
let index = 0
|
||||||
|
ctx.globalAlpha = 0.25
|
||||||
|
ctx.save()
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2)
|
||||||
|
ctx.rotate((-Math.PI / 180) * 25)
|
||||||
|
ctx.translate(-canvas.width / 2, -canvas.height / 2)
|
||||||
|
ctx.translate(-130, -120)
|
||||||
|
for (let x = 0; x < 5; x++) {
|
||||||
|
for (let y = 0; y < 5; y++) {
|
||||||
|
const coverIndex = index % bookCovers.length
|
||||||
|
let libraryItemId = bookCovers[coverIndex]
|
||||||
|
index++
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.crossOrigin = 'anonymous'
|
||||||
|
img.addEventListener('load', () => {
|
||||||
|
let sw = img.width
|
||||||
|
if (img.width > img.height) {
|
||||||
|
sw = img.height
|
||||||
|
}
|
||||||
|
let sx = -(sw - img.width) / 2
|
||||||
|
let sy = -(sw - img.height) / 2
|
||||||
|
ctx.drawImage(img, sx, sy, sw, sw, 215 * x, 215 * y, 215, 215)
|
||||||
|
resolve()
|
||||||
|
if (this.yearStats.finishedBooksWithCovers.includes(libraryItemId) && !finishedBookCoverImgs[libraryItemId]) {
|
||||||
|
finishedBookCoverImgs[libraryItemId] = {
|
||||||
|
img,
|
||||||
|
sx,
|
||||||
|
sy,
|
||||||
|
sw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
img.addEventListener('error', () => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
img.src = this.$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
|
// Create gradient
|
||||||
|
const grd1 = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
|
||||||
|
grd1.addColorStop(0, '#000000aa')
|
||||||
|
grd1.addColorStop(1, '#cd9d49aa')
|
||||||
|
ctx.fillStyle = grd1
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Top Abs icon
|
||||||
|
let tanColor = '#ffdb70'
|
||||||
|
ctx.fillStyle = tanColor
|
||||||
|
ctx.font = '42px absicons'
|
||||||
|
ctx.fillText('\ue900', 15, 36)
|
||||||
|
|
||||||
|
// Top text
|
||||||
|
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
||||||
|
addText(`${this.year} YEAR IN REVIEW`, '18px', 'bold', 'white', '1px', 65, 51)
|
||||||
|
|
||||||
|
// Top left box
|
||||||
|
createRoundedRect(50, 100, 340, 160)
|
||||||
|
addText(this.yearStats.numBooksFinished, '64px', 'bold', 'white', '0px', 160, 165)
|
||||||
|
addText('books finished', '28px', 'normal', tanColor, '0px', 160, 210)
|
||||||
|
const readIconPath = new Path2D()
|
||||||
|
readIconPath.addPath(new Path2D('M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z'), { a: 2, d: 2, e: 100, f: 160 })
|
||||||
|
ctx.fillStyle = '#ffffff'
|
||||||
|
ctx.fill(readIconPath)
|
||||||
|
|
||||||
|
// Box top right
|
||||||
|
createRoundedRect(410, 100, 340, 160)
|
||||||
|
addText(this.$elapsedPrettyExtended(this.yearStats.totalListeningTime, true, false), '40px', 'bold', 'white', '0px', 500, 165)
|
||||||
|
addText('spent listening', '28px', 'normal', tanColor, '0px', 500, 205)
|
||||||
|
addIcon('watch_later', 'white', '52px', 440, 180)
|
||||||
|
|
||||||
|
// Box bottom left
|
||||||
|
createRoundedRect(50, 280, 340, 160)
|
||||||
|
addText(this.yearStats.totalListeningSessions, '64px', 'bold', 'white', '0px', 160, 345)
|
||||||
|
addText('sessions', '28px', 'normal', tanColor, '1px', 160, 390)
|
||||||
|
addIcon('headphones', 'white', '52px', 95, 360)
|
||||||
|
|
||||||
|
// Box bottom right
|
||||||
|
createRoundedRect(410, 280, 340, 160)
|
||||||
|
addText(this.yearStats.numBooksListened, '64px', 'bold', 'white', '0px', 500, 345)
|
||||||
|
addText('books listened to', '28px', 'normal', tanColor, '0px', 500, 390)
|
||||||
|
addIcon('local_library', 'white', '52px', 440, 360)
|
||||||
|
|
||||||
|
if (!this.variant) {
|
||||||
|
// Text stats
|
||||||
|
const topNarrator = this.yearStats.mostListenedNarrator
|
||||||
|
if (topNarrator) {
|
||||||
|
addText('TOP NARRATOR', '24px', 'normal', tanColor, '1px', 70, 520)
|
||||||
|
addText(topNarrator.name, '36px', 'bolder', 'white', '0px', 70, 564, 330)
|
||||||
|
addText(this.$elapsedPrettyExtended(topNarrator.time, true, false), '24px', 'lighter', 'white', '1px', 70, 599)
|
||||||
|
}
|
||||||
|
|
||||||
|
const topGenre = this.yearStats.topGenres[0]
|
||||||
|
if (topGenre) {
|
||||||
|
addText('TOP GENRE', '24px', 'normal', tanColor, '1px', 430, 520)
|
||||||
|
addText(topGenre.genre, '36px', 'bolder', 'white', '0px', 430, 564, 330)
|
||||||
|
addText(this.$elapsedPrettyExtended(topGenre.time, true, false), '24px', 'lighter', 'white', '1px', 430, 599)
|
||||||
|
}
|
||||||
|
|
||||||
|
const topAuthor = this.yearStats.topAuthors[0]
|
||||||
|
if (topAuthor) {
|
||||||
|
addText('TOP AUTHOR', '24px', 'normal', tanColor, '1px', 70, 670)
|
||||||
|
addText(topAuthor.name, '36px', 'bolder', 'white', '0px', 70, 714, 330)
|
||||||
|
addText(this.$elapsedPrettyExtended(topAuthor.time, true, false), '24px', 'lighter', 'white', '1px', 70, 749)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.yearStats.mostListenedMonth?.time) {
|
||||||
|
const jsdate = new Date(this.year, this.yearStats.mostListenedMonth.month, 1)
|
||||||
|
const monthName = this.$formatJsDate(jsdate, 'LLLL')
|
||||||
|
addText('TOP MONTH', '24px', 'normal', tanColor, '1px', 430, 670)
|
||||||
|
addText(monthName, '36px', 'bolder', 'white', '0px', 430, 714, 330)
|
||||||
|
addText(this.$elapsedPrettyExtended(this.yearStats.mostListenedMonth.time, true, false), '24px', 'lighter', 'white', '1px', 430, 749)
|
||||||
|
}
|
||||||
|
} else if (this.variant === 1) {
|
||||||
|
// Bottom images
|
||||||
|
finishedBookCoverImgs = Object.values(finishedBookCoverImgs)
|
||||||
|
if (finishedBookCoverImgs.length > 0) {
|
||||||
|
ctx.textAlign = 'center'
|
||||||
|
addText('Some books finished this year...', '28px', 'normal', tanColor, '0px', canvas.width / 2, 530)
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(5, finishedBookCoverImgs.length); i++) {
|
||||||
|
let imgToAdd = finishedBookCoverImgs[i]
|
||||||
|
ctx.drawImage(imgToAdd.img, imgToAdd.sx, imgToAdd.sy, imgToAdd.sw, imgToAdd.sw, 40 + 145 * i, 570, 140, 140)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.variant === 2) {
|
||||||
|
// Text stats
|
||||||
|
if (this.yearStats.topAuthors.length) {
|
||||||
|
addText('TOP AUTHORS', '24px', 'normal', tanColor, '1px', 70, 524)
|
||||||
|
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
||||||
|
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 584 + i * 60, 330)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.yearStats.topGenres.length) {
|
||||||
|
addText('TOP GENRES', '24px', 'normal', tanColor, '1px', 430, 524)
|
||||||
|
for (let i = 0; i < this.yearStats.topGenres.length; i++) {
|
||||||
|
addText(this.yearStats.topGenres[i].genre, '36px', 'bolder', 'white', '0px', 430, 584 + i * 60, 330)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.canvas = canvas
|
||||||
|
this.dataUrl = canvas.toDataURL('png')
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
share() {
|
||||||
|
this.canvas.toBlob((blob) => {
|
||||||
|
const file = new File([blob], 'yearinreview.png', { type: blob.type })
|
||||||
|
const shareData = {
|
||||||
|
files: [file]
|
||||||
|
}
|
||||||
|
if (navigator.canShare(shareData)) {
|
||||||
|
navigator
|
||||||
|
.share(shareData)
|
||||||
|
.then(() => {
|
||||||
|
console.log('Share success')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to share', error)
|
||||||
|
if (error.name !== 'AbortError') {
|
||||||
|
this.$toast.error('Failed to share: ' + error.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$toast.error('Cannot share natively on this device')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async init() {
|
||||||
|
this.$emit('update:processing', true)
|
||||||
|
this.yearStats = await this.$axios.$get(`/api/me/stats/year/${this.year}`).catch((err) => {
|
||||||
|
console.error('Failed to load stats for year', err)
|
||||||
|
this.$toast.error('Failed to load year stats')
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
await this.initCanvas()
|
||||||
|
this.$emit('update:processing', false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-1 sm:p-4 mb-4">
|
||||||
|
<!-- hack to get icon fonts loaded on init -->
|
||||||
|
<div class="h-0 w-0 overflow-hidden opacity-0">
|
||||||
|
<span class="material-icons-outlined">close</span>
|
||||||
|
<span class="abs-icons icon-audiobookshelf" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<p class="hidden md:block text-xl font-semibold">{{ $getString('HeaderYearReview', [yearInReviewYear]) }}</p>
|
||||||
|
<div class="hidden md:block flex-grow" />
|
||||||
|
<ui-btn class="w-full md:w-auto" @click.stop="clickShowYearInReview">{{ showYearInReview ? $strings.LabelYearReviewHide :
|
||||||
|
$strings.LabelYearReviewShow }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- your year in review -->
|
||||||
|
<div v-if="showYearInReview">
|
||||||
|
<div class="w-full h-px bg-slate-200/10 my-4" />
|
||||||
|
|
||||||
|
<div class="flex items-center justify-center mb-2 max-w-[800px] mx-auto">
|
||||||
|
<!-- previous button -->
|
||||||
|
<ui-btn small :disabled="!yearInReviewVariant || processingYearInReview" class="inline-flex items-center font-semibold" @click="yearInReviewVariant--">
|
||||||
|
<span class="material-icons text-lg sm:pr-1 py-px sm:py-0">chevron_left</span>
|
||||||
|
<span class="hidden sm:inline-block pr-2">{{ $strings.ButtonPrevious }}</span>
|
||||||
|
</ui-btn>
|
||||||
|
<!-- share button -->
|
||||||
|
<ui-btn v-if="showShareButton" small :disabled="processingYearInReview" class="inline-flex sm:hidden items-center font-semibold ml-1 sm:ml-2" @click="shareYearInReview">{{
|
||||||
|
$strings.ButtonShare }}
|
||||||
|
</ui-btn>
|
||||||
|
|
||||||
|
<div class="flex-grow" />
|
||||||
|
<p class="hidden sm:block text-lg font-semibold">{{ $getString('LabelPersonalYearReview', [yearInReviewVariant + 1]) }}
|
||||||
|
</p>
|
||||||
|
<p class="block sm:hidden text-lg font-semibold">{{ yearInReviewVariant + 1 }}</p>
|
||||||
|
<div class="flex-grow" />
|
||||||
|
|
||||||
|
<!-- refresh button -->
|
||||||
|
<ui-btn small :disabled="processingYearInReview" class="inline-flex items-center font-semibold mr-1 sm:mr-2" @click="refreshYearInReview">
|
||||||
|
<span class="hidden sm:inline-block">{{ $strings.ButtonRefresh }}</span>
|
||||||
|
<span class="material-icons sm:!hidden text-lg py-px">refresh</span>
|
||||||
|
</ui-btn>
|
||||||
|
<!-- next button -->
|
||||||
|
<ui-btn small :disabled="yearInReviewVariant >= 2 || processingYearInReview" class="inline-flex items-center font-semibold" @click="yearInReviewVariant++">
|
||||||
|
<span class="hidden sm:inline-block pl-2">{{ $strings.ButtonNext }}</span>
|
||||||
|
<span class="material-icons-outlined text-lg sm:pl-1 py-px sm:py-0">chevron_right</span>
|
||||||
|
</ui-btn>
|
||||||
|
</div>
|
||||||
|
<stats-year-in-review ref="yearInReview" :variant="yearInReviewVariant" :year="yearInReviewYear" :processing.sync="processingYearInReview" />
|
||||||
|
|
||||||
|
<!-- your year in review short -->
|
||||||
|
<div class="w-full max-w-[800px] mx-auto my-4">
|
||||||
|
<!-- share button -->
|
||||||
|
<ui-btn v-if="showShareButton" small :disabled="processingYearInReviewShort" class="inline-flex sm:hidden items-center font-semibold mb-1" @click="shareYearInReviewShort">{{ $strings.ButtonShare }}</ui-btn>
|
||||||
|
<stats-year-in-review-short ref="yearInReviewShort" :year="yearInReviewYear" :processing.sync="processingYearInReviewShort" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- your server in review -->
|
||||||
|
<div v-if="isAdminOrUp" class="w-full max-w-[800px] mx-auto mb-2 mt-4 border-t pt-4 border-white/10">
|
||||||
|
<div class="flex items-center justify-center mb-2">
|
||||||
|
<!-- previous button -->
|
||||||
|
<ui-btn small :disabled="!yearInReviewServerVariant || processingYearInReviewServer" class="inline-flex items-center font-semibold" @click="yearInReviewServerVariant--">
|
||||||
|
<span class="material-icons text-lg sm:pr-1 py-px sm:py-0">chevron_left</span>
|
||||||
|
<span class="hidden sm:inline-block pr-2">{{ $strings.ButtonPrevious }}</span>
|
||||||
|
</ui-btn>
|
||||||
|
<!-- share button -->
|
||||||
|
<ui-btn v-if="showShareButton" small :disabled="processingYearInReviewServer" class="inline-flex sm:hidden items-center font-semibold ml-1 sm:ml-2" @click="shareYearInReviewServer">{{ $strings.ButtonShare }}
|
||||||
|
</ui-btn>
|
||||||
|
|
||||||
|
<div class="flex-grow" />
|
||||||
|
<p class="hidden sm:block text-lg font-semibold">{{ $getString('LabelServerYearReview', [yearInReviewServerVariant + 1]) }}</p>
|
||||||
|
<p class="block sm:hidden text-lg font-semibold">{{ yearInReviewServerVariant + 1 }}</p>
|
||||||
|
<div class="flex-grow" />
|
||||||
|
|
||||||
|
<!-- refresh button -->
|
||||||
|
<ui-btn small :disabled="processingYearInReviewServer" class="inline-flex items-center font-semibold mr-1 sm:mr-2" @click="refreshYearInReviewServer">
|
||||||
|
<span class="hidden sm:inline-block">{{ $strings.ButtonRefresh }}</span>
|
||||||
|
<span class="material-icons sm:!hidden text-lg py-px">refresh</span>
|
||||||
|
</ui-btn>
|
||||||
|
<!-- next button -->
|
||||||
|
<ui-btn small :disabled="yearInReviewServerVariant >= 2 || processingYearInReviewServer" class="inline-flex items-center font-semibold" @click="yearInReviewServerVariant++">
|
||||||
|
<span class="hidden sm:inline-block pl-2">{{ $strings.ButtonNext }}</span>
|
||||||
|
<span class="material-icons-outlined text-lg sm:pl-1 py-px sm:py-0">chevron_right</span>
|
||||||
|
</ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<stats-year-in-review-server v-if="isAdminOrUp" ref="yearInReviewServer" :year="yearInReviewYear" :variant="yearInReviewServerVariant" :processing.sync="processingYearInReviewServer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showYearInReview: false,
|
||||||
|
yearInReviewYear: 0,
|
||||||
|
yearInReviewVariant: 0,
|
||||||
|
yearInReviewServerVariant: 0,
|
||||||
|
processingYearInReview: false,
|
||||||
|
processingYearInReviewShort: false,
|
||||||
|
processingYearInReviewServer: false,
|
||||||
|
showShareButton: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isAdminOrUp() {
|
||||||
|
return this.$store.getters['user/getIsAdminOrUp']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
shareYearInReviewServer() {
|
||||||
|
this.$refs.yearInReviewServer.share()
|
||||||
|
},
|
||||||
|
shareYearInReview() {
|
||||||
|
this.$refs.yearInReview.share()
|
||||||
|
},
|
||||||
|
shareYearInReviewShort() {
|
||||||
|
this.$refs.yearInReviewShort.share()
|
||||||
|
},
|
||||||
|
refreshYearInReviewServer() {
|
||||||
|
this.$refs.yearInReviewServer.refresh()
|
||||||
|
},
|
||||||
|
refreshYearInReview() {
|
||||||
|
this.$refs.yearInReview.refresh()
|
||||||
|
this.$refs.yearInReviewShort.refresh()
|
||||||
|
},
|
||||||
|
clickShowYearInReview() {
|
||||||
|
this.showYearInReview = !this.showYearInReview
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
this.yearInReviewYear = new Date().getFullYear()
|
||||||
|
// When not December show previous year
|
||||||
|
if (new Date().getMonth() < 11) {
|
||||||
|
this.yearInReviewYear--
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (typeof navigator.share !== 'undefined' && navigator.share) {
|
||||||
|
this.showShareButton = true
|
||||||
|
} else {
|
||||||
|
console.warn('Navigator.share not supported')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="processing" class="max-w-[800px] h-80 md:h-[800px] mx-auto flex items-center justify-center">
|
||||||
|
<widgets-loading-spinner />
|
||||||
|
</div>
|
||||||
|
<img v-else-if="dataUrl" :src="dataUrl" class="mx-auto" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
variant: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
processing: Boolean,
|
||||||
|
year: Number
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
canvas: null,
|
||||||
|
dataUrl: null,
|
||||||
|
yearStats: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
variant() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initCanvas() {
|
||||||
|
if (!this.yearStats) return
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = 800
|
||||||
|
canvas.height = 800
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
const createRoundedRect = (x, y, w, h) => {
|
||||||
|
const grd1 = ctx.createLinearGradient(x, y, x + w, y + h)
|
||||||
|
grd1.addColorStop(0, '#44444455')
|
||||||
|
grd1.addColorStop(1, '#ffffff11')
|
||||||
|
ctx.fillStyle = grd1
|
||||||
|
ctx.strokeStyle = '#C0C0C088'
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.roundRect(x, y, w, h, [20])
|
||||||
|
ctx.fill()
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
const addText = (text, fontSize, fontWeight, color, letterSpacing, x, y, maxWidth = 0) => {
|
||||||
|
ctx.fillStyle = color
|
||||||
|
ctx.font = `${fontWeight} ${fontSize} Source Sans Pro`
|
||||||
|
ctx.letterSpacing = letterSpacing
|
||||||
|
|
||||||
|
// If maxWidth is specified then continue to remove chars until under maxWidth and add ellipsis
|
||||||
|
if (maxWidth) {
|
||||||
|
let txtWidth = ctx.measureText(text).width
|
||||||
|
while (txtWidth > maxWidth) {
|
||||||
|
console.warn(`Text "${text}" is greater than max width ${maxWidth} (width:${txtWidth})`)
|
||||||
|
if (text.endsWith('...')) text = text.slice(0, -4) // Repeated checks remove 1 char at a time
|
||||||
|
else text = text.slice(0, -3) // First check remove last 3 chars
|
||||||
|
text += '...'
|
||||||
|
txtWidth = ctx.measureText(text).width
|
||||||
|
console.log(`Checking text "${text}" (width:${txtWidth})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillText(text, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bg color
|
||||||
|
ctx.fillStyle = '#232323'
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Cover image tiles
|
||||||
|
let imgsToAdd = {}
|
||||||
|
|
||||||
|
if (this.yearStats.booksAddedWithCovers.length) {
|
||||||
|
let index = 0
|
||||||
|
ctx.globalAlpha = 0.25
|
||||||
|
ctx.save()
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2)
|
||||||
|
ctx.rotate((-Math.PI / 180) * 25)
|
||||||
|
ctx.translate(-canvas.width / 2, -canvas.height / 2)
|
||||||
|
ctx.translate(-130, -120)
|
||||||
|
for (let x = 0; x < 5; x++) {
|
||||||
|
for (let y = 0; y < 5; y++) {
|
||||||
|
const coverIndex = index % this.yearStats.booksAddedWithCovers.length
|
||||||
|
let libraryItemId = this.yearStats.booksAddedWithCovers[coverIndex]
|
||||||
|
index++
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.crossOrigin = 'anonymous'
|
||||||
|
img.addEventListener('load', () => {
|
||||||
|
let sw = img.width
|
||||||
|
if (img.width > img.height) {
|
||||||
|
sw = img.height
|
||||||
|
}
|
||||||
|
let sx = -(sw - img.width) / 2
|
||||||
|
let sy = -(sw - img.height) / 2
|
||||||
|
ctx.drawImage(img, sx, sy, sw, sw, 215 * x, 215 * y, 215, 215)
|
||||||
|
if (!imgsToAdd[libraryItemId]) {
|
||||||
|
imgsToAdd[libraryItemId] = {
|
||||||
|
img,
|
||||||
|
sx,
|
||||||
|
sy,
|
||||||
|
sw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
img.addEventListener('error', () => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
img.src = this.$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
|
// Create gradient
|
||||||
|
const grd1 = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
|
||||||
|
grd1.addColorStop(0, '#000000aa')
|
||||||
|
grd1.addColorStop(1, '#cd9d49aa')
|
||||||
|
ctx.fillStyle = grd1
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Top Abs icon
|
||||||
|
let tanColor = '#ffdb70'
|
||||||
|
ctx.fillStyle = tanColor
|
||||||
|
ctx.font = '42px absicons'
|
||||||
|
ctx.fillText('\ue900', 15, 36)
|
||||||
|
|
||||||
|
// Top text
|
||||||
|
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
||||||
|
addText(`${this.year} YEAR IN REVIEW`, '18px', 'bold', 'white', '1px', 65, 51)
|
||||||
|
|
||||||
|
// Top left box
|
||||||
|
createRoundedRect(40, 100, 230, 100)
|
||||||
|
ctx.textAlign = 'center'
|
||||||
|
addText(this.yearStats.numBooksAdded, '48px', 'bold', 'white', '0px', 155, 140)
|
||||||
|
addText('books added', '18px', 'normal', tanColor, '0px', 155, 170)
|
||||||
|
|
||||||
|
// Box top right
|
||||||
|
createRoundedRect(285, 100, 230, 100)
|
||||||
|
addText(this.yearStats.numAuthorsAdded, '48px', 'bold', 'white', '0px', 400, 140)
|
||||||
|
addText('authors added', '18px', 'normal', tanColor, '0px', 400, 170)
|
||||||
|
|
||||||
|
// Box bottom left
|
||||||
|
createRoundedRect(530, 100, 230, 100)
|
||||||
|
addText(this.yearStats.numListeningSessions, '48px', 'bold', 'white', '0px', 645, 140)
|
||||||
|
addText('sessions', '18px', 'normal', tanColor, '1px', 645, 170)
|
||||||
|
|
||||||
|
// Text stats
|
||||||
|
if (this.yearStats.totalBooksAddedSize) {
|
||||||
|
addText('Your book collection grew to...', '24px', 'normal', tanColor, '0px', canvas.width / 2, 260)
|
||||||
|
addText(this.$bytesPretty(this.yearStats.totalBooksSize), '36px', 'bolder', 'white', '0px', canvas.width / 2, 300)
|
||||||
|
addText('+' + this.$bytesPretty(this.yearStats.totalBooksAddedSize), '20px', 'lighter', 'white', '0px', canvas.width / 2, 330)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.yearStats.totalBooksAddedDuration) {
|
||||||
|
addText('With a total duration of...', '24px', 'normal', tanColor, '0px', canvas.width / 2, 400)
|
||||||
|
addText(this.$elapsedPrettyExtended(this.yearStats.totalBooksDuration, true, false), '36px', 'bolder', 'white', '0px', canvas.width / 2, 440)
|
||||||
|
addText('+' + this.$elapsedPrettyExtended(this.yearStats.totalBooksAddedDuration, true, false), '20px', 'lighter', 'white', '0px', canvas.width / 2, 470)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.variant) {
|
||||||
|
// Bottom images
|
||||||
|
imgsToAdd = Object.values(imgsToAdd)
|
||||||
|
if (imgsToAdd.length > 0) {
|
||||||
|
addText('Some additions include...', '24px', 'normal', tanColor, '0px', canvas.width / 2, 540)
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(5, imgsToAdd.length); i++) {
|
||||||
|
let imgToAdd = imgsToAdd[i]
|
||||||
|
ctx.drawImage(imgToAdd.img, imgToAdd.sx, imgToAdd.sy, imgToAdd.sw, imgToAdd.sw, 40 + 145 * i, 580, 140, 140)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.variant === 1) {
|
||||||
|
// Text stats
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
if (this.yearStats.topAuthors.length) {
|
||||||
|
addText('TOP AUTHORS', '24px', 'normal', tanColor, '1px', 70, 549)
|
||||||
|
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
||||||
|
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 609 + i * 60, 330)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.yearStats.topNarrators.length) {
|
||||||
|
addText('TOP NARRATORS', '24px', 'normal', tanColor, '1px', 430, 549)
|
||||||
|
for (let i = 0; i < this.yearStats.topNarrators.length; i++) {
|
||||||
|
addText(this.yearStats.topNarrators[i].name, '36px', 'bolder', 'white', '0px', 430, 609 + i * 60, 330)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.variant === 2) {
|
||||||
|
// Text stats
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
if (this.yearStats.topAuthors.length) {
|
||||||
|
addText('TOP AUTHORS', '24px', 'normal', tanColor, '1px', 70, 549)
|
||||||
|
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
||||||
|
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 609 + i * 60, 330)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.yearStats.topGenres.length) {
|
||||||
|
addText('TOP GENRES', '24px', 'normal', tanColor, '1px', 430, 549)
|
||||||
|
for (let i = 0; i < this.yearStats.topGenres.length; i++) {
|
||||||
|
addText(this.yearStats.topGenres[i].genre, '36px', 'bolder', 'white', '0px', 430, 609 + i * 60, 330)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.canvas = canvas
|
||||||
|
this.dataUrl = canvas.toDataURL('png')
|
||||||
|
},
|
||||||
|
share() {
|
||||||
|
this.canvas.toBlob((blob) => {
|
||||||
|
const file = new File([blob], 'yearinreviewserver.png', { type: blob.type })
|
||||||
|
const shareData = {
|
||||||
|
files: [file]
|
||||||
|
}
|
||||||
|
if (navigator.canShare(shareData)) {
|
||||||
|
navigator
|
||||||
|
.share(shareData)
|
||||||
|
.then(() => {
|
||||||
|
console.log('Share success')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to share', error)
|
||||||
|
if (error.name !== 'AbortError') {
|
||||||
|
this.$toast.error('Failed to share: ' + error.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$toast.error('Cannot share natively on this device')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
async init() {
|
||||||
|
this.$emit('update:processing', true)
|
||||||
|
this.yearStats = await this.$axios.$get(`/api/stats/year/${this.year}`).catch((err) => {
|
||||||
|
console.error('Failed to load stats for year', err)
|
||||||
|
this.$toast.error('Failed to load year stats')
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
await this.initCanvas()
|
||||||
|
this.$emit('update:processing', false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="processing" class="max-w-[600px] h-32 sm:h-[200px] flex items-center justify-center">
|
||||||
|
<widgets-loading-spinner />
|
||||||
|
</div>
|
||||||
|
<img v-else-if="dataUrl" :src="dataUrl" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
processing: Boolean,
|
||||||
|
year: Number
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
canvas: null,
|
||||||
|
dataUrl: null,
|
||||||
|
yearStats: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initCanvas() {
|
||||||
|
if (!this.yearStats) return
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = 600
|
||||||
|
canvas.height = 200
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
const createRoundedRect = (x, y, w, h) => {
|
||||||
|
const grd1 = ctx.createLinearGradient(x, y, x + w, y + h)
|
||||||
|
grd1.addColorStop(0, '#44444455')
|
||||||
|
grd1.addColorStop(1, '#ffffff11')
|
||||||
|
ctx.fillStyle = grd1
|
||||||
|
ctx.strokeStyle = '#C0C0C088'
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.roundRect(x, y, w, h, [20])
|
||||||
|
ctx.fill()
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
const addText = (text, fontSize, fontWeight, color, letterSpacing, x, y, maxWidth = 0) => {
|
||||||
|
ctx.fillStyle = color
|
||||||
|
ctx.font = `${fontWeight} ${fontSize} Source Sans Pro`
|
||||||
|
ctx.letterSpacing = letterSpacing
|
||||||
|
|
||||||
|
// If maxWidth is specified then continue to remove chars until under maxWidth and add ellipsis
|
||||||
|
if (maxWidth) {
|
||||||
|
let txtWidth = ctx.measureText(text).width
|
||||||
|
while (txtWidth > maxWidth) {
|
||||||
|
console.warn(`Text "${text}" is greater than max width ${maxWidth} (width:${txtWidth})`)
|
||||||
|
if (text.endsWith('...')) text = text.slice(0, -4) // Repeated checks remove 1 char at a time
|
||||||
|
else text = text.slice(0, -3) // First check remove last 3 chars
|
||||||
|
text += '...'
|
||||||
|
txtWidth = ctx.measureText(text).width
|
||||||
|
console.log(`Checking text "${text}" (width:${txtWidth})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillText(text, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addIcon = (icon, color, fontSize, x, y) => {
|
||||||
|
ctx.fillStyle = color
|
||||||
|
ctx.font = `${fontSize} Material Icons Outlined`
|
||||||
|
ctx.fillText(icon, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bg color
|
||||||
|
ctx.fillStyle = '#232323'
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Cover image tiles
|
||||||
|
const bookCovers = this.yearStats.finishedBooksWithCovers
|
||||||
|
bookCovers.push(...this.yearStats.booksWithCovers)
|
||||||
|
|
||||||
|
if (bookCovers.length) {
|
||||||
|
let index = 0
|
||||||
|
ctx.globalAlpha = 0.25
|
||||||
|
ctx.save()
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2)
|
||||||
|
ctx.rotate((-Math.PI / 180) * 25)
|
||||||
|
ctx.translate(-canvas.width / 2, -canvas.height / 2)
|
||||||
|
ctx.translate(-10, -90)
|
||||||
|
for (let x = 0; x < 4; x++) {
|
||||||
|
for (let y = 0; y < 3; y++) {
|
||||||
|
const coverIndex = index % bookCovers.length
|
||||||
|
let libraryItemId = bookCovers[coverIndex]
|
||||||
|
index++
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.crossOrigin = 'anonymous'
|
||||||
|
img.addEventListener('load', () => {
|
||||||
|
let sw = img.width
|
||||||
|
if (img.width > img.height) {
|
||||||
|
sw = img.height
|
||||||
|
}
|
||||||
|
let sx = -(sw - img.width) / 2
|
||||||
|
let sy = -(sw - img.height) / 2
|
||||||
|
ctx.drawImage(img, sx, sy, sw, sw, 155 * x, 155 * y, 155, 155)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
img.addEventListener('error', () => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
img.src = this.$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
|
// Create gradient
|
||||||
|
const grd1 = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
|
||||||
|
grd1.addColorStop(0, '#000000aa')
|
||||||
|
grd1.addColorStop(1, '#cd9d49aa')
|
||||||
|
ctx.fillStyle = grd1
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Top Abs icon
|
||||||
|
let tanColor = '#ffdb70'
|
||||||
|
ctx.fillStyle = tanColor
|
||||||
|
ctx.font = '42px absicons'
|
||||||
|
ctx.fillText('\ue900', 15, 36)
|
||||||
|
|
||||||
|
// Top text
|
||||||
|
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
||||||
|
addText(`${this.year} YEAR IN REVIEW`, '18px', 'bold', 'white', '1px', 65, 51)
|
||||||
|
|
||||||
|
// Top left box
|
||||||
|
createRoundedRect(15, 75, 280, 110)
|
||||||
|
addText(this.yearStats.numBooksFinished, '48px', 'bold', 'white', '0px', 105, 120)
|
||||||
|
addText('books finished', '20px', 'normal', tanColor, '0px', 105, 155)
|
||||||
|
const readIconPath = new Path2D()
|
||||||
|
readIconPath.addPath(new Path2D('M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z'), { a: 1.5, d: 1.5, e: 55, f: 115 })
|
||||||
|
ctx.fillStyle = '#ffffff'
|
||||||
|
ctx.fill(readIconPath)
|
||||||
|
|
||||||
|
createRoundedRect(305, 75, 280, 110)
|
||||||
|
addText(this.yearStats.numBooksListened, '48px', 'bold', 'white', '0px', 400, 120)
|
||||||
|
addText('books listened to', '20px', 'normal', tanColor, '0px', 400, 155)
|
||||||
|
addIcon('local_library', 'white', '42px', 345, 130)
|
||||||
|
|
||||||
|
this.canvas = canvas
|
||||||
|
this.dataUrl = canvas.toDataURL('png')
|
||||||
|
},
|
||||||
|
share() {
|
||||||
|
this.canvas.toBlob((blob) => {
|
||||||
|
const file = new File([blob], 'yearinreviewshort.png', { type: blob.type })
|
||||||
|
const shareData = {
|
||||||
|
files: [file]
|
||||||
|
}
|
||||||
|
if (navigator.canShare(shareData)) {
|
||||||
|
navigator
|
||||||
|
.share(shareData)
|
||||||
|
.then(() => {
|
||||||
|
console.log('Share success')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to share', error)
|
||||||
|
if (error.name !== 'AbortError') {
|
||||||
|
this.$toast.error('Failed to share: ' + error.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$toast.error('Cannot share natively on this device')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
async init() {
|
||||||
|
this.$emit('update:processing', true)
|
||||||
|
this.yearStats = await this.$axios.$get(`/api/me/stats/year/${this.year}`).catch((err) => {
|
||||||
|
console.error('Failed to load stats for year', err)
|
||||||
|
this.$toast.error('Failed to load year stats')
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
await this.initCanvas()
|
||||||
|
this.$emit('update:processing', false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="text-center mt-4">
|
<div class="text-center mt-4 relative">
|
||||||
<div class="flex py-4">
|
<div class="flex py-4">
|
||||||
<ui-file-input ref="fileInput" class="mr-2" accept=".audiobookshelf" @change="backupUploaded">{{ $strings.ButtonUploadBackup }}</ui-file-input>
|
<ui-file-input ref="fileInput" class="mr-2" accept=".audiobookshelf" @change="backupUploaded">{{ $strings.ButtonUploadBackup }}</ui-file-input>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
@@ -54,6 +54,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</prompt-dialog>
|
</prompt-dialog>
|
||||||
|
|
||||||
|
<div v-if="isApplyingBackup" class="absolute inset-0 w-full h-full flex items-center justify-center bg-black/20 rounded-md">
|
||||||
|
<ui-loading-indicator />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -64,6 +68,7 @@ export default {
|
|||||||
showConfirmApply: false,
|
showConfirmApply: false,
|
||||||
selectedBackup: null,
|
selectedBackup: null,
|
||||||
isBackingUp: false,
|
isBackingUp: false,
|
||||||
|
isApplyingBackup: false,
|
||||||
processing: false,
|
processing: false,
|
||||||
backups: []
|
backups: []
|
||||||
}
|
}
|
||||||
@@ -85,19 +90,21 @@ export default {
|
|||||||
},
|
},
|
||||||
confirm() {
|
confirm() {
|
||||||
this.showConfirmApply = false
|
this.showConfirmApply = false
|
||||||
|
this.isApplyingBackup = true
|
||||||
|
|
||||||
this.$axios
|
this.$axios
|
||||||
.$get(`/api/backups/${this.selectedBackup.id}/apply`)
|
.$get(`/api/backups/${this.selectedBackup.id}/apply`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isBackingUp = false
|
|
||||||
location.replace('/config/backups?backup=1')
|
location.replace('/config/backups?backup=1')
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.isBackingUp = false
|
|
||||||
console.error('Failed to apply backup', error)
|
console.error('Failed to apply backup', error)
|
||||||
const errorMsg = error.response.data || this.$strings.ToastBackupRestoreFailed
|
const errorMsg = error.response.data || this.$strings.ToastBackupRestoreFailed
|
||||||
this.$toast.error(errorMsg)
|
this.$toast.error(errorMsg)
|
||||||
})
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isApplyingBackup = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
deleteBackupClick(backup) {
|
deleteBackupClick(backup) {
|
||||||
if (confirm(this.$getString('MessageConfirmDeleteBackup', [this.$formatDatetime(backup.createdAt, this.dateFormat, this.timeFormat)]))) {
|
if (confirm(this.$getString('MessageConfirmDeleteBackup', [this.$formatDatetime(backup.createdAt, this.dateFormat, this.timeFormat)]))) {
|
||||||
@@ -180,7 +187,6 @@ export default {
|
|||||||
this.loadBackups()
|
this.loadBackups()
|
||||||
if (this.$route.query.backup) {
|
if (this.$route.query.backup) {
|
||||||
this.$toast.success('Backup applied successfully')
|
this.$toast.success('Backup applied successfully')
|
||||||
this.$router.replace('/config')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-40">
|
||||||
|
<table v-if="providers.length" id="providers">
|
||||||
|
<tr>
|
||||||
|
<th>{{ $strings.LabelName }}</th>
|
||||||
|
<th>URL</th>
|
||||||
|
<th>Authorization Header Value</th>
|
||||||
|
<th class="w-12"></th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="provider in providers" :key="provider.id">
|
||||||
|
<td class="text-sm">{{ provider.name }}</td>
|
||||||
|
<td class="text-sm">{{ provider.url }}</td>
|
||||||
|
<td class="text-sm">
|
||||||
|
<span v-if="provider.authHeaderValue" class="custom-provider-api-key">{{ provider.authHeaderValue }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="h-8 w-8 flex items-center justify-center text-white text-opacity-50 hover:text-error cursor-pointer" @click.stop="removeProvider(provider)">
|
||||||
|
<button type="button" :aria-label="$strings.ButtonDelete" class="material-icons text-base">delete</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div v-else-if="!processing" class="text-center py-8">
|
||||||
|
<p class="text-lg">No custom metadata providers</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="processing" class="absolute inset-0 h-full flex items-center justify-center bg-black/40 rounded-md">
|
||||||
|
<ui-loading-indicator />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
providers: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
processing: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
removeProvider(provider) {
|
||||||
|
const payload = {
|
||||||
|
message: `Are you sure you want remove custom metadata provider "${provider.name}"?`,
|
||||||
|
callback: (confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.$emit('update:processing', true)
|
||||||
|
|
||||||
|
this.$axios
|
||||||
|
.$delete(`/api/custom-metadata-providers/${provider.id}`)
|
||||||
|
.then(() => {
|
||||||
|
this.$toast.success('Provider removed')
|
||||||
|
this.$emit('removed', provider.id)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to remove provider', error)
|
||||||
|
this.$toast.error('Failed to remove provider')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.$emit('update:processing', false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'yesNo'
|
||||||
|
}
|
||||||
|
this.$store.commit('globals/setConfirmPrompt', payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#providers {
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid #474747;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#providers td,
|
||||||
|
#providers th {
|
||||||
|
/* border: 1px solid #2e2e2e; */
|
||||||
|
padding: 8px 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#providers td.py-0 {
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#providers tr:nth-child(even) {
|
||||||
|
background-color: #373838;
|
||||||
|
}
|
||||||
|
|
||||||
|
#providers tr:nth-child(odd) {
|
||||||
|
background-color: #2f2f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#providers tr:hover {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
#providers th {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
background-color: #272727;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-provider-api-key {
|
||||||
|
padding: 1px;
|
||||||
|
background-color: #272727;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: transparent;
|
||||||
|
transition: color, background-color 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-provider-api-key:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition name="slide">
|
<transition name="slide">
|
||||||
<div class="w-full" v-show="showFiles">
|
<div class="w-full" v-if="showFiles">
|
||||||
<table class="text-sm tracksTable">
|
<table class="text-sm tracksTable">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left px-4">{{ $strings.LabelPath }}</th>
|
<th class="text-left px-4">{{ $strings.LabelPath }}</th>
|
||||||
@@ -70,7 +70,7 @@ export default {
|
|||||||
},
|
},
|
||||||
audioFiles() {
|
audioFiles() {
|
||||||
if (this.libraryItem.mediaType === 'podcast') {
|
if (this.libraryItem.mediaType === 'podcast') {
|
||||||
return this.libraryItem.media?.episodes.map((ep) => ep.audioFile) || []
|
return this.libraryItem.media?.episodes.map((ep) => ep.audioFile).filter((af) => af) || []
|
||||||
}
|
}
|
||||||
return this.libraryItem.media?.audioFiles || []
|
return this.libraryItem.media?.audioFiles || []
|
||||||
},
|
},
|
||||||
|
|||||||
+103
-74
@@ -1,18 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full px-2 py-3 overflow-hidden relative border-b border-white border-opacity-10" @mouseover="mouseover" @mouseleave="mouseleave">
|
<div :id="`lazy-episode-${index}`" class="w-full h-full cursor-pointer" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||||
<div v-if="episode" class="flex items-center cursor-pointer" :class="{ 'opacity-70': isSelected || selectionMode }" @click="clickedEpisode">
|
<div class="flex" @click="clickedEpisode">
|
||||||
<div class="flex-grow px-2">
|
<div class="flex-grow">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-sm font-semibold">{{ title }}</span>
|
<span class="text-sm font-semibold">{{ episodeTitle }}</span>
|
||||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
<widgets-podcast-type-indicator :type="episodeType" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-sm text-gray-200 episode-subtitle mt-1.5 mb-0.5" v-html="subtitle"></p>
|
<div class="h-10 flex items-center mt-1.5 mb-0.5 overflow-hidden">
|
||||||
<div class="flex justify-between pt-2 max-w-xl">
|
<p class="text-sm text-gray-200 line-clamp-2" v-html="episodeSubtitle"></p>
|
||||||
<p v-if="episode.season" class="text-sm text-gray-300">Season #{{ episode.season }}</p>
|
</div>
|
||||||
<p v-if="episode.episode" class="text-sm text-gray-300">Episode #{{ episode.episode }}</p>
|
<div class="h-8 flex items-center">
|
||||||
<p v-if="episode.chapters?.length" class="text-sm text-gray-300">{{ episode.chapters.length }} Chapters</p>
|
<div class="w-full inline-flex justify-between max-w-xl">
|
||||||
<p v-if="publishedAt" class="text-sm text-gray-300">Published {{ $formatDate(publishedAt, dateFormat) }}</p>
|
<p v-if="episode?.season" class="text-sm text-gray-300">Season #{{ episode.season }}</p>
|
||||||
|
<p v-if="episode?.episode" class="text-sm text-gray-300">Episode #{{ episode.episode }}</p>
|
||||||
|
<p v-if="episode?.chapters?.length" class="text-sm text-gray-300">{{ episode.chapters.length }} Chapters</p>
|
||||||
|
<p v-if="publishedAt" class="text-sm text-gray-300">Published {{ $formatDate(publishedAt, dateFormat) }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center pt-2">
|
<div class="flex items-center pt-2">
|
||||||
@@ -37,10 +41,11 @@
|
|||||||
<ui-icon-btn v-if="userCanDelete" icon="close" borderless @click="removeClick" />
|
<ui-icon-btn v-if="userCanDelete" icon="close" borderless @click="removeClick" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isHovering || isSelected || selectionMode" class="hidden md:block w-12 min-w-12" />
|
<div v-if="isHovering || isSelected || isSelectionMode" class="hidden md:block w-12 min-w-12" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isSelected || selectionMode" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-10 z-10 cursor-pointer" @click.stop="clickedSelectionBg" />
|
|
||||||
<div class="hidden md:block md:w-12 md:min-w-12 md:-right-0 md:absolute md:top-0 h-full transform transition-transform z-20" :class="!isHovering && !isSelected && !selectionMode ? 'translate-x-24' : 'translate-x-0'">
|
<div v-if="isSelected || isSelectionMode" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-10 z-10 cursor-pointer" @click.stop="clickedSelectionBg" />
|
||||||
|
<div class="hidden md:block md:w-12 md:min-w-12 md:-right-0 md:absolute md:top-0 h-full transform transition-transform z-20" :class="!isHovering && !isSelected && !isSelectionMode ? 'translate-x-24' : 'translate-x-0'">
|
||||||
<div class="flex h-full items-center">
|
<div class="flex h-full items-center">
|
||||||
<div class="mx-1">
|
<div class="mx-1">
|
||||||
<ui-checkbox v-model="isSelected" @input="selectedUpdated" checkbox-bg="bg" />
|
<ui-checkbox v-model="isSelected" @input="selectedUpdated" checkbox-bg="bg" />
|
||||||
@@ -55,84 +60,91 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
index: Number,
|
||||||
libraryItemId: String,
|
libraryItemId: String,
|
||||||
episode: {
|
episode: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => null
|
||||||
},
|
}
|
||||||
selectionMode: Boolean
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isProcessingReadUpdate: false,
|
isProcessingReadUpdate: false,
|
||||||
processingRemove: false,
|
processingRemove: false,
|
||||||
isHovering: false,
|
isHovering: false,
|
||||||
isSelected: false
|
isSelected: false,
|
||||||
|
isSelectionMode: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
store() {
|
||||||
|
return this.$store || this.$nuxt.$store
|
||||||
|
},
|
||||||
|
axios() {
|
||||||
|
return this.$axios || this.$nuxt.$axios
|
||||||
|
},
|
||||||
userCanUpdate() {
|
userCanUpdate() {
|
||||||
return this.$store.getters['user/getUserCanUpdate']
|
return this.store.getters['user/getUserCanUpdate']
|
||||||
},
|
},
|
||||||
userCanDelete() {
|
userCanDelete() {
|
||||||
return this.$store.getters['user/getUserCanDelete']
|
return this.store.getters['user/getUserCanDelete']
|
||||||
},
|
},
|
||||||
audioFile() {
|
episodeId() {
|
||||||
return this.episode.audioFile
|
return this.episode?.id || ''
|
||||||
},
|
},
|
||||||
title() {
|
episodeTitle() {
|
||||||
return this.episode.title || ''
|
return this.episode?.title || ''
|
||||||
},
|
},
|
||||||
subtitle() {
|
episodeSubtitle() {
|
||||||
return this.episode.subtitle || this.description
|
return this.episode?.subtitle || ''
|
||||||
},
|
},
|
||||||
description() {
|
episodeType() {
|
||||||
return this.episode.description || ''
|
return this.episode?.episodeType || ''
|
||||||
},
|
},
|
||||||
duration() {
|
publishedAt() {
|
||||||
return this.$secondsToTimestamp(this.episode.duration)
|
return this.episode?.publishedAt
|
||||||
},
|
},
|
||||||
libraryItemIdStreaming() {
|
dateFormat() {
|
||||||
return this.$store.getters['getLibraryItemIdStreaming']
|
return this.store.state.serverSettings.dateFormat
|
||||||
},
|
|
||||||
isStreamingFromDifferentLibrary() {
|
|
||||||
return this.$store.getters['getIsStreamingFromDifferentLibrary']
|
|
||||||
},
|
|
||||||
isStreaming() {
|
|
||||||
return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.episode.id)
|
|
||||||
},
|
|
||||||
isQueued() {
|
|
||||||
return this.$store.getters['getIsMediaQueued'](this.libraryItemId, this.episode.id)
|
|
||||||
},
|
|
||||||
streamIsPlaying() {
|
|
||||||
return this.$store.state.streamIsPlaying && this.isStreaming
|
|
||||||
},
|
},
|
||||||
itemProgress() {
|
itemProgress() {
|
||||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId, this.episode.id)
|
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId, this.episodeId)
|
||||||
},
|
},
|
||||||
itemProgressPercent() {
|
itemProgressPercent() {
|
||||||
return this.itemProgress ? this.itemProgress.progress : 0
|
return this.itemProgress?.progress || 0
|
||||||
},
|
},
|
||||||
userIsFinished() {
|
userIsFinished() {
|
||||||
return this.itemProgress ? !!this.itemProgress.isFinished : false
|
return !!this.itemProgress?.isFinished
|
||||||
|
},
|
||||||
|
libraryItemIdStreaming() {
|
||||||
|
return this.store.getters['getLibraryItemIdStreaming']
|
||||||
|
},
|
||||||
|
isStreamingFromDifferentLibrary() {
|
||||||
|
return this.store.getters['getIsStreamingFromDifferentLibrary']
|
||||||
|
},
|
||||||
|
isStreaming() {
|
||||||
|
return this.store.getters['getIsMediaStreaming'](this.libraryItemId, this.episodeId)
|
||||||
|
},
|
||||||
|
isQueued() {
|
||||||
|
return this.store.getters['getIsMediaQueued'](this.libraryItemId, this.episodeId)
|
||||||
|
},
|
||||||
|
streamIsPlaying() {
|
||||||
|
return this.store.state.streamIsPlaying && this.isStreaming
|
||||||
},
|
},
|
||||||
timeRemaining() {
|
timeRemaining() {
|
||||||
if (this.streamIsPlaying) return 'Playing'
|
if (this.streamIsPlaying) return 'Playing'
|
||||||
if (!this.itemProgress) return this.$elapsedPretty(this.episode.duration)
|
if (!this.itemProgress) return this.$elapsedPretty(this.episode?.duration || 0)
|
||||||
if (this.userIsFinished) return 'Finished'
|
if (this.userIsFinished) return 'Finished'
|
||||||
var remaining = Math.floor(this.itemProgress.duration - this.itemProgress.currentTime)
|
|
||||||
|
const duration = this.itemProgress.duration || this.episode?.duration || 0
|
||||||
|
const remaining = Math.floor(duration - this.itemProgress.currentTime)
|
||||||
return `${this.$elapsedPretty(remaining)} left`
|
return `${this.$elapsedPretty(remaining)} left`
|
||||||
},
|
|
||||||
publishedAt() {
|
|
||||||
return this.episode.publishedAt
|
|
||||||
},
|
|
||||||
dateFormat() {
|
|
||||||
return this.$store.state.serverSettings.dateFormat
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickAddToPlaylist() {
|
setSelectionMode(isSelectionMode) {
|
||||||
this.$emit('addToPlaylist', this.episode)
|
this.isSelectionMode = isSelectionMode
|
||||||
|
if (!this.isSelectionMode) this.isSelected = false
|
||||||
},
|
},
|
||||||
clickedEpisode() {
|
clickedEpisode() {
|
||||||
this.$emit('view', this.episode)
|
this.$emit('view', this.episode)
|
||||||
@@ -150,16 +162,23 @@ export default {
|
|||||||
mouseleave() {
|
mouseleave() {
|
||||||
this.isHovering = false
|
this.isHovering = false
|
||||||
},
|
},
|
||||||
clickEdit() {
|
|
||||||
this.$emit('edit', this.episode)
|
|
||||||
},
|
|
||||||
playClick() {
|
playClick() {
|
||||||
if (this.streamIsPlaying) {
|
if (this.streamIsPlaying) {
|
||||||
this.$eventBus.$emit('pause-item')
|
const eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||||
|
eventBus.$emit('pause-item')
|
||||||
} else {
|
} else {
|
||||||
this.$emit('play', this.episode)
|
this.$emit('play', this.episode)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
queueBtnClick() {
|
||||||
|
if (this.isQueued) {
|
||||||
|
// Remove from queue
|
||||||
|
this.store.commit('removeItemFromQueue', { libraryItemId: this.libraryItemId, episodeId: this.episodeId })
|
||||||
|
} else {
|
||||||
|
// Add to queue
|
||||||
|
this.$emit('addToQueue', this.episode)
|
||||||
|
}
|
||||||
|
},
|
||||||
toggleFinished(confirmed = false) {
|
toggleFinished(confirmed = false) {
|
||||||
if (!this.userIsFinished && this.itemProgressPercent > 0 && !confirmed) {
|
if (!this.userIsFinished && this.itemProgressPercent > 0 && !confirmed) {
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -171,37 +190,47 @@ export default {
|
|||||||
},
|
},
|
||||||
type: 'yesNo'
|
type: 'yesNo'
|
||||||
}
|
}
|
||||||
this.$store.commit('globals/setConfirmPrompt', payload)
|
this.store.commit('globals/setConfirmPrompt', payload)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatePayload = {
|
const updatePayload = {
|
||||||
isFinished: !this.userIsFinished
|
isFinished: !this.userIsFinished
|
||||||
}
|
}
|
||||||
this.isProcessingReadUpdate = true
|
this.isProcessingReadUpdate = true
|
||||||
this.$axios
|
this.axios
|
||||||
.$patch(`/api/me/progress/${this.libraryItemId}/${this.episode.id}`, updatePayload)
|
.$patch(`/api/me/progress/${this.libraryItemId}/${this.episodeId}`, updatePayload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isProcessingReadUpdate = false
|
this.isProcessingReadUpdate = false
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed', error)
|
console.error('Failed', error)
|
||||||
this.isProcessingReadUpdate = false
|
this.isProcessingReadUpdate = false
|
||||||
this.$toast.error(updatePayload.isFinished ? this.$strings.ToastItemMarkedAsFinishedFailed : this.$strings.ToastItemMarkedAsNotFinishedFailed)
|
const toast = this.$toast || this.$nuxt.$toast
|
||||||
|
toast.error(updatePayload.isFinished ? this.$strings.ToastItemMarkedAsFinishedFailed : this.$strings.ToastItemMarkedAsNotFinishedFailed)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
clickAddToPlaylist() {
|
||||||
|
this.$emit('addToPlaylist', this.episode)
|
||||||
|
},
|
||||||
|
clickEdit() {
|
||||||
|
this.$emit('edit', this.episode)
|
||||||
|
},
|
||||||
removeClick() {
|
removeClick() {
|
||||||
this.$emit('remove', this.episode)
|
this.$emit('remove', this.episode)
|
||||||
},
|
},
|
||||||
queueBtnClick() {
|
destroy() {
|
||||||
if (this.isQueued) {
|
// destroy the vue listeners, etc
|
||||||
// Remove from queue
|
this.$destroy()
|
||||||
this.$store.commit('removeItemFromQueue', { libraryItemId: this.libraryItemId, episodeId: this.episode.id })
|
|
||||||
} else {
|
// remove the element from the DOM
|
||||||
// Add to queue
|
if (this.$el && this.$el.parentNode) {
|
||||||
this.$emit('addToQueue', this.episode)
|
this.$el.parentNode.removeChild(this.$el)
|
||||||
|
} else if (this.$el && this.$el.remove) {
|
||||||
|
this.$el.remove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
mounted() {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
+188
-20
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full py-6">
|
<div id="lazy-episodes-table" class="w-full py-6">
|
||||||
<div class="flex flex-wrap flex-col md:flex-row md:items-center mb-4">
|
<div class="flex flex-wrap flex-col md:flex-row md:items-center mb-4">
|
||||||
<div class="flex items-center flex-nowrap whitespace-nowrap mb-2 md:mb-0">
|
<div class="flex items-center flex-nowrap whitespace-nowrap mb-2 md:mb-0">
|
||||||
<p class="text-lg mb-0 font-semibold">{{ $strings.HeaderEpisodes }}</p>
|
<p class="text-lg mb-0 font-semibold">{{ $strings.HeaderEpisodes }}</p>
|
||||||
@@ -18,28 +18,41 @@
|
|||||||
<ui-btn :disabled="processing" small class="ml-2 h-9" @click="clearSelected">{{ $strings.ButtonCancel }}</ui-btn>
|
<ui-btn :disabled="processing" small class="ml-2 h-9" @click="clearSelected">{{ $strings.ButtonCancel }}</ui-btn>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<controls-filter-select v-model="filterKey" :items="filterItems" class="w-36 h-9 md:ml-4" />
|
<controls-filter-select v-model="filterKey" :items="filterItems" class="w-36 h-9 md:ml-4" @change="filterSortChanged" />
|
||||||
<controls-sort-select v-model="sortKey" :descending.sync="sortDesc" :items="sortItems" class="w-44 md:w-48 h-9 ml-1 sm:ml-4" />
|
<controls-sort-select v-model="sortKey" :descending.sync="sortDesc" :items="sortItems" class="w-44 md:w-48 h-9 ml-1 sm:ml-4" @change="filterSortChanged" />
|
||||||
<div class="flex-grow md:hidden" />
|
<div class="flex-grow md:hidden" />
|
||||||
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" class="ml-1" @action="contextMenuAction" />
|
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" class="ml-1" @action="contextMenuAction" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="!episodes.length" class="py-4 text-center text-lg">{{ $strings.MessageNoEpisodes }}</p>
|
<!-- <p v-if="!episodes.length" class="py-4 text-center text-lg">{{ $strings.MessageNoEpisodes }}</p> -->
|
||||||
<div v-if="episodes.length" class="w-full py-3 mx-auto flex">
|
<div v-if="episodes.length" class="w-full py-3 mx-auto flex">
|
||||||
<form @submit.prevent="submit" class="flex flex-grow">
|
<form @submit.prevent="submit" class="flex flex-grow">
|
||||||
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="flex-grow mr-2 text-sm md:text-base" />
|
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="flex-grow mr-2 text-sm md:text-base" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="episode in episodesList">
|
<div class="relative min-h-[176px]">
|
||||||
<tables-podcast-episode-table-row ref="episodeRow" :key="episode.id" :episode="episode" :library-item-id="libraryItem.id" :selection-mode="isSelectionMode" class="item" @play="playEpisode" @remove="removeEpisode" @edit="editEpisode" @view="viewEpisode" @selected="episodeSelected" @addToQueue="addEpisodeToQueue" @addToPlaylist="addToPlaylist" />
|
<template v-for="episode in totalEpisodes">
|
||||||
</template>
|
<div :key="episode" :id="`episode-${episode - 1}`" class="w-full h-44 px-2 py-3 overflow-hidden relative border-b border-white/10">
|
||||||
|
<!-- episode is mounted here -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-if="isSearching" class="w-full h-full absolute inset-0 flex justify-center py-12" :class="{ 'bg-black/50': totalEpisodes }">
|
||||||
|
<ui-loading-indicator />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!totalEpisodes" class="h-44 flex items-center justify-center">
|
||||||
|
<p class="text-lg">{{ $strings.MessageNoEpisodes }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<modals-podcast-remove-episode v-model="showPodcastRemoveModal" @input="removeEpisodeModalToggled" :library-item="libraryItem" :episodes="episodesToRemove" @clearSelected="clearSelected" />
|
<modals-podcast-remove-episode v-model="showPodcastRemoveModal" @input="removeEpisodeModalToggled" :library-item="libraryItem" :episodes="episodesToRemove" @clearSelected="clearSelected" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import LazyEpisodeRow from './LazyEpisodeRow.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
libraryItem: {
|
libraryItem: {
|
||||||
@@ -60,13 +73,21 @@ export default {
|
|||||||
processing: false,
|
processing: false,
|
||||||
search: null,
|
search: null,
|
||||||
searchTimeout: null,
|
searchTimeout: null,
|
||||||
searchText: null
|
searchText: null,
|
||||||
|
isSearching: false,
|
||||||
|
totalEpisodes: 0,
|
||||||
|
episodesPerPage: null,
|
||||||
|
episodeIndexesMounted: [],
|
||||||
|
episodeComponentRefs: {},
|
||||||
|
windowHeight: 0,
|
||||||
|
episodesTableOffsetTop: 0,
|
||||||
|
episodeRowHeight: 176
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
libraryItem: {
|
libraryItem: {
|
||||||
handler() {
|
handler() {
|
||||||
this.init()
|
this.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -194,13 +215,19 @@ export default {
|
|||||||
submit() {},
|
submit() {},
|
||||||
inputUpdate() {
|
inputUpdate() {
|
||||||
clearTimeout(this.searchTimeout)
|
clearTimeout(this.searchTimeout)
|
||||||
|
this.isSearching = true
|
||||||
|
let searchStart = this.searchText
|
||||||
this.searchTimeout = setTimeout(() => {
|
this.searchTimeout = setTimeout(() => {
|
||||||
if (!this.search || !this.search.trim()) {
|
this.isSearching = false
|
||||||
|
if (!this.search?.trim()) {
|
||||||
this.searchText = ''
|
this.searchText = ''
|
||||||
return
|
} else {
|
||||||
|
this.searchText = this.search.toLowerCase().trim()
|
||||||
}
|
}
|
||||||
this.searchText = this.search.toLowerCase().trim()
|
if (searchStart !== this.searchText) {
|
||||||
}, 500)
|
this.init()
|
||||||
|
}
|
||||||
|
}, 750)
|
||||||
},
|
},
|
||||||
contextMenuAction({ action }) {
|
contextMenuAction({ action }) {
|
||||||
if (action === 'quick-match-episodes') {
|
if (action === 'quick-match-episodes') {
|
||||||
@@ -304,24 +331,30 @@ export default {
|
|||||||
if (!val) this.episodesToRemove = []
|
if (!val) this.episodesToRemove = []
|
||||||
},
|
},
|
||||||
clearSelected() {
|
clearSelected() {
|
||||||
const episodeRows = this.$refs.episodeRow
|
|
||||||
if (episodeRows && episodeRows.length) {
|
|
||||||
for (const epRow of episodeRows) {
|
|
||||||
if (epRow) epRow.isSelected = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.selectedEpisodes = []
|
this.selectedEpisodes = []
|
||||||
|
this.setSelectionModeForEpisodes()
|
||||||
},
|
},
|
||||||
removeSelectedEpisodes() {
|
removeSelectedEpisodes() {
|
||||||
this.episodesToRemove = this.selectedEpisodes
|
this.episodesToRemove = this.selectedEpisodes
|
||||||
this.showPodcastRemoveModal = true
|
this.showPodcastRemoveModal = true
|
||||||
},
|
},
|
||||||
episodeSelected({ isSelected, episode }) {
|
episodeSelected({ isSelected, episode }) {
|
||||||
|
let isSelectionModeBefore = this.isSelectionMode
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
this.selectedEpisodes.push(episode)
|
this.selectedEpisodes.push(episode)
|
||||||
} else {
|
} else {
|
||||||
this.selectedEpisodes = this.selectedEpisodes.filter((ep) => ep.id !== episode.id)
|
this.selectedEpisodes = this.selectedEpisodes.filter((ep) => ep.id !== episode.id)
|
||||||
}
|
}
|
||||||
|
if (this.isSelectionMode !== isSelectionModeBefore) {
|
||||||
|
this.setSelectionModeForEpisodes()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setSelectionModeForEpisodes() {
|
||||||
|
for (const key in this.episodeComponentRefs) {
|
||||||
|
if (this.episodeComponentRefs[key]?.setSelectionMode) {
|
||||||
|
this.episodeComponentRefs[key].setSelectionMode(this.isSelectionMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
playEpisode(episode) {
|
playEpisode(episode) {
|
||||||
const queueItems = []
|
const queueItems = []
|
||||||
@@ -367,12 +400,147 @@ export default {
|
|||||||
this.$store.commit('globals/setSelectedEpisode', episode)
|
this.$store.commit('globals/setSelectedEpisode', episode)
|
||||||
this.$store.commit('globals/setShowViewPodcastEpisodeModal', true)
|
this.$store.commit('globals/setShowViewPodcastEpisodeModal', true)
|
||||||
},
|
},
|
||||||
init() {
|
destroyEpisodeComponents() {
|
||||||
|
for (const key in this.episodeComponentRefs) {
|
||||||
|
if (this.episodeComponentRefs[key]?.destroy) {
|
||||||
|
this.episodeComponentRefs[key].destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.episodeComponentRefs = {}
|
||||||
|
this.episodeIndexesMounted = []
|
||||||
|
},
|
||||||
|
mountEpisode(index) {
|
||||||
|
const episodeEl = document.getElementById(`episode-${index}`)
|
||||||
|
if (!episodeEl) {
|
||||||
|
console.warn('Episode row el not found at ' + index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.episodeIndexesMounted.push(index)
|
||||||
|
|
||||||
|
if (this.episodeComponentRefs[index]) {
|
||||||
|
const episodeComponent = this.episodeComponentRefs[index]
|
||||||
|
episodeEl.appendChild(episodeComponent.$el)
|
||||||
|
if (this.isSelectionMode) {
|
||||||
|
episodeComponent.setSelectionMode(true)
|
||||||
|
if (this.selectedEpisodes.some((i) => i.id === episodeComponent.episodeId)) {
|
||||||
|
episodeComponent.isSelected = true
|
||||||
|
} else {
|
||||||
|
episodeComponent.isSelected = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
episodeComponent.setSelectionMode(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const _this = this
|
||||||
|
const ComponentClass = Vue.extend(LazyEpisodeRow)
|
||||||
|
const instance = new ComponentClass({
|
||||||
|
propsData: {
|
||||||
|
index,
|
||||||
|
libraryItemId: this.libraryItem.id,
|
||||||
|
episode: this.episodesList[index]
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$on('selected', (payload) => {
|
||||||
|
_this.episodeSelected(payload)
|
||||||
|
})
|
||||||
|
this.$on('view', (payload) => {
|
||||||
|
_this.viewEpisode(payload)
|
||||||
|
})
|
||||||
|
this.$on('play', (payload) => {
|
||||||
|
_this.playEpisode(payload)
|
||||||
|
})
|
||||||
|
this.$on('addToQueue', (payload) => {
|
||||||
|
_this.addEpisodeToQueue(payload)
|
||||||
|
})
|
||||||
|
this.$on('remove', (payload) => {
|
||||||
|
_this.removeEpisode(payload)
|
||||||
|
})
|
||||||
|
this.$on('edit', (payload) => {
|
||||||
|
_this.editEpisode(payload)
|
||||||
|
})
|
||||||
|
this.$on('addToPlaylist', (payload) => {
|
||||||
|
_this.addToPlaylist(payload)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.episodeComponentRefs[index] = instance
|
||||||
|
instance.$mount()
|
||||||
|
episodeEl.appendChild(instance.$el)
|
||||||
|
|
||||||
|
if (this.isSelectionMode) {
|
||||||
|
instance.setSelectionMode(true)
|
||||||
|
if (this.selectedEpisodes.some((i) => i.id === this.episodesList[index].id)) {
|
||||||
|
instance.isSelected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mountEpisodes(startIndex, endIndex) {
|
||||||
|
for (let i = startIndex; i < endIndex; i++) {
|
||||||
|
if (!this.episodeIndexesMounted.includes(i)) {
|
||||||
|
this.mountEpisode(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scroll(evt) {
|
||||||
|
if (!evt?.target?.scrollTop) return
|
||||||
|
const scrollTop = Math.max(evt.target.scrollTop - this.episodesTableOffsetTop, 0)
|
||||||
|
let firstEpisodeIndex = Math.floor(scrollTop / this.episodeRowHeight)
|
||||||
|
let lastEpisodeIndex = Math.ceil((scrollTop + this.windowHeight) / this.episodeRowHeight)
|
||||||
|
lastEpisodeIndex = Math.min(this.totalEpisodes - 1, lastEpisodeIndex)
|
||||||
|
|
||||||
|
this.episodeIndexesMounted = this.episodeIndexesMounted.filter((_index) => {
|
||||||
|
if (_index < firstEpisodeIndex || _index >= lastEpisodeIndex) {
|
||||||
|
const el = document.getElementById(`lazy-episode-${_index}`)
|
||||||
|
if (el) el.remove()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
this.mountEpisodes(firstEpisodeIndex, lastEpisodeIndex + 1)
|
||||||
|
},
|
||||||
|
initListeners() {
|
||||||
|
const itemPageWrapper = document.getElementById('item-page-wrapper')
|
||||||
|
if (itemPageWrapper) {
|
||||||
|
itemPageWrapper.addEventListener('scroll', this.scroll)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeListeners() {
|
||||||
|
const itemPageWrapper = document.getElementById('item-page-wrapper')
|
||||||
|
if (itemPageWrapper) {
|
||||||
|
itemPageWrapper.removeEventListener('scroll', this.scroll)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterSortChanged() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
this.episodesCopy = this.episodes.map((ep) => ({ ...ep }))
|
this.episodesCopy = this.episodes.map((ep) => ({ ...ep }))
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.destroyEpisodeComponents()
|
||||||
|
this.totalEpisodes = this.episodesList.length
|
||||||
|
|
||||||
|
const lazyEpisodesTableEl = document.getElementById('lazy-episodes-table')
|
||||||
|
this.episodesTableOffsetTop = (lazyEpisodesTableEl?.offsetTop || 0) + 64
|
||||||
|
|
||||||
|
this.windowHeight = window.innerHeight
|
||||||
|
this.episodesPerPage = Math.ceil(this.windowHeight / this.episodeRowHeight)
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.mountEpisodes(0, Math.min(this.episodesPerPage, this.totalEpisodes))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.episodesCopy = this.episodes.map((ep) => ({ ...ep }))
|
||||||
|
this.initListeners()
|
||||||
this.init()
|
this.init()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.removeListeners()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
<label class="flex justify-start items-center" :class="!disabled ? 'cursor-pointer' : ''">
|
<label class="flex justify-start items-center" :class="!disabled ? 'cursor-pointer' : ''">
|
||||||
<div class="border-2 rounded flex flex-shrink-0 justify-center items-center" :class="wrapperClass">
|
<div class="border-2 rounded flex flex-shrink-0 justify-center items-center" :class="wrapperClass">
|
||||||
<input v-model="selected" :disabled="disabled" type="checkbox" class="opacity-0 absolute" :class="!disabled ? 'cursor-pointer' : ''" />
|
<input v-model="selected" :disabled="disabled" type="checkbox" class="opacity-0 absolute" :class="!disabled ? 'cursor-pointer' : ''" />
|
||||||
<svg v-if="selected" class="fill-current pointer-events-none" :class="svgClass" viewBox="0 0 20 20"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z" /></svg>
|
<span v-if="partial" class="material-icons text-base leading-none text-gray-400">remove</span>
|
||||||
|
<svg v-else-if="selected" class="fill-current pointer-events-none" :class="svgClass" viewBox="0 0 20 20"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z" /></svg>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label" class="select-none" :class="[labelClassname, disabled ? 'text-gray-400' : 'text-gray-100']">{{ label }}</div>
|
<div v-if="label" class="select-none" :class="[labelClassname, disabled ? 'text-gray-400' : 'text-gray-100']">{{ label }}</div>
|
||||||
</label>
|
</label>
|
||||||
@@ -31,7 +32,8 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
disabled: Boolean
|
disabled: Boolean,
|
||||||
|
partial: Boolean
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative w-full" v-click-outside="clickOutsideObj">
|
<div class="relative w-full" v-click-outside="clickOutsideObj">
|
||||||
<p class="text-sm font-semibold px-1" :class="disabled ? 'text-gray-300' : ''">{{ label }}</p>
|
<p v-if="label" class="text-sm font-semibold px-1" :class="disabled ? 'text-gray-300' : ''">{{ label }}</p>
|
||||||
<button type="button" :aria-label="longLabel" :disabled="disabled" class="relative w-full border rounded shadow-sm pl-3 pr-8 py-2 text-left sm:text-sm" :class="buttonClass" aria-haspopup="listbox" aria-expanded="true" @click.stop.prevent="clickShowMenu">
|
<button type="button" :aria-label="longLabel" :disabled="disabled" class="relative w-full border rounded shadow-sm pl-3 pr-8 py-2 text-left sm:text-sm" :class="buttonClass" aria-haspopup="listbox" aria-expanded="true" @click.stop.prevent="clickShowMenu">
|
||||||
<span class="flex items-center">
|
<span class="flex items-center">
|
||||||
<span class="block truncate font-sans" :class="{ 'font-semibold': selectedSubtext, 'text-sm': small }">{{ selectedText }}</span>
|
<span class="block truncate font-sans" :class="{ 'font-semibold': selectedSubtext, 'text-sm': small }">{{ selectedText }}</span>
|
||||||
@@ -64,7 +64,7 @@ export default {
|
|||||||
},
|
},
|
||||||
itemsToShow() {
|
itemsToShow() {
|
||||||
return this.items.map((i) => {
|
return this.items.map((i) => {
|
||||||
if (typeof i === 'string') {
|
if (typeof i === 'string' || typeof i === 'number') {
|
||||||
return {
|
return {
|
||||||
text: i,
|
text: i,
|
||||||
value: i
|
value: i
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<label class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">{{ label }}</label>
|
<label v-if="label" class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">{{ label }}</label>
|
||||||
<div ref="wrapper" class="relative">
|
<div ref="wrapper" class="relative">
|
||||||
<form @submit.prevent="submitForm">
|
<form @submit.prevent="submitForm">
|
||||||
<div ref="inputWrapper" class="input-wrapper flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-2" :class="disabled ? 'pointer-events-none bg-black-300 text-gray-400' : 'bg-primary'">
|
<div ref="inputWrapper" class="input-wrapper flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-2" :class="disabled ? 'pointer-events-none bg-black-300 text-gray-400' : 'bg-primary'">
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
<input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" />
|
<input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" class="h-full bg-primary focus:outline-none px-1 w-6" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ul ref="menu" v-show="showMenu" class="absolute z-60 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
<ul ref="menu" v-show="showMenu" class="absolute z-60 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
||||||
<template v-for="item in itemsToShow">
|
<template v-for="item in itemsToShow">
|
||||||
<li :key="item" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
|
<li :key="item" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="itemsToShow[selectedMenuItemIndex] === item ? 'text-yellow-300' : ''" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-normal ml-3 block truncate">{{ item }}</span>
|
<span class="font-normal ml-3 block truncate">{{ item }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,7 +50,11 @@ export default {
|
|||||||
label: String,
|
label: String,
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
readonly: Boolean,
|
readonly: Boolean,
|
||||||
showEdit: Boolean
|
showEdit: Boolean,
|
||||||
|
menuDisabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -58,7 +62,9 @@ export default {
|
|||||||
currentSearch: null,
|
currentSearch: null,
|
||||||
typingTimeout: null,
|
typingTimeout: null,
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
menu: null
|
menu: null,
|
||||||
|
filteredItems: null,
|
||||||
|
selectedMenuItemIndex: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -77,7 +83,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
showMenu() {
|
showMenu() {
|
||||||
return this.isFocused
|
return this.isFocused && !this.menuDisabled
|
||||||
},
|
},
|
||||||
wrapperClass() {
|
wrapperClass() {
|
||||||
var classes = []
|
var classes = []
|
||||||
@@ -87,24 +93,63 @@ export default {
|
|||||||
return classes.join(' ')
|
return classes.join(' ')
|
||||||
},
|
},
|
||||||
itemsToShow() {
|
itemsToShow() {
|
||||||
if (!this.currentSearch || !this.textInput) {
|
if (!this.currentSearch || !this.textInput || !this.filteredItems) {
|
||||||
return this.items
|
return this.items
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.items.filter((i) => {
|
return this.filteredItems
|
||||||
var iValue = String(i).toLowerCase()
|
|
||||||
return iValue.includes(this.currentSearch.toLowerCase())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
editItem(item) {
|
editItem(item) {
|
||||||
this.$emit('edit', item)
|
this.$emit('edit', item)
|
||||||
},
|
},
|
||||||
keydownInput() {
|
search() {
|
||||||
|
if (!this.textInput) {
|
||||||
|
this.filteredItems = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.currentSearch = this.textInput
|
||||||
|
|
||||||
|
const results = this.items.filter((i) => {
|
||||||
|
var iValue = String(i).toLowerCase()
|
||||||
|
return iValue.includes(this.currentSearch.toLowerCase())
|
||||||
|
})
|
||||||
|
|
||||||
|
this.filteredItems = results || []
|
||||||
|
},
|
||||||
|
keydownInput(event) {
|
||||||
|
let items = this.itemsToShow
|
||||||
|
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
||||||
|
event.preventDefault()
|
||||||
|
if (!items.length) return
|
||||||
|
if (event.key === 'ArrowDown') {
|
||||||
|
if (this.selectedMenuItemIndex === null) {
|
||||||
|
this.selectedMenuItemIndex = 0
|
||||||
|
} else {
|
||||||
|
this.selectedMenuItemIndex = Math.min(this.selectedMenuItemIndex + 1, items.length - 1)
|
||||||
|
}
|
||||||
|
} else if (event.key === 'ArrowUp') {
|
||||||
|
if (this.selectedMenuItemIndex === null) {
|
||||||
|
this.selectedMenuItemIndex = items.length - 1
|
||||||
|
} else {
|
||||||
|
this.selectedMenuItemIndex = Math.max(this.selectedMenuItemIndex - 1, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.recalcScroll()
|
||||||
|
return
|
||||||
|
} else if (event.key === 'Enter') {
|
||||||
|
if (this.selectedMenuItemIndex !== null) {
|
||||||
|
this.clickedOption(event, items[this.selectedMenuItemIndex])
|
||||||
|
} else {
|
||||||
|
this.submitForm()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.selectedMenuItemIndex = null
|
||||||
clearTimeout(this.typingTimeout)
|
clearTimeout(this.typingTimeout)
|
||||||
this.typingTimeout = setTimeout(() => {
|
this.typingTimeout = setTimeout(() => {
|
||||||
this.currentSearch = this.textInput
|
this.search()
|
||||||
}, 100)
|
}, 100)
|
||||||
this.setInputWidth()
|
this.setInputWidth()
|
||||||
},
|
},
|
||||||
@@ -116,6 +161,24 @@ export default {
|
|||||||
this.recalcMenuPos()
|
this.recalcMenuPos()
|
||||||
}, 50)
|
}, 50)
|
||||||
},
|
},
|
||||||
|
recalcScroll() {
|
||||||
|
if (!this.menu) return
|
||||||
|
var menuItems = this.menu.querySelectorAll('li')
|
||||||
|
if (!menuItems.length) return
|
||||||
|
var selectedItem = menuItems[this.selectedMenuItemIndex]
|
||||||
|
if (!selectedItem) return
|
||||||
|
var menuHeight = this.menu.offsetHeight
|
||||||
|
var itemHeight = selectedItem.offsetHeight
|
||||||
|
var itemTop = selectedItem.offsetTop
|
||||||
|
var itemBottom = itemTop + itemHeight
|
||||||
|
if (itemBottom > this.menu.scrollTop + menuHeight) {
|
||||||
|
let menuPaddingBottom = parseFloat(window.getComputedStyle(this.menu).paddingBottom)
|
||||||
|
this.menu.scrollTop = itemBottom - menuHeight + menuPaddingBottom
|
||||||
|
} else if (itemTop < this.menu.scrollTop) {
|
||||||
|
let menuPaddingTop = parseFloat(window.getComputedStyle(this.menu).paddingTop)
|
||||||
|
this.menu.scrollTop = itemTop - menuPaddingTop
|
||||||
|
}
|
||||||
|
},
|
||||||
recalcMenuPos() {
|
recalcMenuPos() {
|
||||||
if (!this.menu || !this.$refs.inputWrapper) return
|
if (!this.menu || !this.$refs.inputWrapper) return
|
||||||
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
||||||
@@ -204,7 +267,10 @@ export default {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
if (this.$refs.input) this.$refs.input.focus()
|
if (this.$refs.input) {
|
||||||
|
this.$refs.input.style.width = '24px'
|
||||||
|
this.$refs.input.focus()
|
||||||
|
}
|
||||||
|
|
||||||
var newSelected = null
|
var newSelected = null
|
||||||
if (this.selected.includes(itemValue)) {
|
if (this.selected.includes(itemValue)) {
|
||||||
@@ -215,6 +281,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.textInput = null
|
this.textInput = null
|
||||||
this.currentSearch = null
|
this.currentSearch = null
|
||||||
|
this.selectedMenuItemIndex = null
|
||||||
this.$emit('input', newSelected)
|
this.$emit('input', newSelected)
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.recalcMenuPos()
|
this.recalcMenuPos()
|
||||||
@@ -241,6 +308,7 @@ export default {
|
|||||||
this.$emit('newItem', item)
|
this.$emit('newItem', item)
|
||||||
this.textInput = null
|
this.textInput = null
|
||||||
this.currentSearch = null
|
this.currentSearch = null
|
||||||
|
this.selectedMenuItemIndex = null
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.blur()
|
this.blur()
|
||||||
})
|
})
|
||||||
@@ -257,6 +325,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.insertNewItem(this.textInput)
|
this.insertNewItem(this.textInput)
|
||||||
}
|
}
|
||||||
|
if (this.$refs.input) this.$refs.input.style.width = '24px'
|
||||||
},
|
},
|
||||||
scroll() {
|
scroll() {
|
||||||
this.recalcMenuPos()
|
this.recalcMenuPos()
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
<div v-if="showEdit && !disabled" class="rounded-full cursor-pointer w-6 h-6 mx-0.5 bg-bg flex items-center justify-center">
|
<div v-if="showEdit && !disabled" class="rounded-full cursor-pointer w-6 h-6 mx-0.5 bg-bg flex items-center justify-center">
|
||||||
<span class="material-icons text-white hover:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem">add</span>
|
<span class="material-icons text-white hover:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem">add</span>
|
||||||
</div>
|
</div>
|
||||||
<input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" />
|
<input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" class="h-full bg-primary focus:outline-none px-1 w-6" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ul ref="menu" v-show="showMenu" class="absolute z-60 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
<ul ref="menu" v-show="showMenu" class="absolute z-60 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
||||||
<template v-for="item in itemsToShow">
|
<template v-for="item in itemsToShow">
|
||||||
<li :key="item.id" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
|
<li :key="item.id" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="itemsToShow[selectedMenuItemIndex] === item ? 'text-yellow-300' : ''" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-normal ml-3 block truncate">{{ item.name }}</span>
|
<span class="font-normal ml-3 block truncate">{{ item.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,7 +63,8 @@ export default {
|
|||||||
typingTimeout: null,
|
typingTimeout: null,
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
menu: null,
|
menu: null,
|
||||||
items: []
|
items: [],
|
||||||
|
selectedMenuItemIndex: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -122,7 +123,35 @@ export default {
|
|||||||
|
|
||||||
this.items = results || []
|
this.items = results || []
|
||||||
},
|
},
|
||||||
keydownInput() {
|
keydownInput(event) {
|
||||||
|
let items = this.itemsToShow
|
||||||
|
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
||||||
|
event.preventDefault()
|
||||||
|
if (!items.length) return
|
||||||
|
if (event.key === 'ArrowDown') {
|
||||||
|
if (this.selectedMenuItemIndex === null) {
|
||||||
|
this.selectedMenuItemIndex = 0
|
||||||
|
} else {
|
||||||
|
this.selectedMenuItemIndex = Math.min(this.selectedMenuItemIndex + 1, items.length - 1)
|
||||||
|
}
|
||||||
|
} else if (event.key === 'ArrowUp') {
|
||||||
|
if (this.selectedMenuItemIndex === null) {
|
||||||
|
this.selectedMenuItemIndex = items.length - 1
|
||||||
|
} else {
|
||||||
|
this.selectedMenuItemIndex = Math.max(this.selectedMenuItemIndex - 1, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.recalcScroll()
|
||||||
|
return
|
||||||
|
} else if (event.key === 'Enter') {
|
||||||
|
if (this.selectedMenuItemIndex !== null) {
|
||||||
|
this.clickedOption(event, items[this.selectedMenuItemIndex])
|
||||||
|
} else {
|
||||||
|
this.submitForm()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.selectedMenuItemIndex = null
|
||||||
clearTimeout(this.typingTimeout)
|
clearTimeout(this.typingTimeout)
|
||||||
this.typingTimeout = setTimeout(() => {
|
this.typingTimeout = setTimeout(() => {
|
||||||
this.search()
|
this.search()
|
||||||
@@ -137,6 +166,24 @@ export default {
|
|||||||
this.recalcMenuPos()
|
this.recalcMenuPos()
|
||||||
}, 50)
|
}, 50)
|
||||||
},
|
},
|
||||||
|
recalcScroll() {
|
||||||
|
if (!this.menu) return
|
||||||
|
var menuItems = this.menu.querySelectorAll('li')
|
||||||
|
if (!menuItems.length) return
|
||||||
|
var selectedItem = menuItems[this.selectedMenuItemIndex]
|
||||||
|
if (!selectedItem) return
|
||||||
|
var menuHeight = this.menu.offsetHeight
|
||||||
|
var itemHeight = selectedItem.offsetHeight
|
||||||
|
var itemTop = selectedItem.offsetTop
|
||||||
|
var itemBottom = itemTop + itemHeight
|
||||||
|
if (itemBottom > this.menu.scrollTop + menuHeight) {
|
||||||
|
let menuPaddingBottom = parseFloat(window.getComputedStyle(this.menu).paddingBottom)
|
||||||
|
this.menu.scrollTop = itemBottom - menuHeight + menuPaddingBottom
|
||||||
|
} else if (itemTop < this.menu.scrollTop) {
|
||||||
|
let menuPaddingTop = parseFloat(window.getComputedStyle(this.menu).paddingTop)
|
||||||
|
this.menu.scrollTop = itemTop - menuPaddingTop
|
||||||
|
}
|
||||||
|
},
|
||||||
recalcMenuPos() {
|
recalcMenuPos() {
|
||||||
if (!this.menu || !this.$refs.inputWrapper) return
|
if (!this.menu || !this.$refs.inputWrapper) return
|
||||||
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
||||||
@@ -228,7 +275,10 @@ export default {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
if (this.$refs.input) this.$refs.input.focus()
|
if (this.$refs.input) {
|
||||||
|
this.$refs.input.style.width = '24px'
|
||||||
|
this.$refs.input.focus()
|
||||||
|
}
|
||||||
|
|
||||||
let newSelected = null
|
let newSelected = null
|
||||||
if (this.getIsSelected(item.id)) {
|
if (this.getIsSelected(item.id)) {
|
||||||
@@ -244,6 +294,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.textInput = null
|
this.textInput = null
|
||||||
this.currentSearch = null
|
this.currentSearch = null
|
||||||
|
this.selectedMenuItemIndex = null
|
||||||
|
|
||||||
this.$emit('input', newSelected)
|
this.$emit('input', newSelected)
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@@ -271,6 +322,7 @@ export default {
|
|||||||
this.$emit('newItem', item)
|
this.$emit('newItem', item)
|
||||||
this.textInput = null
|
this.textInput = null
|
||||||
this.currentSearch = null
|
this.currentSearch = null
|
||||||
|
this.selectedMenuItemIndex = null
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.blur()
|
this.blur()
|
||||||
})
|
})
|
||||||
@@ -291,6 +343,7 @@ export default {
|
|||||||
name: this.textInput
|
name: this.textInput
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (this.$refs.input) this.$refs.input.style.width = '24px'
|
||||||
},
|
},
|
||||||
scroll() {
|
scroll() {
|
||||||
this.recalcMenuPos()
|
this.recalcMenuPos()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="default-style">
|
||||||
<p v-if="label" class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': disabled }">
|
<p v-if="label" class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': disabled }">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</p>
|
</p>
|
||||||
@@ -29,31 +29,31 @@ export default {
|
|||||||
config() {
|
config() {
|
||||||
return {
|
return {
|
||||||
toolbar: {
|
toolbar: {
|
||||||
getDefaultHTML: () => ` <div class="trix-button-row">
|
getDefaultHTML: () => `<div class="trix-button-row">
|
||||||
<span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">
|
<span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" title="#{lang.bold}" tabindex="-1">#{lang.bold}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" title="${this.$strings.LabelFontBold}" tabindex="-1">${this.$strings.LabelFontBold}</button>
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" title="#{lang.italic}" tabindex="-1">#{lang.italic}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" title="${this.$strings.LabelFontItalic}" tabindex="-1">${this.$strings.LabelFontItalic}</button>
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" title="#{lang.strike}" tabindex="-1">#{lang.strike}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" title="${this.$strings.LabelFontStrikethrough}" tabindex="-1">${this.$strings.LabelFontStrikethrough}</button>
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" title="#{lang.link}" tabindex="-1">#{lang.link}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" title="${this.$strings.LabelTextEditorLink}" tabindex="-1">${this.$strings.LabelTextEditorLink}</button>
|
||||||
</span>
|
</span>
|
||||||
<span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">
|
<span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" title="#{lang.bullets}" tabindex="-1">#{lang.bullets}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" title="${this.$strings.LabelTextEditorBulletedList}" tabindex="-1">${this.$strings.LabelTextEditorBulletedList}</button>
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" title="#{lang.numbers}" tabindex="-1">#{lang.numbers}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" title="${this.$strings.LabelTextEditorNumberedList}" tabindex="-1">${this.$strings.LabelTextEditorNumberedList}</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="trix-button-group-spacer"></span>
|
<span class="trix-button-group-spacer"></span>
|
||||||
<span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">
|
<span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" title="#{lang.undo}" tabindex="-1">#{lang.undo}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" title="${this.$strings.LabelUndo}" tabindex="-1">${this.$strings.LabelUndo}</button>
|
||||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" title="#{lang.redo}" tabindex="-1">#{lang.redo}</button>
|
<button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" title="${this.$strings.LabelRedo}" tabindex="-1">${this.$strings.LabelRedo}</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="trix-dialogs" data-trix-dialogs>
|
<div class="trix-dialogs" data-trix-dialogs>
|
||||||
<div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">
|
<div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">
|
||||||
<div class="trix-dialog__link-fields">
|
<div class="trix-dialog__link-fields">
|
||||||
<input type="url" name="href" class="trix-input trix-input--dialog" placeholder="#{lang.urlPlaceholder}" aria-label="#{lang.url}" required data-trix-input>
|
<input type="url" name="href" class="trix-input trix-input--dialog" placeholder="" aria-label="URL" required data-trix-input>
|
||||||
<div class="trix-button-group">
|
<div class="trix-button-group">
|
||||||
<input type="button" class="trix-button trix-button--dialog" value="#{lang.link}" data-trix-method="setAttribute">
|
<input type="button" class="trix-button trix-button--dialog" value="${this.$strings.LabelTextEditorLink}" data-trix-method="setAttribute">
|
||||||
<input type="button" class="trix-button trix-button--dialog" value="#{lang.unlink}" data-trix-method="removeAttribute">
|
<input type="button" class="trix-button trix-button--dialog" value="${this.$strings.LabelTextEditorUnlink}" data-trix-method="removeAttribute">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="wrapper" class="relative">
|
<div ref="wrapper" class="relative">
|
||||||
<input :id="inputId" 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" 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">
|
<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>
|
<span class="material-icons text-gray-300 cursor-pointer" style="font-size: 1.1rem" @click.stop.prevent="clear">close</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,6 +33,7 @@ export default {
|
|||||||
textCenter: Boolean,
|
textCenter: Boolean,
|
||||||
clearable: Boolean,
|
clearable: Boolean,
|
||||||
inputId: String,
|
inputId: String,
|
||||||
|
inputName: String,
|
||||||
step: [String, Number],
|
step: [String, Number],
|
||||||
min: [String, Number]
|
min: [String, Number]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'right'
|
default: 'right'
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Delay showing the tooltip after X milliseconds of hovering
|
||||||
|
*/
|
||||||
|
delayOnShow: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
disabled: Boolean
|
disabled: Boolean
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -22,7 +29,8 @@ export default {
|
|||||||
tooltip: null,
|
tooltip: null,
|
||||||
tooltipId: null,
|
tooltipId: null,
|
||||||
isShowing: false,
|
isShowing: false,
|
||||||
hideTimeout: null
|
hideTimeout: null,
|
||||||
|
delayOnShowTimeout: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -59,29 +67,44 @@ export default {
|
|||||||
this.tooltip = tooltip
|
this.tooltip = tooltip
|
||||||
},
|
},
|
||||||
setTooltipPosition(tooltip) {
|
setTooltipPosition(tooltip) {
|
||||||
var boxChow = this.$refs.box.getBoundingClientRect()
|
const boxRect = this.$refs.box.getBoundingClientRect()
|
||||||
|
|
||||||
|
const shouldMount = !tooltip.isConnected
|
||||||
|
|
||||||
var shouldMount = !tooltip.isConnected
|
|
||||||
// Calculate size of tooltip
|
// Calculate size of tooltip
|
||||||
if (shouldMount) document.body.appendChild(tooltip)
|
if (shouldMount) document.body.appendChild(tooltip)
|
||||||
var { width, height } = tooltip.getBoundingClientRect()
|
const tooltipRect = tooltip.getBoundingClientRect()
|
||||||
if (shouldMount) tooltip.remove()
|
if (shouldMount) tooltip.remove()
|
||||||
|
|
||||||
var top = 0
|
// Subtracting scrollbar size
|
||||||
var left = 0
|
const windowHeight = window.innerHeight - 8
|
||||||
|
const windowWidth = window.innerWidth - 8
|
||||||
|
|
||||||
|
let top = 0
|
||||||
|
let left = 0
|
||||||
if (this.direction === 'right') {
|
if (this.direction === 'right') {
|
||||||
top = boxChow.top - height / 2 + boxChow.height / 2
|
top = Math.max(0, boxRect.top - tooltipRect.height / 2 + boxRect.height / 2)
|
||||||
left = boxChow.left + boxChow.width + 4
|
left = Math.max(0, boxRect.left + boxRect.width + 4)
|
||||||
} else if (this.direction === 'bottom') {
|
} else if (this.direction === 'bottom') {
|
||||||
top = boxChow.top + boxChow.height + 4
|
top = Math.max(0, boxRect.top + boxRect.height + 4)
|
||||||
left = boxChow.left - width / 2 + boxChow.width / 2
|
left = Math.max(0, boxRect.left - tooltipRect.width / 2 + boxRect.width / 2)
|
||||||
} else if (this.direction === 'top') {
|
} else if (this.direction === 'top') {
|
||||||
top = boxChow.top - height - 4
|
top = Math.max(0, boxRect.top - tooltipRect.height - 4)
|
||||||
left = boxChow.left - width / 2 + boxChow.width / 2
|
left = Math.max(0, boxRect.left - tooltipRect.width / 2 + boxRect.width / 2)
|
||||||
} else if (this.direction === 'left') {
|
} else if (this.direction === 'left') {
|
||||||
top = boxChow.top - height / 2 + boxChow.height / 2
|
top = Math.max(0, boxRect.top - tooltipRect.height / 2 + boxRect.height / 2)
|
||||||
left = boxChow.left - width - 4
|
left = Math.max(0, boxRect.left - tooltipRect.width - 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shift left if tooltip would overflow the window on the right
|
||||||
|
if (left + tooltipRect.width > windowWidth) {
|
||||||
|
left -= left + tooltipRect.width - windowWidth
|
||||||
|
}
|
||||||
|
// Shift up if tooltip would overflow the window on the bottom
|
||||||
|
if (top + tooltipRect.height > windowHeight) {
|
||||||
|
top -= top + tooltipRect.height - windowHeight
|
||||||
|
}
|
||||||
|
|
||||||
tooltip.style.top = top + 'px'
|
tooltip.style.top = top + 'px'
|
||||||
tooltip.style.left = left + 'px'
|
tooltip.style.left = left + 'px'
|
||||||
},
|
},
|
||||||
@@ -107,15 +130,33 @@ export default {
|
|||||||
this.isShowing = false
|
this.isShowing = false
|
||||||
},
|
},
|
||||||
cancelHide() {
|
cancelHide() {
|
||||||
if (this.hideTimeout) clearTimeout(this.hideTimeout)
|
clearTimeout(this.hideTimeout)
|
||||||
},
|
},
|
||||||
mouseover() {
|
mouseover() {
|
||||||
if (!this.isShowing) this.showTooltip()
|
if (this.isShowing || this.disabled) return
|
||||||
|
|
||||||
|
if (this.delayOnShow) {
|
||||||
|
if (this.delayOnShowTimeout) {
|
||||||
|
// Delay already running
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delayOnShowTimeout = setTimeout(() => {
|
||||||
|
this.showTooltip()
|
||||||
|
this.delayOnShowTimeout = null
|
||||||
|
}, this.delayOnShow)
|
||||||
|
} else {
|
||||||
|
this.showTooltip()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mouseleave() {
|
mouseleave() {
|
||||||
if (this.isShowing) {
|
if (!this.isShowing) {
|
||||||
this.hideTimeout = setTimeout(this.hideTooltip, 100)
|
clearTimeout(this.delayOnShowTimeout)
|
||||||
|
this.delayOnShowTimeout = null
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.hideTimeout = setTimeout(this.hideTooltip, 100)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<Nuxt :key="currentLang" />
|
<Nuxt :key="currentLang" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-stream-container ref="streamContainer" />
|
<app-media-player-container ref="mediaPlayerContainer" />
|
||||||
|
|
||||||
<modals-item-edit-modal />
|
<modals-item-edit-modal />
|
||||||
<modals-collections-add-create-modal />
|
<modals-collections-add-create-modal />
|
||||||
@@ -129,23 +129,23 @@ export default {
|
|||||||
this.$eventBus.$emit('socket_init')
|
this.$eventBus.$emit('socket_init')
|
||||||
},
|
},
|
||||||
streamOpen(stream) {
|
streamOpen(stream) {
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.streamOpen(stream)
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamOpen(stream)
|
||||||
},
|
},
|
||||||
streamClosed(streamId) {
|
streamClosed(streamId) {
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.streamClosed(streamId)
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamClosed(streamId)
|
||||||
},
|
},
|
||||||
streamProgress(data) {
|
streamProgress(data) {
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.streamProgress(data)
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamProgress(data)
|
||||||
},
|
},
|
||||||
streamReady() {
|
streamReady() {
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.streamReady()
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamReady()
|
||||||
},
|
},
|
||||||
streamReset(payload) {
|
streamReset(payload) {
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.streamReset(payload)
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamReset(payload)
|
||||||
},
|
},
|
||||||
streamError({ id, errorMessage }) {
|
streamError({ id, errorMessage }) {
|
||||||
this.$toast.error(`Stream Failed: ${errorMessage}`)
|
this.$toast.error(`Stream Failed: ${errorMessage}`)
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.streamError(id)
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.streamError(id)
|
||||||
},
|
},
|
||||||
libraryAdded(library) {
|
libraryAdded(library) {
|
||||||
this.$store.commit('libraries/addUpdate', library)
|
this.$store.commit('libraries/addUpdate', library)
|
||||||
@@ -247,7 +247,7 @@ export default {
|
|||||||
this.multiSessionCurrentSessionId = null
|
this.multiSessionCurrentSessionId = null
|
||||||
this.$toast.dismiss('multiple-sessions')
|
this.$toast.dismiss('multiple-sessions')
|
||||||
}
|
}
|
||||||
if (this.$refs.streamContainer) this.$refs.streamContainer.sessionClosedEvent(sessionId)
|
if (this.$refs.mediaPlayerContainer) this.$refs.mediaPlayerContainer.sessionClosedEvent(sessionId)
|
||||||
},
|
},
|
||||||
userMediaProgressUpdate(payload) {
|
userMediaProgressUpdate(payload) {
|
||||||
this.$store.commit('user/updateMediaProgress', payload)
|
this.$store.commit('user/updateMediaProgress', payload)
|
||||||
@@ -328,6 +328,14 @@ export default {
|
|||||||
|
|
||||||
this.$store.commit('libraries/setEReaderDevices', data.ereaderDevices)
|
this.$store.commit('libraries/setEReaderDevices', data.ereaderDevices)
|
||||||
},
|
},
|
||||||
|
customMetadataProviderAdded(provider) {
|
||||||
|
if (!provider?.id) return
|
||||||
|
this.$store.commit('scanners/addCustomMetadataProvider', provider)
|
||||||
|
},
|
||||||
|
customMetadataProviderRemoved(provider) {
|
||||||
|
if (!provider?.id) return
|
||||||
|
this.$store.commit('scanners/removeCustomMetadataProvider', provider)
|
||||||
|
},
|
||||||
initializeSocket() {
|
initializeSocket() {
|
||||||
this.socket = this.$nuxtSocket({
|
this.socket = this.$nuxtSocket({
|
||||||
name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
|
name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
|
||||||
@@ -406,6 +414,10 @@ export default {
|
|||||||
this.socket.on('batch_quickmatch_complete', this.batchQuickMatchComplete)
|
this.socket.on('batch_quickmatch_complete', this.batchQuickMatchComplete)
|
||||||
|
|
||||||
this.socket.on('admin_message', this.adminMessageEvt)
|
this.socket.on('admin_message', this.adminMessageEvt)
|
||||||
|
|
||||||
|
// Custom metadata provider Listeners
|
||||||
|
this.socket.on('custom_metadata_provider_added', this.customMetadataProviderAdded)
|
||||||
|
this.socket.on('custom_metadata_provider_removed', this.customMetadataProviderRemoved)
|
||||||
},
|
},
|
||||||
showUpdateToast(versionData) {
|
showUpdateToast(versionData) {
|
||||||
var ignoreVersion = localStorage.getItem('ignoreVersion')
|
var ignoreVersion = localStorage.getItem('ignoreVersion')
|
||||||
|
|||||||
+12
-11
@@ -25,11 +25,13 @@ module.exports = {
|
|||||||
meta: [
|
meta: [
|
||||||
{ charset: 'utf-8' },
|
{ charset: 'utf-8' },
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
{ hid: 'description', name: 'description', content: '' }
|
{ hid: 'description', name: 'description', content: '' },
|
||||||
|
{ hid: 'robots', name: 'robots', content: 'noindex' }
|
||||||
],
|
],
|
||||||
script: [],
|
script: [],
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'icon', type: 'image/x-icon', href: (process.env.ROUTER_BASE_PATH || '') + '/favicon.ico' }
|
{ rel: 'icon', type: 'image/x-icon', href: (process.env.ROUTER_BASE_PATH || '') + '/favicon.ico' },
|
||||||
|
{ rel: 'apple-touch-icon', href: (process.env.ROUTER_BASE_PATH || '') + '/ios_icon.png' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ module.exports = {
|
|||||||
|
|
||||||
// Global CSS: https://go.nuxtjs.dev/config-css
|
// Global CSS: https://go.nuxtjs.dev/config-css
|
||||||
css: [
|
css: [
|
||||||
|
'@/assets/tailwind.css',
|
||||||
'@/assets/app.css'
|
'@/assets/app.css'
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -58,9 +61,7 @@ module.exports = {
|
|||||||
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
|
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
|
||||||
buildModules: [
|
buildModules: [
|
||||||
// https://go.nuxtjs.dev/tailwindcss
|
// https://go.nuxtjs.dev/tailwindcss
|
||||||
'@nuxtjs/tailwindcss',
|
'@nuxtjs/pwa'
|
||||||
'@nuxtjs/pwa',
|
|
||||||
'@nuxt/postcss8'
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// Modules: https://go.nuxtjs.dev/config-modules
|
// Modules: https://go.nuxtjs.dev/config-modules
|
||||||
@@ -96,7 +97,7 @@ module.exports = {
|
|||||||
meta: {
|
meta: {
|
||||||
appleStatusBarStyle: 'black',
|
appleStatusBarStyle: 'black',
|
||||||
name: 'Audiobookshelf',
|
name: 'Audiobookshelf',
|
||||||
theme_color: '#373838',
|
theme_color: '#232323',
|
||||||
mobileAppIOS: true,
|
mobileAppIOS: true,
|
||||||
nativeUI: true
|
nativeUI: true
|
||||||
},
|
},
|
||||||
@@ -104,16 +105,16 @@ module.exports = {
|
|||||||
name: 'Audiobookshelf',
|
name: 'Audiobookshelf',
|
||||||
short_name: 'Audiobookshelf',
|
short_name: 'Audiobookshelf',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
background_color: '#373838',
|
background_color: '#232323',
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: (process.env.ROUTER_BASE_PATH || '') + '/icon.svg',
|
src: (process.env.ROUTER_BASE_PATH || '') + '/icon.svg',
|
||||||
sizes: "any"
|
sizes: 'any'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: (process.env.ROUTER_BASE_PATH || '') + '/icon64.png',
|
src: (process.env.ROUTER_BASE_PATH || '') + '/icon192.png',
|
||||||
type: "image/png",
|
type: 'image/png',
|
||||||
sizes: "64x64"
|
sizes: 'any'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+6595
-20372
File diff suppressed because it is too large
Load Diff
+4
-6
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.6.0",
|
"version": "2.8.1",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast client",
|
"description": "Self-hosted audiobook and podcast client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
@@ -21,9 +21,9 @@
|
|||||||
"cron-parser": "^4.7.1",
|
"cron-parser": "^4.7.1",
|
||||||
"date-fns": "^2.25.0",
|
"date-fns": "^2.25.0",
|
||||||
"epubjs": "^0.3.88",
|
"epubjs": "^0.3.88",
|
||||||
"hls.js": "^1.0.7",
|
"hls.js": "^1.5.7",
|
||||||
"libarchive.js": "^1.3.0",
|
"libarchive.js": "^1.3.0",
|
||||||
"nuxt": "^2.15.8",
|
"nuxt": "^2.17.3",
|
||||||
"nuxt-socket-io": "^1.1.18",
|
"nuxt-socket-io": "^1.1.18",
|
||||||
"trix": "^1.3.1",
|
"trix": "^1.3.1",
|
||||||
"v-click-outside": "^3.1.2",
|
"v-click-outside": "^3.1.2",
|
||||||
@@ -31,11 +31,9 @@
|
|||||||
"vuedraggable": "^2.24.3"
|
"vuedraggable": "^2.24.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/postcss8": "^1.1.3",
|
|
||||||
"@nuxtjs/pwa": "^3.3.5",
|
"@nuxtjs/pwa": "^3.3.5",
|
||||||
"@nuxtjs/tailwindcss": "^4.2.1",
|
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.7",
|
||||||
"postcss": "^8.3.6",
|
"postcss": "^8.3.6",
|
||||||
"tailwindcss": "^3.1.4"
|
"tailwindcss": "^3.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
<div class="w-full h-px bg-white/10 my-4" />
|
<div class="w-full h-px bg-white/10 my-4" />
|
||||||
|
|
||||||
<p v-if="!isGuest" class="mb-4 text-lg">{{ $strings.HeaderChangePassword }}</p>
|
<p v-if="showChangePasswordForm" class="mb-4 text-lg">{{ $strings.HeaderChangePassword }}</p>
|
||||||
<form v-if="!isGuest" @submit.prevent="submitChangePassword">
|
<form v-if="showChangePasswordForm" @submit.prevent="submitChangePassword">
|
||||||
<ui-text-input-with-label v-model="password" :disabled="changingPassword" type="password" :label="$strings.LabelPassword" class="my-2" />
|
<ui-text-input-with-label v-model="password" :disabled="changingPassword" type="password" :label="$strings.LabelPassword" class="my-2" />
|
||||||
<ui-text-input-with-label v-model="newPassword" :disabled="changingPassword" type="password" :label="$strings.LabelNewPassword" class="my-2" />
|
<ui-text-input-with-label v-model="newPassword" :disabled="changingPassword" type="password" :label="$strings.LabelNewPassword" class="my-2" />
|
||||||
<ui-text-input-with-label v-model="confirmPassword" :disabled="changingPassword" type="password" :label="$strings.LabelConfirmPassword" class="my-2" />
|
<ui-text-input-with-label v-model="confirmPassword" :disabled="changingPassword" type="password" :label="$strings.LabelConfirmPassword" class="my-2" />
|
||||||
@@ -68,6 +68,13 @@ export default {
|
|||||||
},
|
},
|
||||||
isGuest() {
|
isGuest() {
|
||||||
return this.usertype === 'guest'
|
return this.usertype === 'guest'
|
||||||
|
},
|
||||||
|
isPasswordAuthEnabled() {
|
||||||
|
const activeAuthMethods = this.$store.getters['getServerSetting']('authActiveAuthMethods') || []
|
||||||
|
return activeAuthMethods.includes('local')
|
||||||
|
},
|
||||||
|
showChangePasswordForm() {
|
||||||
|
return !this.isGuest && this.isPasswordAuthEnabled
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -75,19 +82,33 @@ export default {
|
|||||||
this.$setLanguageCode(lang)
|
this.$setLanguageCode(lang)
|
||||||
},
|
},
|
||||||
logout() {
|
logout() {
|
||||||
var rootSocket = this.$root.socket || {}
|
// Disconnect from socket
|
||||||
const logoutPayload = {
|
if (this.$root.socket) {
|
||||||
socketId: rootSocket.id
|
console.log('Disconnecting from socket', this.$root.socket.id)
|
||||||
|
this.$root.socket.removeAllListeners()
|
||||||
|
this.$root.socket.disconnect()
|
||||||
}
|
}
|
||||||
this.$axios.$post('/logout', logoutPayload).catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
if (localStorage.getItem('token')) {
|
if (localStorage.getItem('token')) {
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
}
|
}
|
||||||
this.$store.commit('libraries/setUserPlaylists', [])
|
this.$store.commit('libraries/setUserPlaylists', [])
|
||||||
this.$store.commit('libraries/setCollections', [])
|
this.$store.commit('libraries/setCollections', [])
|
||||||
this.$router.push('/login')
|
|
||||||
|
this.$axios
|
||||||
|
.$post('/logout')
|
||||||
|
.then((logoutPayload) => {
|
||||||
|
const redirect_url = logoutPayload.redirect_url
|
||||||
|
|
||||||
|
if (redirect_url) {
|
||||||
|
window.location.href = redirect_url
|
||||||
|
} else {
|
||||||
|
this.$router.push('/login')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
resetForm() {
|
resetForm() {
|
||||||
this.password = null
|
this.password = null
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<div class="w-full h-full max-h-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative">
|
<div class="w-full h-full max-h-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative">
|
||||||
<div v-if="!chapterData" class="flex p-20">
|
<div v-if="!chapterData" class="flex p-20">
|
||||||
<ui-text-input-with-label v-model="asinInput" label="ASIN" />
|
<ui-text-input-with-label v-model.trim="asinInput" label="ASIN" />
|
||||||
<ui-dropdown v-model="regionInput" :label="$strings.LabelRegion" small :items="audibleRegions" class="w-32 mx-1" />
|
<ui-dropdown v-model="regionInput" :label="$strings.LabelRegion" small :items="audibleRegions" class="w-32 mx-1" />
|
||||||
<ui-btn small color="primary" class="mt-5" @click="findChapters">{{ $strings.ButtonSearch }}</ui-btn>
|
<ui-btn small color="primary" class="mt-5" @click="findChapters">{{ $strings.ButtonSearch }}</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="author.description" class="text-white text-opacity-60 uppercase text-xs mb-2">{{ $strings.LabelDescription }}</p>
|
<p v-if="author.description" class="text-white text-opacity-60 uppercase text-xs mb-2">{{ $strings.LabelDescription }}</p>
|
||||||
<p class="text-white max-w-3xl text-sm leading-5 whitespace-pre-wrap">{{ author.description }}</p>
|
<p ref="description" id="author-description" class="text-white max-w-3xl text-base whitespace-pre-wrap" :class="{ 'show-full': showFullDescription }">{{ author.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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -62,7 +65,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {
|
||||||
|
isDescriptionClamped: false,
|
||||||
|
showFullDescription: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
streamLibraryItem() {
|
streamLibraryItem() {
|
||||||
@@ -82,6 +88,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
checkDescriptionClamped() {
|
||||||
|
if (!this.$refs.description) return
|
||||||
|
this.isDescriptionClamped = this.$refs.description.scrollHeight > this.$refs.description.clientHeight
|
||||||
|
},
|
||||||
editAuthor() {
|
editAuthor() {
|
||||||
this.$store.commit('globals/showEditAuthorModal', this.author)
|
this.$store.commit('globals/showEditAuthorModal', this.author)
|
||||||
},
|
},
|
||||||
@@ -93,6 +103,7 @@ export default {
|
|||||||
series: this.authorSeries,
|
series: this.authorSeries,
|
||||||
libraryItems: this.libraryItems
|
libraryItems: this.libraryItems
|
||||||
}
|
}
|
||||||
|
this.$nextTick(this.checkDescriptionClamped)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
authorRemoved(author) {
|
authorRemoved(author) {
|
||||||
@@ -104,6 +115,7 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (!this.author) this.$router.replace('/')
|
if (!this.author) this.$router.replace('/')
|
||||||
|
this.checkDescriptionClamped()
|
||||||
|
|
||||||
this.$root.socket.on('author_updated', this.authorUpdated)
|
this.$root.socket.on('author_updated', this.authorUpdated)
|
||||||
this.$root.socket.on('author_removed', this.authorRemoved)
|
this.$root.socket.on('author_removed', this.authorRemoved)
|
||||||
@@ -114,3 +126,18 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#author-description {
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 4;
|
||||||
|
max-height: 6.25rem;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
#author-description.show-full {
|
||||||
|
-webkit-line-clamp: unset;
|
||||||
|
max-height: 999rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,44 +20,44 @@
|
|||||||
<div class="overflow-hidden">
|
<div class="overflow-hidden">
|
||||||
<transition name="slide">
|
<transition name="slide">
|
||||||
<div v-if="openMapOptions" class="flex flex-wrap">
|
<div v-if="openMapOptions" class="flex flex-wrap">
|
||||||
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 w-1/2">
|
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.subtitle" />
|
<ui-checkbox v-model="selectedBatchUsage.subtitle" />
|
||||||
<ui-text-input-with-label ref="subtitleInput" v-model="batchDetails.subtitle" :disabled="!selectedBatchUsage.subtitle" :label="$strings.LabelSubtitle" class="mb-4 ml-4" />
|
<ui-text-input-with-label ref="subtitleInput" v-model="batchDetails.subtitle" :disabled="!selectedBatchUsage.subtitle" :label="$strings.LabelSubtitle" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isPodcastLibrary" class="flex items-center px-4 w-1/2">
|
<div v-if="!isPodcastLibrary" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.authors" />
|
<ui-checkbox v-model="selectedBatchUsage.authors" />
|
||||||
<!-- Authors filter only contains authors in this library, uses filter data -->
|
<!-- Authors filter only contains authors in this library, uses filter data -->
|
||||||
<ui-multi-select-query-input ref="authorsSelect" v-model="batchDetails.authors" :disabled="!selectedBatchUsage.authors" :label="$strings.LabelAuthors" filter-key="authors" class="mb-4 ml-4" />
|
<ui-multi-select-query-input ref="authorsSelect" v-model="batchDetails.authors" :disabled="!selectedBatchUsage.authors" :label="$strings.LabelAuthors" filter-key="authors" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 w-1/2">
|
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.publishedYear" />
|
<ui-checkbox v-model="selectedBatchUsage.publishedYear" />
|
||||||
<ui-text-input-with-label ref="publishedYearInput" v-model="batchDetails.publishedYear" :disabled="!selectedBatchUsage.publishedYear" :label="$strings.LabelPublishYear" class="mb-4 ml-4" />
|
<ui-text-input-with-label ref="publishedYearInput" v-model="batchDetails.publishedYear" :disabled="!selectedBatchUsage.publishedYear" :label="$strings.LabelPublishYear" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isPodcastLibrary" class="flex items-center px-4 w-1/2">
|
<div v-if="!isPodcastLibrary" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.series" />
|
<ui-checkbox v-model="selectedBatchUsage.series" />
|
||||||
<ui-multi-select ref="seriesSelect" v-model="batchDetails.series" :disabled="!selectedBatchUsage.series" :label="$strings.LabelSeries" :items="existingSeriesNames" @newItem="newSeriesItem" @removedItem="removedSeriesItem" class="mb-4 ml-4" />
|
<ui-multi-select ref="seriesSelect" v-model="batchDetails.series" :disabled="!selectedBatchUsage.series" :label="$strings.LabelSeries" :items="existingSeriesNames" @newItem="newSeriesItem" @removedItem="removedSeriesItem" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-4 w-1/2">
|
<div class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.genres" />
|
<ui-checkbox v-model="selectedBatchUsage.genres" />
|
||||||
<ui-multi-select ref="genresSelect" v-model="batchDetails.genres" :disabled="!selectedBatchUsage.genres" :label="$strings.LabelGenres" :items="genreItems" @newItem="newGenreItem" @removedItem="removedGenreItem" class="mb-4 ml-4" />
|
<ui-multi-select ref="genresSelect" v-model="batchDetails.genres" :disabled="!selectedBatchUsage.genres" :label="$strings.LabelGenres" :items="genreItems" @newItem="newGenreItem" @removedItem="removedGenreItem" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-4 w-1/2">
|
<div class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.tags" />
|
<ui-checkbox v-model="selectedBatchUsage.tags" />
|
||||||
<ui-multi-select ref="tagsSelect" v-model="batchDetails.tags" :label="$strings.LabelTags" :disabled="!selectedBatchUsage.tags" :items="tagItems" @newItem="newTagItem" @removedItem="removedTagItem" class="mb-4 ml-4" />
|
<ui-multi-select ref="tagsSelect" v-model="batchDetails.tags" :label="$strings.LabelTags" :disabled="!selectedBatchUsage.tags" :items="tagItems" @newItem="newTagItem" @removedItem="removedTagItem" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isPodcastLibrary" class="flex items-center px-4 w-1/2">
|
<div v-if="!isPodcastLibrary" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.narrators" />
|
<ui-checkbox v-model="selectedBatchUsage.narrators" />
|
||||||
<ui-multi-select ref="narratorsSelect" v-model="batchDetails.narrators" :disabled="!selectedBatchUsage.narrators" :label="$strings.LabelNarrators" :items="narratorItems" @newItem="newNarratorItem" @removedItem="removedNarratorItem" class="mb-4 ml-4" />
|
<ui-multi-select ref="narratorsSelect" v-model="batchDetails.narrators" :disabled="!selectedBatchUsage.narrators" :label="$strings.LabelNarrators" :items="narratorItems" @newItem="newNarratorItem" @removedItem="removedNarratorItem" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 w-1/2">
|
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.publisher" />
|
<ui-checkbox v-model="selectedBatchUsage.publisher" />
|
||||||
<ui-text-input-with-label ref="publisherInput" v-model="batchDetails.publisher" :disabled="!selectedBatchUsage.publisher" :label="$strings.LabelPublisher" class="mb-4 ml-4" />
|
<ui-text-input-with-label ref="publisherInput" v-model="batchDetails.publisher" :disabled="!selectedBatchUsage.publisher" :label="$strings.LabelPublisher" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isMapAppend" class="flex items-center px-4 w-1/2">
|
<div v-if="!isMapAppend" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.language" />
|
<ui-checkbox v-model="selectedBatchUsage.language" />
|
||||||
<ui-text-input-with-label ref="languageInput" v-model="batchDetails.language" :disabled="!selectedBatchUsage.language" :label="$strings.LabelLanguage" class="mb-4 ml-4" />
|
<ui-text-input-with-label ref="languageInput" v-model="batchDetails.language" :disabled="!selectedBatchUsage.language" :label="$strings.LabelLanguage" class="mb-5 ml-4" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isMapAppend" class="flex items-center px-4 w-1/2">
|
<div v-if="!isMapAppend" class="flex items-center px-4 h-18 w-1/2">
|
||||||
<ui-checkbox v-model="selectedBatchUsage.explicit" />
|
<ui-checkbox v-model="selectedBatchUsage.explicit" />
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<ui-checkbox
|
<ui-checkbox
|
||||||
@@ -71,6 +71,20 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!isPodcastLibrary && !isMapAppend" class="flex items-center px-4 h-18 w-1/2">
|
||||||
|
<ui-checkbox v-model="selectedBatchUsage.abridged" />
|
||||||
|
<div class="ml-4">
|
||||||
|
<ui-checkbox
|
||||||
|
v-model="batchDetails.abridged"
|
||||||
|
:label="$strings.LabelAbridged"
|
||||||
|
:disabled="!selectedBatchUsage.abridged"
|
||||||
|
:checkbox-bg="!selectedBatchUsage.abridged ? 'bg' : 'primary'"
|
||||||
|
:check-color="!selectedBatchUsage.abridged ? 'gray-600' : 'green-500'"
|
||||||
|
border-color="gray-600"
|
||||||
|
:label-class="!selectedBatchUsage.abridged ? 'pl-2 text-base text-gray-400 font-semibold' : 'pl-2 text-base font-semibold'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex items-center justify-end p-4">
|
<div class="w-full flex items-center justify-end p-4">
|
||||||
<ui-btn color="success" :disabled="!hasSelectedBatchUsage" :padding-x="8" small class="text-base" :loading="isProcessing" @click="mapBatchDetails">{{ $strings.ButtonApply }}</ui-btn>
|
<ui-btn color="success" :disabled="!hasSelectedBatchUsage" :padding-x="8" small class="text-base" :loading="isProcessing" @click="mapBatchDetails">{{ $strings.ButtonApply }}</ui-btn>
|
||||||
@@ -139,7 +153,8 @@ export default {
|
|||||||
narrators: [],
|
narrators: [],
|
||||||
publisher: null,
|
publisher: null,
|
||||||
language: null,
|
language: null,
|
||||||
explicit: false
|
explicit: false,
|
||||||
|
abridged: false
|
||||||
},
|
},
|
||||||
selectedBatchUsage: {
|
selectedBatchUsage: {
|
||||||
subtitle: false,
|
subtitle: false,
|
||||||
@@ -151,7 +166,8 @@ export default {
|
|||||||
narrators: false,
|
narrators: false,
|
||||||
publisher: false,
|
publisher: false,
|
||||||
language: false,
|
language: false,
|
||||||
explicit: false
|
explicit: false,
|
||||||
|
abridged: false
|
||||||
},
|
},
|
||||||
appendableKeys: ['authors', 'genres', 'tags', 'narrators', 'series'],
|
appendableKeys: ['authors', 'genres', 'tags', 'narrators', 'series'],
|
||||||
openMapOptions: false
|
openMapOptions: false
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="authentication-settings">
|
<div id="authentication-settings">
|
||||||
<app-settings-content :header-text="$strings.HeaderAuthentication">
|
<app-settings-content :header-text="$strings.HeaderAuthentication">
|
||||||
|
<div class="w-full border border-white/10 rounded-xl p-4 my-4 bg-primary/25">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<ui-checkbox v-model="showCustomLoginMessage" checkbox-bg="bg" />
|
||||||
|
<p class="text-lg pl-4">Custom Message on Login</p>
|
||||||
|
</div>
|
||||||
|
<transition name="slide">
|
||||||
|
<div v-if="showCustomLoginMessage" class="w-full pt-4">
|
||||||
|
<ui-rich-text-editor v-model="newAuthSettings.authLoginCustomMessage" />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="w-full border border-white/10 rounded-xl p-4 my-4 bg-primary/25">
|
<div class="w-full border border-white/10 rounded-xl p-4 my-4 bg-primary/25">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<ui-checkbox v-model="enableLocalAuth" checkbox-bg="bg" />
|
<ui-checkbox v-model="enableLocalAuth" checkbox-bg="bg" />
|
||||||
@@ -46,6 +58,9 @@
|
|||||||
|
|
||||||
<ui-text-input-with-label ref="openidClientSecret" v-model="newAuthSettings.authOpenIDClientSecret" :disabled="savingSettings" :label="'Client Secret'" class="mb-2" />
|
<ui-text-input-with-label ref="openidClientSecret" v-model="newAuthSettings.authOpenIDClientSecret" :disabled="savingSettings" :label="'Client Secret'" 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" />
|
||||||
|
|
||||||
<ui-text-input-with-label ref="buttonTextInput" v-model="newAuthSettings.authOpenIDButtonText" :disabled="savingSettings" :label="$strings.LabelButtonText" class="mb-2" />
|
<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 items-center pt-1 mb-2">
|
||||||
@@ -100,6 +115,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
enableLocalAuth: false,
|
enableLocalAuth: false,
|
||||||
enableOpenIDAuth: false,
|
enableOpenIDAuth: false,
|
||||||
|
showCustomLoginMessage: false,
|
||||||
savingSettings: false,
|
savingSettings: false,
|
||||||
newAuthSettings: {}
|
newAuthSettings: {}
|
||||||
}
|
}
|
||||||
@@ -187,6 +203,25 @@ export default {
|
|||||||
this.$toast.error('Client Secret required')
|
this.$toast.error('Client Secret required')
|
||||||
isValid = false
|
isValid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidRedirectURI(uri) {
|
||||||
|
// Check for somestring://someother/string
|
||||||
|
const pattern = new RegExp('^\\w+://[\\w\\.-]+(/[\\w\\./-]*)*$', 'i')
|
||||||
|
return pattern.test(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uris = this.newAuthSettings.authOpenIDMobileRedirectURIs
|
||||||
|
if (uris.includes('*') && uris.length > 1) {
|
||||||
|
this.$toast.error('Mobile Redirect URIs: Asterisk (*) must be the only entry if used')
|
||||||
|
isValid = false
|
||||||
|
} else {
|
||||||
|
uris.forEach((uri) => {
|
||||||
|
if (uri !== '*' && !isValidRedirectURI(uri)) {
|
||||||
|
this.$toast.error(`Mobile Redirect URIs: Invalid URI ${uri}`)
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return isValid
|
return isValid
|
||||||
},
|
},
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
@@ -199,6 +234,10 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.showCustomLoginMessage || !this.newAuthSettings.authLoginCustomMessage?.trim()) {
|
||||||
|
this.newAuthSettings.authLoginCustomMessage = null
|
||||||
|
}
|
||||||
|
|
||||||
this.newAuthSettings.authActiveAuthMethods = []
|
this.newAuthSettings.authActiveAuthMethods = []
|
||||||
if (this.enableLocalAuth) this.newAuthSettings.authActiveAuthMethods.push('local')
|
if (this.enableLocalAuth) this.newAuthSettings.authActiveAuthMethods.push('local')
|
||||||
if (this.enableOpenIDAuth) this.newAuthSettings.authActiveAuthMethods.push('openid')
|
if (this.enableOpenIDAuth) this.newAuthSettings.authActiveAuthMethods.push('openid')
|
||||||
@@ -208,7 +247,11 @@ export default {
|
|||||||
.$patch('/api/auth-settings', this.newAuthSettings)
|
.$patch('/api/auth-settings', this.newAuthSettings)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.$store.commit('setServerSettings', data.serverSettings)
|
this.$store.commit('setServerSettings', data.serverSettings)
|
||||||
this.$toast.success('Server settings updated')
|
if (data.updated) {
|
||||||
|
this.$toast.success('Server settings updated')
|
||||||
|
} else {
|
||||||
|
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed to update server settings', error)
|
console.error('Failed to update server settings', error)
|
||||||
@@ -224,6 +267,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.enableLocalAuth = this.authMethods.includes('local')
|
this.enableLocalAuth = this.authMethods.includes('local')
|
||||||
this.enableOpenIDAuth = this.authMethods.includes('openid')
|
this.enableOpenIDAuth = this.authMethods.includes('openid')
|
||||||
|
this.showCustomLoginMessage = !!this.authSettings.authLoginCustomMessage
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -178,9 +178,9 @@
|
|||||||
</a>
|
</a>
|
||||||
<p class="pl-4 pr-2 text-sm text-yellow-400">
|
<p class="pl-4 pr-2 text-sm text-yellow-400">
|
||||||
{{ $strings.MessageJoinUsOn }}
|
{{ $strings.MessageJoinUsOn }}
|
||||||
<a class="underline" href="https://discord.gg/pJsjuNCKRq" target="_blank">discord</a>
|
<a class="underline" href="https://discord.gg/HQgCbd6E75" target="_blank">discord</a>
|
||||||
</p>
|
</p>
|
||||||
<a href="https://discord.gg/pJsjuNCKRq" target="_blank" class="text-white hover:text-gray-200 hover:scale-150 hover:rotate-6 transform duration-500">
|
<a href="https://discord.gg/HQgCbd6E75" target="_blank" class="text-white hover:text-gray-200 hover:scale-150 hover:rotate-6 transform duration-500">
|
||||||
<svg width="31" height="24" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="31" height="24" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0)">
|
<g clip-path="url(#clip0)">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative">
|
||||||
|
<app-settings-content :header-text="$strings.HeaderCustomMetadataProviders">
|
||||||
|
<template #header-prefix>
|
||||||
|
<nuxt-link to="/config/item-metadata-utils" class="w-8 h-8 flex items-center justify-center rounded-full cursor-pointer hover:bg-white hover:bg-opacity-10 text-center mr-2">
|
||||||
|
<span class="material-icons text-2xl">arrow_back</span>
|
||||||
|
</nuxt-link>
|
||||||
|
</template>
|
||||||
|
<template #header-items>
|
||||||
|
<ui-tooltip :text="$strings.LabelClickForMoreInfo" class="inline-flex ml-2">
|
||||||
|
<a href="https://www.audiobookshelf.org/guides/custom-metadata-providers" target="_blank" class="inline-flex">
|
||||||
|
<span class="material-icons text-xl w-5 text-gray-200">help_outline</span>
|
||||||
|
</a>
|
||||||
|
</ui-tooltip>
|
||||||
|
<div class="flex-grow" />
|
||||||
|
|
||||||
|
<ui-btn color="primary" small @click="setShowAddModal">{{ $strings.ButtonAdd }}</ui-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<tables-custom-metadata-provider-table :providers="providers" :processing.sync="processing" class="pt-2" @removed="providerRemoved" />
|
||||||
|
<modals-add-custom-metadata-provider-modal ref="addModal" v-model="showAddModal" @added="providerAdded" />
|
||||||
|
</app-settings-content>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async asyncData({ store, redirect }) {
|
||||||
|
if (!store.getters['user/getIsAdminOrUp']) {
|
||||||
|
redirect('/')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showAddModal: false,
|
||||||
|
processing: false,
|
||||||
|
providers: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
providerRemoved(providerId) {
|
||||||
|
this.providers = this.providers.filter((p) => p.id !== providerId)
|
||||||
|
},
|
||||||
|
providerAdded(provider) {
|
||||||
|
this.providers.push(provider)
|
||||||
|
},
|
||||||
|
setShowAddModal() {
|
||||||
|
this.showAddModal = true
|
||||||
|
},
|
||||||
|
loadProviders() {
|
||||||
|
this.processing = true
|
||||||
|
this.$axios
|
||||||
|
.$get('/api/custom-metadata-providers')
|
||||||
|
.then((res) => {
|
||||||
|
this.providers = res.providers
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed', error)
|
||||||
|
this.$toast.error('Failed to load custom metadata providers')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadProviders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
@@ -13,6 +13,12 @@
|
|||||||
<span class="material-icons">arrow_forward</span>
|
<span class="material-icons">arrow_forward</span>
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
|
<nuxt-link to="/config/item-metadata-utils/custom-metadata-providers" class="block w-full rounded bg-primary/40 hover:bg-primary/60 text-gray-300 hover:text-white p-4 my-2">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<p>{{ $strings.HeaderCustomMetadataProviders }}</p>
|
||||||
|
<span class="material-icons">arrow_forward</span>
|
||||||
|
</div>
|
||||||
|
</nuxt-link>
|
||||||
</app-settings-content>
|
</app-settings-content>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div ref="container" class="relative w-full h-full bg-primary border-bg overflow-x-hidden overflow-y-auto text-red shadow-inner rounded-md" style="max-height: 800px; min-height: 550px">
|
<div ref="container" id="log-container" class="relative w-full h-full bg-primary border-bg overflow-x-hidden overflow-y-auto text-red shadow-inner rounded-md" style="min-height: 550px">
|
||||||
<template v-for="(log, index) in logs">
|
<template v-for="(log, index) in logs">
|
||||||
<div :key="index" class="flex flex-nowrap px-2 py-1 items-start text-sm bg-opacity-10" :class="`bg-${logColors[log.level]}`">
|
<div :key="index" class="flex flex-nowrap px-2 py-1 items-start text-sm bg-opacity-10" :class="`bg-${logColors[log.level]}`">
|
||||||
<p class="text-gray-400 w-36 font-mono text-xs">{{ log.timestamp }}</p>
|
<p class="text-gray-400 w-36 font-mono text-xs">{{ log.timestamp }}</p>
|
||||||
@@ -136,7 +136,15 @@ export default {
|
|||||||
this.loadedLogs = this.loadedLogs.slice(-5000)
|
this.loadedLogs = this.loadedLogs.slice(-5000)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
init(attempts = 0) {
|
async loadLoggerData() {
|
||||||
|
const loggerData = await this.$axios.$get('/api/logger-data').catch((error) => {
|
||||||
|
console.error('Failed to load logger data', error)
|
||||||
|
this.$toast.error('Failed to load logger data')
|
||||||
|
})
|
||||||
|
|
||||||
|
this.loadedLogs = loggerData?.currentDailyLogs || []
|
||||||
|
},
|
||||||
|
async init(attempts = 0) {
|
||||||
if (!this.$root.socket) {
|
if (!this.$root.socket) {
|
||||||
if (attempts > 10) {
|
if (attempts > 10) {
|
||||||
return console.error('Failed to setup socket listeners')
|
return console.error('Failed to setup socket listeners')
|
||||||
@@ -147,14 +155,11 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.loadLoggerData()
|
||||||
|
|
||||||
this.newServerSettings = this.serverSettings ? { ...this.serverSettings } : {}
|
this.newServerSettings = this.serverSettings ? { ...this.serverSettings } : {}
|
||||||
this.$root.socket.on('daily_logs', this.dailyLogsLoaded)
|
|
||||||
this.$root.socket.on('log', this.logEvtReceived)
|
this.$root.socket.on('log', this.logEvtReceived)
|
||||||
this.$root.socket.emit('set_log_listener', this.newServerSettings.logLevel)
|
this.$root.socket.emit('set_log_listener', this.newServerSettings.logLevel)
|
||||||
this.$root.socket.emit('fetch_daily_logs')
|
|
||||||
},
|
|
||||||
dailyLogsLoaded(lines) {
|
|
||||||
this.loadedLogs = lines
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
@@ -166,13 +171,15 @@ export default {
|
|||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (!this.$root.socket) return
|
if (!this.$root.socket) return
|
||||||
this.$root.socket.emit('remove_log_listener')
|
this.$root.socket.emit('remove_log_listener')
|
||||||
this.$root.socket.off('daily_logs', this.dailyLogsLoaded)
|
|
||||||
this.$root.socket.off('log', this.logEvtReceived)
|
this.$root.socket.off('log', this.logEvtReceived)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
#log-container {
|
||||||
|
height: calc(100vh - 285px);
|
||||||
|
}
|
||||||
.logmessage {
|
.logmessage {
|
||||||
width: calc(100% - 208px);
|
width: calc(100% - 208px);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,37 +5,72 @@
|
|||||||
<ui-dropdown v-model="selectedUser" :items="userItems" :label="$strings.LabelFilterByUser" small class="max-w-48" @input="updateUserFilter" />
|
<ui-dropdown v-model="selectedUser" :items="userItems" :label="$strings.LabelFilterByUser" small class="max-w-48" @input="updateUserFilter" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="listeningSessions.length" class="block max-w-full">
|
<div v-if="listeningSessions.length" class="block max-w-full relative">
|
||||||
<table class="userSessionsTable">
|
<table class="userSessionsTable">
|
||||||
<tr class="bg-primary bg-opacity-40">
|
<tr class="bg-primary bg-opacity-40">
|
||||||
<th class="w-48 min-w-48 text-left">{{ $strings.LabelItem }}</th>
|
<th class="w-6 min-w-6 text-left hidden md:table-cell h-11">
|
||||||
<th class="w-20 min-w-20 text-left hidden md:table-cell">{{ $strings.LabelUser }}</th>
|
<ui-checkbox v-model="isAllSelected" :partial="numSelected > 0 && !isAllSelected" small checkbox-bg="bg" />
|
||||||
<th class="w-32 min-w-32 text-left hidden md:table-cell">{{ $strings.LabelPlayMethod }}</th>
|
</th>
|
||||||
<th class="w-32 min-w-32 text-left hidden sm:table-cell">{{ $strings.LabelDeviceInfo }}</th>
|
<th v-if="numSelected" class="flex-grow text-left" :colspan="7">
|
||||||
<th class="w-32 min-w-32">{{ $strings.LabelTimeListened }}</th>
|
<div class="flex items-center">
|
||||||
<th class="w-16 min-w-16">{{ $strings.LabelLastTime }}</th>
|
<p>{{ $getString('MessageSelected', [numSelected]) }}</p>
|
||||||
<th class="flex-grow hidden sm:table-cell">{{ $strings.LabelLastUpdate }}</th>
|
<div class="flex-grow" />
|
||||||
|
<ui-btn small color="error" :loading="deletingSessions" @click.stop="removeSessionsClick">{{ $strings.ButtonRemove }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th v-if="!numSelected" class="flex-grow sm:flex-grow-0 sm:w-48 sm:max-w-48 text-left group cursor-pointer" @click.stop="sortColumn('displayTitle')">
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
{{ $strings.LabelItem }} <span :class="{ 'opacity-0 group-hover:opacity-30': !isSortSelected('displayTitle') }" class="material-icons text-base pl-px">{{ sortDesc ? 'arrow_drop_down' : 'arrow_drop_up' }}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th v-if="!numSelected" class="w-20 min-w-20 text-left hidden md:table-cell">{{ $strings.LabelUser }}</th>
|
||||||
|
<th v-if="!numSelected" class="w-26 min-w-26 text-left hidden md:table-cell group cursor-pointer" @click.stop="sortColumn('playMethod')">
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
{{ $strings.LabelPlayMethod }} <span :class="{ 'opacity-0 group-hover:opacity-30': !isSortSelected('playMethod') }" class="material-icons text-base pl-px">{{ sortDesc ? 'arrow_drop_down' : 'arrow_drop_up' }}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th v-if="!numSelected" class="w-32 min-w-32 text-left hidden sm:table-cell">{{ $strings.LabelDeviceInfo }}</th>
|
||||||
|
<th v-if="!numSelected" class="w-24 min-w-24 sm:w-32 sm:min-w-32 group cursor-pointer" @click.stop="sortColumn('timeListening')">
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
{{ $strings.LabelTimeListened }} <span :class="{ 'opacity-0 group-hover:opacity-30': !isSortSelected('timeListening') }" class="material-icons text-base pl-px hidden sm:inline-block">{{ sortDesc ? 'arrow_drop_down' : 'arrow_drop_up' }}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th v-if="!numSelected" class="w-24 min-w-24 group cursor-pointer" @click.stop="sortColumn('currentTime')">
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
{{ $strings.LabelLastTime }} <span :class="{ 'opacity-0 group-hover:opacity-30': !isSortSelected('currentTime') }" class="material-icons text-base pl-px hidden sm:inline-block">{{ sortDesc ? 'arrow_drop_down' : 'arrow_drop_up' }}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th v-if="!numSelected" class="flex-grow hidden sm:table-cell cursor-pointer group" @click.stop="sortColumn('updatedAt')">
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
{{ $strings.LabelLastUpdate }} <span :class="{ 'opacity-0 group-hover:opacity-30': !isSortSelected('updatedAt') }" class="material-icons text-base pl-px">{{ sortDesc ? 'arrow_drop_down' : 'arrow_drop_up' }}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr v-for="session in listeningSessions" :key="session.id" class="cursor-pointer" @click="showSession(session)">
|
<tr v-for="session in listeningSessions" :key="session.id" :class="{ selected: session.selected }" class="cursor-pointer" @click="clickSessionRow(session)">
|
||||||
<td class="py-1 max-w-48">
|
<td class="hidden md:table-cell py-1 max-w-6 relative">
|
||||||
|
<ui-checkbox v-model="session.selected" small checkbox-bg="bg" />
|
||||||
|
<!-- overlay of the checkbox so that the entire box is clickable -->
|
||||||
|
<div class="absolute inset-0 w-full h-full" @click.stop="session.selected = !session.selected" />
|
||||||
|
</td>
|
||||||
|
<td class="py-1 flex-grow sm:flex-grow-0 sm:w-48 sm:max-w-48">
|
||||||
<p class="text-xs text-gray-200 truncate">{{ session.displayTitle }}</p>
|
<p class="text-xs text-gray-200 truncate">{{ session.displayTitle }}</p>
|
||||||
<p class="text-xs text-gray-400 truncate">{{ session.displayAuthor }}</p>
|
<p class="text-xs text-gray-400 truncate">{{ session.displayAuthor }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden md:table-cell">
|
<td class="hidden md:table-cell w-20 min-w-20">
|
||||||
<p v-if="filteredUserUsername" class="text-xs">{{ filteredUserUsername }}</p>
|
<p v-if="filteredUserUsername" class="text-xs">{{ filteredUserUsername }}</p>
|
||||||
<p v-else class="text-xs">{{ session.user ? session.user.username : 'N/A' }}</p>
|
<p v-else class="text-xs">{{ session.user ? session.user.username : 'N/A' }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden md:table-cell">
|
<td class="hidden md:table-cell w-26 min-w-26">
|
||||||
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }}</p>
|
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden sm:table-cell">
|
<td class="hidden sm:table-cell w-32 min-w-32">
|
||||||
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
|
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<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>
|
<p class="text-xs font-mono">{{ $elapsedPretty(session.timeListening) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center hover:underline" @click.stop="clickCurrentTime(session)">
|
<td class="text-center hover:underline w-24 min-w-24" @click.stop="clickCurrentTime(session)">
|
||||||
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center hidden sm:table-cell">
|
<td class="text-center hidden sm:table-cell">
|
||||||
@@ -45,10 +80,22 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div class="flex items-center justify-end my-2">
|
<!-- table bottom options -->
|
||||||
<ui-icon-btn icon="arrow_back_ios_new" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage === 0" @click="prevPage" />
|
<div class="flex items-center my-2">
|
||||||
<p class="text-sm mx-1">Page {{ currentPage + 1 }} of {{ numPages }}</p>
|
<div class="flex-grow" />
|
||||||
<ui-icon-btn icon="arrow_forward_ios" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage >= numPages - 1" @click="nextPage" />
|
<div class="hidden sm:inline-flex items-center">
|
||||||
|
<p class="text-sm whitespace-nowrap">{{ $strings.LabelRowsPerPage }}</p>
|
||||||
|
<ui-dropdown v-model="itemsPerPage" :items="itemsPerPageOptions" small class="w-24 mx-2" @input="updatedItemsPerPage" />
|
||||||
|
</div>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<p class="text-sm mx-2">Page {{ currentPage + 1 }} of {{ numPages }}</p>
|
||||||
|
<ui-icon-btn icon="arrow_back_ios_new" :size="9" icon-font-size="1rem" class="mx-1" :disabled="currentPage === 0" @click="prevPage" />
|
||||||
|
<ui-icon-btn icon="arrow_forward_ios" :size="9" icon-font-size="1rem" class="mx-1" :disabled="currentPage >= numPages - 1" @click="nextPage" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="deletingSessions || loading" class="absolute inset-0 w-full h-full flex items-center justify-center">
|
||||||
|
<ui-loading-indicator />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p v-else class="text-white text-opacity-50">{{ $strings.MessageNoListeningSessions }}</p>
|
<p v-else class="text-white text-opacity-50">{{ $strings.MessageNoListeningSessions }}</p>
|
||||||
@@ -128,6 +175,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
loading: false,
|
||||||
showSessionModal: false,
|
showSessionModal: false,
|
||||||
selectedSession: null,
|
selectedSession: null,
|
||||||
listeningSessions: [],
|
listeningSessions: [],
|
||||||
@@ -138,7 +186,11 @@ export default {
|
|||||||
itemsPerPage: 10,
|
itemsPerPage: 10,
|
||||||
userFilter: null,
|
userFilter: null,
|
||||||
selectedUser: '',
|
selectedUser: '',
|
||||||
processingGoToTimestamp: false
|
sortBy: 'updatedAt',
|
||||||
|
sortDesc: true,
|
||||||
|
processingGoToTimestamp: false,
|
||||||
|
deletingSessions: false,
|
||||||
|
itemsPerPageOptions: [10, 25, 50, 100]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -162,9 +214,85 @@ export default {
|
|||||||
},
|
},
|
||||||
timeFormat() {
|
timeFormat() {
|
||||||
return this.$store.state.serverSettings.timeFormat
|
return this.$store.state.serverSettings.timeFormat
|
||||||
|
},
|
||||||
|
numSelected() {
|
||||||
|
return this.listeningSessions.filter((s) => s.selected).length
|
||||||
|
},
|
||||||
|
isAllSelected: {
|
||||||
|
get() {
|
||||||
|
return this.numSelected === this.listeningSessions.length
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.setSelectionForAll(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isSortSelected(column) {
|
||||||
|
return this.sortBy === column
|
||||||
|
},
|
||||||
|
sortColumn(column) {
|
||||||
|
if (this.sortBy === column) {
|
||||||
|
this.sortDesc = !this.sortDesc
|
||||||
|
} else {
|
||||||
|
this.sortBy = column
|
||||||
|
}
|
||||||
|
this.loadSessions(this.currentPage)
|
||||||
|
},
|
||||||
|
removeSelectedSessions() {
|
||||||
|
if (!this.numSelected) return
|
||||||
|
this.deletingSessions = true
|
||||||
|
|
||||||
|
let isAllSessions = this.isAllSelected
|
||||||
|
const payload = {
|
||||||
|
sessions: this.listeningSessions.filter((s) => s.selected).map((s) => s.id)
|
||||||
|
}
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/sessions/batch/delete`, payload)
|
||||||
|
.then(() => {
|
||||||
|
this.$toast.success('Sessions removed')
|
||||||
|
if (isAllSessions) {
|
||||||
|
// If all sessions were removed from the current page then go to the previous page
|
||||||
|
if (this.currentPage > 0) {
|
||||||
|
this.currentPage--
|
||||||
|
}
|
||||||
|
this.loadSessions(this.currentPage)
|
||||||
|
} else {
|
||||||
|
// Filter out the deleted sessions
|
||||||
|
this.listeningSessions = this.listeningSessions.filter((ls) => !payload.sessions.includes(ls.id))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = error.response?.data || 'Failed to remove sessions'
|
||||||
|
this.$toast.error(errorMsg)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.deletingSessions = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeSessionsClick() {
|
||||||
|
if (!this.numSelected) return
|
||||||
|
const payload = {
|
||||||
|
message: this.$getString('MessageConfirmRemoveListeningSessions', [this.numSelected]),
|
||||||
|
callback: (confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.removeSelectedSessions()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'yesNo'
|
||||||
|
}
|
||||||
|
this.$store.commit('globals/setConfirmPrompt', payload)
|
||||||
|
},
|
||||||
|
setSelectionForAll(val) {
|
||||||
|
this.listeningSessions = this.listeningSessions.map((s) => {
|
||||||
|
s.selected = val
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updatedItemsPerPage() {
|
||||||
|
this.currentPage = 0
|
||||||
|
this.loadSessions(this.currentPage)
|
||||||
|
},
|
||||||
closedSession() {
|
closedSession() {
|
||||||
this.loadOpenSessions()
|
this.loadOpenSessions()
|
||||||
},
|
},
|
||||||
@@ -252,6 +380,13 @@ export default {
|
|||||||
nextPage() {
|
nextPage() {
|
||||||
this.loadSessions(this.currentPage + 1)
|
this.loadSessions(this.currentPage + 1)
|
||||||
},
|
},
|
||||||
|
clickSessionRow(session) {
|
||||||
|
if (this.numSelected > 0) {
|
||||||
|
session.selected = !session.selected
|
||||||
|
} else {
|
||||||
|
this.showSession(session)
|
||||||
|
}
|
||||||
|
},
|
||||||
showSession(session) {
|
showSession(session) {
|
||||||
this.selectedSession = session
|
this.selectedSession = session
|
||||||
this.showSessionModal = true
|
this.showSessionModal = true
|
||||||
@@ -274,11 +409,21 @@ export default {
|
|||||||
return 'Unknown'
|
return 'Unknown'
|
||||||
},
|
},
|
||||||
async loadSessions(page) {
|
async loadSessions(page) {
|
||||||
const userFilterQuery = this.selectedUser ? `&user=${this.selectedUser}` : ''
|
this.loading = true
|
||||||
const data = await this.$axios.$get(`/api/sessions?page=${page}&itemsPerPage=${this.itemsPerPage}${userFilterQuery}`).catch((err) => {
|
const urlSearchParams = new URLSearchParams()
|
||||||
|
urlSearchParams.set('page', page)
|
||||||
|
urlSearchParams.set('itemsPerPage', this.itemsPerPage)
|
||||||
|
urlSearchParams.set('sort', this.sortBy)
|
||||||
|
urlSearchParams.set('desc', this.sortDesc ? '1' : '0')
|
||||||
|
if (this.selectedUser) {
|
||||||
|
urlSearchParams.set('user', this.selectedUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await this.$axios.$get(`/api/sessions?${urlSearchParams.toString()}`).catch((err) => {
|
||||||
console.error('Failed to load listening sessions', err)
|
console.error('Failed to load listening sessions', err)
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
this.loading = false
|
||||||
if (!data) {
|
if (!data) {
|
||||||
this.$toast.error('Failed to load listening sessions')
|
this.$toast.error('Failed to load listening sessions')
|
||||||
return
|
return
|
||||||
@@ -287,8 +432,13 @@ export default {
|
|||||||
this.numPages = data.numPages
|
this.numPages = data.numPages
|
||||||
this.total = data.total
|
this.total = data.total
|
||||||
this.currentPage = data.page
|
this.currentPage = data.page
|
||||||
this.listeningSessions = data.sessions
|
this.listeningSessions = data.sessions.map((ls) => {
|
||||||
this.userFilter = data.userFilter
|
return {
|
||||||
|
...ls,
|
||||||
|
selected: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.userFilter = data.userId
|
||||||
},
|
},
|
||||||
async loadOpenSessions() {
|
async loadOpenSessions() {
|
||||||
const data = await this.$axios.$get('/api/sessions/open').catch((err) => {
|
const data = await this.$axios.$get('/api/sessions/open').catch((err) => {
|
||||||
@@ -326,15 +476,18 @@ export default {
|
|||||||
.userSessionsTable tr:first-child {
|
.userSessionsTable tr:first-child {
|
||||||
background-color: #272727;
|
background-color: #272727;
|
||||||
}
|
}
|
||||||
.userSessionsTable tr:not(:first-child) {
|
.userSessionsTable tr:not(:first-child):not(.selected) {
|
||||||
background-color: #373838;
|
background-color: #373838;
|
||||||
}
|
}
|
||||||
.userSessionsTable tr:not(:first-child):nth-child(odd) {
|
.userSessionsTable tr:not(:first-child):nth-child(odd):not(.selected):not(:hover) {
|
||||||
background-color: #2f2f2f;
|
background-color: #2f2f2f;
|
||||||
}
|
}
|
||||||
.userSessionsTable tr:hover:not(:first-child) {
|
.userSessionsTable tr:hover:not(:first-child) {
|
||||||
background-color: #474747;
|
background-color: #474747;
|
||||||
}
|
}
|
||||||
|
.userSessionsTable tr.selected {
|
||||||
|
background-color: #474747;
|
||||||
|
}
|
||||||
.userSessionsTable td {
|
.userSessionsTable td {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<app-settings-content :header-text="$strings.HeaderYourStats">
|
<!-- Year in review banner shown at the top in December and January -->
|
||||||
|
<stats-year-in-review-banner v-if="showYearInReviewBanner" />
|
||||||
|
|
||||||
|
<app-settings-content :header-text="$strings.HeaderYourStats" class="!mb-4">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="flex p-2">
|
<div class="flex p-2">
|
||||||
<svg class="hidden sm:block h-14 w-14 lg:h-18 lg:w-18" viewBox="0 0 24 24">
|
<svg class="hidden sm:block h-14 w-14 lg:h-18 lg:w-18" viewBox="0 0 24 24">
|
||||||
@@ -63,6 +66,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<stats-heatmap v-if="listeningStats" :days-listening="listeningStats.days" class="my-2" />
|
<stats-heatmap v-if="listeningStats" :days-listening="listeningStats.days" class="my-2" />
|
||||||
</app-settings-content>
|
</app-settings-content>
|
||||||
|
|
||||||
|
<!-- Year in review banner shown at the bottom Feb - Nov -->
|
||||||
|
<stats-year-in-review-banner v-if="!showYearInReviewBanner" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -71,7 +77,8 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
listeningStats: null,
|
listeningStats: null,
|
||||||
windowWidth: 0
|
windowWidth: 0,
|
||||||
|
showYearInReviewBanner: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -119,6 +126,12 @@ export default {
|
|||||||
console.error('Failed to load listening sesions', err)
|
console.error('Failed to load listening sesions', err)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let month = new Date().getMonth()
|
||||||
|
// January and December show year in review banner
|
||||||
|
if (month === 11 || month === 0) {
|
||||||
|
this.showYearInReviewBanner = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="page-wrapper" class="bg-bg page overflow-hidden" :class="streamLibraryItem ? 'streaming' : ''">
|
<div id="page-wrapper" class="bg-bg page overflow-hidden" :class="streamLibraryItem ? 'streaming' : ''">
|
||||||
<div class="w-full h-full overflow-y-auto px-2 py-6 lg:p-8">
|
<div id="item-page-wrapper" class="w-full h-full overflow-y-auto px-2 py-6 lg:p-8">
|
||||||
<div class="flex flex-col lg:flex-row max-w-6xl mx-auto">
|
<div class="flex flex-col lg:flex-row max-w-6xl mx-auto">
|
||||||
<div class="w-full flex justify-center lg:block lg:w-52" style="min-width: 208px">
|
<div class="w-full flex justify-center lg:block lg:w-52" style="min-width: 208px">
|
||||||
<div class="relative group" style="height: fit-content">
|
<div class="relative group" style="height: fit-content">
|
||||||
@@ -125,7 +125,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-4 w-full">
|
<div class="my-4 w-full">
|
||||||
<p class="text-base text-gray-100 whitespace-pre-line">{{ description }}</p>
|
<p ref="description" id="item-description" 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>
|
||||||
|
|
||||||
<div v-if="invalidAudioFiles.length" class="bg-error border-red-800 shadow-md p-4">
|
<div v-if="invalidAudioFiles.length" class="bg-error border-red-800 shadow-md p-4">
|
||||||
@@ -136,7 +139,7 @@
|
|||||||
|
|
||||||
<widgets-audiobook-data v-if="tracks.length" :library-item-id="libraryItemId" :is-file="isFile" :media="media" />
|
<widgets-audiobook-data v-if="tracks.length" :library-item-id="libraryItemId" :is-file="isFile" :media="media" />
|
||||||
|
|
||||||
<tables-podcast-episodes-table v-if="isPodcast" :library-item="libraryItem" />
|
<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-chapters-table v-if="chapters.length" :library-item="libraryItem" class="mt-6" />
|
||||||
|
|
||||||
@@ -182,7 +185,9 @@ export default {
|
|||||||
podcastFeedEpisodes: [],
|
podcastFeedEpisodes: [],
|
||||||
episodesDownloading: [],
|
episodesDownloading: [],
|
||||||
episodeDownloadsQueued: [],
|
episodeDownloadsQueued: [],
|
||||||
showBookmarksModal: false
|
showBookmarksModal: false,
|
||||||
|
isDescriptionClamped: false,
|
||||||
|
showFullDescription: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -596,10 +601,15 @@ export default {
|
|||||||
this.$store.commit('setBookshelfBookIds', [])
|
this.$store.commit('setBookshelfBookIds', [])
|
||||||
this.$store.commit('showEditModal', this.libraryItem)
|
this.$store.commit('showEditModal', this.libraryItem)
|
||||||
},
|
},
|
||||||
|
checkDescriptionClamped() {
|
||||||
|
if (!this.$refs.description) return
|
||||||
|
this.isDescriptionClamped = this.$refs.description.scrollHeight > this.$refs.description.clientHeight
|
||||||
|
},
|
||||||
libraryItemUpdated(libraryItem) {
|
libraryItemUpdated(libraryItem) {
|
||||||
if (libraryItem.id === this.libraryItemId) {
|
if (libraryItem.id === this.libraryItemId) {
|
||||||
console.log('Item was updated', libraryItem)
|
console.log('Item was updated', libraryItem)
|
||||||
this.libraryItem = libraryItem
|
this.libraryItem = libraryItem
|
||||||
|
this.$nextTick(this.checkDescriptionClamped)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearProgressClick() {
|
clearProgressClick() {
|
||||||
@@ -756,6 +766,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.checkDescriptionClamped()
|
||||||
|
|
||||||
this.episodeDownloadsQueued = this.libraryItem.episodeDownloadsQueued || []
|
this.episodeDownloadsQueued = this.libraryItem.episodeDownloadsQueued || []
|
||||||
this.episodesDownloading = this.libraryItem.episodesDownloading || []
|
this.episodesDownloading = this.libraryItem.episodesDownloading || []
|
||||||
|
|
||||||
@@ -782,3 +794,18 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#item-description {
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 4;
|
||||||
|
max-height: 6.25rem;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
#item-description.show-full {
|
||||||
|
-webkit-line-clamp: unset;
|
||||||
|
max-height: 999rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<app-book-shelf-toolbar page="authors" is-home :authors="authors" />
|
<app-book-shelf-toolbar page="authors" is-home :authors="authors" />
|
||||||
<div id="bookshelf" class="w-full h-full p-8 overflow-y-auto">
|
<div id="bookshelf" class="w-full h-full p-8 overflow-y-auto">
|
||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
<template v-for="author in authors">
|
<template v-for="author in authorsSorted">
|
||||||
<cards-author-card :key="author.id" :author="author" :width="160" :height="200" class="p-3" @edit="editAuthor" />
|
<cards-author-card :key="author.id" :author="author" :width="160" :height="200" class="p-3" @edit="editAuthor" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,6 +44,22 @@ export default {
|
|||||||
},
|
},
|
||||||
selectedAuthor() {
|
selectedAuthor() {
|
||||||
return this.$store.state.globals.selectedAuthor
|
return this.$store.state.globals.selectedAuthor
|
||||||
|
},
|
||||||
|
authorSortBy() {
|
||||||
|
return this.$store.getters['user/getUserSetting']('authorSortBy') || 'name'
|
||||||
|
},
|
||||||
|
authorSortDesc() {
|
||||||
|
return !!this.$store.getters['user/getUserSetting']('authorSortDesc')
|
||||||
|
},
|
||||||
|
authorsSorted() {
|
||||||
|
const sortProp = this.authorSortBy
|
||||||
|
const bDesc = this.authorSortDesc ? -1 : 1
|
||||||
|
return this.authors.sort((a, b) => {
|
||||||
|
if (typeof a[sortProp] === 'number' && typeof b[sortProp] === 'number') {
|
||||||
|
return a[sortProp] > b[sortProp] ? bDesc : -bDesc
|
||||||
|
}
|
||||||
|
return a[sortProp].localeCompare(b[sortProp], undefined, { sensitivity: 'base' }) * bDesc
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
<p v-if="!recentEpisodes.length && !processing" class="text-center text-xl">{{ $strings.MessageNoEpisodes }}</p>
|
<p v-if="!recentEpisodes.length && !processing" class="text-center text-xl">{{ $strings.MessageNoEpisodes }}</p>
|
||||||
<template v-for="(episode, index) in episodesMapped">
|
<template v-for="(episode, index) in episodesMapped">
|
||||||
<div :key="episode.id" class="flex py-5 cursor-pointer relative" @click.stop="clickEpisode(episode)">
|
<div :key="episode.id" class="flex py-5 cursor-pointer relative" @click.stop="clickEpisode(episode)">
|
||||||
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId)" :width="96" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="hidden md:block" />
|
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId, episode.updatedAt)" :width="96" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="hidden md:block" />
|
||||||
<div class="flex-grow pl-4 max-w-2xl">
|
<div class="flex-grow pl-4 max-w-2xl">
|
||||||
<!-- mobile -->
|
<!-- mobile -->
|
||||||
<div class="flex md:hidden mb-2">
|
<div class="flex md:hidden mb-2">
|
||||||
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId)" :width="48" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="md:hidden" />
|
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId, episode.updatedAt)" :width="48" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="md:hidden" />
|
||||||
<div class="flex-grow px-2">
|
<div class="flex-grow px-2">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex" @click.stop>
|
<div class="flex" @click.stop>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-sm text-gray-200 mb-4 episode-subtitle-long" v-html="episode.subtitle || episode.description" />
|
<p class="text-sm text-gray-200 mb-4 line-clamp-4" v-html="episode.subtitle || episode.description" />
|
||||||
|
|
||||||
<div class="flex items-center">
|
<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)">
|
<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)">
|
||||||
@@ -54,9 +54,16 @@
|
|||||||
<p class="pl-2 pr-1 text-sm font-semibold">{{ getButtonText(episode) }}</p>
|
<p class="pl-2 pr-1 text-sm font-semibold">{{ getButtonText(episode) }}</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button v-if="libraryItemIdStreaming && !isStreamingFromDifferentLibrary" class="h-8 w-8 flex justify-center items-center mx-2" :class="playerQueueEpisodeIdMap[episode.id] ? 'text-success' : ''" @click.stop="queueBtnClick(episode)">
|
<ui-tooltip v-if="libraryItemIdStreaming && !isStreamingFromDifferentLibrary" :text="playerQueueEpisodeIdMap[episode.id] ? $strings.MessageRemoveFromPlayerQueue : $strings.MessageAddToPlayerQueue" :class="playerQueueEpisodeIdMap[episode.id] ? 'text-success' : ''" direction="top">
|
||||||
<span class="material-icons-outlined text-2xl">{{ playerQueueEpisodeIdMap[episode.id] ? 'playlist_add_check' : 'playlist_add' }}</span>
|
<ui-icon-btn :icon="playerQueueEpisodeIdMap[episode.id] ? 'playlist_add_check' : 'playlist_play'" borderless @click="queueBtnClick(episode)" />
|
||||||
</button>
|
<!-- <button class="h-8 w-8 flex justify-center items-center mx-2" :class="playerQueueEpisodeIdMap[episode.id] ? 'text-success' : ''" @click.stop="queueBtnClick(episode)">
|
||||||
|
<span class="material-icons-outlined text-2xl">{{ playerQueueEpisodeIdMap[episode.id] ? 'playlist_add_check' : 'playlist_add' }}</span>
|
||||||
|
</button> -->
|
||||||
|
</ui-tooltip>
|
||||||
|
|
||||||
|
<ui-tooltip :text="$strings.LabelYourPlaylists" direction="top">
|
||||||
|
<ui-icon-btn icon="playlist_add" borderless @click="clickAddToPlaylist(episode)" />
|
||||||
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -136,6 +143,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
clickAddToPlaylist(episode) {
|
||||||
|
// Makeshift libraryItem
|
||||||
|
const libraryItem = {
|
||||||
|
id: episode.libraryItemId,
|
||||||
|
media: episode.podcast
|
||||||
|
}
|
||||||
|
this.$store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: libraryItem, episode }])
|
||||||
|
this.$store.commit('globals/setShowPlaylistsModal', true)
|
||||||
|
},
|
||||||
async clickEpisode(episode) {
|
async clickEpisode(episode) {
|
||||||
if (this.openingItem) return
|
if (this.openingItem) return
|
||||||
this.openingItem = true
|
this.openingItem = true
|
||||||
@@ -155,7 +171,9 @@ export default {
|
|||||||
if (this.episodeIdStreaming === episode.id) return this.streamIsPlaying ? 'Streaming' : 'Play'
|
if (this.episodeIdStreaming === episode.id) return this.streamIsPlaying ? 'Streaming' : 'Play'
|
||||||
if (!episode.progress) return this.$elapsedPretty(episode.duration)
|
if (!episode.progress) return this.$elapsedPretty(episode.duration)
|
||||||
if (episode.progress.isFinished) return 'Finished'
|
if (episode.progress.isFinished) return 'Finished'
|
||||||
var remaining = Math.floor(episode.progress.duration - episode.progress.currentTime)
|
|
||||||
|
const duration = episode.progress.duration || episode.duration
|
||||||
|
const remaining = Math.floor(duration - episode.progress.currentTime)
|
||||||
return `${this.$elapsedPretty(remaining)} left`
|
return `${this.$elapsedPretty(remaining)} left`
|
||||||
},
|
},
|
||||||
playClick(episodeToPlay) {
|
playClick(episodeToPlay) {
|
||||||
|
|||||||
@@ -45,6 +45,11 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
async asyncData({ params, query, store, app, redirect }) {
|
async asyncData({ params, query, store, app, redirect }) {
|
||||||
|
// Podcast search/add page is restricted to admins
|
||||||
|
if (!store.getters['user/getIsAdminOrUp']) {
|
||||||
|
return redirect(`/library/${params.library}`)
|
||||||
|
}
|
||||||
|
|
||||||
var libraryId = params.library
|
var libraryId = params.library
|
||||||
var libraryData = await store.dispatch('libraries/fetch', libraryId)
|
var libraryData = await store.dispatch('libraries/fetch', libraryId)
|
||||||
if (!libraryData) {
|
if (!libraryData) {
|
||||||
@@ -81,6 +86,9 @@ export default {
|
|||||||
},
|
},
|
||||||
streamLibraryItem() {
|
streamLibraryItem() {
|
||||||
return this.$store.state.streamLibraryItem
|
return this.$store.state.streamLibraryItem
|
||||||
|
},
|
||||||
|
librarySettings() {
|
||||||
|
return this.$store.getters['libraries/getCurrentLibrarySettings']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -146,7 +154,12 @@ export default {
|
|||||||
async submitSearch(term) {
|
async submitSearch(term) {
|
||||||
this.processing = true
|
this.processing = true
|
||||||
this.termSearched = ''
|
this.termSearched = ''
|
||||||
let results = await this.$axios.$get(`/api/search/podcast?term=${encodeURIComponent(term)}`).catch((error) => {
|
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
term,
|
||||||
|
country: this.librarySettings?.podcastSearchRegion || 'us'
|
||||||
|
})
|
||||||
|
let results = await this.$axios.$get(`/api/search/podcast?${searchParams.toString()}`).catch((error) => {
|
||||||
console.error('Search request failed', error)
|
console.error('Search request failed', error)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|||||||
+38
-24
@@ -1,6 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full h-screen bg-bg">
|
<div id="page-wrapper" class="w-full h-screen overflow-y-auto">
|
||||||
<div class="w-full flex h-full items-center justify-center">
|
<div class="absolute z-0 top-0 left-0 px-6 py-3">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<img src="~static/icon.svg" alt="Audiobookshelf Logo" class="w-10 min-w-10 h-10" />
|
||||||
|
<h1 class="text-xl ml-4 hidden lg:block hover:underline">audiobookshelf</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative z-10 w-full flex h-full items-center justify-center">
|
||||||
<div v-if="criticalError" class="w-full max-w-md rounded border border-error border-opacity-25 bg-error bg-opacity-10 p-4">
|
<div v-if="criticalError" class="w-full max-w-md rounded border border-error border-opacity-25 bg-error bg-opacity-10 p-4">
|
||||||
<p class="text-center text-lg font-semibold">{{ $strings.MessageServerCouldNotBeReached }}</p>
|
<p class="text-center text-lg font-semibold">{{ $strings.MessageServerCouldNotBeReached }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,9 +17,9 @@
|
|||||||
|
|
||||||
<form @submit.prevent="submitServerSetup">
|
<form @submit.prevent="submitServerSetup">
|
||||||
<p class="text-lg font-semibold mb-2 pl-1 text-center">Create Root User</p>
|
<p class="text-lg font-semibold mb-2 pl-1 text-center">Create Root User</p>
|
||||||
<ui-text-input-with-label v-model="newRoot.username" label="Username" :disabled="processing" class="w-full mb-3 text-sm" />
|
<ui-text-input-with-label v-model.trim="newRoot.username" label="Username" :disabled="processing" class="w-full mb-3 text-sm" />
|
||||||
<ui-text-input-with-label v-model="newRoot.password" label="Password" type="password" :disabled="processing" class="w-full mb-3 text-sm" />
|
<ui-text-input-with-label v-model.trim="newRoot.password" label="Password" type="password" :disabled="processing" class="w-full mb-3 text-sm" />
|
||||||
<ui-text-input-with-label v-model="confirmPassword" label="Confirm Password" type="password" :disabled="processing" class="w-full mb-3 text-sm" />
|
<ui-text-input-with-label v-model.trim="confirmPassword" label="Confirm Password" type="password" :disabled="processing" class="w-full mb-3 text-sm" />
|
||||||
|
|
||||||
<p class="text-lg font-semibold mt-6 mb-2 pl-1 text-center">Directory Paths</p>
|
<p class="text-lg font-semibold mt-6 mb-2 pl-1 text-center">Directory Paths</p>
|
||||||
<ui-text-input-with-label v-model="ConfigPath" label="Config Path" disabled class="w-full mb-3 text-sm" />
|
<ui-text-input-with-label v-model="ConfigPath" label="Config Path" disabled class="w-full mb-3 text-sm" />
|
||||||
@@ -23,30 +30,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="isInit" class="w-full max-w-md px-8 pb-8 pt-4 -mt-40">
|
<div v-else-if="isInit" class="w-full max-w-md px-8 pb-8 pt-4 lg:-mt-40">
|
||||||
<p class="text-3xl text-white text-center mb-4">{{ $strings.HeaderLogin }}</p>
|
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-4">
|
||||||
|
<p class="text-2xl font-semibold text-center text-white mb-4">{{ $strings.HeaderLogin }}</p>
|
||||||
|
|
||||||
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
||||||
|
|
||||||
<p v-if="error" class="text-error text-center py-2">{{ error }}</p>
|
<p v-if="loginCustomMessage" class="py-2 default-style mb-2" v-html="loginCustomMessage"></p>
|
||||||
|
|
||||||
<form v-show="login_local" @submit.prevent="submitForm">
|
<p v-if="error" class="text-error text-center py-2">{{ error }}</p>
|
||||||
<label class="text-xs text-gray-300 uppercase">{{ $strings.LabelUsername }}</label>
|
|
||||||
<ui-text-input v-model="username" :disabled="processing" class="mb-3 w-full" />
|
|
||||||
|
|
||||||
<label class="text-xs text-gray-300 uppercase">{{ $strings.LabelPassword }}</label>
|
<form v-show="login_local" @submit.prevent="submitForm">
|
||||||
<ui-text-input v-model="password" type="password" :disabled="processing" class="w-full mb-3" />
|
<label class="text-xs text-gray-300 uppercase">{{ $strings.LabelUsername }}</label>
|
||||||
<div class="w-full flex justify-end py-3">
|
<ui-text-input v-model.trim="username" :disabled="processing" class="mb-3 w-full" inputName="username" />
|
||||||
<ui-btn type="submit" :disabled="processing" color="primary" class="leading-none">{{ processing ? 'Checking...' : $strings.ButtonSubmit }}</ui-btn>
|
|
||||||
|
<label class="text-xs text-gray-300 uppercase">{{ $strings.LabelPassword }}</label>
|
||||||
|
<ui-text-input v-model.trim="password" type="password" :disabled="processing" class="w-full mb-3" inputName="password" />
|
||||||
|
<div class="w-full flex justify-end py-3">
|
||||||
|
<ui-btn type="submit" :disabled="processing" color="primary" class="leading-none">{{ processing ? 'Checking...' : $strings.ButtonSubmit }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div v-if="login_local && login_openid" class="w-full h-px bg-white bg-opacity-10 my-4" />
|
||||||
|
|
||||||
|
<div class="w-full flex py-3">
|
||||||
|
<a v-if="login_openid" :href="openidAuthUri" class="w-full abs-btn outline-none rounded-md shadow-md relative border border-gray-600 text-center bg-primary text-white px-8 py-2 leading-none">
|
||||||
|
{{ openIDButtonText }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
|
||||||
<div v-if="login_local && login_openid" class="w-full h-px bg-white bg-opacity-10 my-4" />
|
|
||||||
|
|
||||||
<div class="w-full flex py-3">
|
|
||||||
<a v-if="login_openid" :href="openidAuthUri" class="w-full abs-btn outline-none rounded-md shadow-md relative border border-gray-600 text-center bg-primary text-white px-8 py-2 leading-none">
|
|
||||||
{{ openIDButtonText }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -113,6 +124,9 @@ export default {
|
|||||||
},
|
},
|
||||||
openIDButtonText() {
|
openIDButtonText() {
|
||||||
return this.authFormData?.authOpenIDButtonText || 'Login with OpenId'
|
return this.authFormData?.authOpenIDButtonText || 'Login with OpenId'
|
||||||
|
},
|
||||||
|
loginCustomMessage() {
|
||||||
|
return this.authFormData?.authLoginCustomMessage || null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -14,6 +14,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!selectedLibraryIsPodcast" class="flex items-center mb-6">
|
||||||
|
<label class="flex cursor-pointer pt-4">
|
||||||
|
<ui-toggle-switch v-model="fetchMetadata.enabled" class="inline-flex" />
|
||||||
|
<span class="pl-2 text-base">{{ $strings.LabelAutoFetchMetadata }}</span>
|
||||||
|
</label>
|
||||||
|
<ui-tooltip :text="$strings.LabelAutoFetchMetadataHelp" class="inline-flex pt-4">
|
||||||
|
<span class="pl-1 material-icons icon-text text-sm cursor-pointer">info_outlined</span>
|
||||||
|
</ui-tooltip>
|
||||||
|
|
||||||
|
<div class="flex-grow ml-4">
|
||||||
|
<ui-dropdown v-model="fetchMetadata.provider" :items="providers" :label="$strings.LabelProvider" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<widgets-alert v-if="error" type="error">
|
<widgets-alert v-if="error" type="error">
|
||||||
<p class="text-lg">{{ error }}</p>
|
<p class="text-lg">{{ error }}</p>
|
||||||
</widgets-alert>
|
</widgets-alert>
|
||||||
@@ -61,9 +75,7 @@
|
|||||||
</widgets-alert>
|
</widgets-alert>
|
||||||
|
|
||||||
<!-- Item Upload cards -->
|
<!-- Item Upload cards -->
|
||||||
<template v-for="item in items">
|
<cards-item-upload-card v-for="item in items" :key="item.index" :ref="`itemCard-${item.index}`" :media-type="selectedLibraryMediaType" :item="item" :provider="fetchMetadata.provider" :processing="processing" @remove="removeItem(item)" />
|
||||||
<cards-item-upload-card :ref="`itemCard-${item.index}`" :key="item.index" :media-type="selectedLibraryMediaType" :item="item" :processing="processing" @remove="removeItem(item)" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Upload/Reset btns -->
|
<!-- Upload/Reset btns -->
|
||||||
<div v-show="items.length" class="flex justify-end pb-8 pt-4">
|
<div v-show="items.length" class="flex justify-end pb-8 pt-4">
|
||||||
@@ -92,13 +104,18 @@ export default {
|
|||||||
selectedLibraryId: null,
|
selectedLibraryId: null,
|
||||||
selectedFolderId: null,
|
selectedFolderId: null,
|
||||||
processing: false,
|
processing: false,
|
||||||
uploadFinished: false
|
uploadFinished: false,
|
||||||
|
fetchMetadata: {
|
||||||
|
enabled: false,
|
||||||
|
provider: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedLibrary(newVal) {
|
selectedLibrary(newVal) {
|
||||||
if (newVal && !this.selectedFolderId) {
|
if (newVal && !this.selectedFolderId) {
|
||||||
this.setDefaultFolder()
|
this.setDefaultFolder()
|
||||||
|
this.setMetadataProvider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -133,6 +150,13 @@ export default {
|
|||||||
selectedLibraryIsPodcast() {
|
selectedLibraryIsPodcast() {
|
||||||
return this.selectedLibraryMediaType === 'podcast'
|
return this.selectedLibraryMediaType === 'podcast'
|
||||||
},
|
},
|
||||||
|
providers() {
|
||||||
|
if (this.selectedLibraryIsPodcast) return this.$store.state.scanners.podcastProviders
|
||||||
|
return this.$store.state.scanners.providers
|
||||||
|
},
|
||||||
|
canFetchMetadata() {
|
||||||
|
return !this.selectedLibraryIsPodcast && this.fetchMetadata.enabled
|
||||||
|
},
|
||||||
selectedFolder() {
|
selectedFolder() {
|
||||||
if (!this.selectedLibrary) return null
|
if (!this.selectedLibrary) return null
|
||||||
return this.selectedLibrary.folders.find((fold) => fold.id === this.selectedFolderId)
|
return this.selectedLibrary.folders.find((fold) => fold.id === this.selectedFolderId)
|
||||||
@@ -160,12 +184,16 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setDefaultFolder()
|
this.setDefaultFolder()
|
||||||
|
this.setMetadataProvider()
|
||||||
},
|
},
|
||||||
setDefaultFolder() {
|
setDefaultFolder() {
|
||||||
if (!this.selectedFolderId && this.selectedLibrary && this.selectedLibrary.folders.length) {
|
if (!this.selectedFolderId && this.selectedLibrary && this.selectedLibrary.folders.length) {
|
||||||
this.selectedFolderId = this.selectedLibrary.folders[0].id
|
this.selectedFolderId = this.selectedLibrary.folders[0].id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setMetadataProvider() {
|
||||||
|
this.fetchMetadata.provider ||= this.$store.getters['libraries/getLibraryProvider'](this.selectedLibraryId)
|
||||||
|
},
|
||||||
removeItem(item) {
|
removeItem(item) {
|
||||||
this.items = this.items.filter((b) => b.index !== item.index)
|
this.items = this.items.filter((b) => b.index !== item.index)
|
||||||
if (!this.items.length) {
|
if (!this.items.length) {
|
||||||
@@ -213,27 +241,49 @@ export default {
|
|||||||
var items = e.dataTransfer.items || []
|
var items = e.dataTransfer.items || []
|
||||||
|
|
||||||
var itemResults = await this.uploadHelpers.getItemsFromDrop(items, this.selectedLibraryMediaType)
|
var itemResults = await this.uploadHelpers.getItemsFromDrop(items, this.selectedLibraryMediaType)
|
||||||
this.setResults(itemResults)
|
this.onItemsSelected(itemResults)
|
||||||
},
|
},
|
||||||
inputChanged(e) {
|
inputChanged(e) {
|
||||||
if (!e.target || !e.target.files) return
|
if (!e.target || !e.target.files) return
|
||||||
var _files = Array.from(e.target.files)
|
var _files = Array.from(e.target.files)
|
||||||
if (_files && _files.length) {
|
if (_files && _files.length) {
|
||||||
var itemResults = this.uploadHelpers.getItemsFromPicker(_files, this.selectedLibraryMediaType)
|
var itemResults = this.uploadHelpers.getItemsFromPicker(_files, this.selectedLibraryMediaType)
|
||||||
this.setResults(itemResults)
|
this.onItemsSelected(itemResults)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setResults(itemResults) {
|
onItemsSelected(itemResults) {
|
||||||
|
if (this.itemSelectionSuccessful(itemResults)) {
|
||||||
|
// setTimeout ensures the new item ref is attached before this method is called
|
||||||
|
setTimeout(this.attemptMetadataFetch, 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemSelectionSuccessful(itemResults) {
|
||||||
|
console.log('Upload results', itemResults)
|
||||||
|
|
||||||
if (itemResults.error) {
|
if (itemResults.error) {
|
||||||
this.error = itemResults.error
|
this.error = itemResults.error
|
||||||
this.items = []
|
this.items = []
|
||||||
this.ignoredFiles = []
|
this.ignoredFiles = []
|
||||||
} else {
|
return false
|
||||||
this.error = ''
|
|
||||||
this.items = itemResults.items
|
|
||||||
this.ignoredFiles = itemResults.ignoredFiles
|
|
||||||
}
|
}
|
||||||
console.log('Upload results', itemResults)
|
|
||||||
|
this.error = ''
|
||||||
|
this.items = itemResults.items
|
||||||
|
this.ignoredFiles = itemResults.ignoredFiles
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
attemptMetadataFetch() {
|
||||||
|
if (!this.canFetchMetadata) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items.forEach((item) => {
|
||||||
|
let itemRef = this.$refs[`itemCard-${item.index}`]
|
||||||
|
|
||||||
|
if (itemRef?.length) {
|
||||||
|
itemRef[0].fetchMetadata(this.fetchMetadata.provider)
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
updateItemCardStatus(index, status) {
|
updateItemCardStatus(index, status) {
|
||||||
var ref = this.$refs[`itemCard-${index}`]
|
var ref = this.$refs[`itemCard-${index}`]
|
||||||
@@ -248,8 +298,8 @@ export default {
|
|||||||
var form = new FormData()
|
var form = new FormData()
|
||||||
form.set('title', item.title)
|
form.set('title', item.title)
|
||||||
if (!this.selectedLibraryIsPodcast) {
|
if (!this.selectedLibraryIsPodcast) {
|
||||||
form.set('author', item.author)
|
form.set('author', item.author || '')
|
||||||
form.set('series', item.series)
|
form.set('series', item.series || '')
|
||||||
}
|
}
|
||||||
form.set('library', this.selectedLibraryId)
|
form.set('library', this.selectedLibraryId)
|
||||||
form.set('folder', this.selectedFolderId)
|
form.set('folder', this.selectedFolderId)
|
||||||
@@ -346,6 +396,8 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.selectedLibraryId = this.$store.state.libraries.currentLibraryId
|
this.selectedLibraryId = this.$store.state.libraries.currentLibraryId
|
||||||
|
this.setMetadataProvider()
|
||||||
|
|
||||||
this.setDefaultFolder()
|
this.setDefaultFolder()
|
||||||
window.addEventListener('dragenter', this.dragenter)
|
window.addEventListener('dragenter', this.dragenter)
|
||||||
window.addEventListener('dragleave', this.dragleave)
|
window.addEventListener('dragleave', this.dragleave)
|
||||||
|
|||||||
@@ -139,11 +139,30 @@ export default class LocalAudioPlayer extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var hlsOptions = {
|
var hlsOptions = {
|
||||||
startPosition: this.startTime || -1
|
startPosition: this.startTime || -1,
|
||||||
// No longer needed because token is put in a query string
|
fragLoadPolicy: {
|
||||||
// xhrSetup: (xhr) => {
|
default: {
|
||||||
// xhr.setRequestHeader('Authorization', `Bearer ${this.token}`)
|
maxTimeToFirstByteMs: 10000,
|
||||||
// }
|
maxLoadTimeMs: 120000,
|
||||||
|
timeoutRetry: {
|
||||||
|
maxNumRetry: 4,
|
||||||
|
retryDelayMs: 0,
|
||||||
|
maxRetryDelayMs: 0,
|
||||||
|
},
|
||||||
|
errorRetry: {
|
||||||
|
maxNumRetry: 8,
|
||||||
|
retryDelayMs: 1000,
|
||||||
|
maxRetryDelayMs: 8000,
|
||||||
|
shouldRetry: (retryConfig, retryCount, isTimeout, httpStatus, retry) => {
|
||||||
|
if (httpStatus?.code === 404 && retryConfig?.maxNumRetry > retryCount) {
|
||||||
|
console.log(`[HLS] Server 404 for fragment retry ${retryCount} of ${retryConfig.maxNumRetry}`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return retry
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.hlsInstance = new Hls(hlsOptions)
|
this.hlsInstance = new Hls(hlsOptions)
|
||||||
|
|
||||||
@@ -156,9 +175,15 @@ export default class LocalAudioPlayer extends EventEmitter {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.hlsInstance.on(Hls.Events.ERROR, (e, data) => {
|
this.hlsInstance.on(Hls.Events.ERROR, (e, data) => {
|
||||||
console.error('[HLS] Error', data.type, data.details, data)
|
|
||||||
if (data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
|
if (data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
|
||||||
console.error('[HLS] BUFFER STALLED ERROR')
|
console.error('[HLS] BUFFER STALLED ERROR')
|
||||||
|
} else if (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR) {
|
||||||
|
// Only show error if the fragment is not being retried
|
||||||
|
if (data.errorAction?.action !== 5) {
|
||||||
|
console.error('[HLS] FRAG LOAD ERROR', data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[HLS] Error', data.type, data.details, data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.hlsInstance.on(Hls.Events.DESTROYING, () => {
|
this.hlsInstance.on(Hls.Events.DESTROYING, () => {
|
||||||
|
|||||||
+18
-1
@@ -10,16 +10,21 @@ const languageCodeMap = {
|
|||||||
'de': { label: 'Deutsch', dateFnsLocale: 'de' },
|
'de': { label: 'Deutsch', dateFnsLocale: 'de' },
|
||||||
'en-us': { label: 'English', dateFnsLocale: 'enUS' },
|
'en-us': { label: 'English', dateFnsLocale: 'enUS' },
|
||||||
'es': { label: 'Español', dateFnsLocale: 'es' },
|
'es': { label: 'Español', dateFnsLocale: 'es' },
|
||||||
|
'et': { label: 'Eesti', dateFnsLocale: 'et' },
|
||||||
'fr': { label: 'Français', dateFnsLocale: 'fr' },
|
'fr': { label: 'Français', dateFnsLocale: 'fr' },
|
||||||
'hr': { label: 'Hrvatski', dateFnsLocale: 'hr' },
|
'hr': { label: 'Hrvatski', dateFnsLocale: 'hr' },
|
||||||
'it': { label: 'Italiano', dateFnsLocale: 'it' },
|
'it': { label: 'Italiano', dateFnsLocale: 'it' },
|
||||||
'lt': { label: 'Lietuvių', dateFnsLocale: 'lt' },
|
'lt': { label: 'Lietuvių', dateFnsLocale: 'lt' },
|
||||||
|
'hu': { label: 'Magyar', dateFnsLocale: 'hu' },
|
||||||
'nl': { label: 'Nederlands', dateFnsLocale: 'nl' },
|
'nl': { label: 'Nederlands', dateFnsLocale: 'nl' },
|
||||||
'no': { label: 'Norsk', dateFnsLocale: 'no' },
|
'no': { label: 'Norsk', dateFnsLocale: 'no' },
|
||||||
'pl': { label: 'Polski', dateFnsLocale: 'pl' },
|
'pl': { label: 'Polski', dateFnsLocale: 'pl' },
|
||||||
|
'pt-br': { label: 'Português (Brasil)', dateFnsLocale: 'ptBR' },
|
||||||
'ru': { label: 'Русский', dateFnsLocale: 'ru' },
|
'ru': { label: 'Русский', dateFnsLocale: 'ru' },
|
||||||
'sv': { label: 'Svenska', dateFnsLocale: 'sv' },
|
'sv': { label: 'Svenska', dateFnsLocale: 'sv' },
|
||||||
|
'vi-vn': { label: 'Tiếng Việt', dateFnsLocale: 'vi' },
|
||||||
'zh-cn': { label: '简体中文 (Simplified Chinese)', dateFnsLocale: 'zhCN' },
|
'zh-cn': { label: '简体中文 (Simplified Chinese)', dateFnsLocale: 'zhCN' },
|
||||||
|
'zh-tw': { label: '正體中文 (Traditional Chinese)', dateFnsLocale: 'zhTW' },
|
||||||
}
|
}
|
||||||
Vue.prototype.$languageCodeOptions = Object.keys(languageCodeMap).map(code => {
|
Vue.prototype.$languageCodeOptions = Object.keys(languageCodeMap).map(code => {
|
||||||
return {
|
return {
|
||||||
@@ -28,6 +33,18 @@ 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 = {
|
||||||
|
'us': { label: 'United States' },
|
||||||
|
'cn': { label: '中国' }
|
||||||
|
}
|
||||||
|
Vue.prototype.$podcastSearchRegionOptions = Object.keys(podcastSearchRegionMap).map(code => {
|
||||||
|
return {
|
||||||
|
text: podcastSearchRegionMap[code].label,
|
||||||
|
value: code
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Vue.prototype.$languageCodes = {
|
Vue.prototype.$languageCodes = {
|
||||||
default: defaultCode,
|
default: defaultCode,
|
||||||
current: defaultCode,
|
current: defaultCode,
|
||||||
@@ -83,7 +100,7 @@ async function loadi18n(code) {
|
|||||||
|
|
||||||
Vue.prototype.$setDateFnsLocale(languageCodeMap[code].dateFnsLocale)
|
Vue.prototype.$setDateFnsLocale(languageCodeMap[code].dateFnsLocale)
|
||||||
|
|
||||||
this.$eventBus.$emit('change-lang', code)
|
this?.$eventBus?.$emit('change-lang', code)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ Vue.prototype.$sanitizeFilename = (filename, colonReplacement = ' - ') => {
|
|||||||
.replace(lineBreaks, replacement)
|
.replace(lineBreaks, replacement)
|
||||||
.replace(windowsReservedRe, replacement)
|
.replace(windowsReservedRe, replacement)
|
||||||
.replace(windowsTrailingRe, replacement)
|
.replace(windowsTrailingRe, replacement)
|
||||||
|
.replace(/\s+/g, ' ') // Replace consecutive spaces with a single space
|
||||||
|
|
||||||
// Check if basename is too many bytes
|
// Check if basename is too many bytes
|
||||||
const ext = Path.extname(sanitized) // separate out file extension
|
const ext = Path.extname(sanitized) // separate out file extension
|
||||||
@@ -155,14 +156,14 @@ Vue.prototype.$copyToClipboard = (str, ctx) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function xmlToJson(xml) {
|
function xmlToJson(xml) {
|
||||||
const json = {};
|
const json = {}
|
||||||
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
|
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
|
||||||
const key = res[1] || res[3];
|
const key = res[1] || res[3]
|
||||||
const value = res[2] && xmlToJson(res[2]);
|
const value = res[2] && xmlToJson(res[2])
|
||||||
json[key] = ((value && Object.keys(value).length) ? value : res[2]) || null;
|
json[key] = ((value && Object.keys(value).length) ? value : res[2]) || null
|
||||||
|
|
||||||
}
|
}
|
||||||
return json;
|
return json
|
||||||
}
|
}
|
||||||
Vue.prototype.$xmlToJson = xmlToJson
|
Vue.prototype.$xmlToJson = xmlToJson
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
User-Agent: *
|
||||||
|
Disallow: /
|
||||||
@@ -99,7 +99,7 @@ export const getters = {
|
|||||||
return `http://localhost:3333${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}&ts=${lastUpdate}${raw ? '&raw=1' : ''}`
|
return `http://localhost:3333${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}&ts=${lastUpdate}${raw ? '&raw=1' : ''}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}&ts=${lastUpdate}`
|
return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}&ts=${lastUpdate}${raw ? '&raw=1' : ''}`
|
||||||
},
|
},
|
||||||
getLibraryItemCoverSrcById: (state, getters, rootState, rootGetters) => (libraryItemId, timestamp = null, raw = false) => {
|
getLibraryItemCoverSrcById: (state, getters, rootState, rootGetters) => (libraryItemId, timestamp = null, raw = false) => {
|
||||||
const placeholder = `${rootState.routerBasePath}/book_placeholder.jpg`
|
const placeholder = `${rootState.routerBasePath}/book_placeholder.jpg`
|
||||||
|
|||||||
@@ -80,13 +80,11 @@ export const actions = {
|
|||||||
return state.folders
|
return state.folders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('Loading folders')
|
|
||||||
commit('setFoldersLastUpdate')
|
commit('setFoldersLastUpdate')
|
||||||
|
|
||||||
return this.$axios
|
return this.$axios
|
||||||
.$get('/api/filesystem')
|
.$get('/api/filesystem')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log('Settings folders', res)
|
|
||||||
commit('setFolders', res.directories)
|
commit('setFolders', res.directories)
|
||||||
return res.directories
|
return res.directories
|
||||||
})
|
})
|
||||||
@@ -115,19 +113,23 @@ export const actions = {
|
|||||||
const library = data.library
|
const library = data.library
|
||||||
const filterData = data.filterdata
|
const filterData = data.filterdata
|
||||||
const issues = data.issues || 0
|
const issues = data.issues || 0
|
||||||
|
const customMetadataProviders = data.customMetadataProviders || []
|
||||||
const numUserPlaylists = data.numUserPlaylists
|
const numUserPlaylists = data.numUserPlaylists
|
||||||
|
|
||||||
dispatch('user/checkUpdateLibrarySortFilter', library.mediaType, { root: true })
|
dispatch('user/checkUpdateLibrarySortFilter', library.mediaType, { root: true })
|
||||||
|
|
||||||
|
if (libraryChanging) {
|
||||||
|
commit('setCollections', [])
|
||||||
|
commit('setUserPlaylists', [])
|
||||||
|
}
|
||||||
|
|
||||||
commit('addUpdate', library)
|
commit('addUpdate', library)
|
||||||
commit('setLibraryIssues', issues)
|
commit('setLibraryIssues', issues)
|
||||||
commit('setLibraryFilterData', filterData)
|
commit('setLibraryFilterData', filterData)
|
||||||
commit('setNumUserPlaylists', numUserPlaylists)
|
commit('setNumUserPlaylists', numUserPlaylists)
|
||||||
|
commit('scanners/setCustomMetadataProviders', customMetadataProviders, { root: true })
|
||||||
|
|
||||||
commit('setCurrentLibrary', libraryId)
|
commit('setCurrentLibrary', libraryId)
|
||||||
if (libraryChanging) {
|
|
||||||
commit('setCollections', [])
|
|
||||||
commit('setUserPlaylists', [])
|
|
||||||
}
|
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|||||||
@@ -71,8 +71,56 @@ export const state = () => ({
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {}
|
export const getters = {
|
||||||
|
checkBookProviderExists: state => (providerValue) => {
|
||||||
|
return state.providers.some(p => p.value === providerValue)
|
||||||
|
},
|
||||||
|
checkPodcastProviderExists: state => (providerValue) => {
|
||||||
|
return state.podcastProviders.some(p => p.value === providerValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const actions = {}
|
export const actions = {}
|
||||||
|
|
||||||
export const mutations = {}
|
export const mutations = {
|
||||||
|
addCustomMetadataProvider(state, provider) {
|
||||||
|
if (provider.mediaType === 'book') {
|
||||||
|
if (state.providers.some(p => p.value === provider.slug)) return
|
||||||
|
state.providers.push({
|
||||||
|
text: provider.name,
|
||||||
|
value: provider.slug
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (state.podcastProviders.some(p => p.value === provider.slug)) return
|
||||||
|
state.podcastProviders.push({
|
||||||
|
text: provider.name,
|
||||||
|
value: provider.slug
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeCustomMetadataProvider(state, provider) {
|
||||||
|
if (provider.mediaType === 'book') {
|
||||||
|
state.providers = state.providers.filter(p => p.value !== provider.slug)
|
||||||
|
} else {
|
||||||
|
state.podcastProviders = state.podcastProviders.filter(p => p.value !== provider.slug)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setCustomMetadataProviders(state, providers) {
|
||||||
|
if (!providers?.length) return
|
||||||
|
|
||||||
|
const mediaType = providers[0].mediaType
|
||||||
|
if (mediaType === 'book') {
|
||||||
|
// clear previous values, and add new values to the end
|
||||||
|
state.providers = state.providers.filter((p) => !p.value.startsWith('custom-'))
|
||||||
|
state.providers = [
|
||||||
|
...state.providers,
|
||||||
|
...providers.map((p) => ({
|
||||||
|
text: p.name,
|
||||||
|
value: p.slug
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
// Podcast providers not supported yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,9 @@ export const state = () => ({
|
|||||||
useChapterTrack: false,
|
useChapterTrack: false,
|
||||||
seriesSortBy: 'name',
|
seriesSortBy: 'name',
|
||||||
seriesSortDesc: false,
|
seriesSortDesc: false,
|
||||||
seriesFilterBy: 'all'
|
seriesFilterBy: 'all',
|
||||||
|
authorSortBy: 'name',
|
||||||
|
authorSortDesc: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+41
-1
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Skrýt",
|
"ButtonHide": "Skrýt",
|
||||||
"ButtonHome": "Domů",
|
"ButtonHome": "Domů",
|
||||||
"ButtonIssues": "Problémy",
|
"ButtonIssues": "Problémy",
|
||||||
|
"ButtonJumpBackward": "Jump Backward",
|
||||||
|
"ButtonJumpForward": "Jump Forward",
|
||||||
"ButtonLatest": "Nejnovější",
|
"ButtonLatest": "Nejnovější",
|
||||||
"ButtonLibrary": "Knihovna",
|
"ButtonLibrary": "Knihovna",
|
||||||
"ButtonLogout": "Odhlásit",
|
"ButtonLogout": "Odhlásit",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "Spárovat všechny autory",
|
"ButtonMatchAllAuthors": "Spárovat všechny autory",
|
||||||
"ButtonMatchBooks": "Spárovat Knihy",
|
"ButtonMatchBooks": "Spárovat Knihy",
|
||||||
"ButtonNevermind": "Nevadí",
|
"ButtonNevermind": "Nevadí",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Next Chapter",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Otevřít kanál",
|
"ButtonOpenFeed": "Otevřít kanál",
|
||||||
"ButtonOpenManager": "Otevřít správce",
|
"ButtonOpenManager": "Otevřít správce",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "Přehrát",
|
"ButtonPlay": "Přehrát",
|
||||||
"ButtonPlaying": "Hraje",
|
"ButtonPlaying": "Hraje",
|
||||||
"ButtonPlaylists": "Seznamy skladeb",
|
"ButtonPlaylists": "Seznamy skladeb",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Previous Chapter",
|
||||||
"ButtonPurgeAllCache": "Vyčistit veškerou mezipaměť",
|
"ButtonPurgeAllCache": "Vyčistit veškerou mezipaměť",
|
||||||
"ButtonPurgeItemsCache": "Vyčistit mezipaměť položek",
|
"ButtonPurgeItemsCache": "Vyčistit mezipaměť položek",
|
||||||
"ButtonPurgeMediaProgress": "Vyčistit průběh médií",
|
"ButtonPurgeMediaProgress": "Vyčistit průběh médií",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "Odstranit z fronty",
|
"ButtonQueueRemoveItem": "Odstranit z fronty",
|
||||||
"ButtonQuickMatch": "Rychlé přiřazení",
|
"ButtonQuickMatch": "Rychlé přiřazení",
|
||||||
"ButtonRead": "Číst",
|
"ButtonRead": "Číst",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "Odstranit",
|
"ButtonRemove": "Odstranit",
|
||||||
"ButtonRemoveAll": "Odstranit vše",
|
"ButtonRemoveAll": "Odstranit vše",
|
||||||
"ButtonRemoveAllLibraryItems": "Odstranit všechny položky knihovny",
|
"ButtonRemoveAllLibraryItems": "Odstranit všechny položky knihovny",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "Vybrat cestu ke složce",
|
"ButtonSelectFolderPath": "Vybrat cestu ke složce",
|
||||||
"ButtonSeries": "Série",
|
"ButtonSeries": "Série",
|
||||||
"ButtonSetChaptersFromTracks": "Nastavit kapitoly ze stop",
|
"ButtonSetChaptersFromTracks": "Nastavit kapitoly ze stop",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "Časy posunu",
|
"ButtonShiftTimes": "Časy posunu",
|
||||||
"ButtonShow": "Zobrazit",
|
"ButtonShow": "Zobrazit",
|
||||||
"ButtonStartM4BEncode": "Spustit kódování M4B",
|
"ButtonStartM4BEncode": "Spustit kódování M4B",
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"ButtonUserEdit": "Upravit uživatelské {0}",
|
"ButtonUserEdit": "Upravit uživatelské {0}",
|
||||||
"ButtonViewAll": "Zobrazit vše",
|
"ButtonViewAll": "Zobrazit vše",
|
||||||
"ButtonYes": "Ano",
|
"ButtonYes": "Ano",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error fetching metadata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Could not fetch metadata - try updating title and/or author",
|
||||||
|
"ErrorUploadLacksTitle": "Must have a title",
|
||||||
"HeaderAccount": "Účet",
|
"HeaderAccount": "Účet",
|
||||||
"HeaderAdvanced": "Pokročilé",
|
"HeaderAdvanced": "Pokročilé",
|
||||||
"HeaderAppriseNotificationSettings": "Nastavení oznámení Apprise",
|
"HeaderAppriseNotificationSettings": "Nastavení oznámení Apprise",
|
||||||
@@ -101,6 +113,7 @@
|
|||||||
"HeaderCollectionItems": "Položky kolekce",
|
"HeaderCollectionItems": "Položky kolekce",
|
||||||
"HeaderCover": "Obálka",
|
"HeaderCover": "Obálka",
|
||||||
"HeaderCurrentDownloads": "Aktuální stahování",
|
"HeaderCurrentDownloads": "Aktuální stahování",
|
||||||
|
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
|
||||||
"HeaderDetails": "Podrobnosti",
|
"HeaderDetails": "Podrobnosti",
|
||||||
"HeaderDownloadQueue": "Fronta stahování",
|
"HeaderDownloadQueue": "Fronta stahování",
|
||||||
"HeaderEbookFiles": "Soubory elektronických knih",
|
"HeaderEbookFiles": "Soubory elektronických knih",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Aktualizovat podrobnosti",
|
"HeaderUpdateDetails": "Aktualizovat podrobnosti",
|
||||||
"HeaderUpdateLibrary": "Aktualizovat knihovnu",
|
"HeaderUpdateLibrary": "Aktualizovat knihovnu",
|
||||||
"HeaderUsers": "Uživatelé",
|
"HeaderUsers": "Uživatelé",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Vaše statistiky",
|
"HeaderYourStats": "Vaše statistiky",
|
||||||
"LabelAbridged": "Zkráceno",
|
"LabelAbridged": "Zkráceno",
|
||||||
"LabelAccountType": "Typ účtu",
|
"LabelAccountType": "Typ účtu",
|
||||||
@@ -196,6 +210,8 @@
|
|||||||
"LabelAuthorLastFirst": "Autor (příjmení a jméno)",
|
"LabelAuthorLastFirst": "Autor (příjmení a jméno)",
|
||||||
"LabelAuthors": "Autoři",
|
"LabelAuthors": "Autoři",
|
||||||
"LabelAutoDownloadEpisodes": "Automaticky stahovat epizody",
|
"LabelAutoDownloadEpisodes": "Automaticky stahovat epizody",
|
||||||
|
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Fetches metadata for title, author, and series to streamline uploading. Additional metadata may have to be matched after upload.",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoLaunch": "Auto Launch",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoRegister": "Auto Register",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Příklad",
|
"LabelExample": "Příklad",
|
||||||
"LabelExplicit": "Explicitní",
|
"LabelExplicit": "Explicitní",
|
||||||
"LabelFeedURL": "URL zdroje",
|
"LabelFeedURL": "URL zdroje",
|
||||||
|
"LabelFetchingMetadata": "Fetching Metadata",
|
||||||
"LabelFile": "Soubor",
|
"LabelFile": "Soubor",
|
||||||
"LabelFileBirthtime": "Čas vzniku souboru",
|
"LabelFileBirthtime": "Čas vzniku souboru",
|
||||||
"LabelFileModified": "Soubor změněn",
|
"LabelFileModified": "Soubor změněn",
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
"LabelFinished": "Dokončeno",
|
"LabelFinished": "Dokončeno",
|
||||||
"LabelFolder": "Složka",
|
"LabelFolder": "Složka",
|
||||||
"LabelFolders": "Složky",
|
"LabelFolders": "Složky",
|
||||||
|
"LabelFontBold": "Bold",
|
||||||
"LabelFontFamily": "Rodina písem",
|
"LabelFontFamily": "Rodina písem",
|
||||||
|
"LabelFontItalic": "Italic",
|
||||||
"LabelFontScale": "Měřítko písma",
|
"LabelFontScale": "Měřítko písma",
|
||||||
|
"LabelFontStrikethrough": "Strikethrough",
|
||||||
"LabelFormat": "Formát",
|
"LabelFormat": "Formát",
|
||||||
"LabelGenre": "Žánr",
|
"LabelGenre": "Žánr",
|
||||||
"LabelGenres": "Žánry",
|
"LabelGenres": "Žánry",
|
||||||
@@ -336,7 +356,11 @@
|
|||||||
"LabelMetaTags": "Metaznačky",
|
"LabelMetaTags": "Metaznačky",
|
||||||
"LabelMinute": "Minuta",
|
"LabelMinute": "Minuta",
|
||||||
"LabelMissing": "Chybějící",
|
"LabelMissing": "Chybějící",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Chybějící díly",
|
"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.",
|
||||||
"LabelMore": "Více",
|
"LabelMore": "Více",
|
||||||
"LabelMoreInfo": "Více informací",
|
"LabelMoreInfo": "Více informací",
|
||||||
"LabelName": "Jméno",
|
"LabelName": "Jméno",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Může stahovat",
|
"LabelPermissionsDownload": "Může stahovat",
|
||||||
"LabelPermissionsUpdate": "Může aktualizovat",
|
"LabelPermissionsUpdate": "Může aktualizovat",
|
||||||
"LabelPermissionsUpload": "Může nahrávat",
|
"LabelPermissionsUpload": "Může nahrávat",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Cesta k fotografii/URL",
|
"LabelPhotoPathURL": "Cesta k fotografii/URL",
|
||||||
"LabelPlaylists": "Seznamy skladeb",
|
"LabelPlaylists": "Seznamy skladeb",
|
||||||
"LabelPlayMethod": "Metoda přehrávání",
|
"LabelPlayMethod": "Metoda přehrávání",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasty",
|
"LabelPodcasts": "Podcasty",
|
||||||
|
"LabelPodcastSearchRegion": "Oblast vyhledávání podcastu",
|
||||||
"LabelPodcastType": "Typ podcastu",
|
"LabelPodcastType": "Typ podcastu",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Předpony, které se mají ignorovat (nerozlišují se malá a velká písmena)",
|
"LabelPrefixesToIgnore": "Předpony, které se mají ignorovat (nerozlišují se malá a velká písmena)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Nedávno přidané",
|
"LabelRecentlyAdded": "Nedávno přidané",
|
||||||
"LabelRecentSeries": "Nedávné série",
|
"LabelRecentSeries": "Nedávné série",
|
||||||
"LabelRecommended": "Doporučeno",
|
"LabelRecommended": "Doporučeno",
|
||||||
|
"LabelRedo": "Redo",
|
||||||
"LabelRegion": "Region",
|
"LabelRegion": "Region",
|
||||||
"LabelReleaseDate": "Datum vydání",
|
"LabelReleaseDate": "Datum vydání",
|
||||||
"LabelRemoveCover": "Odstranit obálku",
|
"LabelRemoveCover": "Odstranit obálku",
|
||||||
|
"LabelRowsPerPage": "Rows per page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Vlastní e-mail vlastníka",
|
"LabelRSSFeedCustomOwnerEmail": "Vlastní e-mail vlastníka",
|
||||||
"LabelRSSFeedCustomOwnerName": "Vlastní jméno vlastníka",
|
"LabelRSSFeedCustomOwnerName": "Vlastní jméno vlastníka",
|
||||||
"LabelRSSFeedOpen": "Otevření RSS kanálu",
|
"LabelRSSFeedOpen": "Otevření RSS kanálu",
|
||||||
@@ -416,6 +444,7 @@
|
|||||||
"LabelSeries": "Série",
|
"LabelSeries": "Série",
|
||||||
"LabelSeriesName": "Název série",
|
"LabelSeriesName": "Název série",
|
||||||
"LabelSeriesProgress": "Průběh série",
|
"LabelSeriesProgress": "Průběh série",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Nastavit jako primární",
|
"LabelSetEbookAsPrimary": "Nastavit jako primární",
|
||||||
"LabelSetEbookAsSupplementary": "Nastavit jako doplňkové",
|
"LabelSetEbookAsSupplementary": "Nastavit jako doplňkové",
|
||||||
"LabelSettingsAudiobooksOnly": "Pouze audioknihy",
|
"LabelSettingsAudiobooksOnly": "Pouze audioknihy",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Série, které mají jedinou knihu, budou skryty na stránce série a na domovské stránce.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Série, které mají jedinou knihu, budou skryty na stránce série a na domovské stránce.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Domovská stránka používá zobrazení police s knihami",
|
"LabelSettingsHomePageBookshelfView": "Domovská stránka používá zobrazení police s knihami",
|
||||||
"LabelSettingsLibraryBookshelfView": "Knihovna používá zobrazení police s knihami",
|
"LabelSettingsLibraryBookshelfView": "Knihovna používá zobrazení police s knihami",
|
||||||
|
"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": "Analzyovat podtitul",
|
"LabelSettingsParseSubtitles": "Analzyovat podtitul",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Rozparsovat podtitul z názvů složek audioknih.<br>Podtiul musí být oddělen znakem \" - \"<br>tj. \"Název knihy - Zde Podtitul\" má podtitul \"Zde podtitul\"",
|
"LabelSettingsParseSubtitlesHelp": "Rozparsovat podtitul z názvů složek audioknih.<br>Podtiul musí být oddělen znakem \" - \"<br>tj. \"Název knihy - Zde Podtitul\" má podtitul \"Zde podtitul\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Preferovat spárovaná metadata",
|
"LabelSettingsPreferMatchedMetadata": "Preferovat spárovaná metadata",
|
||||||
@@ -482,6 +513,10 @@
|
|||||||
"LabelTagsAccessibleToUser": "Značky přístupné uživateli",
|
"LabelTagsAccessibleToUser": "Značky přístupné uživateli",
|
||||||
"LabelTagsNotAccessibleToUser": "Značky nepřístupné uživateli",
|
"LabelTagsNotAccessibleToUser": "Značky nepřístupné uživateli",
|
||||||
"LabelTasks": "Spuštěné Úlohy",
|
"LabelTasks": "Spuštěné Úlohy",
|
||||||
|
"LabelTextEditorBulletedList": "Bulleted list",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numbered list",
|
||||||
|
"LabelTextEditorUnlink": "Unlink",
|
||||||
"LabelTheme": "Téma",
|
"LabelTheme": "Téma",
|
||||||
"LabelThemeDark": "Tmavé",
|
"LabelThemeDark": "Tmavé",
|
||||||
"LabelThemeLight": "Světlé",
|
"LabelThemeLight": "Světlé",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Jedna stopa",
|
"LabelTracksSingleTrack": "Jedna stopa",
|
||||||
"LabelType": "Typ",
|
"LabelType": "Typ",
|
||||||
"LabelUnabridged": "Nezkráceno",
|
"LabelUnabridged": "Nezkráceno",
|
||||||
|
"LabelUndo": "Undo",
|
||||||
"LabelUnknown": "Neznámý",
|
"LabelUnknown": "Neznámý",
|
||||||
"LabelUpdateCover": "Aktualizovat obálku",
|
"LabelUpdateCover": "Aktualizovat obálku",
|
||||||
"LabelUpdateCoverHelp": "Povolit přepsání existujících obálek pro vybrané knihy, pokud je nalezena shoda",
|
"LabelUpdateCoverHelp": "Povolit přepsání existujících obálek pro vybrané knihy, pokud je nalezena shoda",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Povolit přepsání existujících údajů o vybraných knihách, když je nalezena shoda",
|
"LabelUpdateDetailsHelp": "Povolit přepsání existujících údajů o vybraných knihách, když je nalezena shoda",
|
||||||
"LabelUploaderDragAndDrop": "Přetáhnout soubory nebo složky",
|
"LabelUploaderDragAndDrop": "Přetáhnout soubory nebo složky",
|
||||||
"LabelUploaderDropFiles": "Odstranit soubory",
|
"LabelUploaderDropFiles": "Odstranit soubory",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||||
"LabelUseChapterTrack": "Použít stopu kapitoly",
|
"LabelUseChapterTrack": "Použít stopu kapitoly",
|
||||||
"LabelUseFullTrack": "Použít celou stopu",
|
"LabelUseFullTrack": "Použít celou stopu",
|
||||||
"LabelUser": "Uživatel",
|
"LabelUser": "Uživatel",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "Zobrazit frontu přehrávače",
|
"LabelViewQueue": "Zobrazit frontu přehrávače",
|
||||||
"LabelVolume": "Hlasitost",
|
"LabelVolume": "Hlasitost",
|
||||||
"LabelWeekdaysToRun": "Dny v týdnu ke spuštění",
|
"LabelWeekdaysToRun": "Dny v týdnu ke spuštění",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Doba trvání vaší audioknihy",
|
"LabelYourAudiobookDuration": "Doba trvání vaší audioknihy",
|
||||||
"LabelYourBookmarks": "Vaše záložky",
|
"LabelYourBookmarks": "Vaše záložky",
|
||||||
"LabelYourPlaylists": "Vaše seznamy přehrávání",
|
"LabelYourPlaylists": "Vaše seznamy přehrávání",
|
||||||
@@ -562,6 +601,7 @@
|
|||||||
"MessageConfirmRemoveCollection": "Opravdu chcete odstranit kolekci \"{0}\"?",
|
"MessageConfirmRemoveCollection": "Opravdu chcete odstranit kolekci \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "Opravdu chcete odstranit epizodu \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Opravdu chcete odstranit epizodu \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "Opravdu chcete odstranit {0} epizody?",
|
"MessageConfirmRemoveEpisodes": "Opravdu chcete odstranit {0} epizody?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Opravdu chcete odebrat předčítání \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Opravdu chcete odebrat předčítání \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "Opravdu chcete odstranit svůj playlist \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Opravdu chcete odstranit svůj playlist \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "Opravdu chcete přejmenovat žánr \"{0}\" na \"{1}\" pro všechny položky?",
|
"MessageConfirmRenameGenre": "Opravdu chcete přejmenovat žánr \"{0}\" na \"{1}\" pro všechny položky?",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Opravdu chcete obnovit zálohu vytvořenou dne?",
|
"MessageRestoreBackupConfirm": "Opravdu chcete obnovit zálohu vytvořenou dne?",
|
||||||
"MessageRestoreBackupWarning": "Obnovení zálohy přepíše celou databázi umístěnou v /config a obálku obrázků v /metadata/items & /metadata/authors.<br /><br />Backups nezmění žádné soubory ve složkách knihovny. Pokud jste povolili nastavení serveru pro ukládání obrázků obalu a metadat do složek knihovny, nebudou zálohovány ani přepsány.<br /><br />Všichni klienti používající váš server budou automaticky obnoveni.",
|
"MessageRestoreBackupWarning": "Obnovení zálohy přepíše celou databázi umístěnou v /config a obálku obrázků v /metadata/items & /metadata/authors.<br /><br />Backups nezmění žádné soubory ve složkách knihovny. Pokud jste povolili nastavení serveru pro ukládání obrázků obalu a metadat do složek knihovny, nebudou zálohovány ani přepsány.<br /><br />Všichni klienti používající váš server budou automaticky obnoveni.",
|
||||||
"MessageSearchResultsFor": "Výsledky hledání pro",
|
"MessageSearchResultsFor": "Výsledky hledání pro",
|
||||||
|
"MessageSelected": "{0} selected",
|
||||||
"MessageServerCouldNotBeReached": "Server je nedostupný",
|
"MessageServerCouldNotBeReached": "Server je nedostupný",
|
||||||
"MessageSetChaptersFromTracksDescription": "Nastavit kapitoly jako kapitolu a název kapitoly jako název zvukového souboru",
|
"MessageSetChaptersFromTracksDescription": "Nastavit kapitoly jako kapitolu a název kapitoly jako název zvukového souboru",
|
||||||
"MessageStartPlaybackAtTime": "Spustit přehrávání pro \"{0}\" v {1}?",
|
"MessageStartPlaybackAtTime": "Spustit přehrávání pro \"{0}\" v {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "Uživatel root je jediný uživatel, který může mít prázdné heslo",
|
"NoteChangeRootPassword": "Uživatel root je jediný uživatel, který může mít prázdné heslo",
|
||||||
"NoteChapterEditorTimes": "Poznámka: Čas začátku první kapitoly musí zůstat v 0:00 a čas začátku poslední kapitoly nesmí překročit tuto dobu trvání audioknihy.",
|
"NoteChapterEditorTimes": "Poznámka: Čas začátku první kapitoly musí zůstat v 0:00 a čas začátku poslední kapitoly nesmí překročit tuto dobu trvání audioknihy.",
|
||||||
"NoteFolderPicker": "Poznámka: složky, které jsou již namapovány, nebudou zobrazeny",
|
"NoteFolderPicker": "Poznámka: složky, které jsou již namapovány, nebudou zobrazeny",
|
||||||
"NoteFolderPickerDebian": "Poznámka: Výběr složek pro instalaci debianu není plně implementován. Cestu ke své knihovně byste měli zadat přímo.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Upozornění: Většina aplikací pro podcasty bude vyžadovat, aby adresa URL kanálu RSS používala protokol HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Upozornění: Většina aplikací pro podcasty bude vyžadovat, aby adresa URL kanálu RSS používala protokol HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Upozornění: 1 nebo více epizod nemá datum vydání. Některé podcastové aplikace to vyžadují.",
|
"NoteRSSFeedPodcastAppsPubDate": "Upozornění: 1 nebo více epizod nemá datum vydání. Některé podcastové aplikace to vyžadují.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Se složkami s multimediálními soubory bude zacházeno jako se samostatnými položkami knihovny.",
|
"NoteUploaderFoldersWithMediaFiles": "Se složkami s multimediálními soubory bude zacházeno jako se samostatnými položkami knihovny.",
|
||||||
|
|||||||
+41
-1
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Skjul",
|
"ButtonHide": "Skjul",
|
||||||
"ButtonHome": "Hjem",
|
"ButtonHome": "Hjem",
|
||||||
"ButtonIssues": "Problemer",
|
"ButtonIssues": "Problemer",
|
||||||
|
"ButtonJumpBackward": "Jump Backward",
|
||||||
|
"ButtonJumpForward": "Jump Forward",
|
||||||
"ButtonLatest": "Seneste",
|
"ButtonLatest": "Seneste",
|
||||||
"ButtonLibrary": "Bibliotek",
|
"ButtonLibrary": "Bibliotek",
|
||||||
"ButtonLogout": "Log ud",
|
"ButtonLogout": "Log ud",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "Match alle forfattere",
|
"ButtonMatchAllAuthors": "Match alle forfattere",
|
||||||
"ButtonMatchBooks": "Match bøger",
|
"ButtonMatchBooks": "Match bøger",
|
||||||
"ButtonNevermind": "Glem det",
|
"ButtonNevermind": "Glem det",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Next Chapter",
|
||||||
"ButtonOk": "OK",
|
"ButtonOk": "OK",
|
||||||
"ButtonOpenFeed": "Åbn feed",
|
"ButtonOpenFeed": "Åbn feed",
|
||||||
"ButtonOpenManager": "Åbn manager",
|
"ButtonOpenManager": "Åbn manager",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "Afspil",
|
"ButtonPlay": "Afspil",
|
||||||
"ButtonPlaying": "Afspiller",
|
"ButtonPlaying": "Afspiller",
|
||||||
"ButtonPlaylists": "Afspilningslister",
|
"ButtonPlaylists": "Afspilningslister",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Previous Chapter",
|
||||||
"ButtonPurgeAllCache": "Ryd al cache",
|
"ButtonPurgeAllCache": "Ryd al cache",
|
||||||
"ButtonPurgeItemsCache": "Ryd elementcache",
|
"ButtonPurgeItemsCache": "Ryd elementcache",
|
||||||
"ButtonPurgeMediaProgress": "Ryd Medieforløb",
|
"ButtonPurgeMediaProgress": "Ryd Medieforløb",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "Fjern fra kø",
|
"ButtonQueueRemoveItem": "Fjern fra kø",
|
||||||
"ButtonQuickMatch": "Hurtig Match",
|
"ButtonQuickMatch": "Hurtig Match",
|
||||||
"ButtonRead": "Læs",
|
"ButtonRead": "Læs",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "Fjern",
|
"ButtonRemove": "Fjern",
|
||||||
"ButtonRemoveAll": "Fjern Alle",
|
"ButtonRemoveAll": "Fjern Alle",
|
||||||
"ButtonRemoveAllLibraryItems": "Fjern Alle Bibliotekselementer",
|
"ButtonRemoveAllLibraryItems": "Fjern Alle Bibliotekselementer",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "Vælg Mappen Sti",
|
"ButtonSelectFolderPath": "Vælg Mappen Sti",
|
||||||
"ButtonSeries": "Serie",
|
"ButtonSeries": "Serie",
|
||||||
"ButtonSetChaptersFromTracks": "Sæt kapitler fra spor",
|
"ButtonSetChaptersFromTracks": "Sæt kapitler fra spor",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "Skift Tider",
|
"ButtonShiftTimes": "Skift Tider",
|
||||||
"ButtonShow": "Vis",
|
"ButtonShow": "Vis",
|
||||||
"ButtonStartM4BEncode": "Start M4B Kode",
|
"ButtonStartM4BEncode": "Start M4B Kode",
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"ButtonUserEdit": "Rediger bruger {0}",
|
"ButtonUserEdit": "Rediger bruger {0}",
|
||||||
"ButtonViewAll": "Vis Alle",
|
"ButtonViewAll": "Vis Alle",
|
||||||
"ButtonYes": "Ja",
|
"ButtonYes": "Ja",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error fetching metadata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Could not fetch metadata - try updating title and/or author",
|
||||||
|
"ErrorUploadLacksTitle": "Must have a title",
|
||||||
"HeaderAccount": "Konto",
|
"HeaderAccount": "Konto",
|
||||||
"HeaderAdvanced": "Avanceret",
|
"HeaderAdvanced": "Avanceret",
|
||||||
"HeaderAppriseNotificationSettings": "Apprise Notifikationsindstillinger",
|
"HeaderAppriseNotificationSettings": "Apprise Notifikationsindstillinger",
|
||||||
@@ -101,6 +113,7 @@
|
|||||||
"HeaderCollectionItems": "Samlingselementer",
|
"HeaderCollectionItems": "Samlingselementer",
|
||||||
"HeaderCover": "Omslag",
|
"HeaderCover": "Omslag",
|
||||||
"HeaderCurrentDownloads": "Nuværende Downloads",
|
"HeaderCurrentDownloads": "Nuværende Downloads",
|
||||||
|
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
|
||||||
"HeaderDetails": "Detaljer",
|
"HeaderDetails": "Detaljer",
|
||||||
"HeaderDownloadQueue": "Download Kø",
|
"HeaderDownloadQueue": "Download Kø",
|
||||||
"HeaderEbookFiles": "E-bogsfiler",
|
"HeaderEbookFiles": "E-bogsfiler",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Opdater Detaljer",
|
"HeaderUpdateDetails": "Opdater Detaljer",
|
||||||
"HeaderUpdateLibrary": "Opdater Bibliotek",
|
"HeaderUpdateLibrary": "Opdater Bibliotek",
|
||||||
"HeaderUsers": "Brugere",
|
"HeaderUsers": "Brugere",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Dine Statistikker",
|
"HeaderYourStats": "Dine Statistikker",
|
||||||
"LabelAbridged": "Abridged",
|
"LabelAbridged": "Abridged",
|
||||||
"LabelAccountType": "Kontotype",
|
"LabelAccountType": "Kontotype",
|
||||||
@@ -196,6 +210,8 @@
|
|||||||
"LabelAuthorLastFirst": "Forfatter (Efternavn, Fornavn)",
|
"LabelAuthorLastFirst": "Forfatter (Efternavn, Fornavn)",
|
||||||
"LabelAuthors": "Forfattere",
|
"LabelAuthors": "Forfattere",
|
||||||
"LabelAutoDownloadEpisodes": "Auto Download Episoder",
|
"LabelAutoDownloadEpisodes": "Auto Download Episoder",
|
||||||
|
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Fetches metadata for title, author, and series to streamline uploading. Additional metadata may have to be matched after upload.",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoLaunch": "Auto Launch",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoRegister": "Auto Register",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Eksempel",
|
"LabelExample": "Eksempel",
|
||||||
"LabelExplicit": "Eksplisit",
|
"LabelExplicit": "Eksplisit",
|
||||||
"LabelFeedURL": "Feed URL",
|
"LabelFeedURL": "Feed URL",
|
||||||
|
"LabelFetchingMetadata": "Fetching Metadata",
|
||||||
"LabelFile": "Fil",
|
"LabelFile": "Fil",
|
||||||
"LabelFileBirthtime": "Fødselstidspunkt for fil",
|
"LabelFileBirthtime": "Fødselstidspunkt for fil",
|
||||||
"LabelFileModified": "Fil ændret",
|
"LabelFileModified": "Fil ændret",
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
"LabelFinished": "Færdig",
|
"LabelFinished": "Færdig",
|
||||||
"LabelFolder": "Mappe",
|
"LabelFolder": "Mappe",
|
||||||
"LabelFolders": "Mapper",
|
"LabelFolders": "Mapper",
|
||||||
|
"LabelFontBold": "Bold",
|
||||||
"LabelFontFamily": "Fontfamilie",
|
"LabelFontFamily": "Fontfamilie",
|
||||||
|
"LabelFontItalic": "Italic",
|
||||||
"LabelFontScale": "Skriftstørrelse",
|
"LabelFontScale": "Skriftstørrelse",
|
||||||
|
"LabelFontStrikethrough": "Strikethrough",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Genrer",
|
"LabelGenres": "Genrer",
|
||||||
@@ -336,7 +356,11 @@
|
|||||||
"LabelMetaTags": "Meta-tags",
|
"LabelMetaTags": "Meta-tags",
|
||||||
"LabelMinute": "Minut",
|
"LabelMinute": "Minut",
|
||||||
"LabelMissing": "Mangler",
|
"LabelMissing": "Mangler",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Manglende dele",
|
"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.",
|
||||||
"LabelMore": "Mere",
|
"LabelMore": "Mere",
|
||||||
"LabelMoreInfo": "Mere info",
|
"LabelMoreInfo": "Mere info",
|
||||||
"LabelName": "Navn",
|
"LabelName": "Navn",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Kan downloade",
|
"LabelPermissionsDownload": "Kan downloade",
|
||||||
"LabelPermissionsUpdate": "Kan opdatere",
|
"LabelPermissionsUpdate": "Kan opdatere",
|
||||||
"LabelPermissionsUpload": "Kan uploade",
|
"LabelPermissionsUpload": "Kan uploade",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Foto sti/URL",
|
"LabelPhotoPathURL": "Foto sti/URL",
|
||||||
"LabelPlaylists": "Afspilningslister",
|
"LabelPlaylists": "Afspilningslister",
|
||||||
"LabelPlayMethod": "Afspilningsmetode",
|
"LabelPlayMethod": "Afspilningsmetode",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "Podcast søgeområde",
|
||||||
"LabelPodcastType": "Podcast type",
|
"LabelPodcastType": "Podcast type",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Præfikser der skal ignoreres (skal ikke skelne mellem store og små bogstaver)",
|
"LabelPrefixesToIgnore": "Præfikser der skal ignoreres (skal ikke skelne mellem store og små bogstaver)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Senest tilføjet",
|
"LabelRecentlyAdded": "Senest tilføjet",
|
||||||
"LabelRecentSeries": "Seneste serie",
|
"LabelRecentSeries": "Seneste serie",
|
||||||
"LabelRecommended": "Anbefalet",
|
"LabelRecommended": "Anbefalet",
|
||||||
|
"LabelRedo": "Redo",
|
||||||
"LabelRegion": "Region",
|
"LabelRegion": "Region",
|
||||||
"LabelReleaseDate": "Udgivelsesdato",
|
"LabelReleaseDate": "Udgivelsesdato",
|
||||||
"LabelRemoveCover": "Fjern omslag",
|
"LabelRemoveCover": "Fjern omslag",
|
||||||
|
"LabelRowsPerPage": "Rows per page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Brugerdefineret ejerens e-mail",
|
"LabelRSSFeedCustomOwnerEmail": "Brugerdefineret ejerens e-mail",
|
||||||
"LabelRSSFeedCustomOwnerName": "Brugerdefineret ejerens navn",
|
"LabelRSSFeedCustomOwnerName": "Brugerdefineret ejerens navn",
|
||||||
"LabelRSSFeedOpen": "Åben RSS-feed",
|
"LabelRSSFeedOpen": "Åben RSS-feed",
|
||||||
@@ -416,6 +444,7 @@
|
|||||||
"LabelSeries": "Serie",
|
"LabelSeries": "Serie",
|
||||||
"LabelSeriesName": "Serienavn",
|
"LabelSeriesName": "Serienavn",
|
||||||
"LabelSeriesProgress": "Seriefremskridt",
|
"LabelSeriesProgress": "Seriefremskridt",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Indstil som primær",
|
"LabelSetEbookAsPrimary": "Indstil som primær",
|
||||||
"LabelSetEbookAsSupplementary": "Indstil som supplerende",
|
"LabelSetEbookAsSupplementary": "Indstil som supplerende",
|
||||||
"LabelSettingsAudiobooksOnly": "Kun lydbøger",
|
"LabelSettingsAudiobooksOnly": "Kun lydbøger",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Serier med en enkelt bog vil blive skjult fra serie-siden og hjemmesidehylder.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Serier med en enkelt bog vil blive skjult fra serie-siden og hjemmesidehylder.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Brug bogreolvisning på startside",
|
"LabelSettingsHomePageBookshelfView": "Brug bogreolvisning på startside",
|
||||||
"LabelSettingsLibraryBookshelfView": "Brug bogreolvisning i biblioteket",
|
"LabelSettingsLibraryBookshelfView": "Brug bogreolvisning i biblioteket",
|
||||||
|
"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": "Fortolk undertekster",
|
"LabelSettingsParseSubtitles": "Fortolk undertekster",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Udtræk undertekster fra lydbogsmappenavne.<br>Undertitler skal adskilles af \" - \"<br>f.eks. \"Bogtitel - En undertitel her\" har undertitlen \"En undertitel her\"",
|
"LabelSettingsParseSubtitlesHelp": "Udtræk undertekster fra lydbogsmappenavne.<br>Undertitler skal adskilles af \" - \"<br>f.eks. \"Bogtitel - En undertitel her\" har undertitlen \"En undertitel her\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Foretræk matchede metadata",
|
"LabelSettingsPreferMatchedMetadata": "Foretræk matchede metadata",
|
||||||
@@ -482,6 +513,10 @@
|
|||||||
"LabelTagsAccessibleToUser": "Mærker tilgængelige for bruger",
|
"LabelTagsAccessibleToUser": "Mærker tilgængelige for bruger",
|
||||||
"LabelTagsNotAccessibleToUser": "Mærker ikke tilgængelige for bruger",
|
"LabelTagsNotAccessibleToUser": "Mærker ikke tilgængelige for bruger",
|
||||||
"LabelTasks": "Kører opgaver",
|
"LabelTasks": "Kører opgaver",
|
||||||
|
"LabelTextEditorBulletedList": "Bulleted list",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numbered list",
|
||||||
|
"LabelTextEditorUnlink": "Unlink",
|
||||||
"LabelTheme": "Tema",
|
"LabelTheme": "Tema",
|
||||||
"LabelThemeDark": "Mørk",
|
"LabelThemeDark": "Mørk",
|
||||||
"LabelThemeLight": "Lys",
|
"LabelThemeLight": "Lys",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Enkeltspors",
|
"LabelTracksSingleTrack": "Enkeltspors",
|
||||||
"LabelType": "Type",
|
"LabelType": "Type",
|
||||||
"LabelUnabridged": "Uforkortet",
|
"LabelUnabridged": "Uforkortet",
|
||||||
|
"LabelUndo": "Undo",
|
||||||
"LabelUnknown": "Ukendt",
|
"LabelUnknown": "Ukendt",
|
||||||
"LabelUpdateCover": "Opdater omslag",
|
"LabelUpdateCover": "Opdater omslag",
|
||||||
"LabelUpdateCoverHelp": "Tillad overskrivning af eksisterende omslag for de valgte bøger, når der findes en match",
|
"LabelUpdateCoverHelp": "Tillad overskrivning af eksisterende omslag for de valgte bøger, når der findes en match",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Tillad overskrivning af eksisterende detaljer for de valgte bøger, når der findes en match",
|
"LabelUpdateDetailsHelp": "Tillad overskrivning af eksisterende detaljer for de valgte bøger, når der findes en match",
|
||||||
"LabelUploaderDragAndDrop": "Træk og slip filer eller mapper",
|
"LabelUploaderDragAndDrop": "Træk og slip filer eller mapper",
|
||||||
"LabelUploaderDropFiles": "Smid filer",
|
"LabelUploaderDropFiles": "Smid filer",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||||
"LabelUseChapterTrack": "Brug kapitel-spor",
|
"LabelUseChapterTrack": "Brug kapitel-spor",
|
||||||
"LabelUseFullTrack": "Brug fuldt spor",
|
"LabelUseFullTrack": "Brug fuldt spor",
|
||||||
"LabelUser": "Bruger",
|
"LabelUser": "Bruger",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "Se afspilningskø",
|
"LabelViewQueue": "Se afspilningskø",
|
||||||
"LabelVolume": "Volumen",
|
"LabelVolume": "Volumen",
|
||||||
"LabelWeekdaysToRun": "Ugedage til kørsel",
|
"LabelWeekdaysToRun": "Ugedage til kørsel",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Din lydbogsvarighed",
|
"LabelYourAudiobookDuration": "Din lydbogsvarighed",
|
||||||
"LabelYourBookmarks": "Dine bogmærker",
|
"LabelYourBookmarks": "Dine bogmærker",
|
||||||
"LabelYourPlaylists": "Dine spillelister",
|
"LabelYourPlaylists": "Dine spillelister",
|
||||||
@@ -562,6 +601,7 @@
|
|||||||
"MessageConfirmRemoveCollection": "Er du sikker på, at du vil fjerne samlingen \"{0}\"?",
|
"MessageConfirmRemoveCollection": "Er du sikker på, at du vil fjerne samlingen \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "Er du sikker på, at du vil fjerne episoden \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Er du sikker på, at du vil fjerne episoden \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "Er du sikker på, at du vil fjerne {0} episoder?",
|
"MessageConfirmRemoveEpisodes": "Er du sikker på, at du vil fjerne {0} episoder?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Er du sikker på, at du vil fjerne fortælleren \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Er du sikker på, at du vil fjerne fortælleren \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "Er du sikker på, at du vil fjerne din spilleliste \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Er du sikker på, at du vil fjerne din spilleliste \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "Er du sikker på, at du vil omdøbe genre \"{0}\" til \"{1}\" for alle elementer?",
|
"MessageConfirmRenameGenre": "Er du sikker på, at du vil omdøbe genre \"{0}\" til \"{1}\" for alle elementer?",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Er du sikker på, at du vil gendanne sikkerhedskopien oprettet den",
|
"MessageRestoreBackupConfirm": "Er du sikker på, at du vil gendanne sikkerhedskopien oprettet den",
|
||||||
"MessageRestoreBackupWarning": "Gendannelse af en sikkerhedskopi vil overskrive hele databasen, som er placeret på /config, og omslagsbilleder i /metadata/items & /metadata/authors.<br /><br />Sikkerhedskopier ændrer ikke nogen filer i dine biblioteksmapper. Hvis du har aktiveret serverindstillinger for at gemme omslagskunst og metadata i dine biblioteksmapper, sikkerhedskopieres eller overskrives disse ikke.<br /><br />Alle klienter, der bruger din server, opdateres automatisk.",
|
"MessageRestoreBackupWarning": "Gendannelse af en sikkerhedskopi vil overskrive hele databasen, som er placeret på /config, og omslagsbilleder i /metadata/items & /metadata/authors.<br /><br />Sikkerhedskopier ændrer ikke nogen filer i dine biblioteksmapper. Hvis du har aktiveret serverindstillinger for at gemme omslagskunst og metadata i dine biblioteksmapper, sikkerhedskopieres eller overskrives disse ikke.<br /><br />Alle klienter, der bruger din server, opdateres automatisk.",
|
||||||
"MessageSearchResultsFor": "Søgeresultater for",
|
"MessageSearchResultsFor": "Søgeresultater for",
|
||||||
|
"MessageSelected": "{0} selected",
|
||||||
"MessageServerCouldNotBeReached": "Serveren kunne ikke nås",
|
"MessageServerCouldNotBeReached": "Serveren kunne ikke nås",
|
||||||
"MessageSetChaptersFromTracksDescription": "Indstil kapitler ved at bruge hver lydfil som et kapitel og kapiteloverskrift som lydfilnavn",
|
"MessageSetChaptersFromTracksDescription": "Indstil kapitler ved at bruge hver lydfil som et kapitel og kapiteloverskrift som lydfilnavn",
|
||||||
"MessageStartPlaybackAtTime": "Start afspilning for \"{0}\" kl. {1}?",
|
"MessageStartPlaybackAtTime": "Start afspilning for \"{0}\" kl. {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "Root-brugeren er den eneste bruger, der kan have en tom adgangskode",
|
"NoteChangeRootPassword": "Root-brugeren er den eneste bruger, der kan have en tom adgangskode",
|
||||||
"NoteChapterEditorTimes": "Bemærk: Første kapitel starttidspunkt skal forblive kl. 0:00, og det sidste kapitel starttidspunkt må ikke overstige denne lydbogs varighed.",
|
"NoteChapterEditorTimes": "Bemærk: Første kapitel starttidspunkt skal forblive kl. 0:00, og det sidste kapitel starttidspunkt må ikke overstige denne lydbogs varighed.",
|
||||||
"NoteFolderPicker": "Bemærk: Mapper, der allerede er mappet, vises ikke",
|
"NoteFolderPicker": "Bemærk: Mapper, der allerede er mappet, vises ikke",
|
||||||
"NoteFolderPickerDebian": "Bemærk: Mappicker for Debian-installationen er ikke fuldt implementeret. Du bør indtaste stien til dit bibliotek direkte.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Advarsel: De fleste podcast-apps kræver, at RSS-feedets URL bruger HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Advarsel: De fleste podcast-apps kræver, at RSS-feedets URL bruger HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Advarsel: En eller flere af dine episoder har ikke en Pub Date. Nogle podcast-apps kræver dette.",
|
"NoteRSSFeedPodcastAppsPubDate": "Advarsel: En eller flere af dine episoder har ikke en Pub Date. Nogle podcast-apps kræver dette.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Mapper med mediefiler håndteres som separate bibliotekselementer.",
|
"NoteUploaderFoldersWithMediaFiles": "Mapper med mediefiler håndteres som separate bibliotekselementer.",
|
||||||
|
|||||||
+155
-115
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"ButtonAdd": "Hinzufügen",
|
"ButtonAdd": "Hinzufügen",
|
||||||
"ButtonAddChapters": "Kapitel hinzufügen",
|
"ButtonAddChapters": "Kapitel hinzufügen",
|
||||||
"ButtonAddDevice": "Add Device",
|
"ButtonAddDevice": "Gerät hinzufügen",
|
||||||
"ButtonAddLibrary": "Add Library",
|
"ButtonAddLibrary": "Bibliothek hinzufügen",
|
||||||
"ButtonAddPodcasts": "Podcasts hinzufügen",
|
"ButtonAddPodcasts": "Podcasts hinzufügen",
|
||||||
"ButtonAddUser": "Add User",
|
"ButtonAddUser": "Benutzer hinzufügen",
|
||||||
"ButtonAddYourFirstLibrary": "Erstelle deine erste Bibliothek",
|
"ButtonAddYourFirstLibrary": "Erstelle deine erste Bibliothek",
|
||||||
"ButtonApply": "Übernehmen",
|
"ButtonApply": "Übernehmen",
|
||||||
"ButtonApplyChapters": "Kapitel anwenden",
|
"ButtonApplyChapters": "Kapitel anwenden",
|
||||||
"ButtonAuthors": "Autoren",
|
"ButtonAuthors": "Autoren",
|
||||||
"ButtonBrowseForFolder": "Ordnersuche",
|
"ButtonBrowseForFolder": "Ordnersuche",
|
||||||
"ButtonCancel": "Abbrechen",
|
"ButtonCancel": "Abbrechen",
|
||||||
"ButtonCancelEncode": "Abbruch der Verschlüsselung",
|
"ButtonCancelEncode": "Codierung abbrechen",
|
||||||
"ButtonChangeRootPassword": "Hauptpasswort ändern",
|
"ButtonChangeRootPassword": "Hauptpasswort ändern",
|
||||||
"ButtonCheckAndDownloadNewEpisodes": "Überprüfe & lade neue Episoden herunter",
|
"ButtonCheckAndDownloadNewEpisodes": "Überprüfe & lade neue Episoden herunter",
|
||||||
"ButtonChooseAFolder": "Wähle einen Ordner",
|
"ButtonChooseAFolder": "Wähle einen Ordner",
|
||||||
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Ausblenden",
|
"ButtonHide": "Ausblenden",
|
||||||
"ButtonHome": "Startseite",
|
"ButtonHome": "Startseite",
|
||||||
"ButtonIssues": "Probleme",
|
"ButtonIssues": "Probleme",
|
||||||
|
"ButtonJumpBackward": "Zurück springen",
|
||||||
|
"ButtonJumpForward": "Vorwärts springen",
|
||||||
"ButtonLatest": "Neuste",
|
"ButtonLatest": "Neuste",
|
||||||
"ButtonLibrary": "Bibliothek",
|
"ButtonLibrary": "Bibliothek",
|
||||||
"ButtonLogout": "Abmelden",
|
"ButtonLogout": "Abmelden",
|
||||||
@@ -41,28 +43,34 @@
|
|||||||
"ButtonMatchAllAuthors": "Online Metadaten-Abgleich (alle Autoren)",
|
"ButtonMatchAllAuthors": "Online Metadaten-Abgleich (alle Autoren)",
|
||||||
"ButtonMatchBooks": "Online Metadaten-Abgleich (alle Medien)",
|
"ButtonMatchBooks": "Online Metadaten-Abgleich (alle Medien)",
|
||||||
"ButtonNevermind": "Abbrechen",
|
"ButtonNevermind": "Abbrechen",
|
||||||
|
"ButtonNext": "Vor",
|
||||||
|
"ButtonNextChapter": "Nächstes Kapitel",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Feed öffnen",
|
"ButtonOpenFeed": "Feed öffnen",
|
||||||
"ButtonOpenManager": "Manager öffnen",
|
"ButtonOpenManager": "Manager öffnen",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "Abspielen",
|
"ButtonPlay": "Abspielen",
|
||||||
"ButtonPlaying": "Spielt",
|
"ButtonPlaying": "Spielt",
|
||||||
"ButtonPlaylists": "Wiedergabelisten",
|
"ButtonPlaylists": "Wiedergabelisten",
|
||||||
"ButtonPurgeAllCache": "Lösche alle Zwischenspeicher",
|
"ButtonPrevious": "Zurück",
|
||||||
"ButtonPurgeItemsCache": "Lösche Medien-Zwischenspeicher",
|
"ButtonPreviousChapter": "Vorheriges Kapitel",
|
||||||
|
"ButtonPurgeAllCache": "Cache leeren",
|
||||||
|
"ButtonPurgeItemsCache": "Lösche Medien-Cache",
|
||||||
"ButtonPurgeMediaProgress": "Lösche Hörfortschritte",
|
"ButtonPurgeMediaProgress": "Lösche Hörfortschritte",
|
||||||
"ButtonQueueAddItem": "Zur Warteschlange hinzufügen",
|
"ButtonQueueAddItem": "Zur Warteschlange hinzufügen",
|
||||||
"ButtonQueueRemoveItem": "Aus der Warteschlange entfernen",
|
"ButtonQueueRemoveItem": "Aus der Warteschlange entfernen",
|
||||||
"ButtonQuickMatch": "Schnellabgleich",
|
"ButtonQuickMatch": "Schnellabgleich",
|
||||||
"ButtonRead": "Lese",
|
"ButtonRead": "Lesen",
|
||||||
|
"ButtonRefresh": "Neu Laden",
|
||||||
"ButtonRemove": "Löschen",
|
"ButtonRemove": "Löschen",
|
||||||
"ButtonRemoveAll": "Alles löschen",
|
"ButtonRemoveAll": "Alles löschen",
|
||||||
"ButtonRemoveAllLibraryItems": "Lösche alle Bibliothekseinträge",
|
"ButtonRemoveAllLibraryItems": "Lösche alle Bibliothekseinträge",
|
||||||
"ButtonRemoveFromContinueListening": "Lösche den Eintrag aus der Fortsetzungsliste",
|
"ButtonRemoveFromContinueListening": "Lösche den Eintrag aus der Fortsetzungsliste",
|
||||||
"ButtonRemoveFromContinueReading": "Remove from Continue Reading",
|
"ButtonRemoveFromContinueReading": "Lösche die Serie aus der Lesefortsetzungsliste",
|
||||||
"ButtonRemoveSeriesFromContinueSeries": "Lösche die Serie aus der Serienfortsetzungsliste",
|
"ButtonRemoveSeriesFromContinueSeries": "Lösche die Serie aus der Serienfortsetzungsliste",
|
||||||
"ButtonReScan": "Neu scannen",
|
"ButtonReScan": "Neu scannen",
|
||||||
"ButtonReset": "Zurücksetzen",
|
"ButtonReset": "Zurücksetzen",
|
||||||
"ButtonResetToDefault": "Reset to default",
|
"ButtonResetToDefault": "Zurücksetzen auf Standard",
|
||||||
"ButtonRestore": "Wiederherstellen",
|
"ButtonRestore": "Wiederherstellen",
|
||||||
"ButtonSave": "Speichern",
|
"ButtonSave": "Speichern",
|
||||||
"ButtonSaveAndClose": "Speichern & Schließen",
|
"ButtonSaveAndClose": "Speichern & Schließen",
|
||||||
@@ -70,9 +78,10 @@
|
|||||||
"ButtonScan": "Partial-Scan (nur geänderte/neue Medien)",
|
"ButtonScan": "Partial-Scan (nur geänderte/neue Medien)",
|
||||||
"ButtonScanLibrary": "Bibliothek scannen",
|
"ButtonScanLibrary": "Bibliothek scannen",
|
||||||
"ButtonSearch": "Suchen",
|
"ButtonSearch": "Suchen",
|
||||||
"ButtonSelectFolderPath": "Auswahl Ordnerpfad",
|
"ButtonSelectFolderPath": "Ordnerpfad auswählen",
|
||||||
"ButtonSeries": "Serien",
|
"ButtonSeries": "Serien",
|
||||||
"ButtonSetChaptersFromTracks": "Kapitelerstellung aus Audiodateien",
|
"ButtonSetChaptersFromTracks": "Kapitelerstellung aus Audiodateien",
|
||||||
|
"ButtonShare": "Teilen",
|
||||||
"ButtonShiftTimes": "Zeitverschiebung",
|
"ButtonShiftTimes": "Zeitverschiebung",
|
||||||
"ButtonShow": "Anzeigen",
|
"ButtonShow": "Anzeigen",
|
||||||
"ButtonStartM4BEncode": "M4B-Kodierung starten",
|
"ButtonStartM4BEncode": "M4B-Kodierung starten",
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"ButtonUserEdit": "Benutzer {0} bearbeiten",
|
"ButtonUserEdit": "Benutzer {0} bearbeiten",
|
||||||
"ButtonViewAll": "Alles anzeigen",
|
"ButtonViewAll": "Alles anzeigen",
|
||||||
"ButtonYes": "Ja",
|
"ButtonYes": "Ja",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Fehler beim Abrufen der Metadaten",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Metadaten konnten nicht abgerufen werden. Versuche den Titel und oder den Autor zu aktualisieren",
|
||||||
|
"ErrorUploadLacksTitle": "Es muss ein Titel eingegeben werden",
|
||||||
"HeaderAccount": "Konto",
|
"HeaderAccount": "Konto",
|
||||||
"HeaderAdvanced": "Erweitert",
|
"HeaderAdvanced": "Erweitert",
|
||||||
"HeaderAppriseNotificationSettings": "Apprise Benachrichtigungseinstellungen",
|
"HeaderAppriseNotificationSettings": "Apprise Benachrichtigungseinstellungen",
|
||||||
@@ -101,21 +113,22 @@
|
|||||||
"HeaderCollectionItems": "Sammlungseinträge",
|
"HeaderCollectionItems": "Sammlungseinträge",
|
||||||
"HeaderCover": "Titelbild",
|
"HeaderCover": "Titelbild",
|
||||||
"HeaderCurrentDownloads": "Aktuelle Downloads",
|
"HeaderCurrentDownloads": "Aktuelle Downloads",
|
||||||
|
"HeaderCustomMetadataProviders": "Benutzerdefinierte Metadata Anbieter",
|
||||||
"HeaderDetails": "Details",
|
"HeaderDetails": "Details",
|
||||||
"HeaderDownloadQueue": "Download Warteschlange",
|
"HeaderDownloadQueue": "Download Warteschlange",
|
||||||
"HeaderEbookFiles": "E-Book Dateien",
|
"HeaderEbookFiles": "E-Book Dateien",
|
||||||
"HeaderEmail": "Email",
|
"HeaderEmail": "Email",
|
||||||
"HeaderEmailSettings": "Email Einstellungen",
|
"HeaderEmailSettings": "Email Einstellungen",
|
||||||
"HeaderEpisodes": "Episoden",
|
"HeaderEpisodes": "Episoden",
|
||||||
"HeaderEreaderDevices": "Ereader Geräte",
|
"HeaderEreaderDevices": "E-Reader Geräte",
|
||||||
"HeaderEreaderSettings": "Ereader Einstellungen",
|
"HeaderEreaderSettings": "E-Reader Einstellungen",
|
||||||
"HeaderFiles": "Dateien",
|
"HeaderFiles": "Dateien",
|
||||||
"HeaderFindChapters": "Kapitel suchen",
|
"HeaderFindChapters": "Kapitel suchen",
|
||||||
"HeaderIgnoredFiles": "Ignorierte Dateien",
|
"HeaderIgnoredFiles": "Ignorierte Dateien",
|
||||||
"HeaderItemFiles": "Medien-Dateien",
|
"HeaderItemFiles": "Medien-Dateien",
|
||||||
"HeaderItemMetadataUtils": "Metadaten",
|
"HeaderItemMetadataUtils": "Metadaten",
|
||||||
"HeaderLastListeningSession": "Letzte Hörsitzung",
|
"HeaderLastListeningSession": "Letzte Hörsitzung",
|
||||||
"HeaderLatestEpisodes": "Letzte Episoden",
|
"HeaderLatestEpisodes": "Neueste Episoden",
|
||||||
"HeaderLibraries": "Bibliotheken",
|
"HeaderLibraries": "Bibliotheken",
|
||||||
"HeaderLibraryFiles": "Alle Dateien",
|
"HeaderLibraryFiles": "Alle Dateien",
|
||||||
"HeaderLibraryStats": "Bibliotheksstatistiken",
|
"HeaderLibraryStats": "Bibliotheksstatistiken",
|
||||||
@@ -127,7 +140,7 @@
|
|||||||
"HeaderManageTags": "Tags verwalten",
|
"HeaderManageTags": "Tags verwalten",
|
||||||
"HeaderMapDetails": "Stapelverarbeitung",
|
"HeaderMapDetails": "Stapelverarbeitung",
|
||||||
"HeaderMatch": "Metadaten",
|
"HeaderMatch": "Metadaten",
|
||||||
"HeaderMetadataOrderOfPrecedence": "Metadata order of precedence",
|
"HeaderMetadataOrderOfPrecedence": "Metadaten Rangfolge",
|
||||||
"HeaderMetadataToEmbed": "Einzubettende Metadaten",
|
"HeaderMetadataToEmbed": "Einzubettende Metadaten",
|
||||||
"HeaderNewAccount": "Neues Konto",
|
"HeaderNewAccount": "Neues Konto",
|
||||||
"HeaderNewLibrary": "Neue Bibliothek",
|
"HeaderNewLibrary": "Neue Bibliothek",
|
||||||
@@ -135,9 +148,9 @@
|
|||||||
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentifizierung",
|
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentifizierung",
|
||||||
"HeaderOpenRSSFeed": "RSS-Feed öffnen",
|
"HeaderOpenRSSFeed": "RSS-Feed öffnen",
|
||||||
"HeaderOtherFiles": "Sonstige Dateien",
|
"HeaderOtherFiles": "Sonstige Dateien",
|
||||||
"HeaderPasswordAuthentication": "Password Authentifizierung",
|
"HeaderPasswordAuthentication": "Passwort Authentifizierung",
|
||||||
"HeaderPermissions": "Berechtigungen",
|
"HeaderPermissions": "Berechtigungen",
|
||||||
"HeaderPlayerQueue": "Spieler Warteschlange",
|
"HeaderPlayerQueue": "Player Warteschlange",
|
||||||
"HeaderPlaylist": "Wiedergabeliste",
|
"HeaderPlaylist": "Wiedergabeliste",
|
||||||
"HeaderPlaylistItems": "Einträge in der Wiedergabeliste",
|
"HeaderPlaylistItems": "Einträge in der Wiedergabeliste",
|
||||||
"HeaderPodcastsToAdd": "Podcasts zum Hinzufügen",
|
"HeaderPodcastsToAdd": "Podcasts zum Hinzufügen",
|
||||||
@@ -146,7 +159,7 @@
|
|||||||
"HeaderRemoveEpisodes": "Lösche {0} Episoden",
|
"HeaderRemoveEpisodes": "Lösche {0} Episoden",
|
||||||
"HeaderRSSFeedGeneral": "RSS Details",
|
"HeaderRSSFeedGeneral": "RSS Details",
|
||||||
"HeaderRSSFeedIsOpen": "RSS-Feed ist geöffnet",
|
"HeaderRSSFeedIsOpen": "RSS-Feed ist geöffnet",
|
||||||
"HeaderRSSFeeds": "RSS Feeds",
|
"HeaderRSSFeeds": "RSS-Feeds",
|
||||||
"HeaderSavedMediaProgress": "Gespeicherte Hörfortschritte",
|
"HeaderSavedMediaProgress": "Gespeicherte Hörfortschritte",
|
||||||
"HeaderSchedule": "Zeitplan",
|
"HeaderSchedule": "Zeitplan",
|
||||||
"HeaderScheduleLibraryScans": "Automatische Bibliotheksscans",
|
"HeaderScheduleLibraryScans": "Automatische Bibliotheksscans",
|
||||||
@@ -157,7 +170,7 @@
|
|||||||
"HeaderSettingsExperimental": "Experimentelle Funktionen",
|
"HeaderSettingsExperimental": "Experimentelle Funktionen",
|
||||||
"HeaderSettingsGeneral": "Allgemein",
|
"HeaderSettingsGeneral": "Allgemein",
|
||||||
"HeaderSettingsScanner": "Scanner",
|
"HeaderSettingsScanner": "Scanner",
|
||||||
"HeaderSleepTimer": "Einschlaf-Timer",
|
"HeaderSleepTimer": "Sleep-Timer",
|
||||||
"HeaderStatsLargestItems": "Größte Medien",
|
"HeaderStatsLargestItems": "Größte Medien",
|
||||||
"HeaderStatsLongestItems": "Längste Medien (h)",
|
"HeaderStatsLongestItems": "Längste Medien (h)",
|
||||||
"HeaderStatsMinutesListeningChart": "Hörminuten (letzte 7 Tage)",
|
"HeaderStatsMinutesListeningChart": "Hörminuten (letzte 7 Tage)",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Details aktualisieren",
|
"HeaderUpdateDetails": "Details aktualisieren",
|
||||||
"HeaderUpdateLibrary": "Bibliothek aktualisieren",
|
"HeaderUpdateLibrary": "Bibliothek aktualisieren",
|
||||||
"HeaderUsers": "Benutzer",
|
"HeaderUsers": "Benutzer",
|
||||||
|
"HeaderYearReview": "Jahr {0} in Übersicht",
|
||||||
"HeaderYourStats": "Eigene Statistiken",
|
"HeaderYourStats": "Eigene Statistiken",
|
||||||
"LabelAbridged": "Gekürzt",
|
"LabelAbridged": "Gekürzt",
|
||||||
"LabelAccountType": "Kontoart",
|
"LabelAccountType": "Kontoart",
|
||||||
@@ -188,18 +202,20 @@
|
|||||||
"LabelAll": "Alle",
|
"LabelAll": "Alle",
|
||||||
"LabelAllUsers": "Alle Benutzer",
|
"LabelAllUsers": "Alle Benutzer",
|
||||||
"LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen",
|
"LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen",
|
||||||
"LabelAllUsersIncludingGuests": "All Benutzer und Gäste",
|
"LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste",
|
||||||
"LabelAlreadyInYourLibrary": "In der Bibliothek vorhanden",
|
"LabelAlreadyInYourLibrary": "Bereits in der Bibliothek",
|
||||||
"LabelAppend": "Anhängen",
|
"LabelAppend": "Anhängen",
|
||||||
"LabelAuthor": "Autor",
|
"LabelAuthor": "Autor",
|
||||||
"LabelAuthorFirstLast": "Autor (Vorname Nachname)",
|
"LabelAuthorFirstLast": "Autor (Vorname Nachname)",
|
||||||
"LabelAuthorLastFirst": "Autor (Nachname, Vorname)",
|
"LabelAuthorLastFirst": "Autor (Nachname, Vorname)",
|
||||||
"LabelAuthors": "Autoren",
|
"LabelAuthors": "Autoren",
|
||||||
"LabelAutoDownloadEpisodes": "Episoden automatisch herunterladen",
|
"LabelAutoDownloadEpisodes": "Episoden automatisch herunterladen",
|
||||||
|
"LabelAutoFetchMetadata": "Automatisches Abholen der Metadaten",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Abholen der Metadaten von Titel, Autor und Serien, um das Hochladen zu optimieren. Möglicherweise müssen zusätzliche Metadaten nach dem Hochladen abgeglichen werden.",
|
||||||
"LabelAutoLaunch": "Automatischer Start",
|
"LabelAutoLaunch": "Automatischer Start",
|
||||||
"LabelAutoLaunchDescription": "Automatische Weiterleitung zum Authentifizierungsanbieter beim Navigieren zur Anmeldeseite (manueller Überschreibungspfad <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Automatische Weiterleitung zum Authentifizierungsanbieter beim Navigieren zur Anmeldeseite (manueller Überschreibungspfad <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Automatische Registrierung",
|
"LabelAutoRegister": "Automatische Registrierung",
|
||||||
"LabelAutoRegisterDescription": "Automatische neue Neutzer anlegen nach dem Einloggen",
|
"LabelAutoRegisterDescription": "Automatische neue Neutzer anlegen nach dem Registrieren",
|
||||||
"LabelBackToUser": "Zurück zum Benutzer",
|
"LabelBackToUser": "Zurück zum Benutzer",
|
||||||
"LabelBackupLocation": "Backup-Ort",
|
"LabelBackupLocation": "Backup-Ort",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Automatische Sicherung aktivieren",
|
"LabelBackupsEnableAutomaticBackups": "Automatische Sicherung aktivieren",
|
||||||
@@ -207,16 +223,16 @@
|
|||||||
"LabelBackupsMaxBackupSize": "Maximale Sicherungsgröße (in GB)",
|
"LabelBackupsMaxBackupSize": "Maximale Sicherungsgröße (in GB)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Zum Schutz vor Fehlkonfigurationen schlagen Sicherungen fehl, wenn sie die konfigurierte Größe überschreiten.",
|
"LabelBackupsMaxBackupSizeHelp": "Zum Schutz vor Fehlkonfigurationen schlagen Sicherungen fehl, wenn sie die konfigurierte Größe überschreiten.",
|
||||||
"LabelBackupsNumberToKeep": "Anzahl der aufzubewahrenden Sicherungen",
|
"LabelBackupsNumberToKeep": "Anzahl der aufzubewahrenden Sicherungen",
|
||||||
"LabelBackupsNumberToKeepHelp": "Es wird immer nur 1 Sicherung auf einmal entfernt. Wenn Sie bereits mehrere Sicherungen als die definierte max. Anzahl haben, sollten Sie diese manuell entfernen.",
|
"LabelBackupsNumberToKeepHelp": "Es wird immer nur 1 Sicherung auf einmal entfernt. Wenn du bereits mehrere Sicherungen als die definierte max. Anzahl hast, solltest du diese manuell entfernen.",
|
||||||
"LabelBitrate": "Bitrate",
|
"LabelBitrate": "Bitrate",
|
||||||
"LabelBooks": "Bücher",
|
"LabelBooks": "Bücher",
|
||||||
"LabelButtonText": "Button Text",
|
"LabelButtonText": "Button Text",
|
||||||
"LabelChangePassword": "Passwort ändern",
|
"LabelChangePassword": "Passwort ändern",
|
||||||
"LabelChannels": "Kanäle",
|
"LabelChannels": "Kanäle",
|
||||||
"LabelChapters": "Kapitel",
|
"LabelChapters": "Kapitel",
|
||||||
"LabelChaptersFound": "gefundene Kapitel",
|
"LabelChaptersFound": "Gefundene Kapitel",
|
||||||
"LabelChapterTitle": "Kapitelüberschrift",
|
"LabelChapterTitle": "Kapitelüberschrift",
|
||||||
"LabelClickForMoreInfo": "Click for more info",
|
"LabelClickForMoreInfo": "Klicken für mehr Informationen",
|
||||||
"LabelClosePlayer": "Player schließen",
|
"LabelClosePlayer": "Player schließen",
|
||||||
"LabelCodec": "Codec",
|
"LabelCodec": "Codec",
|
||||||
"LabelCollapseSeries": "Serien zusammenfassen",
|
"LabelCollapseSeries": "Serien zusammenfassen",
|
||||||
@@ -225,7 +241,7 @@
|
|||||||
"LabelComplete": "Vollständig",
|
"LabelComplete": "Vollständig",
|
||||||
"LabelConfirmPassword": "Passwort bestätigen",
|
"LabelConfirmPassword": "Passwort bestätigen",
|
||||||
"LabelContinueListening": "Weiterhören",
|
"LabelContinueListening": "Weiterhören",
|
||||||
"LabelContinueReading": "Lesen fortsetzen",
|
"LabelContinueReading": "Weiterlesen",
|
||||||
"LabelContinueSeries": "Serien fortsetzen",
|
"LabelContinueSeries": "Serien fortsetzen",
|
||||||
"LabelCover": "Titelbild",
|
"LabelCover": "Titelbild",
|
||||||
"LabelCoverImageURL": "URL des Titelbildes",
|
"LabelCoverImageURL": "URL des Titelbildes",
|
||||||
@@ -240,23 +256,23 @@
|
|||||||
"LabelDeselectAll": "Alles abwählen",
|
"LabelDeselectAll": "Alles abwählen",
|
||||||
"LabelDevice": "Gerät",
|
"LabelDevice": "Gerät",
|
||||||
"LabelDeviceInfo": "Geräteinformationen",
|
"LabelDeviceInfo": "Geräteinformationen",
|
||||||
"LabelDeviceIsAvailableTo": "Dem Geärt ist es möglich zu ...",
|
"LabelDeviceIsAvailableTo": "Dem Gerät ist es möglich zu ...",
|
||||||
"LabelDirectory": "Verzeichnis",
|
"LabelDirectory": "Verzeichnis",
|
||||||
"LabelDiscFromFilename": "CD aus dem Dateinamen",
|
"LabelDiscFromFilename": "CD aus dem Dateinamen",
|
||||||
"LabelDiscFromMetadata": "CD aus den Metadaten",
|
"LabelDiscFromMetadata": "CD aus den Metadaten",
|
||||||
"LabelDiscover": "Entdecken",
|
"LabelDiscover": "Entdecken",
|
||||||
"LabelDownload": "Herunterladen",
|
"LabelDownload": "Herunterladen",
|
||||||
"LabelDownloadNEpisodes": "Download {0} episodes",
|
"LabelDownloadNEpisodes": "Download {0} Episoden",
|
||||||
"LabelDuration": "Laufzeit",
|
"LabelDuration": "Laufzeit",
|
||||||
"LabelDurationFound": "Gefundene Laufzeit:",
|
"LabelDurationFound": "Gefundene Laufzeit:",
|
||||||
"LabelEbook": "E-Book",
|
"LabelEbook": "E-Book",
|
||||||
"LabelEbooks": "E-Books",
|
"LabelEbooks": "E-Books",
|
||||||
"LabelEdit": "Bearbeiten",
|
"LabelEdit": "Bearbeiten",
|
||||||
"LabelEmail": "Email",
|
"LabelEmail": "Email",
|
||||||
"LabelEmailSettingsFromAddress": "Von Address",
|
"LabelEmailSettingsFromAddress": "Von Adresse",
|
||||||
"LabelEmailSettingsSecure": "Sicherheit",
|
"LabelEmailSettingsSecure": "Sicher",
|
||||||
"LabelEmailSettingsSecureHelp": "Wenn \"true\", verwendet die Verbindung TLS, wenn sie eine Verbindung zum Server herstellt. Bei \"false\" wird TLS verwendet, wenn der Server die STARTTLS-Erweiterung unterstützt. In den meisten Fällen setzen Sie diesen Wert auf \"true\", wenn Sie eine Verbindung zu Port 465 herstellen. Für Port 587 oder 25 behalten Sie den Wert \"false\" bei. (von nodemailer.com/smtp/#authentication)",
|
"LabelEmailSettingsSecureHelp": "Wenn \"an\", verwendet die Verbindung TLS, wenn du eine Verbindung zum Server herstellst. Bei \"aus\" wird TLS verwendet, wenn der Server die STARTTLS-Erweiterung unterstützt. In den meisten Fällen solltest du diesen Wert auf \"an\" schalten, wenn du eine Verbindung zu Port 465 herstellst. Für Port 587 oder 25 behalte den Wert \"aus\" bei. (von nodemailer.com/smtp/#authentication)",
|
||||||
"LabelEmailSettingsTestAddress": "Test Addresse",
|
"LabelEmailSettingsTestAddress": "Test Adresse",
|
||||||
"LabelEmbeddedCover": "Eingebettetes Cover",
|
"LabelEmbeddedCover": "Eingebettetes Cover",
|
||||||
"LabelEnable": "Aktivieren",
|
"LabelEnable": "Aktivieren",
|
||||||
"LabelEnd": "Ende",
|
"LabelEnd": "Ende",
|
||||||
@@ -266,24 +282,28 @@
|
|||||||
"LabelExample": "Beispiel",
|
"LabelExample": "Beispiel",
|
||||||
"LabelExplicit": "Explizit (Altersbeschränkung)",
|
"LabelExplicit": "Explizit (Altersbeschränkung)",
|
||||||
"LabelFeedURL": "Feed URL",
|
"LabelFeedURL": "Feed URL",
|
||||||
|
"LabelFetchingMetadata": "Abholen der Metadaten",
|
||||||
"LabelFile": "Datei",
|
"LabelFile": "Datei",
|
||||||
"LabelFileBirthtime": "Datei erstellt",
|
"LabelFileBirthtime": "Datei erstellt",
|
||||||
"LabelFileModified": "Datei geändert",
|
"LabelFileModified": "Datei geändert",
|
||||||
"LabelFilename": "Dateiname",
|
"LabelFilename": "Dateiname",
|
||||||
"LabelFilterByUser": "Nach Benutzern filtern",
|
"LabelFilterByUser": "Nach Benutzern filtern",
|
||||||
"LabelFindEpisodes": "Episoden suchen",
|
"LabelFindEpisodes": "Episoden suchen",
|
||||||
"LabelFinished": "beendet",
|
"LabelFinished": "Beendet",
|
||||||
"LabelFolder": "Ordner",
|
"LabelFolder": "Ordner",
|
||||||
"LabelFolders": "Verzeichnisse",
|
"LabelFolders": "Verzeichnisse",
|
||||||
|
"LabelFontBold": "Fett",
|
||||||
"LabelFontFamily": "Schriftfamilie",
|
"LabelFontFamily": "Schriftfamilie",
|
||||||
|
"LabelFontItalic": "Kursiv",
|
||||||
"LabelFontScale": "Schriftgröße",
|
"LabelFontScale": "Schriftgröße",
|
||||||
|
"LabelFontStrikethrough": "Durchgestrichen",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Kategorie",
|
"LabelGenre": "Kategorie",
|
||||||
"LabelGenres": "Kategorien",
|
"LabelGenres": "Kategorien",
|
||||||
"LabelHardDeleteFile": "Datei dauerhaft löschen",
|
"LabelHardDeleteFile": "Datei dauerhaft löschen",
|
||||||
"LabelHasEbook": "mit E-Book",
|
"LabelHasEbook": "E-Book verfügbar",
|
||||||
"LabelHasSupplementaryEbook": "mit zusätlichem E-Book",
|
"LabelHasSupplementaryEbook": "Ergänzendes E-Book verfügbar",
|
||||||
"LabelHighestPriority": "Highest priority",
|
"LabelHighestPriority": "Höchste Priorität",
|
||||||
"LabelHost": "Host",
|
"LabelHost": "Host",
|
||||||
"LabelHour": "Stunde",
|
"LabelHour": "Stunde",
|
||||||
"LabelIcon": "Symbol",
|
"LabelIcon": "Symbol",
|
||||||
@@ -305,9 +325,9 @@
|
|||||||
"LabelItem": "Medium",
|
"LabelItem": "Medium",
|
||||||
"LabelLanguage": "Sprache",
|
"LabelLanguage": "Sprache",
|
||||||
"LabelLanguageDefaultServer": "Standard-Server-Sprache",
|
"LabelLanguageDefaultServer": "Standard-Server-Sprache",
|
||||||
"LabelLastBookAdded": "Zuletzt hinzugefügtes Medium",
|
"LabelLastBookAdded": "Zuletzt hinzugefügtes Buch",
|
||||||
"LabelLastBookUpdated": "Zuletzt aktualisiertes Medium",
|
"LabelLastBookUpdated": "Zuletzt aktualisiertes Buch",
|
||||||
"LabelLastSeen": "Zuletzt angesehen",
|
"LabelLastSeen": "Zuletzt gesehen",
|
||||||
"LabelLastTime": "Letztes Mal",
|
"LabelLastTime": "Letztes Mal",
|
||||||
"LabelLastUpdate": "Letzte Aktualisierung",
|
"LabelLastUpdate": "Letzte Aktualisierung",
|
||||||
"LabelLayout": "Layout",
|
"LabelLayout": "Layout",
|
||||||
@@ -324,33 +344,37 @@
|
|||||||
"LabelLogLevelDebug": "Fehlersuche",
|
"LabelLogLevelDebug": "Fehlersuche",
|
||||||
"LabelLogLevelInfo": "Informationen",
|
"LabelLogLevelInfo": "Informationen",
|
||||||
"LabelLogLevelWarn": "Warnungen",
|
"LabelLogLevelWarn": "Warnungen",
|
||||||
"LabelLookForNewEpisodesAfterDate": "Suchen nach neuen Episoden nach diesem Datum",
|
"LabelLookForNewEpisodesAfterDate": "Suche nach neuen Episoden nach diesem Datum",
|
||||||
"LabelLowestPriority": "Lowest Priority",
|
"LabelLowestPriority": "Niedrigste Priorität",
|
||||||
"LabelMatchExistingUsersBy": "Zuordnen existierender Benutzer mit",
|
"LabelMatchExistingUsersBy": "Zuordnen existierender Benutzer mit",
|
||||||
"LabelMatchExistingUsersByDescription": "Wird zum Verbinden vorhandener Benutzer verwendet. Sobald die Verbindung hergestellt ist, wird den Benutzern eine eindeutige ID von Ihrem SSO-Anbieter zugeordnet",
|
"LabelMatchExistingUsersByDescription": "Wird zum Verbinden vorhandener Benutzer verwendet. Sobald die Verbindung hergestellt ist, wird den Benutzern eine eindeutige ID vom SSO-Anbieter zugeordnet",
|
||||||
"LabelMediaPlayer": "Mediaplayer",
|
"LabelMediaPlayer": "Mediaplayer",
|
||||||
"LabelMediaType": "Medientyp",
|
"LabelMediaType": "Medientyp",
|
||||||
"LabelMetadataOrderOfPrecedenceDescription": "Higher priority metadata sources will override lower priority metadata sources",
|
"LabelMetadataOrderOfPrecedenceDescription": "Höher priorisierte Quellen für Metadaten überschreiben Metadaten aus Quellen die niedriger priorisiert sind.",
|
||||||
"LabelMetadataProvider": "Metadatenanbieter",
|
"LabelMetadataProvider": "Metadatenanbieter",
|
||||||
"LabelMetaTag": "Meta Schlagwort",
|
"LabelMetaTag": "Meta Schlagwort",
|
||||||
"LabelMetaTags": "Meta Tags",
|
"LabelMetaTags": "Meta Tags",
|
||||||
"LabelMinute": "Minute",
|
"LabelMinute": "Minute",
|
||||||
"LabelMissing": "Fehlend",
|
"LabelMissing": "Fehlend",
|
||||||
|
"LabelMissingEbook": "E-Book fehlt",
|
||||||
"LabelMissingParts": "Fehlende Teile",
|
"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.",
|
||||||
"LabelMore": "Mehr",
|
"LabelMore": "Mehr",
|
||||||
"LabelMoreInfo": "Mehr Info",
|
"LabelMoreInfo": "Mehr Infos",
|
||||||
"LabelName": "Name",
|
"LabelName": "Name",
|
||||||
"LabelNarrator": "Erzähler",
|
"LabelNarrator": "Erzähler",
|
||||||
"LabelNarrators": "Erzähler",
|
"LabelNarrators": "Erzähler",
|
||||||
"LabelNew": "Neu",
|
"LabelNew": "Neu",
|
||||||
"LabelNewestAuthors": "Neuste Autoren",
|
"LabelNewestAuthors": "Neueste Autoren",
|
||||||
"LabelNewestEpisodes": "Neueste Episoden",
|
"LabelNewestEpisodes": "Neueste Episoden",
|
||||||
"LabelNewPassword": "Neues Passwort",
|
"LabelNewPassword": "Neues Passwort",
|
||||||
"LabelNextBackupDate": "Nächstes Sicherungsdatum",
|
"LabelNextBackupDate": "Nächstes Sicherungsdatum",
|
||||||
"LabelNextScheduledRun": "Nächster planmäßiger Durchlauf",
|
"LabelNextScheduledRun": "Nächster planmäßiger Durchlauf",
|
||||||
"LabelNoEpisodesSelected": "Keine Episoden ausgewählt",
|
"LabelNoEpisodesSelected": "Keine Episoden ausgewählt",
|
||||||
"LabelNotes": "Hinweise",
|
"LabelNotes": "Notizen",
|
||||||
"LabelNotFinished": "nicht beendet",
|
"LabelNotFinished": "Nicht beendet",
|
||||||
"LabelNotificationAppriseURL": "Apprise URL(s)",
|
"LabelNotificationAppriseURL": "Apprise URL(s)",
|
||||||
"LabelNotificationAvailableVariables": "Verfügbare Variablen",
|
"LabelNotificationAvailableVariables": "Verfügbare Variablen",
|
||||||
"LabelNotificationBodyTemplate": "Textvorlage",
|
"LabelNotificationBodyTemplate": "Textvorlage",
|
||||||
@@ -363,7 +387,7 @@
|
|||||||
"LabelNotStarted": "Nicht begonnen",
|
"LabelNotStarted": "Nicht begonnen",
|
||||||
"LabelNumberOfBooks": "Anzahl der Hörbücher",
|
"LabelNumberOfBooks": "Anzahl der Hörbücher",
|
||||||
"LabelNumberOfEpisodes": "Anzahl der Episoden",
|
"LabelNumberOfEpisodes": "Anzahl der Episoden",
|
||||||
"LabelOpenRSSFeed": "Öffne RSS Feed",
|
"LabelOpenRSSFeed": "Öffne RSS-Feed",
|
||||||
"LabelOverwrite": "Überschreiben",
|
"LabelOverwrite": "Überschreiben",
|
||||||
"LabelPassword": "Passwort",
|
"LabelPassword": "Passwort",
|
||||||
"LabelPath": "Pfad",
|
"LabelPath": "Pfad",
|
||||||
@@ -374,30 +398,34 @@
|
|||||||
"LabelPermissionsDownload": "Herunterladen",
|
"LabelPermissionsDownload": "Herunterladen",
|
||||||
"LabelPermissionsUpdate": "Aktualisieren",
|
"LabelPermissionsUpdate": "Aktualisieren",
|
||||||
"LabelPermissionsUpload": "Hochladen",
|
"LabelPermissionsUpload": "Hochladen",
|
||||||
|
"LabelPersonalYearReview": "Dein Jahr in Übersicht ({0})",
|
||||||
"LabelPhotoPathURL": "Foto Pfad/URL",
|
"LabelPhotoPathURL": "Foto Pfad/URL",
|
||||||
"LabelPlaylists": "Wiedergabelisten",
|
"LabelPlaylists": "Wiedergabelisten",
|
||||||
"LabelPlayMethod": "Abspielmethode",
|
"LabelPlayMethod": "Abspielmethode",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "Podcast-Suchregion",
|
||||||
"LabelPodcastType": "Podcast Typ",
|
"LabelPodcastType": "Podcast Typ",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Zu ignorierende(s) Vorwort(e) (Groß- und Kleinschreibung wird nicht berücksichtigt)",
|
"LabelPrefixesToIgnore": "Zu ignorierende(s) Vorwort(e) (Groß- und Kleinschreibung wird nicht berücksichtigt)",
|
||||||
"LabelPreventIndexing": "Verhindere, dass dein Feed von iTunes- und Google-Podcast-Verzeichnissen indiziert wird",
|
"LabelPreventIndexing": "Verhindere, dass dein Feed von iTunes- und Google-Podcast-Verzeichnissen indiziert wird",
|
||||||
"LabelPrimaryEbook": "Haupt-E-Book",
|
"LabelPrimaryEbook": "Primäres E-Book",
|
||||||
"LabelProgress": "Fortschritt",
|
"LabelProgress": "Fortschritt",
|
||||||
"LabelProvider": "Anbieter",
|
"LabelProvider": "Anbieter",
|
||||||
"LabelPubDate": "Veröffentlichungsdatum",
|
"LabelPubDate": "Veröffentlichungsdatum",
|
||||||
"LabelPublisher": "Herausgeber",
|
"LabelPublisher": "Herausgeber",
|
||||||
"LabelPublishYear": "Jahr",
|
"LabelPublishYear": "Jahr",
|
||||||
"LabelRead": "Lesen",
|
"LabelRead": "Lesen",
|
||||||
"LabelReadAgain": "Nocheinmal Lesen",
|
"LabelReadAgain": "Noch einmal Lesen",
|
||||||
"LabelReadEbookWithoutProgress": "E-Book lesen und Fortschritt verwerfen",
|
"LabelReadEbookWithoutProgress": "E-Book lesen und Fortschritt verwerfen",
|
||||||
"LabelRecentlyAdded": "Kürzlich hinzugefügt",
|
"LabelRecentlyAdded": "Kürzlich hinzugefügt",
|
||||||
"LabelRecentSeries": "Aktuelle Serien",
|
"LabelRecentSeries": "Aktuelle Serien",
|
||||||
"LabelRecommended": "Empfohlen",
|
"LabelRecommended": "Empfohlen",
|
||||||
|
"LabelRedo": "Wiederholen",
|
||||||
"LabelRegion": "Region",
|
"LabelRegion": "Region",
|
||||||
"LabelReleaseDate": "Veröffentlichungsdatum",
|
"LabelReleaseDate": "Veröffentlichungsdatum",
|
||||||
"LabelRemoveCover": "Lösche Titelbild",
|
"LabelRemoveCover": "Lösche Titelbild",
|
||||||
|
"LabelRowsPerPage": "Zeilen pro Seite",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Benutzerdefinierte Eigentümer-E-Mail",
|
"LabelRSSFeedCustomOwnerEmail": "Benutzerdefinierte Eigentümer-E-Mail",
|
||||||
"LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers",
|
"LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers",
|
||||||
"LabelRSSFeedOpen": "RSS Feed Offen",
|
"LabelRSSFeedOpen": "RSS Feed Offen",
|
||||||
@@ -405,8 +433,8 @@
|
|||||||
"LabelRSSFeedSlug": "RSS Feed Schlagwort",
|
"LabelRSSFeedSlug": "RSS Feed Schlagwort",
|
||||||
"LabelRSSFeedURL": "RSS Feed URL",
|
"LabelRSSFeedURL": "RSS Feed URL",
|
||||||
"LabelSearchTerm": "Begriff suchen",
|
"LabelSearchTerm": "Begriff suchen",
|
||||||
"LabelSearchTitle": "Titel",
|
"LabelSearchTitle": "Titel suchen",
|
||||||
"LabelSearchTitleOrASIN": "Titel oder ASIN",
|
"LabelSearchTitleOrASIN": "Titel oder ASIN suchen",
|
||||||
"LabelSeason": "Staffel",
|
"LabelSeason": "Staffel",
|
||||||
"LabelSelectAllEpisodes": "Alle Episoden auswählen",
|
"LabelSelectAllEpisodes": "Alle Episoden auswählen",
|
||||||
"LabelSelectEpisodesShowing": "{0} ausgewählte Episoden werden angezeigt",
|
"LabelSelectEpisodesShowing": "{0} ausgewählte Episoden werden angezeigt",
|
||||||
@@ -416,10 +444,11 @@
|
|||||||
"LabelSeries": "Serien",
|
"LabelSeries": "Serien",
|
||||||
"LabelSeriesName": "Serienname",
|
"LabelSeriesName": "Serienname",
|
||||||
"LabelSeriesProgress": "Serienfortschritt",
|
"LabelSeriesProgress": "Serienfortschritt",
|
||||||
"LabelSetEbookAsPrimary": "Setzen als Hauptbuch",
|
"LabelServerYearReview": "Server Jahr in Übersicht ({0})",
|
||||||
"LabelSetEbookAsSupplementary": "Setzen als Ergänzung",
|
"LabelSetEbookAsPrimary": "Als Hauptbuch setzen",
|
||||||
"LabelSettingsAudiobooksOnly": "nur Hörbücher",
|
"LabelSetEbookAsSupplementary": "Als Ergänzung setzen",
|
||||||
"LabelSettingsAudiobooksOnlyHelp": "Wenn Sie diese Einstellung aktivieren, werden E-Book-Dateien ignoriert, es sei denn, sie befinden sich in einem Hörbuchordner. In diesem Fall werden sie als zusätzliche E-Books festgelegt",
|
"LabelSettingsAudiobooksOnly": "Nur Hörbücher",
|
||||||
|
"LabelSettingsAudiobooksOnlyHelp": "Wenn du diese Einstellung aktivierst, werden E-Book-Dateien ignoriert, es sei denn, sie befinden sich in einem Hörbuchordner. In diesem Fall werden sie als zusätzliche E-Books festgelegt",
|
||||||
"LabelSettingsBookshelfViewHelp": "Skeumorphes Design mit Holzeinlegeböden",
|
"LabelSettingsBookshelfViewHelp": "Skeumorphes Design mit Holzeinlegeböden",
|
||||||
"LabelSettingsChromecastSupport": "Chromecastunterstützung",
|
"LabelSettingsChromecastSupport": "Chromecastunterstützung",
|
||||||
"LabelSettingsDateFormat": "Datumsformat",
|
"LabelSettingsDateFormat": "Datumsformat",
|
||||||
@@ -430,13 +459,15 @@
|
|||||||
"LabelSettingsEnableWatcherForLibrary": "Ordnerüberwachung für die Bibliothek aktivieren",
|
"LabelSettingsEnableWatcherForLibrary": "Ordnerüberwachung für die Bibliothek aktivieren",
|
||||||
"LabelSettingsEnableWatcherHelp": "Aktiviert das automatische Hinzufügen/Aktualisieren von Elementen, wenn Dateiänderungen erkannt werden. *Erfordert einen Server-Neustart",
|
"LabelSettingsEnableWatcherHelp": "Aktiviert das automatische Hinzufügen/Aktualisieren von Elementen, wenn Dateiänderungen erkannt werden. *Erfordert einen Server-Neustart",
|
||||||
"LabelSettingsExperimentalFeatures": "Experimentelle Funktionen",
|
"LabelSettingsExperimentalFeatures": "Experimentelle Funktionen",
|
||||||
"LabelSettingsExperimentalFeaturesHelp": "Funktionen welche sich in der Entwicklung befinden, benötigen Ihr Feedback und Ihre Hilfe beim Testen. Klicken Sie hier, um die Github-Diskussion zu öffnen.",
|
"LabelSettingsExperimentalFeaturesHelp": "Funktionen welche sich in der Entwicklung befinden, benötigen dein Feedback und deine Hilfe beim Testen. Klicke hier, um die Github-Diskussion zu öffnen.",
|
||||||
"LabelSettingsFindCovers": "Suche Titelbilder",
|
"LabelSettingsFindCovers": "Suche Titelbilder",
|
||||||
"LabelSettingsFindCoversHelp": "Wenn Ihr Medium kein eingebettetes Titelbild oder kein Titelbild im Ordner hat, versucht der Scanner, ein Titelbild online zu finden.<br>Hinweis: Dies verlängert die Scandauer",
|
"LabelSettingsFindCoversHelp": "Wenn dein Medium kein eingebettetes Titelbild oder kein Titelbild im Ordner hat, versucht der Scanner, ein Titelbild online zu finden.<br>Hinweis: Dies verlängert die Scandauer",
|
||||||
"LabelSettingsHideSingleBookSeries": "Ausblenden einzelzne Bücher",
|
"LabelSettingsHideSingleBookSeries": "Ausblenden einzelner Bücher",
|
||||||
"LabelSettingsHideSingleBookSeriesHelp": "Serien, die ein einzelnes Buch enthalten, werden in den Regalen der Serienseite und der Startseite ausgeblendet.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Serien, die nur ein einzelnes Buch enthalten, werden auf der Startseite und in der Serienansicht ausgeblendet.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Startseite verwendet die Bücherregalansicht",
|
"LabelSettingsHomePageBookshelfView": "Startseite verwendet die Bücherregalansicht",
|
||||||
"LabelSettingsLibraryBookshelfView": "Bibliothek verwendet die Bücherregalansicht",
|
"LabelSettingsLibraryBookshelfView": "Bibliothek verwendet die Bücherregalansicht",
|
||||||
|
"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": "Analysiere Untertitel",
|
"LabelSettingsParseSubtitles": "Analysiere Untertitel",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extrahiere den Untertitel von Medium-Ordnernamen.<br>Untertitel müssen vom eigentlichem Titel durch ein \" - \" getrennt sein. <br>Beispiel: \"Titel - Untertitel\"",
|
"LabelSettingsParseSubtitlesHelp": "Extrahiere den Untertitel von Medium-Ordnernamen.<br>Untertitel müssen vom eigentlichem Titel durch ein \" - \" getrennt sein. <br>Beispiel: \"Titel - Untertitel\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Bevorzuge online abgestimmte Metadaten",
|
"LabelSettingsPreferMatchedMetadata": "Bevorzuge online abgestimmte Metadaten",
|
||||||
@@ -446,7 +477,7 @@
|
|||||||
"LabelSettingsSortingIgnorePrefixes": "Vorwort/Artikel beim Sortieren ignorieren",
|
"LabelSettingsSortingIgnorePrefixes": "Vorwort/Artikel beim Sortieren ignorieren",
|
||||||
"LabelSettingsSortingIgnorePrefixesHelp": "Beispiel: für den Artikel \"der\" würde der Mediumtitel \"Der Buchtitel\" als \"Buchtitel, Der\" sortiert werden.",
|
"LabelSettingsSortingIgnorePrefixesHelp": "Beispiel: für den Artikel \"der\" würde der Mediumtitel \"Der Buchtitel\" als \"Buchtitel, Der\" sortiert werden.",
|
||||||
"LabelSettingsSquareBookCovers": "Benutze quadratische Titelbilder",
|
"LabelSettingsSquareBookCovers": "Benutze quadratische Titelbilder",
|
||||||
"LabelSettingsSquareBookCoversHelp": "Bevorzugen quadratische Titelbilder gegenüber den Standardtielbildern im Verhältnis 1,6:1",
|
"LabelSettingsSquareBookCoversHelp": "Bevorzuge quadratische Titelbilder gegenüber den Standardtielbildern im Verhältnis 1,6:1",
|
||||||
"LabelSettingsStoreCoversWithItem": "Titelbilder im Medienordner speichern",
|
"LabelSettingsStoreCoversWithItem": "Titelbilder im Medienordner speichern",
|
||||||
"LabelSettingsStoreCoversWithItemHelp": "Standardmäßig werden die Titelbilder in /metadata/items gespeichert. Wenn diese Option aktiviert ist, werden die Titelbilder als jpg Datei in dem gleichen Ordner gespeichert in welchem sich auch das Medium befindet. Es wird immer nur eine Datei mit dem Namen \"cover.jpg\" gespeichert.",
|
"LabelSettingsStoreCoversWithItemHelp": "Standardmäßig werden die Titelbilder in /metadata/items gespeichert. Wenn diese Option aktiviert ist, werden die Titelbilder als jpg Datei in dem gleichen Ordner gespeichert in welchem sich auch das Medium befindet. Es wird immer nur eine Datei mit dem Namen \"cover.jpg\" gespeichert.",
|
||||||
"LabelSettingsStoreMetadataWithItem": "Metadaten als OPF-Datei im Medienordner speichern",
|
"LabelSettingsStoreMetadataWithItem": "Metadaten als OPF-Datei im Medienordner speichern",
|
||||||
@@ -454,7 +485,7 @@
|
|||||||
"LabelSettingsTimeFormat": "Zeitformat",
|
"LabelSettingsTimeFormat": "Zeitformat",
|
||||||
"LabelShowAll": "Alles anzeigen",
|
"LabelShowAll": "Alles anzeigen",
|
||||||
"LabelSize": "Größe",
|
"LabelSize": "Größe",
|
||||||
"LabelSleepTimer": "Einschlaf-Timer",
|
"LabelSleepTimer": "Sleep-Timer",
|
||||||
"LabelSlug": "URL Teil",
|
"LabelSlug": "URL Teil",
|
||||||
"LabelStart": "Start",
|
"LabelStart": "Start",
|
||||||
"LabelStarted": "Gestartet",
|
"LabelStarted": "Gestartet",
|
||||||
@@ -467,7 +498,7 @@
|
|||||||
"LabelStatsDays": "Tage",
|
"LabelStatsDays": "Tage",
|
||||||
"LabelStatsDaysListened": "Gehörte Tage",
|
"LabelStatsDaysListened": "Gehörte Tage",
|
||||||
"LabelStatsHours": "Stunden",
|
"LabelStatsHours": "Stunden",
|
||||||
"LabelStatsInARow": "nacheinander",
|
"LabelStatsInARow": "Nacheinander",
|
||||||
"LabelStatsItemsFinished": "Gehörte Medien",
|
"LabelStatsItemsFinished": "Gehörte Medien",
|
||||||
"LabelStatsItemsInLibrary": "Bibliothekseinträge",
|
"LabelStatsItemsInLibrary": "Bibliothekseinträge",
|
||||||
"LabelStatsMinutes": "Minuten",
|
"LabelStatsMinutes": "Minuten",
|
||||||
@@ -482,9 +513,13 @@
|
|||||||
"LabelTagsAccessibleToUser": "Für Benutzer zugängliche Schlagwörter",
|
"LabelTagsAccessibleToUser": "Für Benutzer zugängliche Schlagwörter",
|
||||||
"LabelTagsNotAccessibleToUser": "Für Benutzer nicht zugängliche Schlagwörter",
|
"LabelTagsNotAccessibleToUser": "Für Benutzer nicht zugängliche Schlagwörter",
|
||||||
"LabelTasks": "Laufende Aufgaben",
|
"LabelTasks": "Laufende Aufgaben",
|
||||||
|
"LabelTextEditorBulletedList": "Aufzählungsliste",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "nummerierte Liste",
|
||||||
|
"LabelTextEditorUnlink": "entkoppeln",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Theme",
|
||||||
"LabelThemeDark": "Dark",
|
"LabelThemeDark": "Dunkel",
|
||||||
"LabelThemeLight": "Light",
|
"LabelThemeLight": "Hell",
|
||||||
"LabelTimeBase": "Basiszeit",
|
"LabelTimeBase": "Basiszeit",
|
||||||
"LabelTimeListened": "Gehörte Zeit",
|
"LabelTimeListened": "Gehörte Zeit",
|
||||||
"LabelTimeListenedToday": "Heute gehörte Zeit",
|
"LabelTimeListenedToday": "Heute gehörte Zeit",
|
||||||
@@ -494,12 +529,12 @@
|
|||||||
"LabelToolsEmbedMetadata": "Metadaten einbetten",
|
"LabelToolsEmbedMetadata": "Metadaten einbetten",
|
||||||
"LabelToolsEmbedMetadataDescription": "Bettet die Metadaten einschließlich des Titelbildes und der Kapitel in die Audiodatein ein.",
|
"LabelToolsEmbedMetadataDescription": "Bettet die Metadaten einschließlich des Titelbildes und der Kapitel in die Audiodatein ein.",
|
||||||
"LabelToolsMakeM4b": "M4B-Datei erstellen",
|
"LabelToolsMakeM4b": "M4B-Datei erstellen",
|
||||||
"LabelToolsMakeM4bDescription": "Erstellt eine M4B-Datei (Endung \".m4b\") welche mehrere mp3-Dateien in einer einzigen Datei inkl. derer Metadaten (Beschreibung, Titelbild, Kapitel, ....) zusammenfasst. M4B-Datei können darüber hinaus Lesezeichen speichern und mit einem Abspielschutz (Passwort) versehen werden.",
|
"LabelToolsMakeM4bDescription": "Erstellt eine M4B-Datei (Endung \".m4b\") welche mehrere mp3-Dateien in einer einzigen Datei inkl. derer Metadaten (Beschreibung, Titelbild, Kapitel, ...) zusammenfasst. M4B-Datei können darüber hinaus Lesezeichen speichern und mit einem Abspielschutz (Passwort) versehen werden.",
|
||||||
"LabelToolsSplitM4b": "M4B in MP3's aufteilen",
|
"LabelToolsSplitM4b": "M4B in MP3's aufteilen",
|
||||||
"LabelToolsSplitM4bDescription": "Erstellt aus einer mit Metadaten und nach Kapiteln aufgeteilten M4B-Datei seperate MP3's mit eingebetteten Metadaten, Coverbild und Kapiteln.",
|
"LabelToolsSplitM4bDescription": "Erstellt aus einer mit Metadaten und nach Kapiteln aufgeteilten M4B-Datei seperate MP3's mit eingebetteten Metadaten, Coverbild und Kapiteln.",
|
||||||
"LabelTotalDuration": "Gesamtdauer",
|
"LabelTotalDuration": "Gesamtdauer",
|
||||||
"LabelTotalTimeListened": "Gehörte Gesamtzeit",
|
"LabelTotalTimeListened": "Gehörte Gesamtzeit",
|
||||||
"LabelTrackFromFilename": "Titel von Dateiname",
|
"LabelTrackFromFilename": "Titel aus Dateiname",
|
||||||
"LabelTrackFromMetadata": "Titel aus Metadaten",
|
"LabelTrackFromMetadata": "Titel aus Metadaten",
|
||||||
"LabelTracks": "Dateien",
|
"LabelTracks": "Dateien",
|
||||||
"LabelTracksMultiTrack": "Mehrfachdatei",
|
"LabelTracksMultiTrack": "Mehrfachdatei",
|
||||||
@@ -507,14 +542,16 @@
|
|||||||
"LabelTracksSingleTrack": "Einzeldatei",
|
"LabelTracksSingleTrack": "Einzeldatei",
|
||||||
"LabelType": "Typ",
|
"LabelType": "Typ",
|
||||||
"LabelUnabridged": "Ungekürzt",
|
"LabelUnabridged": "Ungekürzt",
|
||||||
|
"LabelUndo": "Rückgängig machen",
|
||||||
"LabelUnknown": "Unbekannt",
|
"LabelUnknown": "Unbekannt",
|
||||||
"LabelUpdateCover": "Titelbild aktualisieren",
|
"LabelUpdateCover": "Titelbild aktualisieren",
|
||||||
"LabelUpdateCoverHelp": "Erlaube das Überschreiben bestehender Titelbilder für die ausgewählten Hörbücher wenn eine Übereinstimmung gefunden wird",
|
"LabelUpdateCoverHelp": "Erlaube das Überschreiben bestehender Titelbilder für die ausgewählten Hörbücher, wenn eine Übereinstimmung gefunden wird",
|
||||||
"LabelUpdatedAt": "Aktualisiert am",
|
"LabelUpdatedAt": "Aktualisiert am",
|
||||||
"LabelUpdateDetails": "Details aktualisieren",
|
"LabelUpdateDetails": "Details aktualisieren",
|
||||||
"LabelUpdateDetailsHelp": "Erlaube das Überschreiben bestehender Details für die ausgewählten Hörbücher wenn eine Übereinstimmung gefunden wird",
|
"LabelUpdateDetailsHelp": "Erlaube das Überschreiben bestehender Details für die ausgewählten Hörbücher, wenn eine Übereinstimmung gefunden wird",
|
||||||
"LabelUploaderDragAndDrop": "Ziehen und Ablegen von Dateien oder Ordnern",
|
"LabelUploaderDragAndDrop": "Ziehen und Ablegen von Dateien oder Ordnern",
|
||||||
"LabelUploaderDropFiles": "Dateien löschen",
|
"LabelUploaderDropFiles": "Dateien löschen",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatisches Aktualisieren von Titel, Autor und Serie",
|
||||||
"LabelUseChapterTrack": "Kapiteldatei verwenden",
|
"LabelUseChapterTrack": "Kapiteldatei verwenden",
|
||||||
"LabelUseFullTrack": "Gesamte Datei verwenden",
|
"LabelUseFullTrack": "Gesamte Datei verwenden",
|
||||||
"LabelUser": "Benutzer",
|
"LabelUser": "Benutzer",
|
||||||
@@ -523,17 +560,19 @@
|
|||||||
"LabelVersion": "Version",
|
"LabelVersion": "Version",
|
||||||
"LabelViewBookmarks": "Lesezeichen anzeigen",
|
"LabelViewBookmarks": "Lesezeichen anzeigen",
|
||||||
"LabelViewChapters": "Kapitel anzeigen",
|
"LabelViewChapters": "Kapitel anzeigen",
|
||||||
"LabelViewQueue": "Spieler-Warteschlange anzeigen",
|
"LabelViewQueue": "Player-Warteschlange anzeigen",
|
||||||
"LabelVolume": "Volumen",
|
"LabelVolume": "Lautstärke",
|
||||||
"LabelWeekdaysToRun": "Wochentage für die Ausführung",
|
"LabelWeekdaysToRun": "Wochentage für die Ausführung",
|
||||||
"LabelYourAudiobookDuration": "Laufzeit Ihres Mediums",
|
"LabelYearReviewHide": "Verstecke Jahr in Übersicht",
|
||||||
|
"LabelYearReviewShow": "Zeige Jahr in Übersicht",
|
||||||
|
"LabelYourAudiobookDuration": "Laufzeit deines Mediums",
|
||||||
"LabelYourBookmarks": "Lesezeichen",
|
"LabelYourBookmarks": "Lesezeichen",
|
||||||
"LabelYourPlaylists": "Eigene Wiedergabelisten",
|
"LabelYourPlaylists": "Eigene Wiedergabelisten",
|
||||||
"LabelYourProgress": "Fortschritt",
|
"LabelYourProgress": "Fortschritt",
|
||||||
"MessageAddToPlayerQueue": "Zur Abspielwarteliste hinzufügen",
|
"MessageAddToPlayerQueue": "Zur Abspielwarteliste hinzufügen",
|
||||||
"MessageAppriseDescription": "Um diese Funktion nutzen zu können, müssen Sie eine Instanz von <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> laufen haben oder eine API verwenden welche dieselbe Anfragen bearbeiten kann. <br />Die Apprise API Url muss der vollständige URL-Pfad sein, an den die Benachrichtigung gesendet werden soll, z.B. wenn Ihre API-Instanz unter <code>http://192.168.1.1:8337</code> läuft, würden Sie <code>http://192.168.1.1:8337/notify</code> eingeben.",
|
"MessageAppriseDescription": "Um diese Funktion nutzen zu können, musst du eine Instanz von <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> laufen haben oder eine API verwenden welche dieselbe Anfragen bearbeiten kann. <br />Die Apprise API Url muss der vollständige URL-Pfad sein, an den die Benachrichtigung gesendet werden soll, z.B. wenn Ihre API-Instanz unter <code>http://192.168.1.1:8337</code> läuft, würdest du <code>http://192.168.1.1:8337/notify</code> eingeben.",
|
||||||
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in <code>/metadata/items</code> & <code>/metadata/authors</code> gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
|
"MessageBackupsDescription": "In einer Sicherung werden Benutzer, Benutzerfortschritte, Details zu den Bibliotheksobjekten, Servereinstellungen und Bilder welche in <code>/metadata/items</code> & <code>/metadata/authors</code> gespeichert sind gespeichert. Sicherungen enthalten keine Dateien welche in den einzelnen Bibliotheksordnern (Medien-Ordnern) gespeichert sind.",
|
||||||
"MessageBatchQuickMatchDescription": "Der Schnellabgleich versucht, fehlende Titelbilder und Metadaten für die ausgewählten Artikel hinzuzufügen. Aktivieren Sie die nachstehenden Optionen, damit der Schnellabgleich vorhandene Titelbilder und/oder Metadaten überschreiben kann.",
|
"MessageBatchQuickMatchDescription": "Der Schnellabgleich versucht, fehlende Titelbilder und Metadaten für die ausgewählten Artikel hinzuzufügen. Aktiviere die nachstehenden Optionen, damit der Schnellabgleich vorhandene Titelbilder und/oder Metadaten überschreiben kann.",
|
||||||
"MessageBookshelfNoCollections": "Es wurden noch keine Sammlungen erstellt",
|
"MessageBookshelfNoCollections": "Es wurden noch keine Sammlungen erstellt",
|
||||||
"MessageBookshelfNoResultsForFilter": "Keine Ergebnisse für Filter \"{0}: {1}\"",
|
"MessageBookshelfNoResultsForFilter": "Keine Ergebnisse für Filter \"{0}: {1}\"",
|
||||||
"MessageBookshelfNoRSSFeeds": "Keine RSS-Feeds geöffnet",
|
"MessageBookshelfNoRSSFeeds": "Keine RSS-Feeds geöffnet",
|
||||||
@@ -544,56 +583,57 @@
|
|||||||
"MessageChapterErrorStartLtPrev": "Ungültige Kapitelstartzeit: Kapitelanfang < Kapitelanfang vorheriges Kapitel (Kapitelanfang liegt zeitlich vor dem Beginn des vorherigen Kapitels -> Lösung: Kapitelanfang >= Startzeit des vorherigen Kapitels)",
|
"MessageChapterErrorStartLtPrev": "Ungültige Kapitelstartzeit: Kapitelanfang < Kapitelanfang vorheriges Kapitel (Kapitelanfang liegt zeitlich vor dem Beginn des vorherigen Kapitels -> Lösung: Kapitelanfang >= Startzeit des vorherigen Kapitels)",
|
||||||
"MessageChapterStartIsAfter": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumende (Kapitelanfang liegt nach dem Ende des Mediums)",
|
"MessageChapterStartIsAfter": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumende (Kapitelanfang liegt nach dem Ende des Mediums)",
|
||||||
"MessageCheckingCron": "Überprüfe Cron...",
|
"MessageCheckingCron": "Überprüfe Cron...",
|
||||||
"MessageConfirmCloseFeed": "Feed wird geschlossen! Sind Sie sicher?",
|
"MessageConfirmCloseFeed": "Feed wird geschlossen! Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteBackup": "Sicherung für {0} wird gelöscht! Sind Sie sicher?",
|
"MessageConfirmDeleteBackup": "Sicherung für {0} wird gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteFile": "Datei wird vom System gelöscht! Sind Sie sicher?",
|
"MessageConfirmDeleteFile": "Datei wird vom System gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteLibrary": "Bibliothek \"{0}\" wird dauerhaft gelöscht! Sind Sie sicher?",
|
"MessageConfirmDeleteLibrary": "Bibliothek \"{0}\" wird dauerhaft gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteLibraryItem": "Bibliothekselement wird aus der Datenbank + Festplatte gelöscht? Sind Sie sicher?",
|
"MessageConfirmDeleteLibraryItem": "Bibliothekselement wird aus der Datenbank + Festplatte gelöscht? Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteLibraryItems": "{0} Bibliothekselemente werden aus der Datenbank + Festplatte gelöscht? Sind Sie sicher?",
|
"MessageConfirmDeleteLibraryItems": "{0} Bibliothekselemente werden aus der Datenbank + Festplatte gelöscht? Bist du dir sicher?",
|
||||||
"MessageConfirmDeleteSession": "Sitzung wird gelöscht! Sind Sie sicher?",
|
"MessageConfirmDeleteSession": "Sitzung wird gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmForceReScan": "Scanvorgang erzwingen! Sind Sie sicher?",
|
"MessageConfirmForceReScan": "Scanvorgang erzwingen! Bist du dir sicher?",
|
||||||
"MessageConfirmMarkAllEpisodesFinished": "Alle Episoden werden als abgeschlossen markiert! Sind Sie sicher?",
|
"MessageConfirmMarkAllEpisodesFinished": "Alle Episoden werden als abgeschlossen markiert! Bist du dir sicher?",
|
||||||
"MessageConfirmMarkAllEpisodesNotFinished": "Alle Episoden werden als nicht abgeschlossen markiert! Sind Sie sicher?",
|
"MessageConfirmMarkAllEpisodesNotFinished": "Alle Episoden werden als nicht abgeschlossen markiert! Bist du dir sicher?",
|
||||||
"MessageConfirmMarkSeriesFinished": "Alle Medien dieser Reihe werden als abgeschlossen markiert! Sind Sie sicher?",
|
"MessageConfirmMarkSeriesFinished": "Alle Medien dieser Reihe werden als abgeschlossen markiert! Bist du dir sicher?",
|
||||||
"MessageConfirmMarkSeriesNotFinished": "Alle Medien dieser Reihe werden als nicht abgeschlossen markiert! Sind Sie sicher?",
|
"MessageConfirmMarkSeriesNotFinished": "Alle Medien dieser Reihe werden als nicht abgeschlossen markiert! Bist du dir sicher?",
|
||||||
"MessageConfirmQuickEmbed": "Warnung! Audiodateien werden bei der Schnelleinbettung nicht gesichert! Achten Sie darauf, dass Sie eine Sicherungskopie der Audiodateien besitzen. <br><br>Möchten Sie fortfahren?",
|
"MessageConfirmQuickEmbed": "Warnung! Audiodateien werden bei der Schnelleinbettung nicht gesichert! Achte darauf, dass du eine Sicherungskopie der Audiodateien besitzt. <br><br>Möchtest du fortfahren?",
|
||||||
"MessageConfirmRemoveAllChapters": "Alle Kapitel werden entfernt! Sind Sie sicher?",
|
"MessageConfirmRemoveAllChapters": "Alle Kapitel werden entfernt! Bist du dir sicher?",
|
||||||
"MessageConfirmRemoveAuthor": "Autor \"{0}\" wird enfernt! Sind Sie sicher?",
|
"MessageConfirmRemoveAuthor": "Autor \"{0}\" wird enfernt! Bist du dir sicher?",
|
||||||
"MessageConfirmRemoveCollection": "Sammlung \"{0}\" wird gelöscht! Sind Sie sicher?",
|
"MessageConfirmRemoveCollection": "Sammlung \"{0}\" wird gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmRemoveEpisode": "Episode \"{0}\" wird geloscht! Sind Sie sicher?",
|
"MessageConfirmRemoveEpisode": "Episode \"{0}\" wird geloscht! Bist du dir sicher?",
|
||||||
"MessageConfirmRemoveEpisodes": "{0} Episoden werden gelöscht! Sind Sie sicher?",
|
"MessageConfirmRemoveEpisodes": "{0} Episoden werden gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmRemoveNarrator": "Erzähler \"{0}\" wird gelöscht! Sind Sie sicher?",
|
"MessageConfirmRemoveListeningSessions": "Bist du dir sicher, dass du {0} Hörsitzungen enfernen möchtest?",
|
||||||
"MessageConfirmRemovePlaylist": "Wiedergabeliste \"{0}\" wird entfernt! Sind Sie sicher?",
|
"MessageConfirmRemoveNarrator": "Erzähler \"{0}\" wird gelöscht! Bist du dir sicher?",
|
||||||
"MessageConfirmRenameGenre": "Kategorie \"{0}\" in \"{1}\" für alle Hörbücher/Podcasts werden umbenannt! Sind Sie sicher?",
|
"MessageConfirmRemovePlaylist": "Wiedergabeliste \"{0}\" wird entfernt! Bist du dir sicher?",
|
||||||
|
"MessageConfirmRenameGenre": "Kategorie \"{0}\" in \"{1}\" für alle Hörbücher/Podcasts werden umbenannt! Bist du dir sicher?",
|
||||||
"MessageConfirmRenameGenreMergeNote": "Hinweis: Kategorie existiert bereits -> Kategorien werden zusammengelegt.",
|
"MessageConfirmRenameGenreMergeNote": "Hinweis: Kategorie existiert bereits -> Kategorien werden zusammengelegt.",
|
||||||
"MessageConfirmRenameGenreWarning": "Warnung! Ein ähnliche Kategorie mit einem anderen Wortlaut existiert bereits: \"{0}\".",
|
"MessageConfirmRenameGenreWarning": "Warnung! Ein ähnliche Kategorie mit einem anderen Wortlaut existiert bereits: \"{0}\".",
|
||||||
"MessageConfirmRenameTag": "Tag \"{0}\" in \"{1}\" für alle Hörbücher/Podcasts werden umbenannt! Sind Sie sicher?",
|
"MessageConfirmRenameTag": "Tag \"{0}\" in \"{1}\" für alle Hörbücher/Podcasts werden umbenannt! Bist du dir sicher?",
|
||||||
"MessageConfirmRenameTagMergeNote": "Hinweis: Tag existiert bereits -> Tags werden zusammengelegt.",
|
"MessageConfirmRenameTagMergeNote": "Hinweis: Tag existiert bereits -> Tags werden zusammengelegt.",
|
||||||
"MessageConfirmRenameTagWarning": "Warnung! Ein ähnlicher Tag mit einem anderen Wortlaut existiert bereits: \"{0}\".",
|
"MessageConfirmRenameTagWarning": "Warnung! Ein ähnlicher Tag mit einem anderen Wortlaut existiert bereits: \"{0}\".",
|
||||||
"MessageConfirmReScanLibraryItems": "{0} Elemente werden erneut gescannt! Sind Sie sicher?",
|
"MessageConfirmReScanLibraryItems": "{0} Elemente werden erneut gescannt! Bist du dir sicher?",
|
||||||
"MessageConfirmSendEbookToDevice": "{0} E-Book \"{1}\" werden auf das Gerät \"{2}\" gesendet! Sind Sie sicher?",
|
"MessageConfirmSendEbookToDevice": "{0} E-Book \"{1}\" wird auf das Gerät \"{2}\" gesendet! Bist du dir sicher?",
|
||||||
"MessageDownloadingEpisode": "Episode herunterladen",
|
"MessageDownloadingEpisode": "Episode wird heruntergeladen",
|
||||||
"MessageDragFilesIntoTrackOrder": "Verschieben Sie die Dateien in die richtige Reihenfolge",
|
"MessageDragFilesIntoTrackOrder": "Verschiebe die Dateien in die richtige Reihenfolge",
|
||||||
"MessageEmbedFinished": "Einbettung abgeschlossen!",
|
"MessageEmbedFinished": "Einbettung abgeschlossen!",
|
||||||
"MessageEpisodesQueuedForDownload": "{0} Episode(n) in der Warteschlange zum Herunterladen",
|
"MessageEpisodesQueuedForDownload": "{0} Episode(n) in der Warteschlange zum Herunterladen",
|
||||||
"MessageFeedURLWillBe": "Feed-URL wird {0} sein",
|
"MessageFeedURLWillBe": "Feed-URL wird {0} sein",
|
||||||
"MessageFetching": "Abrufen...",
|
"MessageFetching": "Abrufen...",
|
||||||
"MessageForceReScanDescription": "durchsucht alle Dateien neu, wie bei einem frischen Scan. ID3-Tags von Audiodateien, OPF-Dateien und Textdateien werden neu durchsucht.",
|
"MessageForceReScanDescription": "Durchsucht alle Dateien erneut, wie bei einem frischen Scan. ID3-Tags von Audiodateien, OPF-Dateien und Textdateien werden neu durchsucht.",
|
||||||
"MessageImportantNotice": "Wichtiger Hinweis!",
|
"MessageImportantNotice": "Wichtiger Hinweis!",
|
||||||
"MessageInsertChapterBelow": "Kapitel unten einfügen",
|
"MessageInsertChapterBelow": "Kapitel unten einfügen",
|
||||||
"MessageItemsSelected": "{0} ausgewählte Medien",
|
"MessageItemsSelected": "{0} ausgewählte Medien",
|
||||||
"MessageItemsUpdated": "{0} Medien aktualisiert",
|
"MessageItemsUpdated": "{0} Medien aktualisiert",
|
||||||
"MessageJoinUsOn": "Besuchen Sie uns auf",
|
"MessageJoinUsOn": "Besuche uns auf",
|
||||||
"MessageListeningSessionsInTheLastYear": "{0} Ereignisse im letzten Jahr",
|
"MessageListeningSessionsInTheLastYear": "{0} Ereignisse im letzten Jahr",
|
||||||
"MessageLoading": "Laden...",
|
"MessageLoading": "Laden...",
|
||||||
"MessageLoadingFolders": "Lade Ordner...",
|
"MessageLoadingFolders": "Lade Ordner...",
|
||||||
"MessageM4BFailed": "M4B fehlgeschlagen!",
|
"MessageM4BFailed": "M4B fehlgeschlagen!",
|
||||||
"MessageM4BFinished": "M4B beendet!",
|
"MessageM4BFinished": "M4B beendet!",
|
||||||
"MessageMapChapterTitles": "Zuordnen von Kapiteltiteln zu Ihren vorhandenen Medienkapiteln ohne Anpassung der Zeitangaben",
|
"MessageMapChapterTitles": "Zuordnen von Kapiteltiteln zu deinen vorhandenen Medienkapiteln ohne Anpassung der Zeitangaben",
|
||||||
"MessageMarkAllEpisodesFinished": "Alle Episoden als beendet markieren",
|
"MessageMarkAllEpisodesFinished": "Alle Episoden als beendet markieren",
|
||||||
"MessageMarkAllEpisodesNotFinished": "Alle Episoden als ungehört markieren",
|
"MessageMarkAllEpisodesNotFinished": "Alle Episoden als ungehört markieren",
|
||||||
"MessageMarkAsFinished": "Als beendet markieren",
|
"MessageMarkAsFinished": "Als beendet markieren",
|
||||||
"MessageMarkAsNotFinished": "Als nicht abgeschlossen markieren",
|
"MessageMarkAsNotFinished": "Als nicht beendet markieren",
|
||||||
"MessageMatchBooksDescription": "Es wird versucht die Bücher in der Bibliothek mit einem Buch des ausgewählten Suchanbieters abzugleichen um leere Details und das Titelbild auszufüllen. Vorhandene Details werden nicht überschrieben.",
|
"MessageMatchBooksDescription": "Es wird versucht die Bücher in der Bibliothek mit einem Buch des ausgewählten Suchanbieters abzugleichen um leere Details und das Titelbild auszufüllen. Vorhandene Details werden nicht überschrieben.",
|
||||||
"MessageNoAudioTracks": "Keine Audiodateien",
|
"MessageNoAudioTracks": "Keine Audiodateien",
|
||||||
"MessageNoAuthors": "Keine Autoren",
|
"MessageNoAuthors": "Keine Autoren",
|
||||||
@@ -626,7 +666,7 @@
|
|||||||
"MessageNoUpdateNecessary": "Keine Aktualisierung erforderlich",
|
"MessageNoUpdateNecessary": "Keine Aktualisierung erforderlich",
|
||||||
"MessageNoUpdatesWereNecessary": "Keine Aktualisierungen waren notwendig",
|
"MessageNoUpdatesWereNecessary": "Keine Aktualisierungen waren notwendig",
|
||||||
"MessageNoUserPlaylists": "Keine Wiedergabelisten vorhanden",
|
"MessageNoUserPlaylists": "Keine Wiedergabelisten vorhanden",
|
||||||
"MessageOr": "oder",
|
"MessageOr": "Oder",
|
||||||
"MessagePauseChapter": "Kapitelwiedergabe pausieren",
|
"MessagePauseChapter": "Kapitelwiedergabe pausieren",
|
||||||
"MessagePlayChapter": "Kapitelanfang anhören",
|
"MessagePlayChapter": "Kapitelanfang anhören",
|
||||||
"MessagePlaylistCreateFromCollection": "Erstelle eine Wiedergabeliste aus der Sammlung",
|
"MessagePlaylistCreateFromCollection": "Erstelle eine Wiedergabeliste aus der Sammlung",
|
||||||
@@ -635,12 +675,13 @@
|
|||||||
"MessageRemoveChapter": "Kapitel löschen",
|
"MessageRemoveChapter": "Kapitel löschen",
|
||||||
"MessageRemoveEpisodes": "Entferne {0} Episode(n)",
|
"MessageRemoveEpisodes": "Entferne {0} Episode(n)",
|
||||||
"MessageRemoveFromPlayerQueue": "Aus der Abspielwarteliste löschen",
|
"MessageRemoveFromPlayerQueue": "Aus der Abspielwarteliste löschen",
|
||||||
"MessageRemoveUserWarning": "Benutzer \"{0}\" wird dauerhaft gelöscht! Sind Sie sicher?",
|
"MessageRemoveUserWarning": "Benutzer \"{0}\" wird dauerhaft gelöscht! Bist du dir sicher?",
|
||||||
"MessageReportBugsAndContribute": "Fehler melden, Funktionen anfordern und Beiträge leisten auf",
|
"MessageReportBugsAndContribute": "Fehler melden, Funktionen anfordern und mitwirken",
|
||||||
"MessageResetChaptersConfirm": "Kapitel und vorgenommenen Änderungen werden zurückgesetzt und rückgängig gemacht! Sind Sie sicher?",
|
"MessageResetChaptersConfirm": "Kapitel und vorgenommenen Änderungen werden zurückgesetzt und rückgängig gemacht! Bist du dir sicher?",
|
||||||
"MessageRestoreBackupConfirm": "Sind Sie sicher, dass Sie die Sicherung wiederherstellen wollen, welche am",
|
"MessageRestoreBackupConfirm": "Bist du dir sicher, dass du die Sicherung wiederherstellen willst, welche am",
|
||||||
"MessageRestoreBackupWarning": "Bei der Wiederherstellung einer Sicherung wird die gesamte Datenbank unter /config und die Titelbilder in /metadata/items und /metadata/authors überschrieben.<br /><br />Bei der Sicherung werden keine Dateien in Ihren Bibliotheksordnern verändert. Wenn Sie die Servereinstellungen aktiviert haben, um Cover und Metadaten in Ihren Bibliotheksordnern zu speichern, werden diese nicht gesichert oder überschrieben.<br /><br />Alle Clients, die Ihren Server nutzen, werden automatisch aktualisiert.",
|
"MessageRestoreBackupWarning": "Bei der Wiederherstellung einer Sicherung wird die gesamte Datenbank unter /config und die Titelbilder in /metadata/items und /metadata/authors überschrieben.<br /><br />Bei der Sicherung werden keine Dateien in deinen Bibliotheksordnern verändert. Wenn du die Servereinstellungen aktiviert hast, um Cover und Metadaten in deinen Bibliotheksordnern zu speichern, werden diese nicht gesichert oder überschrieben.<br /><br />Alle Clients, die Ihren Server nutzen, werden automatisch aktualisiert.",
|
||||||
"MessageSearchResultsFor": "Suchergebnisse für",
|
"MessageSearchResultsFor": "Suchergebnisse für",
|
||||||
|
"MessageSelected": "{0} ausgewählt",
|
||||||
"MessageServerCouldNotBeReached": "Server kann nicht erreicht werden",
|
"MessageServerCouldNotBeReached": "Server kann nicht erreicht werden",
|
||||||
"MessageSetChaptersFromTracksDescription": "Kaitelerstellung basiert auf den existierenden einzelnen Audiodateien. Pro existierende Audiodatei wird 1 Kapitel erstellt, wobei deren Kapitelname aus dem Audiodateinamen extrahiert wird",
|
"MessageSetChaptersFromTracksDescription": "Kaitelerstellung basiert auf den existierenden einzelnen Audiodateien. Pro existierende Audiodatei wird 1 Kapitel erstellt, wobei deren Kapitelname aus dem Audiodateinamen extrahiert wird",
|
||||||
"MessageStartPlaybackAtTime": "Start der Wiedergabe für \"{0}\" bei {1}?",
|
"MessageStartPlaybackAtTime": "Start der Wiedergabe für \"{0}\" bei {1}?",
|
||||||
@@ -651,16 +692,15 @@
|
|||||||
"MessageValidCronExpression": "Gültiger Cron-Ausdruck",
|
"MessageValidCronExpression": "Gültiger Cron-Ausdruck",
|
||||||
"MessageWatcherIsDisabledGlobally": "Überwachung ist in den Servereinstellungen global deaktiviert",
|
"MessageWatcherIsDisabledGlobally": "Überwachung ist in den Servereinstellungen global deaktiviert",
|
||||||
"MessageXLibraryIsEmpty": "{0} Bibliothek ist leer!",
|
"MessageXLibraryIsEmpty": "{0} Bibliothek ist leer!",
|
||||||
"MessageYourAudiobookDurationIsLonger": "Die Dauer Ihres Mediums ist länger als die gefundene Dauer",
|
"MessageYourAudiobookDurationIsLonger": "Die Dauer deines Mediums ist länger als die gefundene Dauer",
|
||||||
"MessageYourAudiobookDurationIsShorter": "Die Dauer Ihres Mediums ist kürzer als die gefundene Dauer",
|
"MessageYourAudiobookDurationIsShorter": "Die Dauer deines Mediums ist kürzer als die gefundene Dauer",
|
||||||
"NoteChangeRootPassword": "Der Root-Benutzer (Hauptbenutzer) ist der einzige Benutzer, der ein leeres Passwort haben kann",
|
"NoteChangeRootPassword": "Der Root-Benutzer (Hauptbenutzer) ist der einzige Benutzer, der ein leeres Passwort haben kann",
|
||||||
"NoteChapterEditorTimes": "Hinweis: Die Anfangszeit des ersten Kapitels muss bei 0:00 beginnen und die Anfangszeit des letzten Kapitels darf die Dauer des Mediums nicht überschreiten.",
|
"NoteChapterEditorTimes": "Hinweis: Die Anfangszeit des ersten Kapitels muss bei 0:00 beginnen und die Anfangszeit des letzten Kapitels darf die Dauer des Mediums nicht überschreiten.",
|
||||||
"NoteFolderPicker": "Hinweis: Bereits zugeordnete Ordner werden nicht angezeigt.",
|
"NoteFolderPicker": "Hinweis: Bereits zugeordnete Ordner werden nicht angezeigt.",
|
||||||
"NoteFolderPickerDebian": "Hinweis: Der Ordnerauswahldialog für die Debian-Installation ist nicht vollständig implementiert. Sie sollten den Pfad zu Ihrer Bibliothek direkt eingeben.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Warnung: Die meisten Podcast-Apps verlangen, dass die URL des RSS-Feeds HTTPS verwendet.",
|
"NoteRSSFeedPodcastAppsHttps": "Warnung: Die meisten Podcast-Apps verlangen, dass die URL des RSS-Feeds HTTPS verwendet.",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Warnung: 1 oder mehrere Ihrer Episoden haben kein Veröffentlichungsdatum. Einige Podcast-Apps verlangen dies.",
|
"NoteRSSFeedPodcastAppsPubDate": "Warnung: 1 oder mehrere deiner Episoden haben kein Veröffentlichungsdatum. Einige Podcast-Apps verlangen dies.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Ordner mit Mediendateien werden als separate Bibliothekselemente behandelt.",
|
"NoteUploaderFoldersWithMediaFiles": "Ordner mit Mediendateien werden als separate Bibliothekselemente behandelt.",
|
||||||
"NoteUploaderOnlyAudioFiles": "Wenn Sie nur Audiodateien hochladen, wird jede Audiodatei als ein separates Medium behandelt.",
|
"NoteUploaderOnlyAudioFiles": "Wenn du nur Audiodateien hochlädst, wird jede Audiodatei als ein separates Medium behandelt.",
|
||||||
"NoteUploaderUnsupportedFiles": "Nicht unterstützte Dateien werden ignoriert. Bei der Auswahl oder dem Löschen eines Ordners werden andere Dateien, die sich nicht in einem Elementordner befinden, ignoriert.",
|
"NoteUploaderUnsupportedFiles": "Nicht unterstützte Dateien werden ignoriert. Bei der Auswahl oder dem Löschen eines Ordners werden andere Dateien, die sich nicht in einem Elementordner befinden, ignoriert.",
|
||||||
"PlaceholderNewCollection": "Neuer Sammlungsname",
|
"PlaceholderNewCollection": "Neuer Sammlungsname",
|
||||||
"PlaceholderNewFolderPath": "Neuer Ordnerpfad",
|
"PlaceholderNewFolderPath": "Neuer Ordnerpfad",
|
||||||
@@ -728,7 +768,7 @@
|
|||||||
"ToastRSSFeedCloseFailed": "RSS-Feed konnte nicht geschlossen werden",
|
"ToastRSSFeedCloseFailed": "RSS-Feed konnte nicht geschlossen werden",
|
||||||
"ToastRSSFeedCloseSuccess": "RSS-Feed geschlossen",
|
"ToastRSSFeedCloseSuccess": "RSS-Feed geschlossen",
|
||||||
"ToastSendEbookToDeviceFailed": "E-Book konnte nicht auf Gerät übertragen werden",
|
"ToastSendEbookToDeviceFailed": "E-Book konnte nicht auf Gerät übertragen werden",
|
||||||
"ToastSendEbookToDeviceSuccess": "E-Book an Gerät senden \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "E-Book an Gerät \"{0}\" gesendet",
|
||||||
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
|
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
|
||||||
"ToastSeriesUpdateSuccess": "Serien aktualisiert",
|
"ToastSeriesUpdateSuccess": "Serien aktualisiert",
|
||||||
"ToastSessionDeleteFailed": "Sitzung konnte nicht gelöscht werden",
|
"ToastSessionDeleteFailed": "Sitzung konnte nicht gelöscht werden",
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Hide",
|
"ButtonHide": "Hide",
|
||||||
"ButtonHome": "Home",
|
"ButtonHome": "Home",
|
||||||
"ButtonIssues": "Issues",
|
"ButtonIssues": "Issues",
|
||||||
|
"ButtonJumpBackward": "Jump Backward",
|
||||||
|
"ButtonJumpForward": "Jump Forward",
|
||||||
"ButtonLatest": "Latest",
|
"ButtonLatest": "Latest",
|
||||||
"ButtonLibrary": "Library",
|
"ButtonLibrary": "Library",
|
||||||
"ButtonLogout": "Logout",
|
"ButtonLogout": "Logout",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "Match All Authors",
|
"ButtonMatchAllAuthors": "Match All Authors",
|
||||||
"ButtonMatchBooks": "Match Books",
|
"ButtonMatchBooks": "Match Books",
|
||||||
"ButtonNevermind": "Nevermind",
|
"ButtonNevermind": "Nevermind",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Next Chapter",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Open Feed",
|
"ButtonOpenFeed": "Open Feed",
|
||||||
"ButtonOpenManager": "Open Manager",
|
"ButtonOpenManager": "Open Manager",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "Play",
|
"ButtonPlay": "Play",
|
||||||
"ButtonPlaying": "Playing",
|
"ButtonPlaying": "Playing",
|
||||||
"ButtonPlaylists": "Playlists",
|
"ButtonPlaylists": "Playlists",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Previous Chapter",
|
||||||
"ButtonPurgeAllCache": "Purge All Cache",
|
"ButtonPurgeAllCache": "Purge All Cache",
|
||||||
"ButtonPurgeItemsCache": "Purge Items Cache",
|
"ButtonPurgeItemsCache": "Purge Items Cache",
|
||||||
"ButtonPurgeMediaProgress": "Purge Media Progress",
|
"ButtonPurgeMediaProgress": "Purge Media Progress",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "Remove from queue",
|
"ButtonQueueRemoveItem": "Remove from queue",
|
||||||
"ButtonQuickMatch": "Quick Match",
|
"ButtonQuickMatch": "Quick Match",
|
||||||
"ButtonRead": "Read",
|
"ButtonRead": "Read",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "Remove",
|
"ButtonRemove": "Remove",
|
||||||
"ButtonRemoveAll": "Remove All",
|
"ButtonRemoveAll": "Remove All",
|
||||||
"ButtonRemoveAllLibraryItems": "Remove All Library Items",
|
"ButtonRemoveAllLibraryItems": "Remove All Library Items",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "Select Folder Path",
|
"ButtonSelectFolderPath": "Select Folder Path",
|
||||||
"ButtonSeries": "Series",
|
"ButtonSeries": "Series",
|
||||||
"ButtonSetChaptersFromTracks": "Set chapters from tracks",
|
"ButtonSetChaptersFromTracks": "Set chapters from tracks",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "Shift Times",
|
"ButtonShiftTimes": "Shift Times",
|
||||||
"ButtonShow": "Show",
|
"ButtonShow": "Show",
|
||||||
"ButtonStartM4BEncode": "Start M4B Encode",
|
"ButtonStartM4BEncode": "Start M4B Encode",
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"ButtonUserEdit": "Edit user {0}",
|
"ButtonUserEdit": "Edit user {0}",
|
||||||
"ButtonViewAll": "View All",
|
"ButtonViewAll": "View All",
|
||||||
"ButtonYes": "Yes",
|
"ButtonYes": "Yes",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error fetching metadata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Could not fetch metadata - try updating title and/or author",
|
||||||
|
"ErrorUploadLacksTitle": "Must have a title",
|
||||||
"HeaderAccount": "Account",
|
"HeaderAccount": "Account",
|
||||||
"HeaderAdvanced": "Advanced",
|
"HeaderAdvanced": "Advanced",
|
||||||
"HeaderAppriseNotificationSettings": "Apprise Notification Settings",
|
"HeaderAppriseNotificationSettings": "Apprise Notification Settings",
|
||||||
@@ -101,6 +113,7 @@
|
|||||||
"HeaderCollectionItems": "Collection Items",
|
"HeaderCollectionItems": "Collection Items",
|
||||||
"HeaderCover": "Cover",
|
"HeaderCover": "Cover",
|
||||||
"HeaderCurrentDownloads": "Current Downloads",
|
"HeaderCurrentDownloads": "Current Downloads",
|
||||||
|
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
|
||||||
"HeaderDetails": "Details",
|
"HeaderDetails": "Details",
|
||||||
"HeaderDownloadQueue": "Download Queue",
|
"HeaderDownloadQueue": "Download Queue",
|
||||||
"HeaderEbookFiles": "Ebook Files",
|
"HeaderEbookFiles": "Ebook Files",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Update Details",
|
"HeaderUpdateDetails": "Update Details",
|
||||||
"HeaderUpdateLibrary": "Update Library",
|
"HeaderUpdateLibrary": "Update Library",
|
||||||
"HeaderUsers": "Users",
|
"HeaderUsers": "Users",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Your Stats",
|
"HeaderYourStats": "Your Stats",
|
||||||
"LabelAbridged": "Abridged",
|
"LabelAbridged": "Abridged",
|
||||||
"LabelAccountType": "Account Type",
|
"LabelAccountType": "Account Type",
|
||||||
@@ -196,6 +210,8 @@
|
|||||||
"LabelAuthorLastFirst": "Author (Last, First)",
|
"LabelAuthorLastFirst": "Author (Last, First)",
|
||||||
"LabelAuthors": "Authors",
|
"LabelAuthors": "Authors",
|
||||||
"LabelAutoDownloadEpisodes": "Auto Download Episodes",
|
"LabelAutoDownloadEpisodes": "Auto Download Episodes",
|
||||||
|
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Fetches metadata for title, author, and series to streamline uploading. Additional metadata may have to be matched after upload.",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoLaunch": "Auto Launch",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoRegister": "Auto Register",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Example",
|
"LabelExample": "Example",
|
||||||
"LabelExplicit": "Explicit",
|
"LabelExplicit": "Explicit",
|
||||||
"LabelFeedURL": "Feed URL",
|
"LabelFeedURL": "Feed URL",
|
||||||
|
"LabelFetchingMetadata": "Fetching Metadata",
|
||||||
"LabelFile": "File",
|
"LabelFile": "File",
|
||||||
"LabelFileBirthtime": "File Birthtime",
|
"LabelFileBirthtime": "File Birthtime",
|
||||||
"LabelFileModified": "File Modified",
|
"LabelFileModified": "File Modified",
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folders",
|
"LabelFolders": "Folders",
|
||||||
|
"LabelFontBold": "Bold",
|
||||||
"LabelFontFamily": "Font family",
|
"LabelFontFamily": "Font family",
|
||||||
|
"LabelFontItalic": "Italic",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
|
"LabelFontStrikethrough": "Strikethrough",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Genres",
|
"LabelGenres": "Genres",
|
||||||
@@ -336,7 +356,11 @@
|
|||||||
"LabelMetaTags": "Meta Tags",
|
"LabelMetaTags": "Meta Tags",
|
||||||
"LabelMinute": "Minute",
|
"LabelMinute": "Minute",
|
||||||
"LabelMissing": "Missing",
|
"LabelMissing": "Missing",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Missing Parts",
|
"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.",
|
||||||
"LabelMore": "More",
|
"LabelMore": "More",
|
||||||
"LabelMoreInfo": "More Info",
|
"LabelMoreInfo": "More Info",
|
||||||
"LabelName": "Name",
|
"LabelName": "Name",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Can Download",
|
"LabelPermissionsDownload": "Can Download",
|
||||||
"LabelPermissionsUpdate": "Can Update",
|
"LabelPermissionsUpdate": "Can Update",
|
||||||
"LabelPermissionsUpload": "Can Upload",
|
"LabelPermissionsUpload": "Can Upload",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Photo Path/URL",
|
"LabelPhotoPathURL": "Photo Path/URL",
|
||||||
"LabelPlaylists": "Playlists",
|
"LabelPlaylists": "Playlists",
|
||||||
"LabelPlayMethod": "Play Method",
|
"LabelPlayMethod": "Play Method",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "Podcast search region",
|
||||||
"LabelPodcastType": "Podcast Type",
|
"LabelPodcastType": "Podcast Type",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Prefixes to Ignore (case insensitive)",
|
"LabelPrefixesToIgnore": "Prefixes to Ignore (case insensitive)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Recently Added",
|
"LabelRecentlyAdded": "Recently Added",
|
||||||
"LabelRecentSeries": "Recent Series",
|
"LabelRecentSeries": "Recent Series",
|
||||||
"LabelRecommended": "Recommended",
|
"LabelRecommended": "Recommended",
|
||||||
|
"LabelRedo": "Redo",
|
||||||
"LabelRegion": "Region",
|
"LabelRegion": "Region",
|
||||||
"LabelReleaseDate": "Release Date",
|
"LabelReleaseDate": "Release Date",
|
||||||
"LabelRemoveCover": "Remove cover",
|
"LabelRemoveCover": "Remove cover",
|
||||||
|
"LabelRowsPerPage": "Rows per page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
||||||
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
||||||
"LabelRSSFeedOpen": "RSS Feed Open",
|
"LabelRSSFeedOpen": "RSS Feed Open",
|
||||||
@@ -416,6 +444,7 @@
|
|||||||
"LabelSeries": "Series",
|
"LabelSeries": "Series",
|
||||||
"LabelSeriesName": "Series Name",
|
"LabelSeriesName": "Series Name",
|
||||||
"LabelSeriesProgress": "Series Progress",
|
"LabelSeriesProgress": "Series Progress",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Set as primary",
|
"LabelSetEbookAsPrimary": "Set as primary",
|
||||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
|
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
|
||||||
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
|
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
|
||||||
|
"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": "Parse subtitles",
|
"LabelSettingsParseSubtitles": "Parse subtitles",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by \" - \"<br>i.e. \"Book Title - A Subtitle Here\" has the subtitle \"A Subtitle Here\"",
|
"LabelSettingsParseSubtitlesHelp": "Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by \" - \"<br>i.e. \"Book Title - A Subtitle Here\" has the subtitle \"A Subtitle Here\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
||||||
@@ -482,6 +513,10 @@
|
|||||||
"LabelTagsAccessibleToUser": "Tags Accessible to User",
|
"LabelTagsAccessibleToUser": "Tags Accessible to User",
|
||||||
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
||||||
"LabelTasks": "Tasks Running",
|
"LabelTasks": "Tasks Running",
|
||||||
|
"LabelTextEditorBulletedList": "Bulleted list",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numbered list",
|
||||||
|
"LabelTextEditorUnlink": "Unlink",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Theme",
|
||||||
"LabelThemeDark": "Dark",
|
"LabelThemeDark": "Dark",
|
||||||
"LabelThemeLight": "Light",
|
"LabelThemeLight": "Light",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Single-track",
|
"LabelTracksSingleTrack": "Single-track",
|
||||||
"LabelType": "Type",
|
"LabelType": "Type",
|
||||||
"LabelUnabridged": "Unabridged",
|
"LabelUnabridged": "Unabridged",
|
||||||
|
"LabelUndo": "Undo",
|
||||||
"LabelUnknown": "Unknown",
|
"LabelUnknown": "Unknown",
|
||||||
"LabelUpdateCover": "Update Cover",
|
"LabelUpdateCover": "Update Cover",
|
||||||
"LabelUpdateCoverHelp": "Allow overwriting of existing covers for the selected books when a match is located",
|
"LabelUpdateCoverHelp": "Allow overwriting of existing covers for the selected books when a match is located",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
|
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
|
||||||
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
||||||
"LabelUploaderDropFiles": "Drop files",
|
"LabelUploaderDropFiles": "Drop files",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||||
"LabelUseChapterTrack": "Use chapter track",
|
"LabelUseChapterTrack": "Use chapter track",
|
||||||
"LabelUseFullTrack": "Use full track",
|
"LabelUseFullTrack": "Use full track",
|
||||||
"LabelUser": "User",
|
"LabelUser": "User",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "View player queue",
|
"LabelViewQueue": "View player queue",
|
||||||
"LabelVolume": "Volume",
|
"LabelVolume": "Volume",
|
||||||
"LabelWeekdaysToRun": "Weekdays to run",
|
"LabelWeekdaysToRun": "Weekdays to run",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Your audiobook duration",
|
"LabelYourAudiobookDuration": "Your audiobook duration",
|
||||||
"LabelYourBookmarks": "Your Bookmarks",
|
"LabelYourBookmarks": "Your Bookmarks",
|
||||||
"LabelYourPlaylists": "Your Playlists",
|
"LabelYourPlaylists": "Your Playlists",
|
||||||
@@ -562,6 +601,7 @@
|
|||||||
"MessageConfirmRemoveCollection": "Are you sure you want to remove collection \"{0}\"?",
|
"MessageConfirmRemoveCollection": "Are you sure you want to remove collection \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on",
|
"MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on",
|
||||||
"MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.<br /><br />Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.<br /><br />All clients using your server will be automatically refreshed.",
|
"MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.<br /><br />Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.<br /><br />All clients using your server will be automatically refreshed.",
|
||||||
"MessageSearchResultsFor": "Search results for",
|
"MessageSearchResultsFor": "Search results for",
|
||||||
|
"MessageSelected": "{0} selected",
|
||||||
"MessageServerCouldNotBeReached": "Server could not be reached",
|
"MessageServerCouldNotBeReached": "Server could not be reached",
|
||||||
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
||||||
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "Root user is the only user that can have an empty password",
|
"NoteChangeRootPassword": "Root user is the only user that can have an empty password",
|
||||||
"NoteChapterEditorTimes": "Note: First chapter start time must remain at 0:00 and the last chapter start time cannot exceed this audiobooks duration.",
|
"NoteChapterEditorTimes": "Note: First chapter start time must remain at 0:00 and the last chapter start time cannot exceed this audiobooks duration.",
|
||||||
"NoteFolderPicker": "Note: folders already mapped will not be shown",
|
"NoteFolderPicker": "Note: folders already mapped will not be shown",
|
||||||
"NoteFolderPickerDebian": "Note: Folder picker for the debian install is not fully implemented. You should enter the path to your library directly.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Warning: Most podcast apps will require the RSS feed URL is using HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Warning: Most podcast apps will require the RSS feed URL is using HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.",
|
"NoteRSSFeedPodcastAppsPubDate": "Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
||||||
|
|||||||
+76
-36
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"ButtonAdd": "Agregar",
|
"ButtonAdd": "Agregar",
|
||||||
"ButtonAddChapters": "Agregar Capitulo",
|
"ButtonAddChapters": "Agregar Capitulo",
|
||||||
"ButtonAddDevice": "Add Device",
|
"ButtonAddDevice": "Agregar Dispositivo",
|
||||||
"ButtonAddLibrary": "Add Library",
|
"ButtonAddLibrary": "Crear Biblioteca",
|
||||||
"ButtonAddPodcasts": "Agregar Podcasts",
|
"ButtonAddPodcasts": "Agregar Podcasts",
|
||||||
"ButtonAddUser": "Add User",
|
"ButtonAddUser": "Crear Usuario",
|
||||||
"ButtonAddYourFirstLibrary": "Agrega tu Primera Biblioteca",
|
"ButtonAddYourFirstLibrary": "Crea tu Primera Biblioteca",
|
||||||
"ButtonApply": "Aplicar",
|
"ButtonApply": "Aplicar",
|
||||||
"ButtonApplyChapters": "Aplicar Capítulos",
|
"ButtonApplyChapters": "Aplicar Capítulos",
|
||||||
"ButtonAuthors": "Autores",
|
"ButtonAuthors": "Autores",
|
||||||
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Esconder",
|
"ButtonHide": "Esconder",
|
||||||
"ButtonHome": "Inicio",
|
"ButtonHome": "Inicio",
|
||||||
"ButtonIssues": "Problemas",
|
"ButtonIssues": "Problemas",
|
||||||
|
"ButtonJumpBackward": "Retroceder",
|
||||||
|
"ButtonJumpForward": "Adelantar",
|
||||||
"ButtonLatest": "Últimos",
|
"ButtonLatest": "Últimos",
|
||||||
"ButtonLibrary": "Biblioteca",
|
"ButtonLibrary": "Biblioteca",
|
||||||
"ButtonLogout": "Cerrar Sesión",
|
"ButtonLogout": "Cerrar Sesión",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "Encontrar Todos los Autores",
|
"ButtonMatchAllAuthors": "Encontrar Todos los Autores",
|
||||||
"ButtonMatchBooks": "Encontrar Libros",
|
"ButtonMatchBooks": "Encontrar Libros",
|
||||||
"ButtonNevermind": "Olvidar",
|
"ButtonNevermind": "Olvidar",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Siguiente Capítulo",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Abrir Fuente",
|
"ButtonOpenFeed": "Abrir Fuente",
|
||||||
"ButtonOpenManager": "Abrir Editor",
|
"ButtonOpenManager": "Abrir Editor",
|
||||||
|
"ButtonPause": "Pausar",
|
||||||
"ButtonPlay": "Reproducir",
|
"ButtonPlay": "Reproducir",
|
||||||
"ButtonPlaying": "Reproduciendo",
|
"ButtonPlaying": "Reproduciendo",
|
||||||
"ButtonPlaylists": "Listas de Reproducción",
|
"ButtonPlaylists": "Listas de Reproducción",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Capítulo Anterior",
|
||||||
"ButtonPurgeAllCache": "Purgar Todo el Cache",
|
"ButtonPurgeAllCache": "Purgar Todo el Cache",
|
||||||
"ButtonPurgeItemsCache": "Purgar Elementos de Cache",
|
"ButtonPurgeItemsCache": "Purgar Elementos de Cache",
|
||||||
"ButtonPurgeMediaProgress": "Purgar Progreso de Multimedia",
|
"ButtonPurgeMediaProgress": "Purgar Progreso de Multimedia",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "Remover de la Fila",
|
"ButtonQueueRemoveItem": "Remover de la Fila",
|
||||||
"ButtonQuickMatch": "Encontrar Rápido",
|
"ButtonQuickMatch": "Encontrar Rápido",
|
||||||
"ButtonRead": "Leer",
|
"ButtonRead": "Leer",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "Remover",
|
"ButtonRemove": "Remover",
|
||||||
"ButtonRemoveAll": "Remover Todos",
|
"ButtonRemoveAll": "Remover Todos",
|
||||||
"ButtonRemoveAllLibraryItems": "Remover Todos los Elementos de la Biblioteca",
|
"ButtonRemoveAllLibraryItems": "Remover Todos los Elementos de la Biblioteca",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "Seleccionar Ruta de Carpeta",
|
"ButtonSelectFolderPath": "Seleccionar Ruta de Carpeta",
|
||||||
"ButtonSeries": "Series",
|
"ButtonSeries": "Series",
|
||||||
"ButtonSetChaptersFromTracks": "Seleccionar Capítulos Según las Pistas",
|
"ButtonSetChaptersFromTracks": "Seleccionar Capítulos Según las Pistas",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "Desplazar Tiempos",
|
"ButtonShiftTimes": "Desplazar Tiempos",
|
||||||
"ButtonShow": "Mostrar",
|
"ButtonShow": "Mostrar",
|
||||||
"ButtonStartM4BEncode": "Iniciar Codificación M4B",
|
"ButtonStartM4BEncode": "Iniciar Codificación M4B",
|
||||||
@@ -87,12 +96,15 @@
|
|||||||
"ButtonUserEdit": "Editar Usuario {0}",
|
"ButtonUserEdit": "Editar Usuario {0}",
|
||||||
"ButtonViewAll": "Ver Todos",
|
"ButtonViewAll": "Ver Todos",
|
||||||
"ButtonYes": "Aceptar",
|
"ButtonYes": "Aceptar",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error obteniendo metadatos",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "No se pudo obtener metadatos - Intenta actualizar el título y/o autor",
|
||||||
|
"ErrorUploadLacksTitle": "Se debe tener título",
|
||||||
"HeaderAccount": "Cuenta",
|
"HeaderAccount": "Cuenta",
|
||||||
"HeaderAdvanced": "Avanzado",
|
"HeaderAdvanced": "Avanzado",
|
||||||
"HeaderAppriseNotificationSettings": "Ajustes de Notificaciones de Apprise",
|
"HeaderAppriseNotificationSettings": "Ajustes de Notificaciones de Apprise",
|
||||||
"HeaderAudiobookTools": "Herramientas de Gestión de Archivos de Audiolibro",
|
"HeaderAudiobookTools": "Herramientas de Gestión de Archivos de Audiolibro",
|
||||||
"HeaderAudioTracks": "Pistas de Audio",
|
"HeaderAudioTracks": "Pistas de Audio",
|
||||||
"HeaderAuthentication": "Authentication",
|
"HeaderAuthentication": "Autenticación",
|
||||||
"HeaderBackups": "Respaldos",
|
"HeaderBackups": "Respaldos",
|
||||||
"HeaderChangePassword": "Cambiar Contraseña",
|
"HeaderChangePassword": "Cambiar Contraseña",
|
||||||
"HeaderChapters": "Capítulos",
|
"HeaderChapters": "Capítulos",
|
||||||
@@ -101,6 +113,7 @@
|
|||||||
"HeaderCollectionItems": "Elementos en la Colección",
|
"HeaderCollectionItems": "Elementos en la Colección",
|
||||||
"HeaderCover": "Portada",
|
"HeaderCover": "Portada",
|
||||||
"HeaderCurrentDownloads": "Descargando Actualmente",
|
"HeaderCurrentDownloads": "Descargando Actualmente",
|
||||||
|
"HeaderCustomMetadataProviders": "Proveedores de metadatos personalizados",
|
||||||
"HeaderDetails": "Detalles",
|
"HeaderDetails": "Detalles",
|
||||||
"HeaderDownloadQueue": "Lista de Descarga",
|
"HeaderDownloadQueue": "Lista de Descarga",
|
||||||
"HeaderEbookFiles": "Archivos de Ebook",
|
"HeaderEbookFiles": "Archivos de Ebook",
|
||||||
@@ -127,15 +140,15 @@
|
|||||||
"HeaderManageTags": "Administrar Etiquetas",
|
"HeaderManageTags": "Administrar Etiquetas",
|
||||||
"HeaderMapDetails": "Asignar Detalles",
|
"HeaderMapDetails": "Asignar Detalles",
|
||||||
"HeaderMatch": "Encontrar",
|
"HeaderMatch": "Encontrar",
|
||||||
"HeaderMetadataOrderOfPrecedence": "Metadata order of precedence",
|
"HeaderMetadataOrderOfPrecedence": "Orden de precedencia de metadatos",
|
||||||
"HeaderMetadataToEmbed": "Metadatos para Insertar",
|
"HeaderMetadataToEmbed": "Metadatos para Insertar",
|
||||||
"HeaderNewAccount": "Nueva Cuenta",
|
"HeaderNewAccount": "Nueva Cuenta",
|
||||||
"HeaderNewLibrary": "Nueva Biblioteca",
|
"HeaderNewLibrary": "Nueva Biblioteca",
|
||||||
"HeaderNotifications": "Notificaciones",
|
"HeaderNotifications": "Notificaciones",
|
||||||
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentication",
|
"HeaderOpenIDConnectAuthentication": "Autenticación OpenID Connect",
|
||||||
"HeaderOpenRSSFeed": "Abrir fuente RSS",
|
"HeaderOpenRSSFeed": "Abrir fuente RSS",
|
||||||
"HeaderOtherFiles": "Otros Archivos",
|
"HeaderOtherFiles": "Otros Archivos",
|
||||||
"HeaderPasswordAuthentication": "Password Authentication",
|
"HeaderPasswordAuthentication": "Autenticación por contraseña",
|
||||||
"HeaderPermissions": "Permisos",
|
"HeaderPermissions": "Permisos",
|
||||||
"HeaderPlayerQueue": "Fila del Reproductor",
|
"HeaderPlayerQueue": "Fila del Reproductor",
|
||||||
"HeaderPlaylist": "Lista de Reproducción",
|
"HeaderPlaylist": "Lista de Reproducción",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Actualizar Detalles",
|
"HeaderUpdateDetails": "Actualizar Detalles",
|
||||||
"HeaderUpdateLibrary": "Actualizar Biblioteca",
|
"HeaderUpdateLibrary": "Actualizar Biblioteca",
|
||||||
"HeaderUsers": "Usuarios",
|
"HeaderUsers": "Usuarios",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Tus Estadísticas",
|
"HeaderYourStats": "Tus Estadísticas",
|
||||||
"LabelAbridged": "Abreviado",
|
"LabelAbridged": "Abreviado",
|
||||||
"LabelAccountType": "Tipo de Cuenta",
|
"LabelAccountType": "Tipo de Cuenta",
|
||||||
@@ -184,11 +198,11 @@
|
|||||||
"LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección",
|
"LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección",
|
||||||
"LabelAddToPlaylist": "Añadido a la Lista de Reproducción",
|
"LabelAddToPlaylist": "Añadido a la Lista de Reproducción",
|
||||||
"LabelAddToPlaylistBatch": "Se Añadieron {0} Artículos a la Lista de Reproducción",
|
"LabelAddToPlaylistBatch": "Se Añadieron {0} Artículos a la Lista de Reproducción",
|
||||||
"LabelAdminUsersOnly": "Admin users only",
|
"LabelAdminUsersOnly": "Solamente usuarios administradores",
|
||||||
"LabelAll": "Todos",
|
"LabelAll": "Todos",
|
||||||
"LabelAllUsers": "Todos los Usuarios",
|
"LabelAllUsers": "Todos los Usuarios",
|
||||||
"LabelAllUsersExcludingGuests": "All users excluding guests",
|
"LabelAllUsersExcludingGuests": "Todos los usuarios excepto invitados",
|
||||||
"LabelAllUsersIncludingGuests": "All users including guests",
|
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
|
||||||
"LabelAlreadyInYourLibrary": "Ya en la Biblioteca",
|
"LabelAlreadyInYourLibrary": "Ya en la Biblioteca",
|
||||||
"LabelAppend": "Adjuntar",
|
"LabelAppend": "Adjuntar",
|
||||||
"LabelAuthor": "Autor",
|
"LabelAuthor": "Autor",
|
||||||
@@ -196,10 +210,12 @@
|
|||||||
"LabelAuthorLastFirst": "Autor (Apellido, Nombre)",
|
"LabelAuthorLastFirst": "Autor (Apellido, Nombre)",
|
||||||
"LabelAuthors": "Autores",
|
"LabelAuthors": "Autores",
|
||||||
"LabelAutoDownloadEpisodes": "Descargar Episodios Automáticamente",
|
"LabelAutoDownloadEpisodes": "Descargar Episodios Automáticamente",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoFetchMetadata": "Actualizar Metadatos Automáticamente",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoFetchMetadataHelp": "Obtiene metadatos de título, autor y serie para agilizar la carga. Es posible que haya que cotejar metadatos adicionales después de la carga.",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoLaunch": "Lanzamiento automático",
|
||||||
"LabelAutoRegisterDescription": "Automatically create new users after logging in",
|
"LabelAutoLaunchDescription": "Redirigir al proveedor de autenticación automáticamente al navegar a la página de inicio de sesión (ruta de sobreescritura manual <code>/login?autoLaunch=0</code>)",
|
||||||
|
"LabelAutoRegister": "Registro automático",
|
||||||
|
"LabelAutoRegisterDescription": "Crear usuarios automáticamente tras iniciar sesión",
|
||||||
"LabelBackToUser": "Regresar a Usuario",
|
"LabelBackToUser": "Regresar a Usuario",
|
||||||
"LabelBackupLocation": "Ubicación del Respaldo",
|
"LabelBackupLocation": "Ubicación del Respaldo",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático",
|
"LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático",
|
||||||
@@ -210,13 +226,13 @@
|
|||||||
"LabelBackupsNumberToKeepHelp": "Solamente 1 respaldo se removerá a la vez. Si tiene mas respaldos guardados, debe removerlos manualmente.",
|
"LabelBackupsNumberToKeepHelp": "Solamente 1 respaldo se removerá a la vez. Si tiene mas respaldos guardados, debe removerlos manualmente.",
|
||||||
"LabelBitrate": "Bitrate",
|
"LabelBitrate": "Bitrate",
|
||||||
"LabelBooks": "Libros",
|
"LabelBooks": "Libros",
|
||||||
"LabelButtonText": "Button Text",
|
"LabelButtonText": "Texto del botón",
|
||||||
"LabelChangePassword": "Cambiar Contraseña",
|
"LabelChangePassword": "Cambiar Contraseña",
|
||||||
"LabelChannels": "Canales",
|
"LabelChannels": "Canales",
|
||||||
"LabelChapters": "Capítulos",
|
"LabelChapters": "Capítulos",
|
||||||
"LabelChaptersFound": "Capítulo Encontrado",
|
"LabelChaptersFound": "Capítulo Encontrado",
|
||||||
"LabelChapterTitle": "Titulo del Capítulo",
|
"LabelChapterTitle": "Titulo del Capítulo",
|
||||||
"LabelClickForMoreInfo": "Click for more info",
|
"LabelClickForMoreInfo": "Click para más información",
|
||||||
"LabelClosePlayer": "Cerrar Reproductor",
|
"LabelClosePlayer": "Cerrar Reproductor",
|
||||||
"LabelCodec": "Codec",
|
"LabelCodec": "Codec",
|
||||||
"LabelCollapseSeries": "Colapsar Serie",
|
"LabelCollapseSeries": "Colapsar Serie",
|
||||||
@@ -235,12 +251,12 @@
|
|||||||
"LabelCurrently": "En este momento:",
|
"LabelCurrently": "En este momento:",
|
||||||
"LabelCustomCronExpression": "Expresión de Cron Personalizada:",
|
"LabelCustomCronExpression": "Expresión de Cron Personalizada:",
|
||||||
"LabelDatetime": "Hora y Fecha",
|
"LabelDatetime": "Hora y Fecha",
|
||||||
"LabelDeleteFromFileSystemCheckbox": "Delete from file system (uncheck to only remove from database)",
|
"LabelDeleteFromFileSystemCheckbox": "Eliminar archivos del sistema (desmarcar para eliminar sólo de la base de datos)",
|
||||||
"LabelDescription": "Descripción",
|
"LabelDescription": "Descripción",
|
||||||
"LabelDeselectAll": "Deseleccionar Todos",
|
"LabelDeselectAll": "Deseleccionar Todos",
|
||||||
"LabelDevice": "Dispositivo",
|
"LabelDevice": "Dispositivo",
|
||||||
"LabelDeviceInfo": "Información de Dispositivo",
|
"LabelDeviceInfo": "Información de Dispositivo",
|
||||||
"LabelDeviceIsAvailableTo": "Device is available to...",
|
"LabelDeviceIsAvailableTo": "El dispositivo está disponible para...",
|
||||||
"LabelDirectory": "Directorio",
|
"LabelDirectory": "Directorio",
|
||||||
"LabelDiscFromFilename": "Disco a partir del Nombre del Archivo",
|
"LabelDiscFromFilename": "Disco a partir del Nombre del Archivo",
|
||||||
"LabelDiscFromMetadata": "Disco a partir de Metadata",
|
"LabelDiscFromMetadata": "Disco a partir de Metadata",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Ejemplo",
|
"LabelExample": "Ejemplo",
|
||||||
"LabelExplicit": "Explicito",
|
"LabelExplicit": "Explicito",
|
||||||
"LabelFeedURL": "Fuente de URL",
|
"LabelFeedURL": "Fuente de URL",
|
||||||
|
"LabelFetchingMetadata": "Obteniendo metadatos",
|
||||||
"LabelFile": "Archivo",
|
"LabelFile": "Archivo",
|
||||||
"LabelFileBirthtime": "Archivo Creado en",
|
"LabelFileBirthtime": "Archivo Creado en",
|
||||||
"LabelFileModified": "Archivo modificado",
|
"LabelFileModified": "Archivo modificado",
|
||||||
@@ -275,20 +292,23 @@
|
|||||||
"LabelFinished": "Terminado",
|
"LabelFinished": "Terminado",
|
||||||
"LabelFolder": "Carpeta",
|
"LabelFolder": "Carpeta",
|
||||||
"LabelFolders": "Carpetas",
|
"LabelFolders": "Carpetas",
|
||||||
|
"LabelFontBold": "Negrilla",
|
||||||
"LabelFontFamily": "Familia tipográfica",
|
"LabelFontFamily": "Familia tipográfica",
|
||||||
|
"LabelFontItalic": "Itálica",
|
||||||
"LabelFontScale": "Tamaño de Fuente",
|
"LabelFontScale": "Tamaño de Fuente",
|
||||||
|
"LabelFontStrikethrough": "Tachado",
|
||||||
"LabelFormat": "Formato",
|
"LabelFormat": "Formato",
|
||||||
"LabelGenre": "Genero",
|
"LabelGenre": "Genero",
|
||||||
"LabelGenres": "Géneros",
|
"LabelGenres": "Géneros",
|
||||||
"LabelHardDeleteFile": "Eliminar Definitivamente",
|
"LabelHardDeleteFile": "Eliminar Definitivamente",
|
||||||
"LabelHasEbook": "Tiene Ebook",
|
"LabelHasEbook": "Tiene Ebook",
|
||||||
"LabelHasSupplementaryEbook": "Tiene Ebook Suplementario",
|
"LabelHasSupplementaryEbook": "Tiene Ebook Suplementario",
|
||||||
"LabelHighestPriority": "Highest priority",
|
"LabelHighestPriority": "Mayor prioridad",
|
||||||
"LabelHost": "Host",
|
"LabelHost": "Host",
|
||||||
"LabelHour": "Hora",
|
"LabelHour": "Hora",
|
||||||
"LabelIcon": "Icono",
|
"LabelIcon": "Icono",
|
||||||
"LabelImageURLFromTheWeb": "Image URL from the web",
|
"LabelImageURLFromTheWeb": "URL de la imagen",
|
||||||
"LabelIncludeInTracklist": "Incluir en Tracklist",
|
"LabelIncludeInTracklist": "Incluir en la Tracklist",
|
||||||
"LabelIncomplete": "Incompleto",
|
"LabelIncomplete": "Incompleto",
|
||||||
"LabelInProgress": "En Proceso",
|
"LabelInProgress": "En Proceso",
|
||||||
"LabelInterval": "Intervalo",
|
"LabelInterval": "Intervalo",
|
||||||
@@ -325,18 +345,22 @@
|
|||||||
"LabelLogLevelInfo": "Información",
|
"LabelLogLevelInfo": "Información",
|
||||||
"LabelLogLevelWarn": "Advertencia",
|
"LabelLogLevelWarn": "Advertencia",
|
||||||
"LabelLookForNewEpisodesAfterDate": "Buscar Nuevos Episodios a partir de esta Fecha",
|
"LabelLookForNewEpisodesAfterDate": "Buscar Nuevos Episodios a partir de esta Fecha",
|
||||||
"LabelLowestPriority": "Lowest Priority",
|
"LabelLowestPriority": "Menor prioridad",
|
||||||
"LabelMatchExistingUsersBy": "Match existing users by",
|
"LabelMatchExistingUsersBy": "Emparejar a los usuarios existentes por",
|
||||||
"LabelMatchExistingUsersByDescription": "Used for connecting existing users. Once connected, users will be matched by a unique id from your SSO provider",
|
"LabelMatchExistingUsersByDescription": "Se utiliza para conectar usuarios existentes. Una vez conectados, los usuarios serán emparejados por un identificador único de su proveedor de SSO",
|
||||||
"LabelMediaPlayer": "Reproductor de Medios",
|
"LabelMediaPlayer": "Reproductor de Medios",
|
||||||
"LabelMediaType": "Tipo de Multimedia",
|
"LabelMediaType": "Tipo de Multimedia",
|
||||||
"LabelMetadataOrderOfPrecedenceDescription": "Higher priority metadata sources will override lower priority metadata sources",
|
"LabelMetadataOrderOfPrecedenceDescription": "Las fuentes de metadatos de mayor prioridad prevalecerán sobre las de menor prioridad",
|
||||||
"LabelMetadataProvider": "Proveedor de Metadata",
|
"LabelMetadataProvider": "Proveedor de Metadatos",
|
||||||
"LabelMetaTag": "Meta Tag",
|
"LabelMetaTag": "Metaetiqueta",
|
||||||
"LabelMetaTags": "Meta Tags",
|
"LabelMetaTags": "Metaetiquetas",
|
||||||
"LabelMinute": "Minuto",
|
"LabelMinute": "Minuto",
|
||||||
"LabelMissing": "Ausente",
|
"LabelMissing": "Ausente",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Partes Ausentes",
|
"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.",
|
||||||
"LabelMore": "Más",
|
"LabelMore": "Más",
|
||||||
"LabelMoreInfo": "Más Información",
|
"LabelMoreInfo": "Más Información",
|
||||||
"LabelName": "Nombre",
|
"LabelName": "Nombre",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Puede Descargar",
|
"LabelPermissionsDownload": "Puede Descargar",
|
||||||
"LabelPermissionsUpdate": "Puede Actualizar",
|
"LabelPermissionsUpdate": "Puede Actualizar",
|
||||||
"LabelPermissionsUpload": "Puede Subir",
|
"LabelPermissionsUpload": "Puede Subir",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Ruta de Acceso/URL de Foto",
|
"LabelPhotoPathURL": "Ruta de Acceso/URL de Foto",
|
||||||
"LabelPlaylists": "Lista de Reproducción",
|
"LabelPlaylists": "Lista de Reproducción",
|
||||||
"LabelPlayMethod": "Método de Reproducción",
|
"LabelPlayMethod": "Método de Reproducción",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "Región de búsqueda de podcasts",
|
||||||
"LabelPodcastType": "Tipo Podcast",
|
"LabelPodcastType": "Tipo Podcast",
|
||||||
"LabelPort": "Puerto",
|
"LabelPort": "Puerto",
|
||||||
"LabelPrefixesToIgnore": "Prefijos para Ignorar (no distingue entre mayúsculas y minúsculas.)",
|
"LabelPrefixesToIgnore": "Prefijos para Ignorar (no distingue entre mayúsculas y minúsculas.)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Agregado Recientemente",
|
"LabelRecentlyAdded": "Agregado Recientemente",
|
||||||
"LabelRecentSeries": "Series Recientes",
|
"LabelRecentSeries": "Series Recientes",
|
||||||
"LabelRecommended": "Recomendados",
|
"LabelRecommended": "Recomendados",
|
||||||
|
"LabelRedo": "Rehacer",
|
||||||
"LabelRegion": "Región",
|
"LabelRegion": "Región",
|
||||||
"LabelReleaseDate": "Fecha de Estreno",
|
"LabelReleaseDate": "Fecha de Estreno",
|
||||||
"LabelRemoveCover": "Remover Portada",
|
"LabelRemoveCover": "Remover Portada",
|
||||||
|
"LabelRowsPerPage": "Filas por página",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Email de dueño personalizado",
|
"LabelRSSFeedCustomOwnerEmail": "Email de dueño personalizado",
|
||||||
"LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
|
"LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
|
||||||
"LabelRSSFeedOpen": "Fuente RSS Abierta",
|
"LabelRSSFeedOpen": "Fuente RSS Abierta",
|
||||||
@@ -410,12 +438,13 @@
|
|||||||
"LabelSeason": "Temporada",
|
"LabelSeason": "Temporada",
|
||||||
"LabelSelectAllEpisodes": "Seleccionar todos los episodios",
|
"LabelSelectAllEpisodes": "Seleccionar todos los episodios",
|
||||||
"LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
|
"LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
|
||||||
"LabelSelectUsers": "Select users",
|
"LabelSelectUsers": "Seleccionar usuarios",
|
||||||
"LabelSendEbookToDevice": "Enviar Ebook a...",
|
"LabelSendEbookToDevice": "Enviar Ebook a...",
|
||||||
"LabelSequence": "Secuencia",
|
"LabelSequence": "Secuencia",
|
||||||
"LabelSeries": "Series",
|
"LabelSeries": "Series",
|
||||||
"LabelSeriesName": "Nombre de la Serie",
|
"LabelSeriesName": "Nombre de la Serie",
|
||||||
"LabelSeriesProgress": "Progreso de la Serie",
|
"LabelSeriesProgress": "Progreso de la Serie",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Establecer como primario",
|
"LabelSetEbookAsPrimary": "Establecer como primario",
|
||||||
"LabelSetEbookAsSupplementary": "Establecer como suplementario",
|
"LabelSetEbookAsSupplementary": "Establecer como suplementario",
|
||||||
"LabelSettingsAudiobooksOnly": "Sólo Audiolibros",
|
"LabelSettingsAudiobooksOnly": "Sólo Audiolibros",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Las series con un solo libro no aparecerán en la página de series ni la repisa para series de la página principal.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Las series con un solo libro no aparecerán en la página de series ni la repisa para series de la página principal.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Usar la vista de librero en la página principal",
|
"LabelSettingsHomePageBookshelfView": "Usar la vista de librero en la página principal",
|
||||||
"LabelSettingsLibraryBookshelfView": "Usar la vista de librero en la biblioteca",
|
"LabelSettingsLibraryBookshelfView": "Usar la vista de librero en la biblioteca",
|
||||||
|
"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": "Extraer Subtítulos",
|
"LabelSettingsParseSubtitles": "Extraer Subtítulos",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extraer subtítulos de los nombres de las carpetas de los audiolibros.<br>Los subtítulos deben estar separados por \" - \"<br>Por ejemplo: \"Ejemplo de Título - Subtítulo Aquí\" tiene el subtítulo \"Subtítulo Aquí\"",
|
"LabelSettingsParseSubtitlesHelp": "Extraer subtítulos de los nombres de las carpetas de los audiolibros.<br>Los subtítulos deben estar separados por \" - \"<br>Por ejemplo: \"Ejemplo de Título - Subtítulo Aquí\" tiene el subtítulo \"Subtítulo Aquí\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Preferir metadatos encontrados",
|
"LabelSettingsPreferMatchedMetadata": "Preferir metadatos encontrados",
|
||||||
@@ -482,10 +513,14 @@
|
|||||||
"LabelTagsAccessibleToUser": "Etiquetas Accessibles al Usuario",
|
"LabelTagsAccessibleToUser": "Etiquetas Accessibles al Usuario",
|
||||||
"LabelTagsNotAccessibleToUser": "Etiquetas no Accesibles al Usuario",
|
"LabelTagsNotAccessibleToUser": "Etiquetas no Accesibles al Usuario",
|
||||||
"LabelTasks": "Tareas Corriendo",
|
"LabelTasks": "Tareas Corriendo",
|
||||||
|
"LabelTextEditorBulletedList": "Lista con viñetas",
|
||||||
|
"LabelTextEditorLink": "Enlazar",
|
||||||
|
"LabelTextEditorNumberedList": "Lista numerada",
|
||||||
|
"LabelTextEditorUnlink": "Desenlazar",
|
||||||
"LabelTheme": "Tema",
|
"LabelTheme": "Tema",
|
||||||
"LabelThemeDark": "Oscuro",
|
"LabelThemeDark": "Oscuro",
|
||||||
"LabelThemeLight": "Claro",
|
"LabelThemeLight": "Claro",
|
||||||
"LabelTimeBase": "Time Base",
|
"LabelTimeBase": "Tiempo Base",
|
||||||
"LabelTimeListened": "Tiempo Escuchando",
|
"LabelTimeListened": "Tiempo Escuchando",
|
||||||
"LabelTimeListenedToday": "Tiempo Escuchando Hoy",
|
"LabelTimeListenedToday": "Tiempo Escuchando Hoy",
|
||||||
"LabelTimeRemaining": "{0} restante",
|
"LabelTimeRemaining": "{0} restante",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Una pista",
|
"LabelTracksSingleTrack": "Una pista",
|
||||||
"LabelType": "Tipo",
|
"LabelType": "Tipo",
|
||||||
"LabelUnabridged": "No Abreviado",
|
"LabelUnabridged": "No Abreviado",
|
||||||
|
"LabelUndo": "Deshacer",
|
||||||
"LabelUnknown": "Desconocido",
|
"LabelUnknown": "Desconocido",
|
||||||
"LabelUpdateCover": "Actualizar Portada",
|
"LabelUpdateCover": "Actualizar Portada",
|
||||||
"LabelUpdateCoverHelp": "Permitir sobrescribir las portadas existentes de los libros seleccionados cuando sean encontradas.",
|
"LabelUpdateCoverHelp": "Permitir sobrescribir las portadas existentes de los libros seleccionados cuando sean encontradas.",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Permitir sobrescribir detalles existentes de los libros seleccionados cuando sean encontrados",
|
"LabelUpdateDetailsHelp": "Permitir sobrescribir detalles existentes de los libros seleccionados cuando sean encontrados",
|
||||||
"LabelUploaderDragAndDrop": "Arrastre y suelte archivos o carpetas",
|
"LabelUploaderDragAndDrop": "Arrastre y suelte archivos o carpetas",
|
||||||
"LabelUploaderDropFiles": "Suelte los Archivos",
|
"LabelUploaderDropFiles": "Suelte los Archivos",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Buscar título, autor y series automáticamente",
|
||||||
"LabelUseChapterTrack": "Usar pista por capitulo",
|
"LabelUseChapterTrack": "Usar pista por capitulo",
|
||||||
"LabelUseFullTrack": "Usar pista completa",
|
"LabelUseFullTrack": "Usar pista completa",
|
||||||
"LabelUser": "Usuario",
|
"LabelUser": "Usuario",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "Ver Fila del Reproductor",
|
"LabelViewQueue": "Ver Fila del Reproductor",
|
||||||
"LabelVolume": "Volumen",
|
"LabelVolume": "Volumen",
|
||||||
"LabelWeekdaysToRun": "Correr en Días de la Semana",
|
"LabelWeekdaysToRun": "Correr en Días de la Semana",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Duración de tu Audiolibro",
|
"LabelYourAudiobookDuration": "Duración de tu Audiolibro",
|
||||||
"LabelYourBookmarks": "Tus Marcadores",
|
"LabelYourBookmarks": "Tus Marcadores",
|
||||||
"LabelYourPlaylists": "Tus Listas",
|
"LabelYourPlaylists": "Tus Listas",
|
||||||
@@ -548,20 +587,21 @@
|
|||||||
"MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?",
|
"MessageConfirmDeleteBackup": "¿Está seguro de que desea eliminar el respaldo {0}?",
|
||||||
"MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?",
|
"MessageConfirmDeleteFile": "Esto eliminará el archivo de su sistema de archivos. ¿Está seguro?",
|
||||||
"MessageConfirmDeleteLibrary": "¿Está seguro de que desea eliminar permanentemente la biblioteca \"{0}\"?",
|
"MessageConfirmDeleteLibrary": "¿Está seguro de que desea eliminar permanentemente la biblioteca \"{0}\"?",
|
||||||
"MessageConfirmDeleteLibraryItem": "This will delete the library item from the database and your file system. Are you sure?",
|
"MessageConfirmDeleteLibraryItem": "Esto removerá la librería de la base de datos y archivos en tu sistema. ¿Estás seguro?",
|
||||||
"MessageConfirmDeleteLibraryItems": "This will delete {0} library items from the database and your file system. Are you sure?",
|
"MessageConfirmDeleteLibraryItems": "Esto removerá {0} elemento(s) de la librería en base de datos y archivos en tu sistema. ¿Estás seguro?",
|
||||||
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
|
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
|
||||||
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
|
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
|
||||||
"MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
|
"MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
|
||||||
"MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
|
"MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
|
||||||
"MessageConfirmMarkSeriesFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como terminados?",
|
"MessageConfirmMarkSeriesFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como terminados?",
|
||||||
"MessageConfirmMarkSeriesNotFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como no terminados?",
|
"MessageConfirmMarkSeriesNotFinished": "¿Está seguro de que desea marcar todos los libros en esta serie como no terminados?",
|
||||||
"MessageConfirmQuickEmbed": "Warning! Quick embed will not backup your audio files. Make sure that you have a backup of your audio files. <br><br>Would you like to continue?",
|
"MessageConfirmQuickEmbed": "¡Advertencia! La integración rápida no realiza copias de seguridad a ninguno de tus archivos de audio. Asegúrate de haber realizado una copia de los mismos previamente. <br><br>¿Deseas continuar?",
|
||||||
"MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?",
|
"MessageConfirmRemoveAllChapters": "¿Está seguro de que desea remover todos los capitulos?",
|
||||||
"MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?",
|
"MessageConfirmRemoveAuthor": "¿Está seguro de que desea remover el autor \"{0}\"?",
|
||||||
"MessageConfirmRemoveCollection": "¿Está seguro de que desea remover la colección \"{0}\"?",
|
"MessageConfirmRemoveCollection": "¿Está seguro de que desea remover la colección \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "¿Está seguro de que desea remover el episodio \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?",
|
"MessageConfirmRemoveEpisodes": "¿Está seguro de que desea remover {0} episodios?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "¿Está seguro de que desea remover el narrador \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "¿Está seguro de que desea remover la lista de reproducción \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
|
"MessageConfirmRenameGenre": "¿Está seguro de que desea renombrar el genero \"{0}\" a \"{1}\" de todos los elementos?",
|
||||||
@@ -570,7 +610,7 @@
|
|||||||
"MessageConfirmRenameTag": "¿Está seguro de que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?",
|
"MessageConfirmRenameTag": "¿Está seguro de que desea renombrar la etiqueta \"{0}\" a \"{1}\" de todos los elementos?",
|
||||||
"MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe, por lo que se fusionarán.",
|
"MessageConfirmRenameTagMergeNote": "Nota: Esta etiqueta ya existe, por lo que se fusionarán.",
|
||||||
"MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".",
|
"MessageConfirmRenameTagWarning": "Advertencia! Una etiqueta similar ya existe \"{0}\".",
|
||||||
"MessageConfirmReScanLibraryItems": "Are you sure you want to re-scan {0} items?",
|
"MessageConfirmReScanLibraryItems": "¿Estás seguro de querer re escanear {0} elemento(s)?",
|
||||||
"MessageConfirmSendEbookToDevice": "¿Está seguro de que enviar {0} ebook(s) \"{1}\" al dispositivo \"{2}\"?",
|
"MessageConfirmSendEbookToDevice": "¿Está seguro de que enviar {0} ebook(s) \"{1}\" al dispositivo \"{2}\"?",
|
||||||
"MessageDownloadingEpisode": "Descargando Capitulo",
|
"MessageDownloadingEpisode": "Descargando Capitulo",
|
||||||
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas.",
|
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas.",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "¿Está seguro de que desea para restaurar del respaldo creado en",
|
"MessageRestoreBackupConfirm": "¿Está seguro de que desea para restaurar del respaldo creado en",
|
||||||
"MessageRestoreBackupWarning": "Restaurar sobrescribirá toda la base de datos localizada en /config y las imágenes de portadas en /metadata/items y /metadata/authors.<br /><br />El respaldo no modifica ningún archivo en las carpetas de su biblioteca. Si ha habilitado la opción del servidor para almacenar portadas y metadata en las carpetas de su biblioteca, esos archivos no se respaldan o sobrescriben.<br /><br />Todos los clientes que usen su servidor se actualizarán automáticamente.",
|
"MessageRestoreBackupWarning": "Restaurar sobrescribirá toda la base de datos localizada en /config y las imágenes de portadas en /metadata/items y /metadata/authors.<br /><br />El respaldo no modifica ningún archivo en las carpetas de su biblioteca. Si ha habilitado la opción del servidor para almacenar portadas y metadata en las carpetas de su biblioteca, esos archivos no se respaldan o sobrescriben.<br /><br />Todos los clientes que usen su servidor se actualizarán automáticamente.",
|
||||||
"MessageSearchResultsFor": "Resultados de la búsqueda de",
|
"MessageSearchResultsFor": "Resultados de la búsqueda de",
|
||||||
|
"MessageSelected": "{0} seleccionado(s)",
|
||||||
"MessageServerCouldNotBeReached": "No se pudo establecer la conexión con el servidor",
|
"MessageServerCouldNotBeReached": "No se pudo establecer la conexión con el servidor",
|
||||||
"MessageSetChaptersFromTracksDescription": "Establecer capítulos usando cada archivo de audio como un capítulo y el título del capítulo como el nombre del archivo de audio",
|
"MessageSetChaptersFromTracksDescription": "Establecer capítulos usando cada archivo de audio como un capítulo y el título del capítulo como el nombre del archivo de audio",
|
||||||
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
|
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "El usuario Root es el único usuario que puede no tener una contraseña",
|
"NoteChangeRootPassword": "El usuario Root es el único usuario que puede no tener una contraseña",
|
||||||
"NoteChapterEditorTimes": "Nota: El tiempo de inicio del primer capítulo debe permanecer en 0:00, y el tiempo de inicio del último capítulo no puede exceder la duración del audiolibro.",
|
"NoteChapterEditorTimes": "Nota: El tiempo de inicio del primer capítulo debe permanecer en 0:00, y el tiempo de inicio del último capítulo no puede exceder la duración del audiolibro.",
|
||||||
"NoteFolderPicker": "Nota: Las carpetas ya asignadas no se mostrarán",
|
"NoteFolderPicker": "Nota: Las carpetas ya asignadas no se mostrarán",
|
||||||
"NoteFolderPickerDebian": "Nota: El selector de archivos no está completamente implementado para instalaciones en Debian. Deberá ingresar la ruta de la carpeta de su biblioteca directamente.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Advertencia: La mayoría de las aplicaciones de podcast requieren que la URL de la fuente RSS use HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Advertencia: La mayoría de las aplicaciones de podcast requieren que la URL de la fuente RSS use HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Advertencia: 1 o más de sus episodios no tienen fecha de publicación. Algunas aplicaciones de podcast lo requieren.",
|
"NoteRSSFeedPodcastAppsPubDate": "Advertencia: 1 o más de sus episodios no tienen fecha de publicación. Algunas aplicaciones de podcast lo requieren.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Las carpetas con archivos multimedia se manejarán como elementos separados en la biblioteca.",
|
"NoteUploaderFoldersWithMediaFiles": "Las carpetas con archivos multimedia se manejarán como elementos separados en la biblioteca.",
|
||||||
|
|||||||
@@ -0,0 +1,781 @@
|
|||||||
|
{
|
||||||
|
"ButtonAdd": "Lisa",
|
||||||
|
"ButtonAddChapters": "Lisa peatükid",
|
||||||
|
"ButtonAddDevice": "Lisa seade",
|
||||||
|
"ButtonAddLibrary": "Lisa raamatukogu",
|
||||||
|
"ButtonAddPodcasts": "Lisa podcastid",
|
||||||
|
"ButtonAddUser": "Lisa kasutaja",
|
||||||
|
"ButtonAddYourFirstLibrary": "Lisa oma esimene raamatukogu",
|
||||||
|
"ButtonApply": "Rakenda",
|
||||||
|
"ButtonApplyChapters": "Rakenda peatükid",
|
||||||
|
"ButtonAuthors": "Autorid",
|
||||||
|
"ButtonBrowseForFolder": "Sirvi kausta",
|
||||||
|
"ButtonCancel": "Tühista",
|
||||||
|
"ButtonCancelEncode": "Tühista kodeerimine",
|
||||||
|
"ButtonChangeRootPassword": "Muuda põhiparooli",
|
||||||
|
"ButtonCheckAndDownloadNewEpisodes": "Kontrolli ja laadi alla uued episoodid",
|
||||||
|
"ButtonChooseAFolder": "Vali kaust",
|
||||||
|
"ButtonChooseFiles": "Vali failid",
|
||||||
|
"ButtonClearFilter": "Tühista filter",
|
||||||
|
"ButtonCloseFeed": "Sulge voog",
|
||||||
|
"ButtonCollections": "Kogud",
|
||||||
|
"ButtonConfigureScanner": "Konfigureeri skanner",
|
||||||
|
"ButtonCreate": "Loo",
|
||||||
|
"ButtonCreateBackup": "Loo varundus",
|
||||||
|
"ButtonDelete": "Kustuta",
|
||||||
|
"ButtonDownloadQueue": "Järjekord",
|
||||||
|
"ButtonEdit": "Muuda",
|
||||||
|
"ButtonEditChapters": "Muuda peatükke",
|
||||||
|
"ButtonEditPodcast": "Muuda podcasti",
|
||||||
|
"ButtonForceReScan": "Sunnitud uuestiskaneerimine",
|
||||||
|
"ButtonFullPath": "Täielik asukoht",
|
||||||
|
"ButtonHide": "Peida",
|
||||||
|
"ButtonHome": "Avaleht",
|
||||||
|
"ButtonIssues": "Probleemid",
|
||||||
|
"ButtonJumpBackward": "Hüppa tagasi",
|
||||||
|
"ButtonJumpForward": "Hüppa edasi",
|
||||||
|
"ButtonLatest": "Uusim",
|
||||||
|
"ButtonLibrary": "Raamatukogu",
|
||||||
|
"ButtonLogout": "Logi välja",
|
||||||
|
"ButtonLookup": "Otsi",
|
||||||
|
"ButtonManageTracks": "Halda lugusid",
|
||||||
|
"ButtonMapChapterTitles": "Kaardista peatükkide pealkirjad",
|
||||||
|
"ButtonMatchAllAuthors": "Sobita kõik autorid",
|
||||||
|
"ButtonMatchBooks": "Sobita raamatud",
|
||||||
|
"ButtonNevermind": "Pole tähtis",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Järgmine peatükk",
|
||||||
|
"ButtonOk": "Ok",
|
||||||
|
"ButtonOpenFeed": "Ava voog",
|
||||||
|
"ButtonOpenManager": "Ava haldur",
|
||||||
|
"ButtonPause": "Peata",
|
||||||
|
"ButtonPlay": "Mängi",
|
||||||
|
"ButtonPlaying": "Mängib",
|
||||||
|
"ButtonPlaylists": "Esitusloendid",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Eelmine peatükk",
|
||||||
|
"ButtonPurgeAllCache": "Tühjenda kogu vahemälu",
|
||||||
|
"ButtonPurgeItemsCache": "Tühjenda esemete vahemälu",
|
||||||
|
"ButtonPurgeMediaProgress": "Tühjenda meedia edenemine",
|
||||||
|
"ButtonQueueAddItem": "Lisa järjekorda",
|
||||||
|
"ButtonQueueRemoveItem": "Eemalda järjekorrast",
|
||||||
|
"ButtonQuickMatch": "Kiire sobitamine",
|
||||||
|
"ButtonRead": "Loe",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
|
"ButtonRemove": "Eemalda",
|
||||||
|
"ButtonRemoveAll": "Eemalda kõik",
|
||||||
|
"ButtonRemoveAllLibraryItems": "Eemalda kõik raamatukogu esemed",
|
||||||
|
"ButtonRemoveFromContinueListening": "Eemalda jätkake kuulamisest",
|
||||||
|
"ButtonRemoveFromContinueReading": "Eemalda jätkake lugemisest",
|
||||||
|
"ButtonRemoveSeriesFromContinueSeries": "Eemalda seeria jätkamisest",
|
||||||
|
"ButtonReScan": "Uuestiskaneeri",
|
||||||
|
"ButtonReset": "Lähtesta",
|
||||||
|
"ButtonResetToDefault": "Lähtesta vaikeseade",
|
||||||
|
"ButtonRestore": "Taasta",
|
||||||
|
"ButtonSave": "Salvesta",
|
||||||
|
"ButtonSaveAndClose": "Salvesta ja sulge",
|
||||||
|
"ButtonSaveTracklist": "Salvesta lugude loend",
|
||||||
|
"ButtonScan": "Skanneeri",
|
||||||
|
"ButtonScanLibrary": "Skanneeri raamatukogu",
|
||||||
|
"ButtonSearch": "Otsi",
|
||||||
|
"ButtonSelectFolderPath": "Vali kaustatee",
|
||||||
|
"ButtonSeries": "Sarjad",
|
||||||
|
"ButtonSetChaptersFromTracks": "Määra peatükid lugudest",
|
||||||
|
"ButtonShare": "Share",
|
||||||
|
"ButtonShiftTimes": "Nihke ajad",
|
||||||
|
"ButtonShow": "Näita",
|
||||||
|
"ButtonStartM4BEncode": "Alusta M4B kodeerimist",
|
||||||
|
"ButtonStartMetadataEmbed": "Alusta metaandmete lisamist",
|
||||||
|
"ButtonSubmit": "Esita",
|
||||||
|
"ButtonTest": "Test",
|
||||||
|
"ButtonUpload": "Lae üles",
|
||||||
|
"ButtonUploadBackup": "Lae üles varundus",
|
||||||
|
"ButtonUploadCover": "Lae üles ümbris",
|
||||||
|
"ButtonUploadOPMLFile": "Lae üles OPML-fail",
|
||||||
|
"ButtonUserDelete": "Kustuta kasutaja {0}",
|
||||||
|
"ButtonUserEdit": "Muuda kasutajat {0}",
|
||||||
|
"ButtonViewAll": "Vaata kõiki",
|
||||||
|
"ButtonYes": "Jah",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Viga metaandmete hankimisel",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Ei saanud metaandmeid hankida - proovi tiitlit ja/või autorit uuendada",
|
||||||
|
"ErrorUploadLacksTitle": "Peab olema pealkiri",
|
||||||
|
"HeaderAccount": "Konto",
|
||||||
|
"HeaderAdvanced": "Täpsem",
|
||||||
|
"HeaderAppriseNotificationSettings": "Apprise teavitamise seaded",
|
||||||
|
"HeaderAudiobookTools": "Heliraamatu failihaldustööriistad",
|
||||||
|
"HeaderAudioTracks": "Helirajad",
|
||||||
|
"HeaderAuthentication": "Autentimine",
|
||||||
|
"HeaderBackups": "Varukoopiad",
|
||||||
|
"HeaderChangePassword": "Muuda parooli",
|
||||||
|
"HeaderChapters": "Peatükid",
|
||||||
|
"HeaderChooseAFolder": "Vali kaust",
|
||||||
|
"HeaderCollection": "Kogu",
|
||||||
|
"HeaderCollectionItems": "Kogu esemed",
|
||||||
|
"HeaderCover": "Ümbris",
|
||||||
|
"HeaderCurrentDownloads": "Praegused allalaadimised",
|
||||||
|
"HeaderCustomMetadataProviders": "Kohandatud metaandmete pakkujad",
|
||||||
|
"HeaderDetails": "Detailid",
|
||||||
|
"HeaderDownloadQueue": "Allalaadimise järjekord",
|
||||||
|
"HeaderEbookFiles": "E-raamatute failid",
|
||||||
|
"HeaderEmail": "E-post",
|
||||||
|
"HeaderEmailSettings": "E-posti seaded",
|
||||||
|
"HeaderEpisodes": "Episoodid",
|
||||||
|
"HeaderEreaderDevices": "E-lugerite seadmed",
|
||||||
|
"HeaderEreaderSettings": "E-lugerite seadistused",
|
||||||
|
"HeaderFiles": "Failid",
|
||||||
|
"HeaderFindChapters": "Leia peatükid",
|
||||||
|
"HeaderIgnoredFiles": "Ignoreeritud failid",
|
||||||
|
"HeaderItemFiles": "Esemete failid",
|
||||||
|
"HeaderItemMetadataUtils": "Eseme metaandmete tööriistad",
|
||||||
|
"HeaderLastListeningSession": "Viimane kuulamissessioon",
|
||||||
|
"HeaderLatestEpisodes": "Viimased episoodid",
|
||||||
|
"HeaderLibraries": "Raamatukogud",
|
||||||
|
"HeaderLibraryFiles": "Raamatukogu failid",
|
||||||
|
"HeaderLibraryStats": "Raamatukogu statistika",
|
||||||
|
"HeaderListeningSessions": "Kuulamissessioonid",
|
||||||
|
"HeaderListeningStats": "Kuulamise statistika",
|
||||||
|
"HeaderLogin": "Logi sisse",
|
||||||
|
"HeaderLogs": "Logid",
|
||||||
|
"HeaderManageGenres": "Halda žanre",
|
||||||
|
"HeaderManageTags": "Halda silte",
|
||||||
|
"HeaderMapDetails": "Kaardi detailid",
|
||||||
|
"HeaderMatch": "Sobita",
|
||||||
|
"HeaderMetadataOrderOfPrecedence": "Metaandmete eelnevusjärjestus",
|
||||||
|
"HeaderMetadataToEmbed": "Manusta metaandmed",
|
||||||
|
"HeaderNewAccount": "Uus konto",
|
||||||
|
"HeaderNewLibrary": "Uus raamatukogu",
|
||||||
|
"HeaderNotifications": "Teatised",
|
||||||
|
"HeaderOpenIDConnectAuthentication": "OpenID Connect autentimine",
|
||||||
|
"HeaderOpenRSSFeed": "Ava RSS-voog",
|
||||||
|
"HeaderOtherFiles": "Muud failid",
|
||||||
|
"HeaderPasswordAuthentication": "Parooli autentimine",
|
||||||
|
"HeaderPermissions": "Õigused",
|
||||||
|
"HeaderPlayerQueue": "Mängija järjekord",
|
||||||
|
"HeaderPlaylist": "Mänguloend",
|
||||||
|
"HeaderPlaylistItems": "Mänguloendi esemed",
|
||||||
|
"HeaderPodcastsToAdd": "Lisatavad podcastid",
|
||||||
|
"HeaderPreviewCover": "Eelvaate kaas",
|
||||||
|
"HeaderRemoveEpisode": "Eemalda episood",
|
||||||
|
"HeaderRemoveEpisodes": "Eemalda {0} episoodi",
|
||||||
|
"HeaderRSSFeedGeneral": "RSS-i üksikasjad",
|
||||||
|
"HeaderRSSFeedIsOpen": "RSS-voog on avatud",
|
||||||
|
"HeaderRSSFeeds": "RSS-vooged",
|
||||||
|
"HeaderSavedMediaProgress": "Salvestatud meedia edenemine",
|
||||||
|
"HeaderSchedule": "Ajakava",
|
||||||
|
"HeaderScheduleLibraryScans": "Ajasta automaatsed raamatukogu skaneerimised",
|
||||||
|
"HeaderSession": "Sessioon",
|
||||||
|
"HeaderSetBackupSchedule": "Määra varunduse ajakava",
|
||||||
|
"HeaderSettings": "Seaded",
|
||||||
|
"HeaderSettingsDisplay": "Kuva",
|
||||||
|
"HeaderSettingsExperimental": "Katsetusfunktsioonid",
|
||||||
|
"HeaderSettingsGeneral": "Üldised",
|
||||||
|
"HeaderSettingsScanner": "Skanner",
|
||||||
|
"HeaderSleepTimer": "Uinaku taimer",
|
||||||
|
"HeaderStatsLargestItems": "Suurimad esemed",
|
||||||
|
"HeaderStatsLongestItems": "Kõige pikemad esemed (tunnid)",
|
||||||
|
"HeaderStatsMinutesListeningChart": "Kuulamise minutid (viimased 7 päeva)",
|
||||||
|
"HeaderStatsRecentSessions": "Hiljutised sessioonid",
|
||||||
|
"HeaderStatsTop10Authors": "Top 10 autorit",
|
||||||
|
"HeaderStatsTop5Genres": "Top 5 žanrit",
|
||||||
|
"HeaderTableOfContents": "Sisukord",
|
||||||
|
"HeaderTools": "Tööriistad",
|
||||||
|
"HeaderUpdateAccount": "Uuenda kontot",
|
||||||
|
"HeaderUpdateAuthor": "Uuenda autorit",
|
||||||
|
"HeaderUpdateDetails": "Uuenda detaile",
|
||||||
|
"HeaderUpdateLibrary": "Uuenda raamatukogu",
|
||||||
|
"HeaderUsers": "Kasutajad",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
|
"HeaderYourStats": "Sinu statistika",
|
||||||
|
"LabelAbridged": "Kärbitud",
|
||||||
|
"LabelAccountType": "Konto tüüp",
|
||||||
|
"LabelAccountTypeAdmin": "Administraator",
|
||||||
|
"LabelAccountTypeGuest": "Külaline",
|
||||||
|
"LabelAccountTypeUser": "Kasutaja",
|
||||||
|
"LabelActivity": "Tegevus",
|
||||||
|
"LabelAdded": "Lisatud",
|
||||||
|
"LabelAddedAt": "Lisatud",
|
||||||
|
"LabelAddToCollection": "Lisa kogusse",
|
||||||
|
"LabelAddToCollectionBatch": "Lisa {0} raamatut kogusse",
|
||||||
|
"LabelAddToPlaylist": "Lisa mänguloendisse",
|
||||||
|
"LabelAddToPlaylistBatch": "Lisa {0} eset mänguloendisse",
|
||||||
|
"LabelAdminUsersOnly": "Ainult administraatorid",
|
||||||
|
"LabelAll": "Kõik",
|
||||||
|
"LabelAllUsers": "Kõik kasutajad",
|
||||||
|
"LabelAllUsersExcludingGuests": "Kõik kasutajad, välja arvatud külalised",
|
||||||
|
"LabelAllUsersIncludingGuests": "Kõik kasutajad, kaasa arvatud külalised",
|
||||||
|
"LabelAlreadyInYourLibrary": "Juba teie raamatukogus",
|
||||||
|
"LabelAppend": "Lisa",
|
||||||
|
"LabelAuthor": "Autor",
|
||||||
|
"LabelAuthorFirstLast": "Autor (Eesnimi Perekonnanimi)",
|
||||||
|
"LabelAuthorLastFirst": "Autor (Perekonnanimi, Eesnimi)",
|
||||||
|
"LabelAuthors": "Autorid",
|
||||||
|
"LabelAutoDownloadEpisodes": "Automaatne episoodide allalaadimine",
|
||||||
|
"LabelAutoFetchMetadata": "Automaatne metaandmete hankimine",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Toob tiitli, autori ja seeria metaandmed üleslaadimise hõlbustamiseks. Lisametaandmed võivad pärast üleslaadimist vajada vastavust.",
|
||||||
|
"LabelAutoLaunch": "Automaatne käivitamine",
|
||||||
|
"LabelAutoLaunchDescription": "Suunab automaatselt autentimist pakkuvale teenusele, kui navigeeritakse sisselogimislehele (käsitsi ülekirjutuse tee <code>/login?autoLaunch=0</code>)",
|
||||||
|
"LabelAutoRegister": "Automaatne registreerimine",
|
||||||
|
"LabelAutoRegisterDescription": "Loo uued kasutajad automaatselt sisselogimisel",
|
||||||
|
"LabelBackToUser": "Tagasi kasutajale",
|
||||||
|
"LabelBackupLocation": "Varukoopia asukoht",
|
||||||
|
"LabelBackupsEnableAutomaticBackups": "Luba automaatsed varukoopiad",
|
||||||
|
"LabelBackupsEnableAutomaticBackupsHelp": "Varukoopiad salvestatakse /metadata/backups kausta",
|
||||||
|
"LabelBackupsMaxBackupSize": "Maksimaalne varukoopia suurus (GB-des)",
|
||||||
|
"LabelBackupsMaxBackupSizeHelp": "Kaitsena valesti seadistamise vastu ebaõnnestuvad varukoopiad, kui need ületavad seadistatud suuruse.",
|
||||||
|
"LabelBackupsNumberToKeep": "Varukoopiate arv, mida hoida",
|
||||||
|
"LabelBackupsNumberToKeepHelp": "Ühel ajal eemaldatakse ainult 1 varukoopia, seega kui teil on juba rohkem varukoopiaid kui siin määratud, peaksite need käsitsi eemaldama.",
|
||||||
|
"LabelBitrate": "Bittkiirus",
|
||||||
|
"LabelBooks": "Raamatud",
|
||||||
|
"LabelButtonText": "Nupu tekst",
|
||||||
|
"LabelChangePassword": "Muuda parooli",
|
||||||
|
"LabelChannels": "Kanalid",
|
||||||
|
"LabelChapters": "Peatükid",
|
||||||
|
"LabelChaptersFound": "peatükid leitud",
|
||||||
|
"LabelChapterTitle": "Peatüki pealkiri",
|
||||||
|
"LabelClickForMoreInfo": "Klõpsa lisateabe saamiseks",
|
||||||
|
"LabelClosePlayer": "Sulge mängija",
|
||||||
|
"LabelCodec": "Kodek",
|
||||||
|
"LabelCollapseSeries": "Ahenda seeria",
|
||||||
|
"LabelCollection": "Kogu",
|
||||||
|
"LabelCollections": "Kogud",
|
||||||
|
"LabelComplete": "Valmis",
|
||||||
|
"LabelConfirmPassword": "Kinnita parool",
|
||||||
|
"LabelContinueListening": "Jätka kuulamist",
|
||||||
|
"LabelContinueReading": "Jätka lugemist",
|
||||||
|
"LabelContinueSeries": "Jätka seeriat",
|
||||||
|
"LabelCover": "Ümbris",
|
||||||
|
"LabelCoverImageURL": "Ümbrise pildi URL",
|
||||||
|
"LabelCreatedAt": "Loodud",
|
||||||
|
"LabelCronExpression": "Croni valem",
|
||||||
|
"LabelCurrent": "Praegune",
|
||||||
|
"LabelCurrently": "Praegu:",
|
||||||
|
"LabelCustomCronExpression": "Kohandatud Croni valem:",
|
||||||
|
"LabelDatetime": "Kuupäev ja kellaaeg",
|
||||||
|
"LabelDeleteFromFileSystemCheckbox": "Kustuta failisüsteemist (ärge märkige seda ära, et eemaldada ainult andmebaasist)",
|
||||||
|
"LabelDescription": "Kirjeldus",
|
||||||
|
"LabelDeselectAll": "Tühista kõigi valimine",
|
||||||
|
"LabelDevice": "Seade",
|
||||||
|
"LabelDeviceInfo": "Seadme info",
|
||||||
|
"LabelDeviceIsAvailableTo": "Seade on saadaval kasutajale...",
|
||||||
|
"LabelDirectory": "Kataloog",
|
||||||
|
"LabelDiscFromFilename": "Ketas failinimest",
|
||||||
|
"LabelDiscFromMetadata": "Ketas metaandmetest",
|
||||||
|
"LabelDiscover": "Avasta",
|
||||||
|
"LabelDownload": "Lae alla",
|
||||||
|
"LabelDownloadNEpisodes": "Lae alla {0} episoodi",
|
||||||
|
"LabelDuration": "Kestus",
|
||||||
|
"LabelDurationFound": "Leitud kestus:",
|
||||||
|
"LabelEbook": "E-raamat",
|
||||||
|
"LabelEbooks": "E-raamatud",
|
||||||
|
"LabelEdit": "Muuda",
|
||||||
|
"LabelEmail": "E-post",
|
||||||
|
"LabelEmailSettingsFromAddress": "Saatja aadress",
|
||||||
|
"LabelEmailSettingsSecure": "Turvaline",
|
||||||
|
"LabelEmailSettingsSecureHelp": "Kui see on tõene, kasutab ühendus serveriga ühenduse loomisel TLS-i. Kui see on väär, kasutatakse TLS-i, kui server toetab STARTTLS-i laiendust. Enamikul juhtudest seadke see väärtus tõeks, kui ühendate pordile 465. Pordi 587 või 25 korral hoidke seda väär. (nodemailer.com/smtp/#authentication)",
|
||||||
|
"LabelEmailSettingsTestAddress": "Testi aadress",
|
||||||
|
"LabelEmbeddedCover": "Manustatud kaas",
|
||||||
|
"LabelEnable": "Luba",
|
||||||
|
"LabelEnd": "Lõpp",
|
||||||
|
"LabelEpisode": "Episood",
|
||||||
|
"LabelEpisodeTitle": "Episoodi pealkiri",
|
||||||
|
"LabelEpisodeType": "Episoodi tüüp",
|
||||||
|
"LabelExample": "Näide",
|
||||||
|
"LabelExplicit": "Vulgaarne",
|
||||||
|
"LabelFeedURL": "Voogu URL",
|
||||||
|
"LabelFetchingMetadata": "Metaandmete hankimine",
|
||||||
|
"LabelFile": "Fail",
|
||||||
|
"LabelFileBirthtime": "Faili sünniaeg",
|
||||||
|
"LabelFileModified": "Faili muudetud",
|
||||||
|
"LabelFilename": "Failinimi",
|
||||||
|
"LabelFilterByUser": "Filtri alusel kasutaja järgi",
|
||||||
|
"LabelFindEpisodes": "Otsi episoodid",
|
||||||
|
"LabelFinished": "Lõpetatud",
|
||||||
|
"LabelFolder": "Kaust",
|
||||||
|
"LabelFolders": "Kataloogid",
|
||||||
|
"LabelFontBold": "Paks",
|
||||||
|
"LabelFontFamily": "Fondi pere",
|
||||||
|
"LabelFontItalic": "Kaldkiri",
|
||||||
|
"LabelFontScale": "Fondi suurus",
|
||||||
|
"LabelFontStrikethrough": "Üle joonitud",
|
||||||
|
"LabelFormat": "Vorming",
|
||||||
|
"LabelGenre": "Žanr",
|
||||||
|
"LabelGenres": "Žanrid",
|
||||||
|
"LabelHardDeleteFile": "Faili lõplik kustutamine",
|
||||||
|
"LabelHasEbook": "On e-raamat",
|
||||||
|
"LabelHasSupplementaryEbook": "On täiendav e-raamat",
|
||||||
|
"LabelHighestPriority": "Kõrgeim prioriteet",
|
||||||
|
"LabelHost": "Host",
|
||||||
|
"LabelHour": "Tund",
|
||||||
|
"LabelIcon": "Ikoon",
|
||||||
|
"LabelImageURLFromTheWeb": "Pildi URL veebist",
|
||||||
|
"LabelIncludeInTracklist": "Kaasa jälgimisloendis",
|
||||||
|
"LabelIncomplete": "Puudulik",
|
||||||
|
"LabelInProgress": "Pooleli",
|
||||||
|
"LabelInterval": "Intervall",
|
||||||
|
"LabelIntervalCustomDailyWeekly": "Kohandatud päevane/nädalane",
|
||||||
|
"LabelIntervalEvery12Hours": "Iga 12 tunni tagant",
|
||||||
|
"LabelIntervalEvery15Minutes": "Iga 15 minuti tagant",
|
||||||
|
"LabelIntervalEvery2Hours": "Iga 2 tunni tagant",
|
||||||
|
"LabelIntervalEvery30Minutes": "Iga 30 minuti tagant",
|
||||||
|
"LabelIntervalEvery6Hours": "Iga 6 tunni tagant",
|
||||||
|
"LabelIntervalEveryDay": "Iga päev",
|
||||||
|
"LabelIntervalEveryHour": "Iga tunni tagant",
|
||||||
|
"LabelInvalidParts": "Vigased osad",
|
||||||
|
"LabelInvert": "Pööra ümber",
|
||||||
|
"LabelItem": "Kirje",
|
||||||
|
"LabelLanguage": "Keel",
|
||||||
|
"LabelLanguageDefaultServer": "Vaikeserveri keel",
|
||||||
|
"LabelLastBookAdded": "Viimati lisatud raamat",
|
||||||
|
"LabelLastBookUpdated": "Viimati uuendatud raamat",
|
||||||
|
"LabelLastSeen": "Viimati nähtud",
|
||||||
|
"LabelLastTime": "Viimati aeg",
|
||||||
|
"LabelLastUpdate": "Viimane uuendus",
|
||||||
|
"LabelLayout": "Paigutus",
|
||||||
|
"LabelLayoutSinglePage": "Üks lehekülg",
|
||||||
|
"LabelLayoutSplitPage": "Jagatud lehekülg",
|
||||||
|
"LabelLess": "Vähem",
|
||||||
|
"LabelLibrariesAccessibleToUser": "Kasutajale ligipääsetavad raamatukogud",
|
||||||
|
"LabelLibrary": "Raamatukogu",
|
||||||
|
"LabelLibraryItem": "Raamatukogu kirje",
|
||||||
|
"LabelLibraryName": "Raamatukogu nimi",
|
||||||
|
"LabelLimit": "Piirang",
|
||||||
|
"LabelLineSpacing": "Joonevahe",
|
||||||
|
"LabelListenAgain": "Kuula uuesti",
|
||||||
|
"LabelLogLevelDebug": "Silumine",
|
||||||
|
"LabelLogLevelInfo": "Teave",
|
||||||
|
"LabelLogLevelWarn": "Hoiatus",
|
||||||
|
"LabelLookForNewEpisodesAfterDate": "Otsi uusi episoodid pärast seda kuupäeva",
|
||||||
|
"LabelLowestPriority": "Madalaim prioriteet",
|
||||||
|
"LabelMatchExistingUsersBy": "Sobita olemasolevad kasutajad",
|
||||||
|
"LabelMatchExistingUsersByDescription": "Kasutatakse olemasolevate kasutajate ühendamiseks. Ühendatud kasutajaid sobitatakse teie SSO pakkuja unikaalse ID järgi.",
|
||||||
|
"LabelMediaPlayer": "Meediapleier",
|
||||||
|
"LabelMediaType": "Meedia tüüp",
|
||||||
|
"LabelMetadataOrderOfPrecedenceDescription": "Kõrgema prioriteediga metaandmete allikad võtavad üle madalama prioriteediga metaandmete allikad",
|
||||||
|
"LabelMetadataProvider": "Metaandmete pakkuja",
|
||||||
|
"LabelMetaTag": "Meta märge",
|
||||||
|
"LabelMetaTags": "Meta märgendid",
|
||||||
|
"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.",
|
||||||
|
"LabelMore": "Rohkem",
|
||||||
|
"LabelMoreInfo": "Rohkem infot",
|
||||||
|
"LabelName": "Nimi",
|
||||||
|
"LabelNarrator": "Jutustaja",
|
||||||
|
"LabelNarrators": "Jutustajad",
|
||||||
|
"LabelNew": "Uus",
|
||||||
|
"LabelNewestAuthors": "Uusimad autorid",
|
||||||
|
"LabelNewestEpisodes": "Uusimad episoodid",
|
||||||
|
"LabelNewPassword": "Uus parool",
|
||||||
|
"LabelNextBackupDate": "Järgmine varukoopia kuupäev",
|
||||||
|
"LabelNextScheduledRun": "Järgmine ajakava järgmine",
|
||||||
|
"LabelNoEpisodesSelected": "Episoodid pole valitud",
|
||||||
|
"LabelNotes": "Märkused",
|
||||||
|
"LabelNotFinished": "Ei ole lõpetatud",
|
||||||
|
"LabelNotificationAppriseURL": "Apprise URL-id",
|
||||||
|
"LabelNotificationAvailableVariables": "Saadaolevad muutujad",
|
||||||
|
"LabelNotificationBodyTemplate": "Keha mall",
|
||||||
|
"LabelNotificationEvent": "Teavituse sündmus",
|
||||||
|
"LabelNotificationsMaxFailedAttempts": "Maksimaalsed ebaõnnestunud katsed",
|
||||||
|
"LabelNotificationsMaxFailedAttemptsHelp": "Teatised keelatakse, kui need ebaõnnestuvad nii palju kordi",
|
||||||
|
"LabelNotificationsMaxQueueSize": "Teavituste sündmuste maksimaalne järjekorra suurus",
|
||||||
|
"LabelNotificationsMaxQueueSizeHelp": "Sündmused on piiratud 1 sekundiga. Sündmusi ignoreeritakse, kui järjekord on maksimumsuuruses. See takistab teavituste rämpsposti.",
|
||||||
|
"LabelNotificationTitleTemplate": "Pealkirja mall",
|
||||||
|
"LabelNotStarted": "Pole alustatud",
|
||||||
|
"LabelNumberOfBooks": "Raamatute arv",
|
||||||
|
"LabelNumberOfEpisodes": "Episoodide arv",
|
||||||
|
"LabelOpenRSSFeed": "Ava RSS voog",
|
||||||
|
"LabelOverwrite": "Kirjuta üle",
|
||||||
|
"LabelPassword": "Parool",
|
||||||
|
"LabelPath": "Asukoht",
|
||||||
|
"LabelPermissionsAccessAllLibraries": "Saab ligi kõikidele raamatukogudele",
|
||||||
|
"LabelPermissionsAccessAllTags": "Saab ligi kõikidele siltidele",
|
||||||
|
"LabelPermissionsAccessExplicitContent": "Saab ligi vulgaarsele sisule",
|
||||||
|
"LabelPermissionsDelete": "Saab kustutada",
|
||||||
|
"LabelPermissionsDownload": "Saab alla laadida",
|
||||||
|
"LabelPermissionsUpdate": "Saab uuendada",
|
||||||
|
"LabelPermissionsUpload": "Saab üles laadida",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
|
"LabelPhotoPathURL": "Foto tee/URL",
|
||||||
|
"LabelPlaylists": "Mänguloendid",
|
||||||
|
"LabelPlayMethod": "Esitusmeetod",
|
||||||
|
"LabelPodcast": "Podcast",
|
||||||
|
"LabelPodcasts": "Podcastid",
|
||||||
|
"LabelPodcastSearchRegion": "Podcasti otsingu piirkond",
|
||||||
|
"LabelPodcastType": "Podcasti tüüp",
|
||||||
|
"LabelPort": "Port",
|
||||||
|
"LabelPrefixesToIgnore": "Eiramiseks eesliited (tõstutundetu)",
|
||||||
|
"LabelPreventIndexing": "Vältige oma voogu indekseerimist iTunes'i ja Google podcasti kataloogides",
|
||||||
|
"LabelPrimaryEbook": "Esmane e-raamat",
|
||||||
|
"LabelProgress": "Edenemine",
|
||||||
|
"LabelProvider": "Pakkuja",
|
||||||
|
"LabelPubDate": "Avaldamise kuupäev",
|
||||||
|
"LabelPublisher": "Kirjastaja",
|
||||||
|
"LabelPublishYear": "Aasta avaldamine",
|
||||||
|
"LabelRead": "Lugenud",
|
||||||
|
"LabelReadAgain": "Loe uuesti",
|
||||||
|
"LabelReadEbookWithoutProgress": "Lugege e-raamatut ilma edenemist säilitamata",
|
||||||
|
"LabelRecentlyAdded": "Hiljuti lisatud",
|
||||||
|
"LabelRecentSeries": "Hiljutised seeriad",
|
||||||
|
"LabelRecommended": "Soovitatud",
|
||||||
|
"LabelRedo": "Tee uuesti",
|
||||||
|
"LabelRegion": "Piirkond",
|
||||||
|
"LabelReleaseDate": "Väljalaske kuupäev",
|
||||||
|
"LabelRemoveCover": "Eemalda ümbris",
|
||||||
|
"LabelRowsPerPage": "Rida lehe kohta",
|
||||||
|
"LabelRSSFeedCustomOwnerEmail": "Kohandatud omaniku e-post",
|
||||||
|
"LabelRSSFeedCustomOwnerName": "Kohandatud omaniku nimi",
|
||||||
|
"LabelRSSFeedOpen": "Ava RSS voog",
|
||||||
|
"LabelRSSFeedPreventIndexing": "Vältige indekseerimist",
|
||||||
|
"LabelRSSFeedSlug": "RSS voog Slug",
|
||||||
|
"LabelRSSFeedURL": "RSS voog URL",
|
||||||
|
"LabelSearchTerm": "Otsingutermin",
|
||||||
|
"LabelSearchTitle": "Otsi pealkirja",
|
||||||
|
"LabelSearchTitleOrASIN": "Otsi pealkirja või ASIN-i",
|
||||||
|
"LabelSeason": "Hooaeg",
|
||||||
|
"LabelSelectAllEpisodes": "Vali kõik episoodid",
|
||||||
|
"LabelSelectEpisodesShowing": "Valige {0} näidatavat episoodi",
|
||||||
|
"LabelSelectUsers": "Valige kasutajad",
|
||||||
|
"LabelSendEbookToDevice": "Saada e-raamat seadmele...",
|
||||||
|
"LabelSequence": "Järjestus",
|
||||||
|
"LabelSeries": "Seeria",
|
||||||
|
"LabelSeriesName": "Seeria nimi",
|
||||||
|
"LabelSeriesProgress": "Seeria edenemine",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
|
"LabelSetEbookAsPrimary": "Määra peamiseks",
|
||||||
|
"LabelSetEbookAsSupplementary": "Määra täiendavaks",
|
||||||
|
"LabelSettingsAudiobooksOnly": "Ainult heliraamatud",
|
||||||
|
"LabelSettingsAudiobooksOnlyHelp": "Selle seadistuse lubamine eirab e-raamatute faile, välja arvatud juhul, kui need on heliraamatu kaustas, kus need seatakse täiendavate e-raamatutena",
|
||||||
|
"LabelSettingsBookshelfViewHelp": "Skeumorfne kujundus puidust riiulitega",
|
||||||
|
"LabelSettingsChromecastSupport": "Chromecasti tugi",
|
||||||
|
"LabelSettingsDateFormat": "Kuupäeva vorming",
|
||||||
|
"LabelSettingsDisableWatcher": "Keela vaatamine",
|
||||||
|
"LabelSettingsDisableWatcherForLibrary": "Keela kaustavaatamine raamatukogu jaoks",
|
||||||
|
"LabelSettingsDisableWatcherHelp": "Keelab automaatse lisamise/uuendamise, kui failimuudatusi tuvastatakse. *Nõuab serveri taaskäivitamist",
|
||||||
|
"LabelSettingsEnableWatcher": "Luba vaatamine",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Luba kaustavaatamine raamatukogu jaoks",
|
||||||
|
"LabelSettingsEnableWatcherHelp": "Lubab automaatset lisamist/uuendamist, kui tuvastatakse failimuudatused. *Nõuab serveri taaskäivitamist",
|
||||||
|
"LabelSettingsExperimentalFeatures": "Eksperimentaalsed funktsioonid",
|
||||||
|
"LabelSettingsExperimentalFeaturesHelp": "Arengus olevad funktsioonid, mis vajavad teie tagasisidet ja abi testimisel. Klõpsake GitHubi arutelu avamiseks.",
|
||||||
|
"LabelSettingsFindCovers": "Leia ümbrised",
|
||||||
|
"LabelSettingsFindCoversHelp": "Kui teie heliraamatul pole sisseehitatud ümbrist ega ümbrise pilti kaustas, proovib skanner leida ümbrist.<br>Märkus: see pikendab skaneerimisaega",
|
||||||
|
"LabelSettingsHideSingleBookSeries": "Peida üksikute raamatute seeriad",
|
||||||
|
"LabelSettingsHideSingleBookSeriesHelp": "Ühe raamatuga seeriaid peidetakse seeria lehelt ja avalehe riiulitelt.",
|
||||||
|
"LabelSettingsHomePageBookshelfView": "Avaleht kasutage raamatukoguvaadet",
|
||||||
|
"LabelSettingsLibraryBookshelfView": "Raamatukogu kasutamiseks kasutage raamatukoguvaadet",
|
||||||
|
"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": "Lugege subtiitreid",
|
||||||
|
"LabelSettingsParseSubtitlesHelp": "Eraldage subtiitrid heliraamatu kaustade nimedest.<br>Subtiitrid peavad olema eraldatud \" - \".<br>Näiteks: \"Raamatu pealkiri - Siin on alapealkiri\" alapealkiri on \"Siin on alapealkiri\"",
|
||||||
|
"LabelSettingsPreferMatchedMetadata": "Eelista sobitatud metaandmeid",
|
||||||
|
"LabelSettingsPreferMatchedMetadataHelp": "Sobitatud andmed kirjutavad Kiir Sobitamise kasutamisel üle üksikasjad.",
|
||||||
|
"LabelSettingsSkipMatchingBooksWithASIN": "Jätke ASIN-iga sobituvad raamatud vahele",
|
||||||
|
"LabelSettingsSkipMatchingBooksWithISBN": "Jätke ISBN-iga sobituvad raamatud vahele",
|
||||||
|
"LabelSettingsSortingIgnorePrefixes": "Ignoreeri eesliiteid sortimisel",
|
||||||
|
"LabelSettingsSortingIgnorePrefixesHelp": "nt. eesliidet \"the\" kasutades raamatu pealkiri \"The Book Title\" sorteeritakse \"Book Title, The\"",
|
||||||
|
"LabelSettingsSquareBookCovers": "Kasutage ruudukujulisi raamatu kaasi",
|
||||||
|
"LabelSettingsSquareBookCoversHelp": "Eelistage ruudukujulisi kaasi tavaliste 1.6:1 raamatu ümbrise asemel",
|
||||||
|
"LabelSettingsStoreCoversWithItem": "Salvesta kaaned üksusega",
|
||||||
|
"LabelSettingsStoreCoversWithItemHelp": "Vaikimisi salvestatakse kaaned /metadata/items kausta. Selle seadistuse lubamine salvestab kaaned teie raamatukogu üksuse kausta. Hoitakse ainult ühte faili nimega \"kaas\"",
|
||||||
|
"LabelSettingsStoreMetadataWithItem": "Salvesta metaandmed üksusega",
|
||||||
|
"LabelSettingsStoreMetadataWithItemHelp": "Vaikimisi salvestatakse metaandmed /metadata/items kausta. Selle seadistuse lubamine salvestab metaandmed teie raamatukogu üksuse kaustadesse",
|
||||||
|
"LabelSettingsTimeFormat": "Kellaaja vorming",
|
||||||
|
"LabelShowAll": "Näita kõiki",
|
||||||
|
"LabelSize": "Suurus",
|
||||||
|
"LabelSleepTimer": "Uinaku taimer",
|
||||||
|
"LabelSlug": "Slug",
|
||||||
|
"LabelStart": "Alusta",
|
||||||
|
"LabelStarted": "Alustatud",
|
||||||
|
"LabelStartedAt": "Alustatud",
|
||||||
|
"LabelStartTime": "Alustamise aeg",
|
||||||
|
"LabelStatsAudioTracks": "Audiojäljed",
|
||||||
|
"LabelStatsAuthors": "Autorid",
|
||||||
|
"LabelStatsBestDay": "Parim päev",
|
||||||
|
"LabelStatsDailyAverage": "Päevane keskmine",
|
||||||
|
"LabelStatsDays": "Päevad",
|
||||||
|
"LabelStatsDaysListened": "Kuulatud päevad",
|
||||||
|
"LabelStatsHours": "Tunnid",
|
||||||
|
"LabelStatsInARow": "järjest",
|
||||||
|
"LabelStatsItemsFinished": "Lõpetatud üksused",
|
||||||
|
"LabelStatsItemsInLibrary": "Üksused raamatukogus",
|
||||||
|
"LabelStatsMinutes": "minutit",
|
||||||
|
"LabelStatsMinutesListening": "Kuulamise minutid",
|
||||||
|
"LabelStatsOverallDays": "Kokku päevad",
|
||||||
|
"LabelStatsOverallHours": "Kokku tunnid",
|
||||||
|
"LabelStatsWeekListening": "Nädala kuulamine",
|
||||||
|
"LabelSubtitle": "Alapealkiri",
|
||||||
|
"LabelSupportedFileTypes": "Toetatud failitüübid",
|
||||||
|
"LabelTag": "Silt",
|
||||||
|
"LabelTags": "Sildid",
|
||||||
|
"LabelTagsAccessibleToUser": "Kasutajale kättesaadavad sildid",
|
||||||
|
"LabelTagsNotAccessibleToUser": "Kasutajale mittekättesaadavad sildid",
|
||||||
|
"LabelTasks": "Käimasolevad ülesanded",
|
||||||
|
"LabelTextEditorBulletedList": "Punktloend",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numberloend",
|
||||||
|
"LabelTextEditorUnlink": "Eemalda link",
|
||||||
|
"LabelTheme": "Teema",
|
||||||
|
"LabelThemeDark": "Tume",
|
||||||
|
"LabelThemeLight": "Hele",
|
||||||
|
"LabelTimeBase": "Aja alus",
|
||||||
|
"LabelTimeListened": "Kuulatud aeg",
|
||||||
|
"LabelTimeListenedToday": "Täna kuulatud aeg",
|
||||||
|
"LabelTimeRemaining": "{0} jäänud",
|
||||||
|
"LabelTimeToShift": "Nihutamiseks sekundites kuluv aeg",
|
||||||
|
"LabelTitle": "Pealkiri",
|
||||||
|
"LabelToolsEmbedMetadata": "Manusta metaandmed",
|
||||||
|
"LabelToolsEmbedMetadataDescription": "Manusta metaandmed helifailidesse, sealhulgas kaanepilt ja peatükid.",
|
||||||
|
"LabelToolsMakeM4b": "Loo M4B heliraamatu fail",
|
||||||
|
"LabelToolsMakeM4bDescription": "Loo .M4B heliraamatu fail, kuhu on manustatud metaandmed, kaanepilt ja peatükid.",
|
||||||
|
"LabelToolsSplitM4b": "Jaga M4B MP3-deks",
|
||||||
|
"LabelToolsSplitM4bDescription": "Loo MP3-d M4B-st peatükkide kaupa, kus on manustatud metaandmed, kaanepilt ja peatükid.",
|
||||||
|
"LabelTotalDuration": "Kogukestus",
|
||||||
|
"LabelTotalTimeListened": "Kogu kuulatud aeg",
|
||||||
|
"LabelTrackFromFilename": "Jälg nimest",
|
||||||
|
"LabelTrackFromMetadata": "Jälg metaandmetest",
|
||||||
|
"LabelTracks": "Jäljed",
|
||||||
|
"LabelTracksMultiTrack": "Mitmejälg",
|
||||||
|
"LabelTracksNone": "Ühtegi jälgimist",
|
||||||
|
"LabelTracksSingleTrack": "Üksikjälg",
|
||||||
|
"LabelType": "Tüüp",
|
||||||
|
"LabelUnabridged": "Täismahus",
|
||||||
|
"LabelUndo": "Võta tagasi",
|
||||||
|
"LabelUnknown": "Tundmatu",
|
||||||
|
"LabelUpdateCover": "Uuenda kaant",
|
||||||
|
"LabelUpdateCoverHelp": "Luba üle kirjutamine olemasolevate kaante jaoks valitud raamatutele, kui leitakse sobivus",
|
||||||
|
"LabelUpdatedAt": "Uuendatud",
|
||||||
|
"LabelUpdateDetails": "Uuenda üksikasju",
|
||||||
|
"LabelUpdateDetailsHelp": "Luba üle kirjutamine olemasolevate üksikasjade jaoks valitud raamatutele, kui leitakse sobivus",
|
||||||
|
"LabelUploaderDragAndDrop": "Lohista ja aseta faile või kaustu",
|
||||||
|
"LabelUploaderDropFiles": "Aseta failid",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Hangi automaatselt pealkiri, autor ja seeria",
|
||||||
|
"LabelUseChapterTrack": "Kasuta peatüki jälge",
|
||||||
|
"LabelUseFullTrack": "Kasuta täielikku jälge",
|
||||||
|
"LabelUser": "Kasutaja",
|
||||||
|
"LabelUsername": "Kasutajanimi",
|
||||||
|
"LabelValue": "Väärtus",
|
||||||
|
"LabelVersion": "Versioon",
|
||||||
|
"LabelViewBookmarks": "Vaata järjehoidjaid",
|
||||||
|
"LabelViewChapters": "Vaata peatükke",
|
||||||
|
"LabelViewQueue": "Vaata esitusjärjekorda",
|
||||||
|
"LabelVolume": "Heli tugevus",
|
||||||
|
"LabelWeekdaysToRun": "Päevad nädalas käivitamiseks",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
|
"LabelYourAudiobookDuration": "Teie heliraamatu kestus",
|
||||||
|
"LabelYourBookmarks": "Teie järjehoidjad",
|
||||||
|
"LabelYourPlaylists": "Teie esitusloendid",
|
||||||
|
"LabelYourProgress": "Teie edenemine",
|
||||||
|
"MessageAddToPlayerQueue": "Lisa esitusjärjekorda",
|
||||||
|
"MessageAppriseDescription": "Selle funktsiooni kasutamiseks peate käivitama <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> eksemplari või API, mis töötleb samu päringuid. <br />Apprise API URL peaks olema täielik URL-rada teatise saatmiseks, näiteks kui teie API eksemplar töötab aadressil <code>http://192.168.1.1:8337</code>, siis peaksite sisestama <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
"MessageBackupsDescription": "Varukoopiad hõlmavad kasutajaid, kasutajate edenemist, raamatukogu üksikasju, serveri seadeid ja kaustades <code>/metadata/items</code> ja <code>/metadata/authors</code> salvestatud pilte. Varukoopiad ei hõlma ühtegi teie raamatukogu kaustades olevat faili.",
|
||||||
|
"MessageBatchQuickMatchDescription": "Kiire sobitamine üritab lisada valitud üksustele puuduvad kaaned ja metaandmed. Luba allpool olevad valikud, et lubada Kiire sobitamine'il üle kirjutada olemasolevaid kaasi ja/või metaandmeid.",
|
||||||
|
"MessageBookshelfNoCollections": "Te pole veel ühtegi kogumit teinud",
|
||||||
|
"MessageBookshelfNoResultsForFilter": "Filtrile \"{0}: {1}\" pole tulemusi",
|
||||||
|
"MessageBookshelfNoRSSFeeds": "Ühtegi RSS-i voogu pole avatud",
|
||||||
|
"MessageBookshelfNoSeries": "Teil pole ühtegi seeriat",
|
||||||
|
"MessageChapterEndIsAfter": "Peatüki lõpp on pärast teie heliraamatu lõppu",
|
||||||
|
"MessageChapterErrorFirstNotZero": "Esimene peatükk peab algama 0-st",
|
||||||
|
"MessageChapterErrorStartGteDuration": "Vigane algusaeg peab olema väiksem kui heliraamatu kestus",
|
||||||
|
"MessageChapterErrorStartLtPrev": "Vigane algusaeg peab olema suurem või võrdne eelneva peatüki algusajaga",
|
||||||
|
"MessageChapterStartIsAfter": "Peatüki algus on pärast teie heliraamatu lõppu",
|
||||||
|
"MessageCheckingCron": "Croni kontrollimine...",
|
||||||
|
"MessageConfirmCloseFeed": "Olete kindel, et soovite selle voo sulgeda?",
|
||||||
|
"MessageConfirmDeleteBackup": "Olete kindel, et soovite varukoopia kustutada {0} kohta?",
|
||||||
|
"MessageConfirmDeleteFile": "See kustutab faili teie failisüsteemist. Olete kindel?",
|
||||||
|
"MessageConfirmDeleteLibrary": "Olete kindel, et soovite raamatukogu \"{0}\" lõplikult kustutada?",
|
||||||
|
"MessageConfirmDeleteLibraryItem": "See kustutab raamatukogu üksuse andmebaasist ja failisüsteemist. Olete kindel?",
|
||||||
|
"MessageConfirmDeleteLibraryItems": "See kustutab {0} raamatukogu üksust andmebaasist ja failisüsteemist. Olete kindel?",
|
||||||
|
"MessageConfirmDeleteSession": "Olete kindel, et soovite selle seansi kustutada?",
|
||||||
|
"MessageConfirmForceReScan": "Olete kindel, et soovite jõuga uuesti skannida?",
|
||||||
|
"MessageConfirmMarkAllEpisodesFinished": "Olete kindel, et soovite kõik episoodid lõpetatuks märkida?",
|
||||||
|
"MessageConfirmMarkAllEpisodesNotFinished": "Olete kindel, et soovite kõik episoodid mitte lõpetatuks märkida?",
|
||||||
|
"MessageConfirmMarkSeriesFinished": "Olete kindel, et soovite selle seeria kõik raamatud lõpetatuks märkida?",
|
||||||
|
"MessageConfirmMarkSeriesNotFinished": "Olete kindel, et soovite selle seeria kõik raamatud mitte lõpetatuks märkida?",
|
||||||
|
"MessageConfirmQuickEmbed": "Hoiatus! Quick Embed ei tee varukoopiaid teie helifailidest. Veenduge, et teil oleks varukoopia oma helifailidest. <br><br>Kas soovite jätkata?",
|
||||||
|
"MessageConfirmRemoveAllChapters": "Olete kindel, et soovite eemaldada kõik peatükid?",
|
||||||
|
"MessageConfirmRemoveAuthor": "Olete kindel, et soovite autori \"{0}\" eemaldada?",
|
||||||
|
"MessageConfirmRemoveCollection": "Olete kindel, et soovite kogumi \"{0}\" eemaldada?",
|
||||||
|
"MessageConfirmRemoveEpisode": "Olete kindel, et soovite episoodi \"{0}\" eemaldada?",
|
||||||
|
"MessageConfirmRemoveEpisodes": "Olete kindel, et soovite eemaldada {0} episoodi?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Olete kindel, et soovite eemaldada {0} kuulamise sessiooni?",
|
||||||
|
"MessageConfirmRemoveNarrator": "Olete kindel, et soovite jutustaja \"{0}\" eemaldada?",
|
||||||
|
"MessageConfirmRemovePlaylist": "Olete kindel, et soovite eemaldada oma esitusloendi \"{0}\"?",
|
||||||
|
"MessageConfirmRenameGenre": "Olete kindel, et soovite žanri \"{0}\" ümber nimetada kujule \"{1}\" kõikidele üksustele?",
|
||||||
|
"MessageConfirmRenameGenreMergeNote": "Märkus: See žanr on juba olemas, nii et need ühendatakse.",
|
||||||
|
"MessageConfirmRenameGenreWarning": "Hoiatus! Sarnane žanr erineva puhvriga on juba olemas \"{0}\".",
|
||||||
|
"MessageConfirmRenameTag": "Olete kindel, et soovite silti \"{0}\" ümber nimetada kujule \"{1}\" kõikidele üksustele?",
|
||||||
|
"MessageConfirmRenameTagMergeNote": "Märkus: See silt on juba olemas, nii et need ühendatakse.",
|
||||||
|
"MessageConfirmRenameTagWarning": "Hoiatus! Sarnane silt erineva puhvriga on juba olemas \"{0}\".",
|
||||||
|
"MessageConfirmReScanLibraryItems": "Olete kindel, et soovite uuesti skannida {0} üksust?",
|
||||||
|
"MessageConfirmSendEbookToDevice": "Olete kindel, et soovite saata {0} e-raamatu \"{1}\" seadmesse \"{2}\"?",
|
||||||
|
"MessageDownloadingEpisode": "Episoodi allalaadimine",
|
||||||
|
"MessageDragFilesIntoTrackOrder": "Lohistage failid õigesse järjekorda",
|
||||||
|
"MessageEmbedFinished": "Manustamine lõpetatud!",
|
||||||
|
"MessageEpisodesQueuedForDownload": "{0} Episood(i) on allalaadimiseks järjekorras",
|
||||||
|
"MessageFeedURLWillBe": "Toite URL saab olema {0}",
|
||||||
|
"MessageFetching": "Hangitakse...",
|
||||||
|
"MessageForceReScanDescription": "skaneerib kõik failid uuesti nagu värsket skannimist. Heli faili ID3 silte, OPF faile ja tekstifaile skaneeritakse uuesti.",
|
||||||
|
"MessageImportantNotice": "Oluline märkus!",
|
||||||
|
"MessageInsertChapterBelow": "Sisesta peatükk allapoole",
|
||||||
|
"MessageItemsSelected": "{0} Valitud üksust",
|
||||||
|
"MessageItemsUpdated": "{0} Üksust on uuendatud",
|
||||||
|
"MessageJoinUsOn": "Liitu meiega",
|
||||||
|
"MessageListeningSessionsInTheLastYear": "Kuulamissessioone viimase aasta jooksul: {0}",
|
||||||
|
"MessageLoading": "Laadimine...",
|
||||||
|
"MessageLoadingFolders": "Kaustade laadimine...",
|
||||||
|
"MessageM4BFailed": "M4B ebaõnnestus!",
|
||||||
|
"MessageM4BFinished": "M4B lõpetatud!",
|
||||||
|
"MessageMapChapterTitles": "Kaarda peatükkide pealkirjad olemasolevatele heliraamatu peatükkidele, ajatempe ei muudeta",
|
||||||
|
"MessageMarkAllEpisodesFinished": "Märgi kõik episoodid lõpetatuks",
|
||||||
|
"MessageMarkAllEpisodesNotFinished": "Märgi kõik episoodid mitte lõpetatuks",
|
||||||
|
"MessageMarkAsFinished": "Märgi lõpetatuks",
|
||||||
|
"MessageMarkAsNotFinished": "Märgi mitte lõpetatuks",
|
||||||
|
"MessageMatchBooksDescription": "üritab raamatuid raamatukogus sobitada otsingupakkujast leitud raamatuga ning täita tühjad üksikasjad ja kaas. Ei üle kirjuta üksikasju.",
|
||||||
|
"MessageNoAudioTracks": "Ühtegi helijälge pole",
|
||||||
|
"MessageNoAuthors": "Ühtegi autori pole",
|
||||||
|
"MessageNoBackups": "Ühtegi varukoopia pole",
|
||||||
|
"MessageNoBookmarks": "Ühtegi järjehoidjat pole",
|
||||||
|
"MessageNoChapters": "Ühtegi peatükki pole",
|
||||||
|
"MessageNoCollections": "Ühtegi kogumit pole",
|
||||||
|
"MessageNoCoversFound": "Ühtegi kaant pole leitud",
|
||||||
|
"MessageNoDescription": "Kirjeldust pole",
|
||||||
|
"MessageNoDownloadsInProgress": "Praegu allalaadimisi pole",
|
||||||
|
"MessageNoDownloadsQueued": "Pole järjekorras allalaadimisi",
|
||||||
|
"MessageNoEpisodeMatchesFound": "Ühtegi episoodi vastet pole leitud",
|
||||||
|
"MessageNoEpisodes": "Ühtegi episoodi pole",
|
||||||
|
"MessageNoFoldersAvailable": "Ühtegi kausta pole saadaval",
|
||||||
|
"MessageNoGenres": "Ühtegi žanrit pole",
|
||||||
|
"MessageNoIssues": "Ühtegi probleemi pole",
|
||||||
|
"MessageNoItems": "Ühtegi üksust pole",
|
||||||
|
"MessageNoItemsFound": "Ühtegi üksust pole leitud",
|
||||||
|
"MessageNoListeningSessions": "Ühtegi kuulamissessiooni pole",
|
||||||
|
"MessageNoLogs": "Ühtegi logi pole",
|
||||||
|
"MessageNoMediaProgress": "Ühtegi meediaprogressi pole",
|
||||||
|
"MessageNoNotifications": "Ühtegi teavitust pole",
|
||||||
|
"MessageNoPodcastsFound": "Ühtegi podcasti pole leitud",
|
||||||
|
"MessageNoResults": "Ühtegi tulemust pole",
|
||||||
|
"MessageNoSearchResultsFor": "Otsingutulemusi pole märksõna kohta: \"{0}\"",
|
||||||
|
"MessageNoSeries": "Ühtegi seeriat pole",
|
||||||
|
"MessageNoTags": "Ühtegi silti pole",
|
||||||
|
"MessageNoTasksRunning": "Ühtegi käimasolevat ülesannet pole",
|
||||||
|
"MessageNotYetImplemented": "Pole veel ellu viidud",
|
||||||
|
"MessageNoUpdateNecessary": "Ühtegi värskendust pole vaja",
|
||||||
|
"MessageNoUpdatesWereNecessary": "Ühtegi värskendust polnud vaja",
|
||||||
|
"MessageNoUserPlaylists": "Teil pole ühtegi esitusloendit",
|
||||||
|
"MessageOr": "või",
|
||||||
|
"MessagePauseChapter": "Peata peatüki esitamine",
|
||||||
|
"MessagePlayChapter": "Kuula peatüki algust",
|
||||||
|
"MessagePlaylistCreateFromCollection": "Loo esitusloend kogumist",
|
||||||
|
"MessagePodcastHasNoRSSFeedForMatching": "Podcastil pole sobitamiseks RSS-voogu",
|
||||||
|
"MessageQuickMatchDescription": "täidab tühjad üksikasjad ja kaaned raamatukogus esimese otsingutulemusega rakendusest '{0}'. Ei üle kirjuta üksikasju, välja arvatud juhul, kui serveri sätetes on lubatud 'Eelista sobitatud metaandmeid'.",
|
||||||
|
"MessageRemoveChapter": "Eemalda peatükk",
|
||||||
|
"MessageRemoveEpisodes": "Eemalda {0} episood(i)",
|
||||||
|
"MessageRemoveFromPlayerQueue": "Eemalda esitusjärjekorrast",
|
||||||
|
"MessageRemoveUserWarning": "Olete kindel, et soovite kasutaja \"{0}\" lõplikult kustutada?",
|
||||||
|
"MessageReportBugsAndContribute": "Raporteeri vigu, palu funktsioone ja aita kaasa",
|
||||||
|
"MessageResetChaptersConfirm": "Olete kindel, et soovite peatükkide lähtestada ja tehtud muudatused tagasi võtta?",
|
||||||
|
"MessageRestoreBackupConfirm": "Olete kindel, et soovite taastada varukoopia, mis loodi",
|
||||||
|
"MessageRestoreBackupWarning": "Varukoopia taastamine kirjutab üle kogu /config ja /metadata/items & /metadata/authors kaustas oleva andmebaasi. <br /><br />Varukoopiad ei muuda teie raamatukogukaustades olevaid faile. Kui olete lubanud serveri sätetel salvestada kaane kunsti ja metaandmed teie raamatukogu kaustadesse, siis neid ei varundata ega kirjutata üle.<br /><br />Kõik teie serveri kasutavad kliendid värskendatakse automaatselt.",
|
||||||
|
"MessageSearchResultsFor": "Otsingutulemused märksõnale",
|
||||||
|
"MessageSelected": "{0} valitud",
|
||||||
|
"MessageServerCouldNotBeReached": "Serveriga ei saanud ühendust luua",
|
||||||
|
"MessageSetChaptersFromTracksDescription": "Määrake peatükid, kasutades iga helifaili peatükina ja peatüki pealkirjana helifaili nime",
|
||||||
|
"MessageStartPlaybackAtTime": "Alustage \"{0}\" esitamist kell {1}?",
|
||||||
|
"MessageThinking": "Mõtlen...",
|
||||||
|
"MessageUploaderItemFailed": "Üleslaadimine ebaõnnestus",
|
||||||
|
"MessageUploaderItemSuccess": "Edukalt üles laaditud!",
|
||||||
|
"MessageUploading": "Üles laadimine...",
|
||||||
|
"MessageValidCronExpression": "Kehtiv cron-väljend",
|
||||||
|
"MessageWatcherIsDisabledGlobally": "Vaatleja on ülemaailmselt keelatud serveri sätetes",
|
||||||
|
"MessageXLibraryIsEmpty": "{0} raamatukogu on tühi!",
|
||||||
|
"MessageYourAudiobookDurationIsLonger": "Teie heliraamatu kestus on pikem kui leitud kestus",
|
||||||
|
"MessageYourAudiobookDurationIsShorter": "Teie heliraamatu kestus on lühem kui leitud kestus",
|
||||||
|
"NoteChangeRootPassword": "Root kasutajal võib olla ainus kasutaja, kellel võib olla tühi parool",
|
||||||
|
"NoteChapterEditorTimes": "Märkus: Esimese peatüki algusaeg peab jääma 0:00 ja viimase peatüki algusaeg ei tohi ületada selle heliraamatu kestust.",
|
||||||
|
"NoteFolderPicker": "Märkus: juba kaardistatud kaustu ei kuvata",
|
||||||
|
"NoteRSSFeedPodcastAppsHttps": "Hoiatus: Enamik podcasti rakendusi nõuab, et RSS-voogu URL kasutaks HTTPS-i",
|
||||||
|
"NoteRSSFeedPodcastAppsPubDate": "Hoiatus: Üks või mitu teie episoodi ei sisalda publikatsioonikuupäeva. Mõned podcasti rakendused nõuavad seda.",
|
||||||
|
"NoteUploaderFoldersWithMediaFiles": "Kaustu, kus on meediat, käsitletakse eraldi raamatukogu üksustena.",
|
||||||
|
"NoteUploaderOnlyAudioFiles": "Kui laadite üles ainult helifaile, käsitletakse iga helifaili eraldi heliraamatuna.",
|
||||||
|
"NoteUploaderUnsupportedFiles": "Toetamata failid jäetakse tähelepanuta. Kausta valimisel või lohistamisel jäetakse tähelepanuta muud failid, mis pole üksuse kaustas.",
|
||||||
|
"PlaceholderNewCollection": "Uue kogumi nimi",
|
||||||
|
"PlaceholderNewFolderPath": "Uus kausta tee",
|
||||||
|
"PlaceholderNewPlaylist": "Uue esitusloendi nimi",
|
||||||
|
"PlaceholderSearch": "Otsi...",
|
||||||
|
"PlaceholderSearchEpisode": "Otsi episoodi...",
|
||||||
|
"ToastAccountUpdateFailed": "Konto värskendamine ebaõnnestus",
|
||||||
|
"ToastAccountUpdateSuccess": "Konto on värskendatud",
|
||||||
|
"ToastAuthorImageRemoveFailed": "Pildi eemaldamine ebaõnnestus",
|
||||||
|
"ToastAuthorImageRemoveSuccess": "Autori pilt on eemaldatud",
|
||||||
|
"ToastAuthorUpdateFailed": "Autori värskendamine ebaõnnestus",
|
||||||
|
"ToastAuthorUpdateMerged": "Autor liidetud",
|
||||||
|
"ToastAuthorUpdateSuccess": "Autor värskendatud",
|
||||||
|
"ToastAuthorUpdateSuccessNoImageFound": "Autor värskendatud (pilti ei leitud)",
|
||||||
|
"ToastBackupCreateFailed": "Varukoopia loomine ebaõnnestus",
|
||||||
|
"ToastBackupCreateSuccess": "Varukoopia loodud",
|
||||||
|
"ToastBackupDeleteFailed": "Varukoopia kustutamine ebaõnnestus",
|
||||||
|
"ToastBackupDeleteSuccess": "Varukoopia kustutatud",
|
||||||
|
"ToastBackupRestoreFailed": "Varukoopia taastamine ebaõnnestus",
|
||||||
|
"ToastBackupUploadFailed": "Varukoopia üles laadimine ebaõnnestus",
|
||||||
|
"ToastBackupUploadSuccess": "Varukoopia üles laaditud",
|
||||||
|
"ToastBatchUpdateFailed": "Partii värskendamine ebaõnnestus",
|
||||||
|
"ToastBatchUpdateSuccess": "Partii värskendamine õnnestus",
|
||||||
|
"ToastBookmarkCreateFailed": "Järjehoidja loomine ebaõnnestus",
|
||||||
|
"ToastBookmarkCreateSuccess": "Järjehoidja lisatud",
|
||||||
|
"ToastBookmarkRemoveFailed": "Järjehoidja eemaldamine ebaõnnestus",
|
||||||
|
"ToastBookmarkRemoveSuccess": "Järjehoidja eemaldatud",
|
||||||
|
"ToastBookmarkUpdateFailed": "Järjehoidja värskendamine ebaõnnestus",
|
||||||
|
"ToastBookmarkUpdateSuccess": "Järjehoidja värskendatud",
|
||||||
|
"ToastChaptersHaveErrors": "Peatükkidel on vigu",
|
||||||
|
"ToastChaptersMustHaveTitles": "Peatükkidel peab olema pealkiri",
|
||||||
|
"ToastCollectionItemsRemoveFailed": "Üksuse(te) eemaldamine kogumist ebaõnnestus",
|
||||||
|
"ToastCollectionItemsRemoveSuccess": "Üksus(ed) eemaldatud kogumist",
|
||||||
|
"ToastCollectionRemoveFailed": "Kogumi eemaldamine ebaõnnestus",
|
||||||
|
"ToastCollectionRemoveSuccess": "Kogum eemaldatud",
|
||||||
|
"ToastCollectionUpdateFailed": "Kogumi värskendamine ebaõnnestus",
|
||||||
|
"ToastCollectionUpdateSuccess": "Kogum värskendatud",
|
||||||
|
"ToastItemCoverUpdateFailed": "Üksuse kaane värskendamine ebaõnnestus",
|
||||||
|
"ToastItemCoverUpdateSuccess": "Üksuse kaas värskendatud",
|
||||||
|
"ToastItemDetailsUpdateFailed": "Üksuse üksikasjade värskendamine ebaõnnestus",
|
||||||
|
"ToastItemDetailsUpdateSuccess": "Üksuse üksikasjad värskendatud",
|
||||||
|
"ToastItemDetailsUpdateUnneeded": "Üksuse üksikasjade värskendamine pole vajalik",
|
||||||
|
"ToastItemMarkedAsFinishedFailed": "Märgistamine kui lõpetatud ebaõnnestus",
|
||||||
|
"ToastItemMarkedAsFinishedSuccess": "Üksus märgitud kui lõpetatud",
|
||||||
|
"ToastItemMarkedAsNotFinishedFailed": "Märgistamine kui mitte lõpetatud ebaõnnestus",
|
||||||
|
"ToastItemMarkedAsNotFinishedSuccess": "Üksus märgitud kui mitte lõpetatud",
|
||||||
|
"ToastLibraryCreateFailed": "Raamatukogu loomine ebaõnnestus",
|
||||||
|
"ToastLibraryCreateSuccess": "Raamatukogu \"{0}\" loodud",
|
||||||
|
"ToastLibraryDeleteFailed": "Raamatukogu kustutamine ebaõnnestus",
|
||||||
|
"ToastLibraryDeleteSuccess": "Raamatukogu kustutatud",
|
||||||
|
"ToastLibraryScanFailedToStart": "Skanneerimine ei käivitunud",
|
||||||
|
"ToastLibraryScanStarted": "Raamatukogu skaneerimine alustatud",
|
||||||
|
"ToastLibraryUpdateFailed": "Raamatukogu värskendamine ebaõnnestus",
|
||||||
|
"ToastLibraryUpdateSuccess": "Raamatukogu \"{0}\" värskendatud",
|
||||||
|
"ToastPlaylistCreateFailed": "Esitusloendi loomine ebaõnnestus",
|
||||||
|
"ToastPlaylistCreateSuccess": "Esitusloend loodud",
|
||||||
|
"ToastPlaylistRemoveFailed": "Esitusloendi eemaldamine ebaõnnestus",
|
||||||
|
"ToastPlaylistRemoveSuccess": "Esitusloend eemaldatud",
|
||||||
|
"ToastPlaylistUpdateFailed": "Esitusloendi värskendamine ebaõnnestus",
|
||||||
|
"ToastPlaylistUpdateSuccess": "Esitusloend värskendatud",
|
||||||
|
"ToastPodcastCreateFailed": "Podcasti loomine ebaõnnestus",
|
||||||
|
"ToastPodcastCreateSuccess": "Podcast loodud edukalt",
|
||||||
|
"ToastRemoveItemFromCollectionFailed": "Üksuse eemaldamine kogumist ebaõnnestus",
|
||||||
|
"ToastRemoveItemFromCollectionSuccess": "Üksus eemaldatud kogumist",
|
||||||
|
"ToastRSSFeedCloseFailed": "RSS-voogu sulgemine ebaõnnestus",
|
||||||
|
"ToastRSSFeedCloseSuccess": "RSS-voog suletud",
|
||||||
|
"ToastSendEbookToDeviceFailed": "E-raamatu saatmine seadmesse ebaõnnestus",
|
||||||
|
"ToastSendEbookToDeviceSuccess": "E-raamat saadetud seadmesse \"{0}\"",
|
||||||
|
"ToastSeriesUpdateFailed": "Sarja värskendamine ebaõnnestus",
|
||||||
|
"ToastSeriesUpdateSuccess": "Sarja värskendamine õnnestus",
|
||||||
|
"ToastSessionDeleteFailed": "Seansi kustutamine ebaõnnestus",
|
||||||
|
"ToastSessionDeleteSuccess": "Sessioon kustutatud",
|
||||||
|
"ToastSocketConnected": "Pesa ühendatud",
|
||||||
|
"ToastSocketDisconnected": "Pesa ühendus katkenud",
|
||||||
|
"ToastSocketFailedToConnect": "Pesa ühendamine ebaõnnestus",
|
||||||
|
"ToastUserDeleteFailed": "Kasutaja kustutamine ebaõnnestus",
|
||||||
|
"ToastUserDeleteSuccess": "Kasutaja kustutatud"
|
||||||
|
}
|
||||||
+151
-111
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"ButtonAdd": "Ajouter",
|
"ButtonAdd": "Ajouter",
|
||||||
"ButtonAddChapters": "Ajouter le chapitre",
|
"ButtonAddChapters": "Ajouter le chapitre",
|
||||||
"ButtonAddDevice": "Add Device",
|
"ButtonAddDevice": "Ajouter un appareil",
|
||||||
"ButtonAddLibrary": "Add Library",
|
"ButtonAddLibrary": "Ajouter une bibliothèque",
|
||||||
"ButtonAddPodcasts": "Ajouter des podcasts",
|
"ButtonAddPodcasts": "Ajouter des podcasts",
|
||||||
"ButtonAddUser": "Add User",
|
"ButtonAddUser": "Ajouter un utilisateur",
|
||||||
"ButtonAddYourFirstLibrary": "Ajouter votre première bibliothèque",
|
"ButtonAddYourFirstLibrary": "Ajouter votre première bibliothèque",
|
||||||
"ButtonApply": "Appliquer",
|
"ButtonApply": "Appliquer",
|
||||||
"ButtonApplyChapters": "Appliquer les chapitres",
|
"ButtonApplyChapters": "Appliquer aux chapitres",
|
||||||
"ButtonAuthors": "Auteurs",
|
"ButtonAuthors": "Auteurs",
|
||||||
"ButtonBrowseForFolder": "Naviguer vers le répertoire",
|
"ButtonBrowseForFolder": "Naviguer vers le répertoire",
|
||||||
"ButtonCancel": "Annuler",
|
"ButtonCancel": "Annuler",
|
||||||
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Cacher",
|
"ButtonHide": "Cacher",
|
||||||
"ButtonHome": "Accueil",
|
"ButtonHome": "Accueil",
|
||||||
"ButtonIssues": "Parutions",
|
"ButtonIssues": "Parutions",
|
||||||
|
"ButtonJumpBackward": "Retour",
|
||||||
|
"ButtonJumpForward": "Avancer",
|
||||||
"ButtonLatest": "Dernière version",
|
"ButtonLatest": "Dernière version",
|
||||||
"ButtonLibrary": "Bibliothèque",
|
"ButtonLibrary": "Bibliothèque",
|
||||||
"ButtonLogout": "Me déconnecter",
|
"ButtonLogout": "Me déconnecter",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "Chercher tous les auteurs",
|
"ButtonMatchAllAuthors": "Chercher tous les auteurs",
|
||||||
"ButtonMatchBooks": "Chercher les livres",
|
"ButtonMatchBooks": "Chercher les livres",
|
||||||
"ButtonNevermind": "Non merci",
|
"ButtonNevermind": "Non merci",
|
||||||
|
"ButtonNext": "Suivant",
|
||||||
|
"ButtonNextChapter": "Chapitre suivant",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Ouvrir le flux",
|
"ButtonOpenFeed": "Ouvrir le flux",
|
||||||
"ButtonOpenManager": "Ouvrir le gestionnaire",
|
"ButtonOpenManager": "Ouvrir le gestionnaire",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "Écouter",
|
"ButtonPlay": "Écouter",
|
||||||
"ButtonPlaying": "En lecture",
|
"ButtonPlaying": "En lecture",
|
||||||
"ButtonPlaylists": "Listes de lecture",
|
"ButtonPlaylists": "Listes de lecture",
|
||||||
|
"ButtonPrevious": "Précédent",
|
||||||
|
"ButtonPreviousChapter": "Chapitre précédent",
|
||||||
"ButtonPurgeAllCache": "Purger le cache",
|
"ButtonPurgeAllCache": "Purger le cache",
|
||||||
"ButtonPurgeItemsCache": "Purger le cache des articles",
|
"ButtonPurgeItemsCache": "Purger le cache des articles",
|
||||||
"ButtonPurgeMediaProgress": "Purger la progression des médias",
|
"ButtonPurgeMediaProgress": "Purger la progression des médias",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "Supprimer de la liste de lecture",
|
"ButtonQueueRemoveItem": "Supprimer de la liste de lecture",
|
||||||
"ButtonQuickMatch": "Recherche rapide",
|
"ButtonQuickMatch": "Recherche rapide",
|
||||||
"ButtonRead": "Lire",
|
"ButtonRead": "Lire",
|
||||||
|
"ButtonRefresh": "Rafraîchir",
|
||||||
"ButtonRemove": "Supprimer",
|
"ButtonRemove": "Supprimer",
|
||||||
"ButtonRemoveAll": "Supprimer tout",
|
"ButtonRemoveAll": "Supprimer tout",
|
||||||
"ButtonRemoveAllLibraryItems": "Supprimer tous les articles de la bibliothèque",
|
"ButtonRemoveAllLibraryItems": "Supprimer tous les articles de la bibliothèque",
|
||||||
@@ -62,7 +70,7 @@
|
|||||||
"ButtonRemoveSeriesFromContinueSeries": "Ne plus continuer à écouter la série",
|
"ButtonRemoveSeriesFromContinueSeries": "Ne plus continuer à écouter la série",
|
||||||
"ButtonReScan": "Nouvelle analyse",
|
"ButtonReScan": "Nouvelle analyse",
|
||||||
"ButtonReset": "Réinitialiser",
|
"ButtonReset": "Réinitialiser",
|
||||||
"ButtonResetToDefault": "Reset to default",
|
"ButtonResetToDefault": "Réinitialiser aux valeurs par défaut",
|
||||||
"ButtonRestore": "Rétablir",
|
"ButtonRestore": "Rétablir",
|
||||||
"ButtonSave": "Sauvegarder",
|
"ButtonSave": "Sauvegarder",
|
||||||
"ButtonSaveAndClose": "Sauvegarder et Fermer",
|
"ButtonSaveAndClose": "Sauvegarder et Fermer",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "Sélectionner le chemin du dossier",
|
"ButtonSelectFolderPath": "Sélectionner le chemin du dossier",
|
||||||
"ButtonSeries": "Séries",
|
"ButtonSeries": "Séries",
|
||||||
"ButtonSetChaptersFromTracks": "Positionner les chapitres par rapports aux pistes",
|
"ButtonSetChaptersFromTracks": "Positionner les chapitres par rapports aux pistes",
|
||||||
|
"ButtonShare": "Partager",
|
||||||
"ButtonShiftTimes": "Décaler l’horodatage du livre",
|
"ButtonShiftTimes": "Décaler l’horodatage du livre",
|
||||||
"ButtonShow": "Afficher",
|
"ButtonShow": "Afficher",
|
||||||
"ButtonStartM4BEncode": "Démarrer l’encodage M4B",
|
"ButtonStartM4BEncode": "Démarrer l’encodage M4B",
|
||||||
@@ -83,14 +92,17 @@
|
|||||||
"ButtonUploadBackup": "Téléverser une sauvegarde",
|
"ButtonUploadBackup": "Téléverser une sauvegarde",
|
||||||
"ButtonUploadCover": "Téléverser une couverture",
|
"ButtonUploadCover": "Téléverser une couverture",
|
||||||
"ButtonUploadOPMLFile": "Téléverser un fichier OPML",
|
"ButtonUploadOPMLFile": "Téléverser un fichier OPML",
|
||||||
"ButtonUserDelete": "Effacer l’utilisateur {0}",
|
"ButtonUserDelete": "Supprimer l’utilisateur {0}",
|
||||||
"ButtonUserEdit": "Modifier l’utilisateur {0}",
|
"ButtonUserEdit": "Modifier l’utilisateur {0}",
|
||||||
"ButtonViewAll": "Afficher tout",
|
"ButtonViewAll": "Afficher tout",
|
||||||
"ButtonYes": "Oui",
|
"ButtonYes": "Oui",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Erreur lors de la récupération des métadonnées",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Impossible de récupérer les métadonnées - essayez de mettre à jour le titre et/ou l’auteur.",
|
||||||
|
"ErrorUploadLacksTitle": "Doit avoir un titre",
|
||||||
"HeaderAccount": "Compte",
|
"HeaderAccount": "Compte",
|
||||||
"HeaderAdvanced": "Avancé",
|
"HeaderAdvanced": "Avancé",
|
||||||
"HeaderAppriseNotificationSettings": "Configuration des Notifications Apprise",
|
"HeaderAppriseNotificationSettings": "Configuration des notifications Apprise",
|
||||||
"HeaderAudiobookTools": "Outils de Gestion de Fichier Audiobook",
|
"HeaderAudiobookTools": "Outils de gestion de fichiers de livres audio",
|
||||||
"HeaderAudioTracks": "Pistes audio",
|
"HeaderAudioTracks": "Pistes audio",
|
||||||
"HeaderAuthentication": "Authentication",
|
"HeaderAuthentication": "Authentication",
|
||||||
"HeaderBackups": "Sauvegardes",
|
"HeaderBackups": "Sauvegardes",
|
||||||
@@ -98,9 +110,10 @@
|
|||||||
"HeaderChapters": "Chapitres",
|
"HeaderChapters": "Chapitres",
|
||||||
"HeaderChooseAFolder": "Choisir un dossier",
|
"HeaderChooseAFolder": "Choisir un dossier",
|
||||||
"HeaderCollection": "Collection",
|
"HeaderCollection": "Collection",
|
||||||
"HeaderCollectionItems": "Entrées de la Collection",
|
"HeaderCollectionItems": "Entrées de la collection",
|
||||||
"HeaderCover": "Couverture",
|
"HeaderCover": "Couverture",
|
||||||
"HeaderCurrentDownloads": "Téléchargements en cours",
|
"HeaderCurrentDownloads": "Téléchargements en cours",
|
||||||
|
"HeaderCustomMetadataProviders": "Fournisseurs de métadonnées personnalisés",
|
||||||
"HeaderDetails": "Détails",
|
"HeaderDetails": "Détails",
|
||||||
"HeaderDownloadQueue": "File d’attente de téléchargements",
|
"HeaderDownloadQueue": "File d’attente de téléchargements",
|
||||||
"HeaderEbookFiles": "Fichier des livres numériques",
|
"HeaderEbookFiles": "Fichier des livres numériques",
|
||||||
@@ -111,10 +124,10 @@
|
|||||||
"HeaderEreaderSettings": "Options Ereader",
|
"HeaderEreaderSettings": "Options Ereader",
|
||||||
"HeaderFiles": "Fichiers",
|
"HeaderFiles": "Fichiers",
|
||||||
"HeaderFindChapters": "Trouver les chapitres",
|
"HeaderFindChapters": "Trouver les chapitres",
|
||||||
"HeaderIgnoredFiles": "Fichiers Ignorés",
|
"HeaderIgnoredFiles": "Fichiers ignorés",
|
||||||
"HeaderItemFiles": "Fichiers des Articles",
|
"HeaderItemFiles": "Fichiers des articles",
|
||||||
"HeaderItemMetadataUtils": "Outils de gestion des métadonnées",
|
"HeaderItemMetadataUtils": "Outils de gestion des métadonnées",
|
||||||
"HeaderLastListeningSession": "Dernière Session d’écoute",
|
"HeaderLastListeningSession": "Dernière session d’écoute",
|
||||||
"HeaderLatestEpisodes": "Dernier épisodes",
|
"HeaderLatestEpisodes": "Dernier épisodes",
|
||||||
"HeaderLibraries": "Bibliothèque",
|
"HeaderLibraries": "Bibliothèque",
|
||||||
"HeaderLibraryFiles": "Fichier de bibliothèque",
|
"HeaderLibraryFiles": "Fichier de bibliothèque",
|
||||||
@@ -127,15 +140,15 @@
|
|||||||
"HeaderManageTags": "Gérer les étiquettes",
|
"HeaderManageTags": "Gérer les étiquettes",
|
||||||
"HeaderMapDetails": "Édition en masse",
|
"HeaderMapDetails": "Édition en masse",
|
||||||
"HeaderMatch": "Chercher",
|
"HeaderMatch": "Chercher",
|
||||||
"HeaderMetadataOrderOfPrecedence": "Metadata order of precedence",
|
"HeaderMetadataOrderOfPrecedence": "Ordre de priorité des métadonnées",
|
||||||
"HeaderMetadataToEmbed": "Métadonnée à intégrer",
|
"HeaderMetadataToEmbed": "Métadonnées à intégrer",
|
||||||
"HeaderNewAccount": "Nouveau compte",
|
"HeaderNewAccount": "Nouveau compte",
|
||||||
"HeaderNewLibrary": "Nouvelle bibliothèque",
|
"HeaderNewLibrary": "Nouvelle bibliothèque",
|
||||||
"HeaderNotifications": "Notifications",
|
"HeaderNotifications": "Notifications",
|
||||||
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentication",
|
"HeaderOpenIDConnectAuthentication": "Authentification via OpenID Connect",
|
||||||
"HeaderOpenRSSFeed": "Ouvrir Flux RSS",
|
"HeaderOpenRSSFeed": "Ouvrir un flux RSS",
|
||||||
"HeaderOtherFiles": "Autres fichiers",
|
"HeaderOtherFiles": "Autres fichiers",
|
||||||
"HeaderPasswordAuthentication": "Password Authentication",
|
"HeaderPasswordAuthentication": "Authentification par mot de passe",
|
||||||
"HeaderPermissions": "Permissions",
|
"HeaderPermissions": "Permissions",
|
||||||
"HeaderPlayerQueue": "Liste d’écoute",
|
"HeaderPlayerQueue": "Liste d’écoute",
|
||||||
"HeaderPlaylist": "Liste de lecture",
|
"HeaderPlaylist": "Liste de lecture",
|
||||||
@@ -151,7 +164,7 @@
|
|||||||
"HeaderSchedule": "Programmation",
|
"HeaderSchedule": "Programmation",
|
||||||
"HeaderScheduleLibraryScans": "Analyse automatique de la bibliothèque",
|
"HeaderScheduleLibraryScans": "Analyse automatique de la bibliothèque",
|
||||||
"HeaderSession": "Session",
|
"HeaderSession": "Session",
|
||||||
"HeaderSetBackupSchedule": "Activer la Sauvegarde Automatique",
|
"HeaderSetBackupSchedule": "Activer la sauvegarde automatique",
|
||||||
"HeaderSettings": "Paramètres",
|
"HeaderSettings": "Paramètres",
|
||||||
"HeaderSettingsDisplay": "Affichage",
|
"HeaderSettingsDisplay": "Affichage",
|
||||||
"HeaderSettingsExperimental": "Fonctionnalités expérimentales",
|
"HeaderSettingsExperimental": "Fonctionnalités expérimentales",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Mettre à jour les détails",
|
"HeaderUpdateDetails": "Mettre à jour les détails",
|
||||||
"HeaderUpdateLibrary": "Mettre à jour la bibliothèque",
|
"HeaderUpdateLibrary": "Mettre à jour la bibliothèque",
|
||||||
"HeaderUsers": "Utilisateurs",
|
"HeaderUsers": "Utilisateurs",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Vos statistiques",
|
"HeaderYourStats": "Vos statistiques",
|
||||||
"LabelAbridged": "Version courte",
|
"LabelAbridged": "Version courte",
|
||||||
"LabelAccountType": "Type de compte",
|
"LabelAccountType": "Type de compte",
|
||||||
@@ -184,11 +198,11 @@
|
|||||||
"LabelAddToCollectionBatch": "Ajout de {0} livres à la lollection",
|
"LabelAddToCollectionBatch": "Ajout de {0} livres à la lollection",
|
||||||
"LabelAddToPlaylist": "Ajouter à la liste de lecture",
|
"LabelAddToPlaylist": "Ajouter à la liste de lecture",
|
||||||
"LabelAddToPlaylistBatch": "{0} éléments ajoutés à la liste de lecture",
|
"LabelAddToPlaylistBatch": "{0} éléments ajoutés à la liste de lecture",
|
||||||
"LabelAdminUsersOnly": "Admin users only",
|
"LabelAdminUsersOnly": "Administrateurs uniquement",
|
||||||
"LabelAll": "Tout",
|
"LabelAll": "Tout",
|
||||||
"LabelAllUsers": "Tous les utilisateurs",
|
"LabelAllUsers": "Tous les utilisateurs",
|
||||||
"LabelAllUsersExcludingGuests": "All users excluding guests",
|
"LabelAllUsersExcludingGuests": "Tous les utilisateurs à l’exception des invités",
|
||||||
"LabelAllUsersIncludingGuests": "All users including guests",
|
"LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités",
|
||||||
"LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
|
"LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
|
||||||
"LabelAppend": "Ajouter",
|
"LabelAppend": "Ajouter",
|
||||||
"LabelAuthor": "Auteur",
|
"LabelAuthor": "Auteur",
|
||||||
@@ -196,27 +210,29 @@
|
|||||||
"LabelAuthorLastFirst": "Auteur (Nom, Prénom)",
|
"LabelAuthorLastFirst": "Auteur (Nom, Prénom)",
|
||||||
"LabelAuthors": "Auteurs",
|
"LabelAuthors": "Auteurs",
|
||||||
"LabelAutoDownloadEpisodes": "Téléchargement automatique d’épisode",
|
"LabelAutoDownloadEpisodes": "Téléchargement automatique d’épisode",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoFetchMetadata": "Recherche automatique de métadonnées",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoFetchMetadataHelp": "Récupère les métadonnées du titre, de l’auteur et de la série pour simplifier le téléchargement. Il se peut que des métadonnées supplémentaires doivent être ajoutées après le téléchargement.",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoLaunch": "Lancement automatique",
|
||||||
"LabelAutoRegisterDescription": "Automatically create new users after logging in",
|
"LabelAutoLaunchDescription": "Redirection automatique vers le fournisseur d'authentification lors de la navigation vers la page de connexion (chemin de remplacement manuel <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelBackToUser": "Revenir à l’Utilisateur",
|
"LabelAutoRegister": "Enregistrement automatique",
|
||||||
"LabelBackupLocation": "Backup Location",
|
"LabelAutoRegisterDescription": "Créer automatiquement de nouveaux utilisateurs après la connexion",
|
||||||
|
"LabelBackToUser": "Retour à l’utilisateur",
|
||||||
|
"LabelBackupLocation": "Emplacement de la sauvegarde",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Activer les sauvegardes automatiques",
|
"LabelBackupsEnableAutomaticBackups": "Activer les sauvegardes automatiques",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Sauvegardes Enregistrées dans /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Sauvegardes enregistrées dans /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Taille maximale de la sauvegarde (en Go)",
|
"LabelBackupsMaxBackupSize": "Taille maximale de la sauvegarde (en Go)",
|
||||||
"LabelBackupsMaxBackupSizeHelp": "Afin de prévenir les mauvaises configuration, la sauvegarde échouera si elle excède la taille limite.",
|
"LabelBackupsMaxBackupSizeHelp": "Afin de prévenir les mauvaises configuration, la sauvegarde échouera si elle excède la taille limite.",
|
||||||
"LabelBackupsNumberToKeep": "Nombre de sauvegardes à maintenir",
|
"LabelBackupsNumberToKeep": "Nombre de sauvegardes à conserver",
|
||||||
"LabelBackupsNumberToKeepHelp": "Une seule sauvegarde sera effacée à la fois. Si vous avez plus de sauvegardes à effacer, vous devrez le faire manuellement.",
|
"LabelBackupsNumberToKeepHelp": "Seule une sauvegarde sera supprimée à la fois. Si vous avez déjà plus de sauvegardes à effacer, vous devez les supprimer manuellement.",
|
||||||
"LabelBitrate": "Bitrate",
|
"LabelBitrate": "Bitrate",
|
||||||
"LabelBooks": "Livres",
|
"LabelBooks": "Livres",
|
||||||
"LabelButtonText": "Button Text",
|
"LabelButtonText": "Texte du bouton",
|
||||||
"LabelChangePassword": "Modifier le mot de passe",
|
"LabelChangePassword": "Modifier le mot de passe",
|
||||||
"LabelChannels": "Canaux",
|
"LabelChannels": "Canaux",
|
||||||
"LabelChapters": "Chapitres",
|
"LabelChapters": "Chapitres",
|
||||||
"LabelChaptersFound": "Chapitres trouvés",
|
"LabelChaptersFound": "chapitres trouvés",
|
||||||
"LabelChapterTitle": "Titres du chapitre",
|
"LabelChapterTitle": "Titre du chapitre",
|
||||||
"LabelClickForMoreInfo": "Click for more info",
|
"LabelClickForMoreInfo": "Cliquez ici pour plus d’informations",
|
||||||
"LabelClosePlayer": "Fermer le lecteur",
|
"LabelClosePlayer": "Fermer le lecteur",
|
||||||
"LabelCodec": "Codec",
|
"LabelCodec": "Codec",
|
||||||
"LabelCollapseSeries": "Réduire les séries",
|
"LabelCollapseSeries": "Réduire les séries",
|
||||||
@@ -230,20 +246,20 @@
|
|||||||
"LabelCover": "Couverture",
|
"LabelCover": "Couverture",
|
||||||
"LabelCoverImageURL": "URL vers l’image de couverture",
|
"LabelCoverImageURL": "URL vers l’image de couverture",
|
||||||
"LabelCreatedAt": "Créé le",
|
"LabelCreatedAt": "Créé le",
|
||||||
"LabelCronExpression": "Expression Cron",
|
"LabelCronExpression": "Expression cron",
|
||||||
"LabelCurrent": "Courrant",
|
"LabelCurrent": "Actuel",
|
||||||
"LabelCurrently": "En ce moment :",
|
"LabelCurrently": "Actuellement :",
|
||||||
"LabelCustomCronExpression": "Expression cron personnalisée:",
|
"LabelCustomCronExpression": "Expression cron personnalisée :",
|
||||||
"LabelDatetime": "Datetime",
|
"LabelDatetime": "Date",
|
||||||
"LabelDeleteFromFileSystemCheckbox": "Delete from file system (uncheck to only remove from database)",
|
"LabelDeleteFromFileSystemCheckbox": "Supprimer du système de fichiers (décocher pour ne supprimer que de la base de données)",
|
||||||
"LabelDescription": "Description",
|
"LabelDescription": "Description",
|
||||||
"LabelDeselectAll": "Tout déselectionner",
|
"LabelDeselectAll": "Tout déselectionner",
|
||||||
"LabelDevice": "Appareil",
|
"LabelDevice": "Appareil",
|
||||||
"LabelDeviceInfo": "Détail de l’appareil",
|
"LabelDeviceInfo": "Détail de l’appareil",
|
||||||
"LabelDeviceIsAvailableTo": "Device is available to...",
|
"LabelDeviceIsAvailableTo": "L’appareil est disponible pour…",
|
||||||
"LabelDirectory": "Répertoire",
|
"LabelDirectory": "Répertoire",
|
||||||
"LabelDiscFromFilename": "Disque depuis le fichier",
|
"LabelDiscFromFilename": "Depuis le fichier",
|
||||||
"LabelDiscFromMetadata": "Disque depuis les métadonnées",
|
"LabelDiscFromMetadata": "Depuis les métadonnées",
|
||||||
"LabelDiscover": "Découvrir",
|
"LabelDiscover": "Découvrir",
|
||||||
"LabelDownload": "Téléchargement",
|
"LabelDownload": "Téléchargement",
|
||||||
"LabelDownloadNEpisodes": "Télécharger {0} épisode(s)",
|
"LabelDownloadNEpisodes": "Télécharger {0} épisode(s)",
|
||||||
@@ -266,33 +282,37 @@
|
|||||||
"LabelExample": "Exemple",
|
"LabelExample": "Exemple",
|
||||||
"LabelExplicit": "Restriction",
|
"LabelExplicit": "Restriction",
|
||||||
"LabelFeedURL": "URL du flux",
|
"LabelFeedURL": "URL du flux",
|
||||||
|
"LabelFetchingMetadata": "Récupération des métadonnées",
|
||||||
"LabelFile": "Fichier",
|
"LabelFile": "Fichier",
|
||||||
"LabelFileBirthtime": "Création du fichier",
|
"LabelFileBirthtime": "Création du fichier",
|
||||||
"LabelFileModified": "Modification du fichier",
|
"LabelFileModified": "Modification du fichier",
|
||||||
"LabelFilename": "Nom de fichier",
|
"LabelFilename": "Nom de fichier",
|
||||||
"LabelFilterByUser": "Filtrer par l’utilisateur",
|
"LabelFilterByUser": "Filtrer par utilisateur",
|
||||||
"LabelFindEpisodes": "Trouver des épisodes",
|
"LabelFindEpisodes": "Trouver des épisodes",
|
||||||
"LabelFinished": "Fini(e)",
|
"LabelFinished": "Terminé le",
|
||||||
"LabelFolder": "Dossier",
|
"LabelFolder": "Dossier",
|
||||||
"LabelFolders": "Dossiers",
|
"LabelFolders": "Dossiers",
|
||||||
"LabelFontFamily": "Famille de polices",
|
"LabelFontBold": "Gras",
|
||||||
|
"LabelFontFamily": "Polices de caractères",
|
||||||
|
"LabelFontItalic": "Italique",
|
||||||
"LabelFontScale": "Taille de la police de caractère",
|
"LabelFontScale": "Taille de la police de caractère",
|
||||||
|
"LabelFontStrikethrough": "Barrer",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Genres",
|
"LabelGenres": "Genres",
|
||||||
"LabelHardDeleteFile": "Suppression du fichier",
|
"LabelHardDeleteFile": "Suppression du fichier",
|
||||||
"LabelHasEbook": "Dispose d’un livre numérique",
|
"LabelHasEbook": "Dispose d’un livre numérique",
|
||||||
"LabelHasSupplementaryEbook": "Dispose d’un livre numérique supplémentaire",
|
"LabelHasSupplementaryEbook": "Dispose d’un livre numérique supplémentaire",
|
||||||
"LabelHighestPriority": "Highest priority",
|
"LabelHighestPriority": "Priorité la plus élevée",
|
||||||
"LabelHost": "Hôte",
|
"LabelHost": "Hôte",
|
||||||
"LabelHour": "Heure",
|
"LabelHour": "Heure",
|
||||||
"LabelIcon": "Icone",
|
"LabelIcon": "Icône",
|
||||||
"LabelImageURLFromTheWeb": "Image URL from the web",
|
"LabelImageURLFromTheWeb": "URL de l’image à partir du web",
|
||||||
"LabelIncludeInTracklist": "Inclure dans la liste des pistes",
|
"LabelIncludeInTracklist": "Inclure dans la liste de lecture",
|
||||||
"LabelIncomplete": "Incomplet",
|
"LabelIncomplete": "Incomplet",
|
||||||
"LabelInProgress": "En cours",
|
"LabelInProgress": "En cours",
|
||||||
"LabelInterval": "Intervalle",
|
"LabelInterval": "Intervalle",
|
||||||
"LabelIntervalCustomDailyWeekly": "Journalier / Hebdomadaire personnalisé",
|
"LabelIntervalCustomDailyWeekly": "Personnaliser quotidiennement / hebdomadairement",
|
||||||
"LabelIntervalEvery12Hours": "Toutes les 12 heures",
|
"LabelIntervalEvery12Hours": "Toutes les 12 heures",
|
||||||
"LabelIntervalEvery15Minutes": "Toutes les 15 minutes",
|
"LabelIntervalEvery15Minutes": "Toutes les 15 minutes",
|
||||||
"LabelIntervalEvery2Hours": "Toutes les 2 heures",
|
"LabelIntervalEvery2Hours": "Toutes les 2 heures",
|
||||||
@@ -325,20 +345,24 @@
|
|||||||
"LabelLogLevelInfo": "Info",
|
"LabelLogLevelInfo": "Info",
|
||||||
"LabelLogLevelWarn": "Warn",
|
"LabelLogLevelWarn": "Warn",
|
||||||
"LabelLookForNewEpisodesAfterDate": "Chercher de nouveaux épisode après cette date",
|
"LabelLookForNewEpisodesAfterDate": "Chercher de nouveaux épisode après cette date",
|
||||||
"LabelLowestPriority": "Lowest Priority",
|
"LabelLowestPriority": "Priorité la plus basse",
|
||||||
"LabelMatchExistingUsersBy": "Match existing users by",
|
"LabelMatchExistingUsersBy": "Faire correspondre les utilisateurs existants par",
|
||||||
"LabelMatchExistingUsersByDescription": "Used for connecting existing users. Once connected, users will be matched by a unique id from your SSO provider",
|
"LabelMatchExistingUsersByDescription": "Utilisé pour connecter les utilisateurs existants. Une fois connectés, les utilisateurs seront associés à un identifiant unique provenant de votre fournisseur SSO.",
|
||||||
"LabelMediaPlayer": "Lecteur multimédia",
|
"LabelMediaPlayer": "Lecteur multimédia",
|
||||||
"LabelMediaType": "Type de média",
|
"LabelMediaType": "Type de média",
|
||||||
"LabelMetadataOrderOfPrecedenceDescription": "Higher priority metadata sources will override lower priority metadata sources",
|
"LabelMetadataOrderOfPrecedenceDescription": "Les sources de métadonnées ayant une priorité plus élevée auront la priorité sur celles ayant une priorité moins élevée.",
|
||||||
"LabelMetadataProvider": "Fournisseur de métadonnées",
|
"LabelMetadataProvider": "Fournisseur de métadonnées",
|
||||||
"LabelMetaTag": "Etiquette de métadonnée",
|
"LabelMetaTag": "Balise de métadonnée",
|
||||||
"LabelMetaTags": "Etiquettes de métadonnée",
|
"LabelMetaTags": "Balises de métadonnée",
|
||||||
"LabelMinute": "Minute",
|
"LabelMinute": "Minute",
|
||||||
"LabelMissing": "Manquant",
|
"LabelMissing": "Manquant",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Parties manquantes",
|
"LabelMissingParts": "Parties manquantes",
|
||||||
|
"LabelMissingSupplementaryEbook": "Has no supplementary ebook",
|
||||||
|
"LabelMobileRedirectURIs": "URI de redirection mobile autorisés",
|
||||||
|
"LabelMobileRedirectURIsDescription": "Il s'agit d'une liste blanche d’URI 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. L’utilisation d’un astérisque (<code>*</code>) comme seule entrée autorise n’importe quel URI.",
|
||||||
"LabelMore": "Plus",
|
"LabelMore": "Plus",
|
||||||
"LabelMoreInfo": "Plus d’info",
|
"LabelMoreInfo": "Plus d’informations",
|
||||||
"LabelName": "Nom",
|
"LabelName": "Nom",
|
||||||
"LabelNarrator": "Narrateur",
|
"LabelNarrator": "Narrateur",
|
||||||
"LabelNarrators": "Narrateurs",
|
"LabelNarrators": "Narrateurs",
|
||||||
@@ -350,7 +374,7 @@
|
|||||||
"LabelNextScheduledRun": "Prochain lancement prévu",
|
"LabelNextScheduledRun": "Prochain lancement prévu",
|
||||||
"LabelNoEpisodesSelected": "Aucun épisode sélectionné",
|
"LabelNoEpisodesSelected": "Aucun épisode sélectionné",
|
||||||
"LabelNotes": "Notes",
|
"LabelNotes": "Notes",
|
||||||
"LabelNotFinished": "Non terminé(e)",
|
"LabelNotFinished": "Non terminé",
|
||||||
"LabelNotificationAppriseURL": "URL(s) d’Apprise",
|
"LabelNotificationAppriseURL": "URL(s) d’Apprise",
|
||||||
"LabelNotificationAvailableVariables": "Variables disponibles",
|
"LabelNotificationAvailableVariables": "Variables disponibles",
|
||||||
"LabelNotificationBodyTemplate": "Modèle de Message",
|
"LabelNotificationBodyTemplate": "Modèle de Message",
|
||||||
@@ -359,10 +383,10 @@
|
|||||||
"LabelNotificationsMaxFailedAttemptsHelp": "La notification est abandonnée une fois ce seuil atteint",
|
"LabelNotificationsMaxFailedAttemptsHelp": "La notification est abandonnée une fois ce seuil atteint",
|
||||||
"LabelNotificationsMaxQueueSize": "Nombres de notifications maximum à mettre en attente",
|
"LabelNotificationsMaxQueueSize": "Nombres de notifications maximum à mettre en attente",
|
||||||
"LabelNotificationsMaxQueueSizeHelp": "La limite de notification est de un évènement par seconde. Les notifications seront ignorées si la file d’attente est à son maximum. Cela empêche un flot trop important.",
|
"LabelNotificationsMaxQueueSizeHelp": "La limite de notification est de un évènement par seconde. Les notifications seront ignorées si la file d’attente est à son maximum. Cela empêche un flot trop important.",
|
||||||
"LabelNotificationTitleTemplate": "Modèle de Titre",
|
"LabelNotificationTitleTemplate": "Modèle de titre",
|
||||||
"LabelNotStarted": "Non Démarré(e)",
|
"LabelNotStarted": "Pas commencé",
|
||||||
"LabelNumberOfBooks": "Nombre de Livres",
|
"LabelNumberOfBooks": "Nombre de livres",
|
||||||
"LabelNumberOfEpisodes": "Nombre d’Episodes",
|
"LabelNumberOfEpisodes": "Nombre d’épisodes",
|
||||||
"LabelOpenRSSFeed": "Ouvrir le flux RSS",
|
"LabelOpenRSSFeed": "Ouvrir le flux RSS",
|
||||||
"LabelOverwrite": "Écraser",
|
"LabelOverwrite": "Écraser",
|
||||||
"LabelPassword": "Mot de passe",
|
"LabelPassword": "Mot de passe",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Peut télécharger",
|
"LabelPermissionsDownload": "Peut télécharger",
|
||||||
"LabelPermissionsUpdate": "Peut mettre à jour",
|
"LabelPermissionsUpdate": "Peut mettre à jour",
|
||||||
"LabelPermissionsUpload": "Peut téléverser",
|
"LabelPermissionsUpload": "Peut téléverser",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Chemin / URL des photos",
|
"LabelPhotoPathURL": "Chemin / URL des photos",
|
||||||
"LabelPlaylists": "Listes de lecture",
|
"LabelPlaylists": "Listes de lecture",
|
||||||
"LabelPlayMethod": "Méthode d’écoute",
|
"LabelPlayMethod": "Méthode d’écoute",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "Région de recherche de podcasts",
|
||||||
"LabelPodcastType": "Type de Podcast",
|
"LabelPodcastType": "Type de Podcast",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Préfixes à Ignorer (Insensible à la Casse)",
|
"LabelPrefixesToIgnore": "Préfixes à Ignorer (Insensible à la Casse)",
|
||||||
@@ -395,14 +421,16 @@
|
|||||||
"LabelRecentlyAdded": "Derniers ajouts",
|
"LabelRecentlyAdded": "Derniers ajouts",
|
||||||
"LabelRecentSeries": "Séries récentes",
|
"LabelRecentSeries": "Séries récentes",
|
||||||
"LabelRecommended": "Recommandé",
|
"LabelRecommended": "Recommandé",
|
||||||
|
"LabelRedo": "Refaire",
|
||||||
"LabelRegion": "Région",
|
"LabelRegion": "Région",
|
||||||
"LabelReleaseDate": "Date de parution",
|
"LabelReleaseDate": "Date de parution",
|
||||||
"LabelRemoveCover": "Supprimer la couverture",
|
"LabelRemoveCover": "Supprimer la couverture",
|
||||||
|
"LabelRowsPerPage": "Lignes par page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Courriel du propriétaire personnalisé",
|
"LabelRSSFeedCustomOwnerEmail": "Courriel du propriétaire personnalisé",
|
||||||
"LabelRSSFeedCustomOwnerName": "Nom propriétaire personnalisé",
|
"LabelRSSFeedCustomOwnerName": "Nom propriétaire personnalisé",
|
||||||
"LabelRSSFeedOpen": "Flux RSS ouvert",
|
"LabelRSSFeedOpen": "Flux RSS ouvert",
|
||||||
"LabelRSSFeedPreventIndexing": "Empêcher l’indexation",
|
"LabelRSSFeedPreventIndexing": "Empêcher l’indexation",
|
||||||
"LabelRSSFeedSlug": "Identificateur d’adresse du Flux RSS ",
|
"LabelRSSFeedSlug": "Balise URL du flux RSS",
|
||||||
"LabelRSSFeedURL": "Adresse du flux RSS",
|
"LabelRSSFeedURL": "Adresse du flux RSS",
|
||||||
"LabelSearchTerm": "Terme de recherche",
|
"LabelSearchTerm": "Terme de recherche",
|
||||||
"LabelSearchTitle": "Titre de recherche",
|
"LabelSearchTitle": "Titre de recherche",
|
||||||
@@ -410,41 +438,44 @@
|
|||||||
"LabelSeason": "Saison",
|
"LabelSeason": "Saison",
|
||||||
"LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
|
"LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
|
||||||
"LabelSelectEpisodesShowing": "Sélectionner {0} episode(s) en cours",
|
"LabelSelectEpisodesShowing": "Sélectionner {0} episode(s) en cours",
|
||||||
"LabelSelectUsers": "Select users",
|
"LabelSelectUsers": "Sélectionner les utilisateurs",
|
||||||
"LabelSendEbookToDevice": "Envoyer le livre numérique à...",
|
"LabelSendEbookToDevice": "Envoyer le livre numérique à…",
|
||||||
"LabelSequence": "Séquence",
|
"LabelSequence": "Séquence",
|
||||||
"LabelSeries": "Séries",
|
"LabelSeries": "Séries",
|
||||||
"LabelSeriesName": "Nom de la série",
|
"LabelSeriesName": "Nom de la série",
|
||||||
"LabelSeriesProgress": "Progression de séries",
|
"LabelSeriesProgress": "Progression de séries",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Définir comme principale",
|
"LabelSetEbookAsPrimary": "Définir comme principale",
|
||||||
"LabelSetEbookAsSupplementary": "Définir comme supplémentaire",
|
"LabelSetEbookAsSupplementary": "Définir comme supplémentaire",
|
||||||
"LabelSettingsAudiobooksOnly": "Livres audios seulement",
|
"LabelSettingsAudiobooksOnly": "Livres audios seulement",
|
||||||
"LabelSettingsAudiobooksOnlyHelp": "L’activation de ce paramètre ignorera les fichiers “ ebook ”, à moins qu’ils ne se trouvent dans un dossier de livres audio, auquel cas ils seront définis comme des livres numériques supplémentaires.",
|
"LabelSettingsAudiobooksOnlyHelp": "L'activation de ce paramètre ignorera les fichiers de type « livre numériques », sauf s'ils se trouvent dans un dossier spécifique , auquel cas ils seront définis comme des livres numériques supplémentaires.",
|
||||||
"LabelSettingsBookshelfViewHelp": "Interface skeuomorphique avec une étagère en bois",
|
"LabelSettingsBookshelfViewHelp": "Interface skeuomorphique avec une étagère en bois",
|
||||||
"LabelSettingsChromecastSupport": "Support du Chromecast",
|
"LabelSettingsChromecastSupport": "Support du Chromecast",
|
||||||
"LabelSettingsDateFormat": "Format de date",
|
"LabelSettingsDateFormat": "Format de date",
|
||||||
"LabelSettingsDisableWatcher": "Désactiver la surveillance",
|
"LabelSettingsDisableWatcher": "Désactiver la surveillance",
|
||||||
"LabelSettingsDisableWatcherForLibrary": "Désactiver la surveillance des dossiers pour la bibliothèque",
|
"LabelSettingsDisableWatcherForLibrary": "Désactiver la surveillance des dossiers pour la bibliothèque",
|
||||||
"LabelSettingsDisableWatcherHelp": "Désactive la mise à jour automatique lorsque des modifications de fichiers sont détectées. *Nécessite le redémarrage du serveur",
|
"LabelSettingsDisableWatcherHelp": "Désactive la mise à jour automatique lorsque des modifications de fichiers sont détectées. * nécessite le redémarrage du serveur",
|
||||||
"LabelSettingsEnableWatcher": "Activer la veille",
|
"LabelSettingsEnableWatcher": "Activer la veille",
|
||||||
"LabelSettingsEnableWatcherForLibrary": "Activer la surveillance des dossiers pour la bibliothèque",
|
"LabelSettingsEnableWatcherForLibrary": "Activer la surveillance des dossiers pour la bibliothèque",
|
||||||
"LabelSettingsEnableWatcherHelp": "Active la mise à jour automatique automatique lorsque des modifications de fichiers sont détectées. *Nécessite le redémarrage du serveur",
|
"LabelSettingsEnableWatcherHelp": "Active la mise à jour automatique automatique lorsque des modifications de fichiers sont détectées. * nécessite le redémarrage du serveur",
|
||||||
"LabelSettingsExperimentalFeatures": "Fonctionnalités expérimentales",
|
"LabelSettingsExperimentalFeatures": "Fonctionnalités expérimentales",
|
||||||
"LabelSettingsExperimentalFeaturesHelp": "Fonctionnalités en cours de développement sur lesquelles nous attendons votre retour et expérience. Cliquez pour ouvrir la discussion Github.",
|
"LabelSettingsExperimentalFeaturesHelp": "Fonctionnalités en cours de développement sur lesquelles nous attendons votre retour et expérience. Cliquez pour ouvrir la discussion GitHub.",
|
||||||
"LabelSettingsFindCovers": "Chercher des couvertures de livre",
|
"LabelSettingsFindCovers": "Chercher des couvertures de livre",
|
||||||
"LabelSettingsFindCoversHelp": "Si votre livre audio ne possède pas de couverture intégrée ou une image de couverture dans le dossier, l’analyseur tentera de récupérer une couverture.<br>Attention, cela peut augmenter le temps d’analyse.",
|
"LabelSettingsFindCoversHelp": "Si votre livre audio ne possède pas de couverture intégrée ou une image de couverture dans le dossier, l’analyseur tentera de récupérer une couverture.<br>Attention, cela peut augmenter le temps d’analyse.",
|
||||||
"LabelSettingsHideSingleBookSeries": "Masquer les séries de livres uniques",
|
"LabelSettingsHideSingleBookSeries": "Masquer les séries de livres uniques",
|
||||||
"LabelSettingsHideSingleBookSeriesHelp": "Les séries qui ne comportent qu’un seul livre seront masquées sur la page de la série et sur les étagères de la page d’accueil.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Les séries qui ne comportent qu’un seul livre seront masquées sur la page de la série et sur les étagères de la page d’accueil.",
|
||||||
"LabelSettingsHomePageBookshelfView": "La page d’accueil utilise la vue étagère",
|
"LabelSettingsHomePageBookshelfView": "La page d’accueil utilise la vue étagère",
|
||||||
"LabelSettingsLibraryBookshelfView": "La bibliothèque utilise la vue étagère",
|
"LabelSettingsLibraryBookshelfView": "La bibliothèque utilise la vue étagère",
|
||||||
|
"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": "Analyser les sous-titres",
|
"LabelSettingsParseSubtitles": "Analyser les sous-titres",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extrait les sous-titres depuis le dossier du Livre Audio.<br>Les sous-titres doivent être séparés par « - »<br>i.e. « Titre du Livre - Ceci est un sous-titre » aura le sous-titre « Ceci est un sous-titre »",
|
"LabelSettingsParseSubtitlesHelp": "Extrait les sous-titres depuis le dossier du livre audio.<br>Les sous-titres doivent être séparés par « - »<br>c’est-à-dire : « Titre du livre - Ceci est un sous-titre » aura le sous-titre « Ceci est un sous-titre »",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Préférer les métadonnées par correspondance",
|
"LabelSettingsPreferMatchedMetadata": "Préférer les métadonnées par correspondance",
|
||||||
"LabelSettingsPreferMatchedMetadataHelp": "Les métadonnées par correspondance écrase les détails de l’article lors d’une recherche par correspondance rapide. Par défaut, la recherche par correspondance rapide ne comblera que les éléments manquant.",
|
"LabelSettingsPreferMatchedMetadataHelp": "Les métadonnées par correspondance écrase les détails de l’article lors d’une recherche par correspondance rapide. Par défaut, la recherche par correspondance rapide ne comblera que les éléments manquant.",
|
||||||
"LabelSettingsSkipMatchingBooksWithASIN": "Ignorer la recherche par correspondance sur les livres ayant déjà un ASIN",
|
"LabelSettingsSkipMatchingBooksWithASIN": "Ignorer la recherche par correspondance sur les livres ayant déjà un ASIN",
|
||||||
"LabelSettingsSkipMatchingBooksWithISBN": "Ignorer la recherche par correspondance sur les livres ayant déjà un ISBN",
|
"LabelSettingsSkipMatchingBooksWithISBN": "Ignorer la recherche par correspondance sur les livres ayant déjà un ISBN",
|
||||||
"LabelSettingsSortingIgnorePrefixes": "Ignorer les préfixes lors du tri",
|
"LabelSettingsSortingIgnorePrefixes": "Ignorer les préfixes lors du tri",
|
||||||
"LabelSettingsSortingIgnorePrefixesHelp": "i.e. pour le préfixe « le », le livre avec pour titre « Le Titre du Livre » sera trié en tant que « Titre du Livre, Le »",
|
"LabelSettingsSortingIgnorePrefixesHelp": "c’est-à-dire : pour le préfixe « le », le livre avec pour titre « Le Titre du Livre » sera trié en tant que « Titre du Livre, Le »",
|
||||||
"LabelSettingsSquareBookCovers": "Utiliser des couvertures carrées",
|
"LabelSettingsSquareBookCovers": "Utiliser des couvertures carrées",
|
||||||
"LabelSettingsSquareBookCoversHelp": "Préférer les couvertures carrées par rapport aux couvertures standards de ratio 1.6:1.",
|
"LabelSettingsSquareBookCoversHelp": "Préférer les couvertures carrées par rapport aux couvertures standards de ratio 1.6:1.",
|
||||||
"LabelSettingsStoreCoversWithItem": "Enregistrer la couverture avec les articles",
|
"LabelSettingsStoreCoversWithItem": "Enregistrer la couverture avec les articles",
|
||||||
@@ -452,61 +483,66 @@
|
|||||||
"LabelSettingsStoreMetadataWithItem": "Enregistrer les Métadonnées avec les articles",
|
"LabelSettingsStoreMetadataWithItem": "Enregistrer les Métadonnées avec les articles",
|
||||||
"LabelSettingsStoreMetadataWithItemHelp": "Par défaut, les métadonnées sont enregistrées dans /metadata/items",
|
"LabelSettingsStoreMetadataWithItemHelp": "Par défaut, les métadonnées sont enregistrées dans /metadata/items",
|
||||||
"LabelSettingsTimeFormat": "Format d’heure",
|
"LabelSettingsTimeFormat": "Format d’heure",
|
||||||
"LabelShowAll": "Afficher Tout",
|
"LabelShowAll": "Tout afficher",
|
||||||
"LabelSize": "Taille",
|
"LabelSize": "Taille",
|
||||||
"LabelSleepTimer": "Minuterie",
|
"LabelSleepTimer": "Minuterie",
|
||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Balise",
|
||||||
"LabelStart": "Démarrer",
|
"LabelStart": "Démarrer",
|
||||||
"LabelStarted": "Démarré",
|
"LabelStarted": "Démarré",
|
||||||
"LabelStartedAt": "Démarré à",
|
"LabelStartedAt": "Démarré à",
|
||||||
"LabelStartTime": "Heure de Démarrage",
|
"LabelStartTime": "Heure de démarrage",
|
||||||
"LabelStatsAudioTracks": "Pistes Audios",
|
"LabelStatsAudioTracks": "Pistes Audios",
|
||||||
"LabelStatsAuthors": "Auteurs",
|
"LabelStatsAuthors": "Auteurs",
|
||||||
"LabelStatsBestDay": "Meilleur Jour",
|
"LabelStatsBestDay": "Meilleur jour",
|
||||||
"LabelStatsDailyAverage": "Moyenne Journalière",
|
"LabelStatsDailyAverage": "Moyenne journalière",
|
||||||
"LabelStatsDays": "Jours",
|
"LabelStatsDays": "Jours",
|
||||||
"LabelStatsDaysListened": "Jours d’écoute",
|
"LabelStatsDaysListened": "Jours d’écoute",
|
||||||
"LabelStatsHours": "Heures",
|
"LabelStatsHours": "Heures",
|
||||||
"LabelStatsInARow": "d’affilé(s)",
|
"LabelStatsInARow": "d’affilée(s)",
|
||||||
"LabelStatsItemsFinished": "Articles terminés",
|
"LabelStatsItemsFinished": "Articles terminés",
|
||||||
"LabelStatsItemsInLibrary": "Articles dans la Bibliothèque",
|
"LabelStatsItemsInLibrary": "Articles dans la bibliothèque",
|
||||||
"LabelStatsMinutes": "minutes",
|
"LabelStatsMinutes": "minutes",
|
||||||
"LabelStatsMinutesListening": "Minutes d’écoute",
|
"LabelStatsMinutesListening": "Minutes d’écoute",
|
||||||
"LabelStatsOverallDays": "Jours au total",
|
"LabelStatsOverallDays": "Nombre total de jours",
|
||||||
"LabelStatsOverallHours": "Heures au total",
|
"LabelStatsOverallHours": "Nombre total d'heures",
|
||||||
"LabelStatsWeekListening": "Écoute de la semaine",
|
"LabelStatsWeekListening": "Écoute de la semaine",
|
||||||
"LabelSubtitle": "Sous-Titre",
|
"LabelSubtitle": "Sous-titre",
|
||||||
"LabelSupportedFileTypes": "Types de fichiers supportés",
|
"LabelSupportedFileTypes": "Types de fichiers supportés",
|
||||||
"LabelTag": "Étiquette",
|
"LabelTag": "Étiquette",
|
||||||
"LabelTags": "Étiquettes",
|
"LabelTags": "Étiquettes",
|
||||||
"LabelTagsAccessibleToUser": "Étiquettes accessibles à l’utilisateur",
|
"LabelTagsAccessibleToUser": "Étiquettes accessibles à l’utilisateur",
|
||||||
"LabelTagsNotAccessibleToUser": "Étiquettes non accessibles à l’utilisateur",
|
"LabelTagsNotAccessibleToUser": "Étiquettes non accessibles à l’utilisateur",
|
||||||
"LabelTasks": "Tâches en cours",
|
"LabelTasks": "Tâches en cours",
|
||||||
|
"LabelTextEditorBulletedList": "Liste à puces",
|
||||||
|
"LabelTextEditorLink": "Lien",
|
||||||
|
"LabelTextEditorNumberedList": "Liste numérotée",
|
||||||
|
"LabelTextEditorUnlink": "Dissocier",
|
||||||
"LabelTheme": "Thème",
|
"LabelTheme": "Thème",
|
||||||
"LabelThemeDark": "Sombre",
|
"LabelThemeDark": "Sombre",
|
||||||
"LabelThemeLight": "Clair",
|
"LabelThemeLight": "Clair",
|
||||||
"LabelTimeBase": "Base de temps",
|
"LabelTimeBase": "Base de temps",
|
||||||
"LabelTimeListened": "Temps d’écoute",
|
"LabelTimeListened": "Temps d’écoute",
|
||||||
"LabelTimeListenedToday": "Nombres d’écoutes Aujourd’hui",
|
"LabelTimeListenedToday": "Nombres d’écoutes aujourd’hui",
|
||||||
"LabelTimeRemaining": "{0} restantes",
|
"LabelTimeRemaining": "{0} restantes",
|
||||||
"LabelTimeToShift": "Temps de décalage en secondes",
|
"LabelTimeToShift": "Temps de décalage en secondes",
|
||||||
"LabelTitle": "Titre",
|
"LabelTitle": "Titre",
|
||||||
"LabelToolsEmbedMetadata": "Métadonnées Intégrées",
|
"LabelToolsEmbedMetadata": "Métadonnées intégrées",
|
||||||
"LabelToolsEmbedMetadataDescription": "Intègre les métadonnées au fichier audio avec la couverture et les chapitres.",
|
"LabelToolsEmbedMetadataDescription": "Intègre les métadonnées au fichier audio avec la couverture et les chapitres.",
|
||||||
"LabelToolsMakeM4b": "Créer un fichier Livre Audio M4B",
|
"LabelToolsMakeM4b": "Créer un fichier livre audio M4B",
|
||||||
"LabelToolsMakeM4bDescription": "Génère un fichier Livre Audio .M4B avec intégration des métadonnées, image de couverture et les chapitres.",
|
"LabelToolsMakeM4bDescription": "Générer un fichier de livre audio .M4B avec des métadonnées intégrées, une image de couverture et des chapitres.",
|
||||||
"LabelToolsSplitM4b": "Scinde le fichier M4B en fichiers MP3",
|
"LabelToolsSplitM4b": "Scinde le fichier M4B en fichiers MP3",
|
||||||
"LabelToolsSplitM4bDescription": "Créer plusieurs fichier MP3 à partir du découpage par chapitre, en incluant les métadonnées, l’image de couverture et les chapitres.",
|
"LabelToolsSplitM4bDescription": "Créer plusieurs fichier MP3 à partir du découpage par chapitre, en incluant les métadonnées, l’image de couverture et les chapitres.",
|
||||||
"LabelTotalDuration": "Durée Totale",
|
"LabelTotalDuration": "Durée totale",
|
||||||
"LabelTotalTimeListened": "Temps d’écoute total",
|
"LabelTotalTimeListened": "Temps d’écoute total",
|
||||||
"LabelTrackFromFilename": "Piste depuis le fichier",
|
"LabelTrackFromFilename": "Piste depuis le fichier",
|
||||||
"LabelTrackFromMetadata": "Piste depuis les métadonnées",
|
"LabelTrackFromMetadata": "Piste depuis les métadonnées",
|
||||||
"LabelTracks": "Pistes",
|
"LabelTracks": "Pistes",
|
||||||
"LabelTracksMultiTrack": "Piste multiple",
|
"LabelTracksMultiTrack": "Piste multiple",
|
||||||
"LabelTracksNone": "No tracks",
|
"LabelTracksNone": "Aucune piste",
|
||||||
"LabelTracksSingleTrack": "Piste simple",
|
"LabelTracksSingleTrack": "Piste simple",
|
||||||
"LabelType": "Type",
|
"LabelType": "Type",
|
||||||
"LabelUnabridged": "Version intégrale",
|
"LabelUnabridged": "Version intégrale",
|
||||||
|
"LabelUndo": "Annuler",
|
||||||
"LabelUnknown": "Inconnu",
|
"LabelUnknown": "Inconnu",
|
||||||
"LabelUpdateCover": "Mettre à jour la couverture",
|
"LabelUpdateCover": "Mettre à jour la couverture",
|
||||||
"LabelUpdateCoverHelp": "Autoriser la mise à jour de la couverture existante lorsqu’une correspondance est trouvée",
|
"LabelUpdateCoverHelp": "Autoriser la mise à jour de la couverture existante lorsqu’une correspondance est trouvée",
|
||||||
@@ -515,8 +551,9 @@
|
|||||||
"LabelUpdateDetailsHelp": "Autoriser la mise à jour des détails existants lorsqu’une correspondance est trouvée",
|
"LabelUpdateDetailsHelp": "Autoriser la mise à jour des détails existants lorsqu’une correspondance est trouvée",
|
||||||
"LabelUploaderDragAndDrop": "Glisser et déposer des fichiers ou dossiers",
|
"LabelUploaderDragAndDrop": "Glisser et déposer des fichiers ou dossiers",
|
||||||
"LabelUploaderDropFiles": "Déposer des fichiers",
|
"LabelUploaderDropFiles": "Déposer des fichiers",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Récupérer automatiquement le titre, l’auteur et la série",
|
||||||
"LabelUseChapterTrack": "Utiliser la piste du chapitre",
|
"LabelUseChapterTrack": "Utiliser la piste du chapitre",
|
||||||
"LabelUseFullTrack": "Utiliser la piste Complète",
|
"LabelUseFullTrack": "Utiliser la piste complète",
|
||||||
"LabelUser": "Utilisateur",
|
"LabelUser": "Utilisateur",
|
||||||
"LabelUsername": "Nom d’utilisateur",
|
"LabelUsername": "Nom d’utilisateur",
|
||||||
"LabelValue": "Valeur",
|
"LabelValue": "Valeur",
|
||||||
@@ -526,19 +563,21 @@
|
|||||||
"LabelViewQueue": "Afficher la liste de lecture",
|
"LabelViewQueue": "Afficher la liste de lecture",
|
||||||
"LabelVolume": "Volume",
|
"LabelVolume": "Volume",
|
||||||
"LabelWeekdaysToRun": "Jours de la semaine à exécuter",
|
"LabelWeekdaysToRun": "Jours de la semaine à exécuter",
|
||||||
|
"LabelYearReviewHide": "Masquer le bilan de l’année",
|
||||||
|
"LabelYearReviewShow": "Afficher le bilan de l’année",
|
||||||
"LabelYourAudiobookDuration": "Durée de vos livres audios",
|
"LabelYourAudiobookDuration": "Durée de vos livres audios",
|
||||||
"LabelYourBookmarks": "Vos signets",
|
"LabelYourBookmarks": "Vos signets",
|
||||||
"LabelYourPlaylists": "Vos listes de lecture",
|
"LabelYourPlaylists": "Vos listes de lecture",
|
||||||
"LabelYourProgress": "Votre progression",
|
"LabelYourProgress": "Votre progression",
|
||||||
"MessageAddToPlayerQueue": "Ajouter en file d’attente",
|
"MessageAddToPlayerQueue": "Ajouter en file d’attente",
|
||||||
"MessageAppriseDescription": "Nécessite une instance d’<a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API Apprise</a> pour utiliser cette fonctionnalité ou une api qui prend en charge les mêmes requêtes. <br />l’URL de l’API Apprise doit comprendre le chemin complet pour envoyer la notification. Par exemple, si votre instance écoute sur <code>http://192.168.1.1:8337</code> alors vous devez mettre <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Nécessite une instance d’<a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API Apprise</a> pour utiliser cette fonctionnalité ou une api qui prend en charge les mêmes requêtes.<br>L’URL de l’API Apprise doit comprendre le chemin complet pour envoyer la notification. Par exemple, si votre instance écoute sur <code>http://192.168.1.1:8337</code> alors vous devez mettre <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
"MessageBackupsDescription": "Les sauvegardes incluent les utilisateurs, la progression de lecture par utilisateur, les détails des articles des bibliothèques, les paramètres du serveur et les images sauvegardées. Les sauvegardes n’incluent pas les fichiers de votre bibliothèque.",
|
"MessageBackupsDescription": "Les sauvegardes incluent les utilisateurs, la progression de lecture par utilisateur, les détails des articles des bibliothèques, les paramètres du serveur et les images sauvegardées. Les sauvegardes n’incluent pas les fichiers de votre bibliothèque.",
|
||||||
"MessageBatchQuickMatchDescription": "La recherche par correspondance rapide tentera d’ajouter les couvertures et les métadonnées manquantes pour les articles sélectionnés. Activer l’option suivante pour autoriser la recherche par correspondance à écraser les données existantes.",
|
"MessageBatchQuickMatchDescription": "La recherche par correspondance rapide tentera d’ajouter les couvertures et les métadonnées manquantes pour les articles sélectionnés. Activer l’option suivante pour autoriser la recherche par correspondance à écraser les données existantes.",
|
||||||
"MessageBookshelfNoCollections": "Vous n’avez pas encore de collections",
|
"MessageBookshelfNoCollections": "Vous n’avez pas encore de collections",
|
||||||
"MessageBookshelfNoResultsForFilter": "Aucun résultat pour le filtre « {0}: {1} »",
|
"MessageBookshelfNoResultsForFilter": "Aucun résultat pour le filtre « {0} : {1} »",
|
||||||
"MessageBookshelfNoRSSFeeds": "Aucun flux RSS n’est ouvert",
|
"MessageBookshelfNoRSSFeeds": "Aucun flux RSS n’est ouvert",
|
||||||
"MessageBookshelfNoSeries": "Vous n’avez aucune série",
|
"MessageBookshelfNoSeries": "Vous n’avez aucune série",
|
||||||
"MessageChapterEndIsAfter": "Le Chapitre Fin est situé à la fin de votre Livre Audio",
|
"MessageChapterEndIsAfter": "La fin du chapitre se situe après la fin de votre livre audio.",
|
||||||
"MessageChapterErrorFirstNotZero": "Le premier capitre doit débuter à 0",
|
"MessageChapterErrorFirstNotZero": "Le premier capitre doit débuter à 0",
|
||||||
"MessageChapterErrorStartGteDuration": "Horodatage invalide car il doit débuter avant la fin du livre",
|
"MessageChapterErrorStartGteDuration": "Horodatage invalide car il doit débuter avant la fin du livre",
|
||||||
"MessageChapterErrorStartLtPrev": "Horodatage invalide car il doit débuter au moins après le précédent chapitre",
|
"MessageChapterErrorStartLtPrev": "Horodatage invalide car il doit débuter au moins après le précédent chapitre",
|
||||||
@@ -548,20 +587,21 @@
|
|||||||
"MessageConfirmDeleteBackup": "Êtes-vous sûr de vouloir supprimer la sauvegarde de « {0} » ?",
|
"MessageConfirmDeleteBackup": "Êtes-vous sûr de vouloir supprimer la sauvegarde de « {0} » ?",
|
||||||
"MessageConfirmDeleteFile": "Cela supprimera le fichier de votre système de fichiers. Êtes-vous sûr ?",
|
"MessageConfirmDeleteFile": "Cela supprimera le fichier de votre système de fichiers. Êtes-vous sûr ?",
|
||||||
"MessageConfirmDeleteLibrary": "Êtes-vous sûr de vouloir supprimer définitivement la bibliothèque « {0} » ?",
|
"MessageConfirmDeleteLibrary": "Êtes-vous sûr de vouloir supprimer définitivement la bibliothèque « {0} » ?",
|
||||||
"MessageConfirmDeleteLibraryItem": "This will delete the library item from the database and your file system. Are you sure?",
|
"MessageConfirmDeleteLibraryItem": "Cette opération supprimera l’élément de la base de données et de votre système de fichiers. Êtes-vous sûr ?",
|
||||||
"MessageConfirmDeleteLibraryItems": "This will delete {0} library items from the database and your file system. Are you sure?",
|
"MessageConfirmDeleteLibraryItems": "Cette opération supprimera {0} éléments de la base de données et de votre système de fichiers. Êtes-vous sûr ?",
|
||||||
"MessageConfirmDeleteSession": "Êtes-vous sûr de vouloir supprimer cette session ?",
|
"MessageConfirmDeleteSession": "Êtes-vous sûr de vouloir supprimer cette session ?",
|
||||||
"MessageConfirmForceReScan": "Êtes-vous sûr de vouloir lancer une analyse forcée ?",
|
"MessageConfirmForceReScan": "Êtes-vous sûr de vouloir lancer une analyse forcée ?",
|
||||||
"MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr de marquer tous les épisodes comme terminés ?",
|
"MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr de marquer tous les épisodes comme terminés ?",
|
||||||
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr de vouloir marquer tous les épisodes comme non terminés ?",
|
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr de vouloir marquer tous les épisodes comme non terminés ?",
|
||||||
"MessageConfirmMarkSeriesFinished": "Êtes-vous sûr de vouloir marquer tous les livres de cette série comme terminées ?",
|
"MessageConfirmMarkSeriesFinished": "Êtes-vous sûr de vouloir marquer tous les livres de cette série comme terminées ?",
|
||||||
"MessageConfirmMarkSeriesNotFinished": "Êtes-vous sûr de vouloir marquer tous les livres de cette série comme comme non terminés ?",
|
"MessageConfirmMarkSeriesNotFinished": "Êtes-vous sûr de vouloir marquer tous les livres de cette série comme comme non terminés ?",
|
||||||
"MessageConfirmQuickEmbed": "Warning! Quick embed will not backup your audio files. Make sure that you have a backup of your audio files. <br><br>Would you like to continue?",
|
"MessageConfirmQuickEmbed": "Attention ! L’intégration rapide ne sauvegardera pas vos fichiers audio. Assurez-vous d’avoir effectuer une sauvegarde de vos fichiers audio.<br><br>Souhaitez-vous continuer ?",
|
||||||
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr de vouloir supprimer tous les chapitres ?",
|
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr de vouloir supprimer tous les chapitres ?",
|
||||||
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?",
|
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?",
|
||||||
"MessageConfirmRemoveCollection": "Êtes-vous sûr de vouloir supprimer la collection « {0} » ?",
|
"MessageConfirmRemoveCollection": "Êtes-vous sûr de vouloir supprimer la collection « {0} » ?",
|
||||||
"MessageConfirmRemoveEpisode": "Êtes-vous sûr de vouloir supprimer l’épisode « {0} » ?",
|
"MessageConfirmRemoveEpisode": "Êtes-vous sûr de vouloir supprimer l’épisode « {0} » ?",
|
||||||
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr de vouloir supprimer {0} épisodes ?",
|
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr de vouloir supprimer {0} épisodes ?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Êtes-vous sûr de vouloir supprimer le narrateur « {0} » ?",
|
"MessageConfirmRemoveNarrator": "Êtes-vous sûr de vouloir supprimer le narrateur « {0} » ?",
|
||||||
"MessageConfirmRemovePlaylist": "Êtes-vous sûr de vouloir supprimer la liste de lecture « {0} » ?",
|
"MessageConfirmRemovePlaylist": "Êtes-vous sûr de vouloir supprimer la liste de lecture « {0} » ?",
|
||||||
"MessageConfirmRenameGenre": "Êtes-vous sûr de vouloir renommer le genre « {0} » en « {1} » pour tous les articles ?",
|
"MessageConfirmRenameGenre": "Êtes-vous sûr de vouloir renommer le genre « {0} » en « {1} » pour tous les articles ?",
|
||||||
@@ -570,16 +610,16 @@
|
|||||||
"MessageConfirmRenameTag": "Êtes-vous sûr de vouloir renommer l’étiquette « {0} » en « {1} » pour tous les articles ?",
|
"MessageConfirmRenameTag": "Êtes-vous sûr de vouloir renommer l’étiquette « {0} » en « {1} » pour tous les articles ?",
|
||||||
"MessageConfirmRenameTagMergeNote": "Information: Cette étiquette existe déjà et sera fusionnée.",
|
"MessageConfirmRenameTagMergeNote": "Information: Cette étiquette existe déjà et sera fusionnée.",
|
||||||
"MessageConfirmRenameTagWarning": "Attention ! Une étiquette similaire avec une casse différente existe déjà « {0} ».",
|
"MessageConfirmRenameTagWarning": "Attention ! Une étiquette similaire avec une casse différente existe déjà « {0} ».",
|
||||||
"MessageConfirmReScanLibraryItems": "Are you sure you want to re-scan {0} items?",
|
"MessageConfirmReScanLibraryItems": "Êtes-vous sûr de vouloir re-analyser {0} éléments ?",
|
||||||
"MessageConfirmSendEbookToDevice": "Êtes-vous sûr de vouloir envoyer le livre numérique {0} « {1} » à l’appareil « {2} »?",
|
"MessageConfirmSendEbookToDevice": "Êtes-vous sûr de vouloir envoyer le livre numérique {0} « {1} » à l’appareil « {2} »?",
|
||||||
"MessageDownloadingEpisode": "Téléchargement de l’épisode",
|
"MessageDownloadingEpisode": "Téléchargement de l’épisode",
|
||||||
"MessageDragFilesIntoTrackOrder": "Faire glisser les fichiers dans l’ordre correct",
|
"MessageDragFilesIntoTrackOrder": "Faites glisser les fichiers dans l’ordre correct des pistes",
|
||||||
"MessageEmbedFinished": "Intégration terminée !",
|
"MessageEmbedFinished": "Intégration terminée !",
|
||||||
"MessageEpisodesQueuedForDownload": "{0} épisode(s) mis en file pour téléchargement",
|
"MessageEpisodesQueuedForDownload": "{0} épisode(s) mis en file pour téléchargement",
|
||||||
"MessageFeedURLWillBe": "l’URL du flux sera {0}",
|
"MessageFeedURLWillBe": "L’URL du flux sera {0}",
|
||||||
"MessageFetching": "Récupération…",
|
"MessageFetching": "Récupération…",
|
||||||
"MessageForceReScanDescription": "Analysera tous les fichiers de nouveau. Les étiquettes ID3 des fichiers audios, fichiers OPF, et les fichiers textes seront analysés comme s’ils étaient nouveaux.",
|
"MessageForceReScanDescription": "analysera de nouveau tous les fichiers. Les étiquettes ID3 des fichiers audio, les fichiers OPF et les fichiers texte seront analysés comme s’ils étaient nouveaux.",
|
||||||
"MessageImportantNotice": "Information Importante !",
|
"MessageImportantNotice": "Information importante !",
|
||||||
"MessageInsertChapterBelow": "Insérer le chapitre ci-dessous",
|
"MessageInsertChapterBelow": "Insérer le chapitre ci-dessous",
|
||||||
"MessageItemsSelected": "{0} articles sélectionnés",
|
"MessageItemsSelected": "{0} articles sélectionnés",
|
||||||
"MessageItemsUpdated": "{0} articles mis à jour",
|
"MessageItemsUpdated": "{0} articles mis à jour",
|
||||||
@@ -635,12 +675,13 @@
|
|||||||
"MessageRemoveChapter": "Supprimer le chapitre",
|
"MessageRemoveChapter": "Supprimer le chapitre",
|
||||||
"MessageRemoveEpisodes": "Suppression de {0} épisode(s)",
|
"MessageRemoveEpisodes": "Suppression de {0} épisode(s)",
|
||||||
"MessageRemoveFromPlayerQueue": "Supprimer de la liste d’écoute",
|
"MessageRemoveFromPlayerQueue": "Supprimer de la liste d’écoute",
|
||||||
"MessageRemoveUserWarning": "Êtes-vous certain de vouloir supprimer définitivement l’utilisateur « {0} » ?",
|
"MessageRemoveUserWarning": "Êtes-vous sûr de vouloir supprimer définitivement l’utilisateur « {0} » ?",
|
||||||
"MessageReportBugsAndContribute": "Remonter des anomalies, demander des fonctionnalités et contribuer sur",
|
"MessageReportBugsAndContribute": "Remonter des anomalies, demander des fonctionnalités et contribuer sur",
|
||||||
"MessageResetChaptersConfirm": "Êtes-vous certain de vouloir réinitialiser les chapitres et annuler les changements effectués ?",
|
"MessageResetChaptersConfirm": "Êtes-vous sûr de vouloir réinitialiser les chapitres et annuler les changements effectués ?",
|
||||||
"MessageRestoreBackupConfirm": "Êtes-vous certain de vouloir restaurer la sauvegarde créée le",
|
"MessageRestoreBackupConfirm": "Êtes-vous sûr de vouloir restaurer la sauvegarde créée le",
|
||||||
"MessageRestoreBackupWarning": "Restaurer la sauvegarde écrasera la base de donnée située dans le dossier /config ainsi que les images sur /metadata/items et /metadata/authors.<br /><br />Les sauvegardes ne touchent pas aux fichiers de la bibliothèque. Si vous avez activé le paramètre pour sauvegarder les métadonnées et les images de couverture dans le même dossier que les fichiers, ceux-ci ne ni sauvegardés, ni écrasés lors de la restauration.<br /><br />Tous les clients utilisant votre serveur seront automatiquement mis à jour.",
|
"MessageRestoreBackupWarning": "Restaurer la sauvegarde écrasera la base de donnée située dans le dossier /config ainsi que les images sur /metadata/items et /metadata/authors.<br><br>Les sauvegardes ne touchent pas aux fichiers de la bibliothèque. Si vous avez activé le paramètre pour sauvegarder les métadonnées et les images de couverture dans le même dossier que les fichiers, ceux-ci ne ni sauvegardés, ni écrasés lors de la restauration.<br><br>Tous les clients utilisant votre serveur seront automatiquement mis à jour.",
|
||||||
"MessageSearchResultsFor": "Résultats de recherche pour",
|
"MessageSearchResultsFor": "Résultats de recherche pour",
|
||||||
|
"MessageSelected": "{0} sélectionnés",
|
||||||
"MessageServerCouldNotBeReached": "Serveur inaccessible",
|
"MessageServerCouldNotBeReached": "Serveur inaccessible",
|
||||||
"MessageSetChaptersFromTracksDescription": "Positionne un chapitre par fichier audio, avec le titre du fichier comme titre de chapitre",
|
"MessageSetChaptersFromTracksDescription": "Positionne un chapitre par fichier audio, avec le titre du fichier comme titre de chapitre",
|
||||||
"MessageStartPlaybackAtTime": "Démarrer la lecture pour « {0} » à {1} ?",
|
"MessageStartPlaybackAtTime": "Démarrer la lecture pour « {0} » à {1} ?",
|
||||||
@@ -651,12 +692,11 @@
|
|||||||
"MessageValidCronExpression": "Expression cron valide",
|
"MessageValidCronExpression": "Expression cron valide",
|
||||||
"MessageWatcherIsDisabledGlobally": "La surveillance est désactivée par un paramètre global du serveur",
|
"MessageWatcherIsDisabledGlobally": "La surveillance est désactivée par un paramètre global du serveur",
|
||||||
"MessageXLibraryIsEmpty": "La bibliothèque {0} est vide !",
|
"MessageXLibraryIsEmpty": "La bibliothèque {0} est vide !",
|
||||||
"MessageYourAudiobookDurationIsLonger": "La durée de votre Livre Audio est plus longue que la durée trouvée",
|
"MessageYourAudiobookDurationIsLonger": "La durée de votre livre audio est plus longue que la durée trouvée",
|
||||||
"MessageYourAudiobookDurationIsShorter": "La durée de votre Livre Audio est plus courte que la durée trouvée",
|
"MessageYourAudiobookDurationIsShorter": "La durée de votre livre audio est plus courte que la durée trouvée",
|
||||||
"NoteChangeRootPassword": "seul l’utilisateur « root » peut utiliser un mot de passe vide",
|
"NoteChangeRootPassword": "seul l’utilisateur « root » peut utiliser un mot de passe vide",
|
||||||
"NoteChapterEditorTimes": "Information : l’horodatage du premier chapitre doit être à 0:00 et celui du dernier chapitre ne peut se situer au-delà de la durée du Livre Audio.",
|
"NoteChapterEditorTimes": "Information : l’horodatage du premier chapitre doit être à 0:00 et celui du dernier chapitre ne peut se situer au-delà de la durée du livre audio.",
|
||||||
"NoteFolderPicker": "Information : Les dossiers déjà surveillés ne sont pas affichés",
|
"NoteFolderPicker": "Information : les dossiers déjà surveillés ne sont pas affichés",
|
||||||
"NoteFolderPickerDebian": "Information : La sélection de dossier sur une installation debian n’est pas finalisée. Merci de renseigner le chemin complet vers votre bibliothèque manuellement.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Attention : la majorité des application de podcast nécessite une adresse de flux en HTTPS.",
|
"NoteRSSFeedPodcastAppsHttps": "Attention : la majorité des application de podcast nécessite une adresse de flux en HTTPS.",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Attention : un ou plusieurs de vos épisodes ne possèdent pas de date de publication. Certaines applications de podcast le requièrent.",
|
"NoteRSSFeedPodcastAppsPubDate": "Attention : un ou plusieurs de vos épisodes ne possèdent pas de date de publication. Certaines applications de podcast le requièrent.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Les dossiers contenant des fichiers multimédias seront traités comme des éléments distincts de la bibliothèque.",
|
"NoteUploaderFoldersWithMediaFiles": "Les dossiers contenant des fichiers multimédias seront traités comme des éléments distincts de la bibliothèque.",
|
||||||
@@ -665,8 +705,8 @@
|
|||||||
"PlaceholderNewCollection": "Nom de la nouvelle collection",
|
"PlaceholderNewCollection": "Nom de la nouvelle collection",
|
||||||
"PlaceholderNewFolderPath": "Nouveau chemin de dossier",
|
"PlaceholderNewFolderPath": "Nouveau chemin de dossier",
|
||||||
"PlaceholderNewPlaylist": "Nouveau nom de liste de lecture",
|
"PlaceholderNewPlaylist": "Nouveau nom de liste de lecture",
|
||||||
"PlaceholderSearch": "Recherche...",
|
"PlaceholderSearch": "Recherche…",
|
||||||
"PlaceholderSearchEpisode": "Recherche d’épisode...",
|
"PlaceholderSearchEpisode": "Recherche d’épisode…",
|
||||||
"ToastAccountUpdateFailed": "Échec de la mise à jour du compte",
|
"ToastAccountUpdateFailed": "Échec de la mise à jour du compte",
|
||||||
"ToastAccountUpdateSuccess": "Compte mis à jour",
|
"ToastAccountUpdateSuccess": "Compte mis à jour",
|
||||||
"ToastAuthorImageRemoveFailed": "Échec de la suppression de l’image",
|
"ToastAuthorImageRemoveFailed": "Échec de la suppression de l’image",
|
||||||
|
|||||||
+116
-76
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"ButtonAdd": "ઉમેરો",
|
"ButtonAdd": "ઉમેરો",
|
||||||
"ButtonAddChapters": "પ્રકરણો ઉમેરો",
|
"ButtonAddChapters": "પ્રકરણો ઉમેરો",
|
||||||
"ButtonAddDevice": "Add Device",
|
"ButtonAddDevice": "ઉપકરણ ઉમેરો",
|
||||||
"ButtonAddLibrary": "Add Library",
|
"ButtonAddLibrary": "પુસ્તકાલય ઉમેરો",
|
||||||
"ButtonAddPodcasts": "પોડકાસ્ટ ઉમેરો",
|
"ButtonAddPodcasts": "પોડકાસ્ટ ઉમેરો",
|
||||||
"ButtonAddUser": "Add User",
|
"ButtonAddUser": "વપરાશકર્તા ઉમેરો",
|
||||||
"ButtonAddYourFirstLibrary": "તમારી પ્રથમ પુસ્તકાલય ઉમેરો",
|
"ButtonAddYourFirstLibrary": "તમારી પ્રથમ પુસ્તકાલય ઉમેરો",
|
||||||
"ButtonApply": "લાગુ કરો",
|
"ButtonApply": "લાગુ કરો",
|
||||||
"ButtonApplyChapters": "પ્રકરણો લાગુ કરો",
|
"ButtonApplyChapters": "પ્રકરણો લાગુ કરો",
|
||||||
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "છુપાવો",
|
"ButtonHide": "છુપાવો",
|
||||||
"ButtonHome": "ઘર",
|
"ButtonHome": "ઘર",
|
||||||
"ButtonIssues": "સમસ્યાઓ",
|
"ButtonIssues": "સમસ્યાઓ",
|
||||||
|
"ButtonJumpBackward": "Jump Backward",
|
||||||
|
"ButtonJumpForward": "Jump Forward",
|
||||||
"ButtonLatest": "નવીનતમ",
|
"ButtonLatest": "નવીનતમ",
|
||||||
"ButtonLibrary": "પુસ્તકાલય",
|
"ButtonLibrary": "પુસ્તકાલય",
|
||||||
"ButtonLogout": "લૉગ આઉટ",
|
"ButtonLogout": "લૉગ આઉટ",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "બધા મેળ ખાતા લેખકો શોધો",
|
"ButtonMatchAllAuthors": "બધા મેળ ખાતા લેખકો શોધો",
|
||||||
"ButtonMatchBooks": "મેળ ખાતી પુસ્તકો શોધો",
|
"ButtonMatchBooks": "મેળ ખાતી પુસ્તકો શોધો",
|
||||||
"ButtonNevermind": "કંઈ વાંધો નહીં",
|
"ButtonNevermind": "કંઈ વાંધો નહીં",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Next Chapter",
|
||||||
"ButtonOk": "ઓકે",
|
"ButtonOk": "ઓકે",
|
||||||
"ButtonOpenFeed": "ફીડ ખોલો",
|
"ButtonOpenFeed": "ફીડ ખોલો",
|
||||||
"ButtonOpenManager": "મેનેજર ખોલો",
|
"ButtonOpenManager": "મેનેજર ખોલો",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "ચલાવો",
|
"ButtonPlay": "ચલાવો",
|
||||||
"ButtonPlaying": "ચલાવી રહ્યું છે",
|
"ButtonPlaying": "ચલાવી રહ્યું છે",
|
||||||
"ButtonPlaylists": "પ્લેલિસ્ટ",
|
"ButtonPlaylists": "પ્લેલિસ્ટ",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Previous Chapter",
|
||||||
"ButtonPurgeAllCache": "બધો Cache કાઢી નાખો",
|
"ButtonPurgeAllCache": "બધો Cache કાઢી નાખો",
|
||||||
"ButtonPurgeItemsCache": "વસ્તુઓનો Cache કાઢી નાખો",
|
"ButtonPurgeItemsCache": "વસ્તુઓનો Cache કાઢી નાખો",
|
||||||
"ButtonPurgeMediaProgress": "બધું સાંભળ્યું કાઢી નાખો",
|
"ButtonPurgeMediaProgress": "બધું સાંભળ્યું કાઢી નાખો",
|
||||||
@@ -54,15 +61,16 @@
|
|||||||
"ButtonQueueRemoveItem": "કતારથી કાઢી નાખો",
|
"ButtonQueueRemoveItem": "કતારથી કાઢી નાખો",
|
||||||
"ButtonQuickMatch": "ઝડપી મેળ ખવડાવો",
|
"ButtonQuickMatch": "ઝડપી મેળ ખવડાવો",
|
||||||
"ButtonRead": "વાંચો",
|
"ButtonRead": "વાંચો",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "કાઢી નાખો",
|
"ButtonRemove": "કાઢી નાખો",
|
||||||
"ButtonRemoveAll": "બધું કાઢી નાખો",
|
"ButtonRemoveAll": "બધું કાઢી નાખો",
|
||||||
"ButtonRemoveAllLibraryItems": "બધું પુસ્તકાલય વસ્તુઓ કાઢી નાખો",
|
"ButtonRemoveAllLibraryItems": "બધું પુસ્તકાલય વસ્તુઓ કાઢી નાખો",
|
||||||
"ButtonRemoveFromContinueListening": "સાંભળતી પુસ્તકો માંથી કાઢી નાખો",
|
"ButtonRemoveFromContinueListening": "સાંભળતી પુસ્તકો માંથી કાઢી નાખો",
|
||||||
"ButtonRemoveFromContinueReading": "Remove from Continue Reading",
|
"ButtonRemoveFromContinueReading": "સાંભળતી પુસ્તકો માંથી કાઢી નાખો",
|
||||||
"ButtonRemoveSeriesFromContinueSeries": "સાંભળતી સિરીઝ માંથી કાઢી નાખો",
|
"ButtonRemoveSeriesFromContinueSeries": "સાંભળતી સિરીઝ માંથી કાઢી નાખો",
|
||||||
"ButtonReScan": "ફરીથી સ્કેન કરો",
|
"ButtonReScan": "ફરીથી સ્કેન કરો",
|
||||||
"ButtonReset": "રીસેટ કરો",
|
"ButtonReset": "રીસેટ કરો",
|
||||||
"ButtonResetToDefault": "Reset to default",
|
"ButtonResetToDefault": "ડિફોલ્ટ પર રીસેટ કરો",
|
||||||
"ButtonRestore": "પુનઃસ્થાપિત કરો",
|
"ButtonRestore": "પુનઃસ્થાપિત કરો",
|
||||||
"ButtonSave": "સાચવો",
|
"ButtonSave": "સાચવો",
|
||||||
"ButtonSaveAndClose": "સાચવો અને બંધ કરો",
|
"ButtonSaveAndClose": "સાચવો અને બંધ કરો",
|
||||||
@@ -73,12 +81,13 @@
|
|||||||
"ButtonSelectFolderPath": "ફોલ્ડર પથ પસંદ કરો",
|
"ButtonSelectFolderPath": "ફોલ્ડર પથ પસંદ કરો",
|
||||||
"ButtonSeries": "સિરીઝ",
|
"ButtonSeries": "સિરીઝ",
|
||||||
"ButtonSetChaptersFromTracks": "ટ્રેક્સથી પ્રકરણો સેટ કરો",
|
"ButtonSetChaptersFromTracks": "ટ્રેક્સથી પ્રકરણો સેટ કરો",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "સમય શિફ્ટ કરો",
|
"ButtonShiftTimes": "સમય શિફ્ટ કરો",
|
||||||
"ButtonShow": "બતાવો",
|
"ButtonShow": "બતાવો",
|
||||||
"ButtonStartM4BEncode": "M4B એન્કોડ શરૂ કરો",
|
"ButtonStartM4BEncode": "M4B એન્કોડ શરૂ કરો",
|
||||||
"ButtonStartMetadataEmbed": "મેટાડેટા એમ્બેડ શરૂ કરો",
|
"ButtonStartMetadataEmbed": "મેટાડેટા એમ્બેડ શરૂ કરો",
|
||||||
"ButtonSubmit": "સબમિટ કરો",
|
"ButtonSubmit": "સબમિટ કરો",
|
||||||
"ButtonTest": "Test",
|
"ButtonTest": "પરખ કરો",
|
||||||
"ButtonUpload": "અપલોડ કરો",
|
"ButtonUpload": "અપલોડ કરો",
|
||||||
"ButtonUploadBackup": "બેકઅપ અપલોડ કરો",
|
"ButtonUploadBackup": "બેકઅપ અપલોડ કરો",
|
||||||
"ButtonUploadCover": "કવર અપલોડ કરો",
|
"ButtonUploadCover": "કવર અપલોડ કરો",
|
||||||
@@ -87,81 +96,85 @@
|
|||||||
"ButtonUserEdit": "વપરાશકર્તા {0} સંપાદિત કરો",
|
"ButtonUserEdit": "વપરાશકર્તા {0} સંપાદિત કરો",
|
||||||
"ButtonViewAll": "બધું જુઓ",
|
"ButtonViewAll": "બધું જુઓ",
|
||||||
"ButtonYes": "હા",
|
"ButtonYes": "હા",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error fetching metadata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Could not fetch metadata - try updating title and/or author",
|
||||||
|
"ErrorUploadLacksTitle": "Must have a title",
|
||||||
"HeaderAccount": "એકાઉન્ટ",
|
"HeaderAccount": "એકાઉન્ટ",
|
||||||
"HeaderAdvanced": "અડ્વાન્સડ",
|
"HeaderAdvanced": "અડ્વાન્સડ",
|
||||||
"HeaderAppriseNotificationSettings": "Apprise સૂચના સેટિંગ્સ",
|
"HeaderAppriseNotificationSettings": "Apprise સૂચના સેટિંગ્સ",
|
||||||
"HeaderAudiobookTools": "Audiobook File Management Tools",
|
"HeaderAudiobookTools": "ઓડિયોબુક ફાઇલ વ્યવસ્થાપન ટૂલ્સ",
|
||||||
"HeaderAudioTracks": "Audio Tracks",
|
"HeaderAudioTracks": "ઓડિયો ટ્રેક્સ",
|
||||||
"HeaderAuthentication": "Authentication",
|
"HeaderAuthentication": "Authentication",
|
||||||
"HeaderBackups": "Backups",
|
"HeaderBackups": "બેકઅપ્સ",
|
||||||
"HeaderChangePassword": "Change Password",
|
"HeaderChangePassword": "પાસવર્ડ બદલો",
|
||||||
"HeaderChapters": "Chapters",
|
"HeaderChapters": "પ્રકરણો",
|
||||||
"HeaderChooseAFolder": "Choose a Folder",
|
"HeaderChooseAFolder": "ફોલ્ડર પસંદ કરો",
|
||||||
"HeaderCollection": "Collection",
|
"HeaderCollection": "સંગ્રહ",
|
||||||
"HeaderCollectionItems": "Collection Items",
|
"HeaderCollectionItems": "સંગ્રહ વસ્તુઓ",
|
||||||
"HeaderCover": "Cover",
|
"HeaderCover": "આવરણ",
|
||||||
"HeaderCurrentDownloads": "Current Downloads",
|
"HeaderCurrentDownloads": "વર્તમાન ડાઉનલોડ્સ",
|
||||||
"HeaderDetails": "Details",
|
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
|
||||||
"HeaderDownloadQueue": "Download Queue",
|
"HeaderDetails": "વિગતો",
|
||||||
"HeaderEbookFiles": "Ebook Files",
|
"HeaderDownloadQueue": "ડાઉનલોડ કતાર",
|
||||||
"HeaderEmail": "Email",
|
"HeaderEbookFiles": "ઇબુક ફાઇલો",
|
||||||
"HeaderEmailSettings": "Email Settings",
|
"HeaderEmail": "ઈમેલ",
|
||||||
"HeaderEpisodes": "Episodes",
|
"HeaderEmailSettings": "ઈમેલ સેટિંગ્સ",
|
||||||
"HeaderEreaderDevices": "Ereader Devices",
|
"HeaderEpisodes": "એપિસોડ્સ",
|
||||||
"HeaderEreaderSettings": "Ereader Settings",
|
"HeaderEreaderDevices": "ઇરીડર ઉપકરણો",
|
||||||
"HeaderFiles": "Files",
|
"HeaderEreaderSettings": "ઇરીડર સેટિંગ્સ",
|
||||||
"HeaderFindChapters": "Find Chapters",
|
"HeaderFiles": "ફાઇલો",
|
||||||
"HeaderIgnoredFiles": "Ignored Files",
|
"HeaderFindChapters": "પ્રકરણો શોધો",
|
||||||
"HeaderItemFiles": "Item Files",
|
"HeaderIgnoredFiles": "અવગણેલી ફાઇલો",
|
||||||
"HeaderItemMetadataUtils": "Item Metadata Utils",
|
"HeaderItemFiles": "વાસ્તુ ની ફાઈલો",
|
||||||
"HeaderLastListeningSession": "Last Listening Session",
|
"HeaderItemMetadataUtils": "વસ્તુ મેટાડેટા સાધનો",
|
||||||
"HeaderLatestEpisodes": "Latest episodes",
|
"HeaderLastListeningSession": "છેલ્લી સાંભળતી સેશન",
|
||||||
"HeaderLibraries": "Libraries",
|
"HeaderLatestEpisodes": "નવીનતમ એપિસોડ્સ",
|
||||||
"HeaderLibraryFiles": "Library Files",
|
"HeaderLibraries": "પુસ્તકાલયો",
|
||||||
"HeaderLibraryStats": "Library Stats",
|
"HeaderLibraryFiles": "પુસ્તકાલય ફાઇલો",
|
||||||
"HeaderListeningSessions": "Listening Sessions",
|
"HeaderLibraryStats": "પુસ્તકાલય આંકડા",
|
||||||
"HeaderListeningStats": "Listening Stats",
|
"HeaderListeningSessions": "સાંભળતી સેશન્સ",
|
||||||
"HeaderLogin": "Login",
|
"HeaderListeningStats": "સાંભળતી આંકડા",
|
||||||
"HeaderLogs": "Logs",
|
"HeaderLogin": "લોગિન",
|
||||||
"HeaderManageGenres": "Manage Genres",
|
"HeaderLogs": "લોગ્સ",
|
||||||
"HeaderManageTags": "Manage Tags",
|
"HeaderManageGenres": "જાતિઓ મેનેજ કરો",
|
||||||
"HeaderMapDetails": "Map details",
|
"HeaderManageTags": "ટેગ્સ મેનેજ કરો",
|
||||||
"HeaderMatch": "Match",
|
"HeaderMapDetails": "વિગતો મેપ કરો",
|
||||||
"HeaderMetadataOrderOfPrecedence": "Metadata order of precedence",
|
"HeaderMatch": "મેળ ખાતી શોધો",
|
||||||
"HeaderMetadataToEmbed": "Metadata to embed",
|
"HeaderMetadataOrderOfPrecedence": "મેટાડેટા પ્રાધાન્યતાનો ક્રમ",
|
||||||
"HeaderNewAccount": "New Account",
|
"HeaderMetadataToEmbed": "એમ્બેડ કરવા માટે મેટાડેટા",
|
||||||
"HeaderNewLibrary": "New Library",
|
"HeaderNewAccount": "નવું એકાઉન્ટ",
|
||||||
"HeaderNotifications": "Notifications",
|
"HeaderNewLibrary": "નવી પુસ્તકાલય",
|
||||||
|
"HeaderNotifications": "સૂચનાઓ",
|
||||||
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentication",
|
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentication",
|
||||||
"HeaderOpenRSSFeed": "Open RSS Feed",
|
"HeaderOpenRSSFeed": "RSS ફીડ ખોલો",
|
||||||
"HeaderOtherFiles": "Other Files",
|
"HeaderOtherFiles": "અન્ય ફાઇલો",
|
||||||
"HeaderPasswordAuthentication": "Password Authentication",
|
"HeaderPasswordAuthentication": "Password Authentication",
|
||||||
"HeaderPermissions": "Permissions",
|
"HeaderPermissions": "પરવાનગીઓ",
|
||||||
"HeaderPlayerQueue": "Player Queue",
|
"HeaderPlayerQueue": "પ્લેયર કતાર",
|
||||||
"HeaderPlaylist": "Playlist",
|
"HeaderPlaylist": "પ્લેલિસ્ટ",
|
||||||
"HeaderPlaylistItems": "Playlist Items",
|
"HeaderPlaylistItems": "પ્લેલિસ્ટ ની વસ્તુઓ",
|
||||||
"HeaderPodcastsToAdd": "Podcasts to Add",
|
"HeaderPodcastsToAdd": "ઉમેરવા માટે પોડકાસ્ટ્સ",
|
||||||
"HeaderPreviewCover": "Preview Cover",
|
"HeaderPreviewCover": "પૂર્વાવલોકન કવર",
|
||||||
"HeaderRemoveEpisode": "Remove Episode",
|
"HeaderRemoveEpisode": "એપિસોડ કાઢી નાખો",
|
||||||
"HeaderRemoveEpisodes": "Remove {0} Episodes",
|
"HeaderRemoveEpisodes": "{0} એપિસોડ્સ કાઢી નાખો",
|
||||||
"HeaderRSSFeedGeneral": "RSS Details",
|
"HeaderRSSFeedGeneral": "સામાન્ય RSS ફીડ",
|
||||||
"HeaderRSSFeedIsOpen": "RSS Feed is Open",
|
"HeaderRSSFeedIsOpen": "RSS ફીડ ખોલેલી છે",
|
||||||
"HeaderRSSFeeds": "RSS Feeds",
|
"HeaderRSSFeeds": "RSS ફીડ્સ",
|
||||||
"HeaderSavedMediaProgress": "Saved Media Progress",
|
"HeaderSavedMediaProgress": "સાચવેલ મીડિયા પ્રગતિ",
|
||||||
"HeaderSchedule": "Schedule",
|
"HeaderSchedule": "સમયપત્રક",
|
||||||
"HeaderScheduleLibraryScans": "Schedule Automatic Library Scans",
|
"HeaderScheduleLibraryScans": "પુસ્તકાલય સ્કેન સમયપત્રક",
|
||||||
"HeaderSession": "Session",
|
"HeaderSession": "સેશન",
|
||||||
"HeaderSetBackupSchedule": "Set Backup Schedule",
|
"HeaderSetBackupSchedule": "બેકઅપ સમયપત્રક સેટ કરો",
|
||||||
"HeaderSettings": "Settings",
|
"HeaderSettings": "સેટિંગ્સ",
|
||||||
"HeaderSettingsDisplay": "Display",
|
"HeaderSettingsDisplay": "ડિસ્પ્લે સેટિંગ્સ",
|
||||||
"HeaderSettingsExperimental": "Experimental Features",
|
"HeaderSettingsExperimental": "પ્રયોગશીલ સેટિંગ્સ",
|
||||||
"HeaderSettingsGeneral": "General",
|
"HeaderSettingsGeneral": "સામાન્ય સેટિંગ્સ",
|
||||||
"HeaderSettingsScanner": "Scanner",
|
"HeaderSettingsScanner": "સ્કેનર સેટિંગ્સ",
|
||||||
"HeaderSleepTimer": "Sleep Timer",
|
"HeaderSleepTimer": "સ્લીપ ટાઈમર",
|
||||||
"HeaderStatsLargestItems": "Largest Items",
|
"HeaderStatsLargestItems": "સૌથી મોટી વસ્તુઓ",
|
||||||
"HeaderStatsLongestItems": "Longest Items (hrs)",
|
"HeaderStatsLongestItems": "સૌથી લાંબી વસ્તુઓ (કલાક)",
|
||||||
"HeaderStatsMinutesListeningChart": "Minutes Listening (last 7 days)",
|
"HeaderStatsMinutesListeningChart": "સાંભળવાની મિનિટ (છેલ્લા ૭ દિવસ)",
|
||||||
"HeaderStatsRecentSessions": "Recent Sessions",
|
"HeaderStatsRecentSessions": "છેલ્લી સાંભળતી સેશન્સ",
|
||||||
"HeaderStatsTop10Authors": "Top 10 Authors",
|
"HeaderStatsTop10Authors": "Top 10 Authors",
|
||||||
"HeaderStatsTop5Genres": "Top 5 Genres",
|
"HeaderStatsTop5Genres": "Top 5 Genres",
|
||||||
"HeaderTableOfContents": "Table of Contents",
|
"HeaderTableOfContents": "Table of Contents",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Update Details",
|
"HeaderUpdateDetails": "Update Details",
|
||||||
"HeaderUpdateLibrary": "Update Library",
|
"HeaderUpdateLibrary": "Update Library",
|
||||||
"HeaderUsers": "Users",
|
"HeaderUsers": "Users",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Your Stats",
|
"HeaderYourStats": "Your Stats",
|
||||||
"LabelAbridged": "Abridged",
|
"LabelAbridged": "Abridged",
|
||||||
"LabelAccountType": "Account Type",
|
"LabelAccountType": "Account Type",
|
||||||
@@ -196,6 +210,8 @@
|
|||||||
"LabelAuthorLastFirst": "Author (Last, First)",
|
"LabelAuthorLastFirst": "Author (Last, First)",
|
||||||
"LabelAuthors": "Authors",
|
"LabelAuthors": "Authors",
|
||||||
"LabelAutoDownloadEpisodes": "Auto Download Episodes",
|
"LabelAutoDownloadEpisodes": "Auto Download Episodes",
|
||||||
|
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Fetches metadata for title, author, and series to streamline uploading. Additional metadata may have to be matched after upload.",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoLaunch": "Auto Launch",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoRegister": "Auto Register",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Example",
|
"LabelExample": "Example",
|
||||||
"LabelExplicit": "Explicit",
|
"LabelExplicit": "Explicit",
|
||||||
"LabelFeedURL": "Feed URL",
|
"LabelFeedURL": "Feed URL",
|
||||||
|
"LabelFetchingMetadata": "Fetching Metadata",
|
||||||
"LabelFile": "File",
|
"LabelFile": "File",
|
||||||
"LabelFileBirthtime": "File Birthtime",
|
"LabelFileBirthtime": "File Birthtime",
|
||||||
"LabelFileModified": "File Modified",
|
"LabelFileModified": "File Modified",
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folders",
|
"LabelFolders": "Folders",
|
||||||
|
"LabelFontBold": "Bold",
|
||||||
"LabelFontFamily": "ફોન્ટ કુટુંબ",
|
"LabelFontFamily": "ફોન્ટ કુટુંબ",
|
||||||
|
"LabelFontItalic": "Italic",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
|
"LabelFontStrikethrough": "Strikethrough",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Genres",
|
"LabelGenres": "Genres",
|
||||||
@@ -336,7 +356,11 @@
|
|||||||
"LabelMetaTags": "Meta Tags",
|
"LabelMetaTags": "Meta Tags",
|
||||||
"LabelMinute": "Minute",
|
"LabelMinute": "Minute",
|
||||||
"LabelMissing": "Missing",
|
"LabelMissing": "Missing",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Missing Parts",
|
"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.",
|
||||||
"LabelMore": "More",
|
"LabelMore": "More",
|
||||||
"LabelMoreInfo": "More Info",
|
"LabelMoreInfo": "More Info",
|
||||||
"LabelName": "Name",
|
"LabelName": "Name",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Can Download",
|
"LabelPermissionsDownload": "Can Download",
|
||||||
"LabelPermissionsUpdate": "Can Update",
|
"LabelPermissionsUpdate": "Can Update",
|
||||||
"LabelPermissionsUpload": "Can Upload",
|
"LabelPermissionsUpload": "Can Upload",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Photo Path/URL",
|
"LabelPhotoPathURL": "Photo Path/URL",
|
||||||
"LabelPlaylists": "Playlists",
|
"LabelPlaylists": "Playlists",
|
||||||
"LabelPlayMethod": "Play Method",
|
"LabelPlayMethod": "Play Method",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "પોડકાસ્ટ શોધ પ્રદેશ",
|
||||||
"LabelPodcastType": "Podcast Type",
|
"LabelPodcastType": "Podcast Type",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Prefixes to Ignore (case insensitive)",
|
"LabelPrefixesToIgnore": "Prefixes to Ignore (case insensitive)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Recently Added",
|
"LabelRecentlyAdded": "Recently Added",
|
||||||
"LabelRecentSeries": "Recent Series",
|
"LabelRecentSeries": "Recent Series",
|
||||||
"LabelRecommended": "Recommended",
|
"LabelRecommended": "Recommended",
|
||||||
|
"LabelRedo": "Redo",
|
||||||
"LabelRegion": "Region",
|
"LabelRegion": "Region",
|
||||||
"LabelReleaseDate": "Release Date",
|
"LabelReleaseDate": "Release Date",
|
||||||
"LabelRemoveCover": "Remove cover",
|
"LabelRemoveCover": "Remove cover",
|
||||||
|
"LabelRowsPerPage": "Rows per page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
||||||
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
||||||
"LabelRSSFeedOpen": "RSS Feed Open",
|
"LabelRSSFeedOpen": "RSS Feed Open",
|
||||||
@@ -416,6 +444,7 @@
|
|||||||
"LabelSeries": "Series",
|
"LabelSeries": "Series",
|
||||||
"LabelSeriesName": "Series Name",
|
"LabelSeriesName": "Series Name",
|
||||||
"LabelSeriesProgress": "Series Progress",
|
"LabelSeriesProgress": "Series Progress",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Set as primary",
|
"LabelSetEbookAsPrimary": "Set as primary",
|
||||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
|
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
|
||||||
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
|
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
|
||||||
|
"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": "Parse subtitles",
|
"LabelSettingsParseSubtitles": "Parse subtitles",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by \" - \"<br>i.e. \"Book Title - A Subtitle Here\" has the subtitle \"A Subtitle Here\"",
|
"LabelSettingsParseSubtitlesHelp": "Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by \" - \"<br>i.e. \"Book Title - A Subtitle Here\" has the subtitle \"A Subtitle Here\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
||||||
@@ -482,6 +513,10 @@
|
|||||||
"LabelTagsAccessibleToUser": "Tags Accessible to User",
|
"LabelTagsAccessibleToUser": "Tags Accessible to User",
|
||||||
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
||||||
"LabelTasks": "Tasks Running",
|
"LabelTasks": "Tasks Running",
|
||||||
|
"LabelTextEditorBulletedList": "Bulleted list",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numbered list",
|
||||||
|
"LabelTextEditorUnlink": "Unlink",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Theme",
|
||||||
"LabelThemeDark": "Dark",
|
"LabelThemeDark": "Dark",
|
||||||
"LabelThemeLight": "Light",
|
"LabelThemeLight": "Light",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Single-track",
|
"LabelTracksSingleTrack": "Single-track",
|
||||||
"LabelType": "Type",
|
"LabelType": "Type",
|
||||||
"LabelUnabridged": "Unabridged",
|
"LabelUnabridged": "Unabridged",
|
||||||
|
"LabelUndo": "Undo",
|
||||||
"LabelUnknown": "Unknown",
|
"LabelUnknown": "Unknown",
|
||||||
"LabelUpdateCover": "Update Cover",
|
"LabelUpdateCover": "Update Cover",
|
||||||
"LabelUpdateCoverHelp": "Allow overwriting of existing covers for the selected books when a match is located",
|
"LabelUpdateCoverHelp": "Allow overwriting of existing covers for the selected books when a match is located",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
|
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
|
||||||
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
||||||
"LabelUploaderDropFiles": "Drop files",
|
"LabelUploaderDropFiles": "Drop files",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||||
"LabelUseChapterTrack": "Use chapter track",
|
"LabelUseChapterTrack": "Use chapter track",
|
||||||
"LabelUseFullTrack": "Use full track",
|
"LabelUseFullTrack": "Use full track",
|
||||||
"LabelUser": "User",
|
"LabelUser": "User",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "View player queue",
|
"LabelViewQueue": "View player queue",
|
||||||
"LabelVolume": "Volume",
|
"LabelVolume": "Volume",
|
||||||
"LabelWeekdaysToRun": "Weekdays to run",
|
"LabelWeekdaysToRun": "Weekdays to run",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Your audiobook duration",
|
"LabelYourAudiobookDuration": "Your audiobook duration",
|
||||||
"LabelYourBookmarks": "Your Bookmarks",
|
"LabelYourBookmarks": "Your Bookmarks",
|
||||||
"LabelYourPlaylists": "Your Playlists",
|
"LabelYourPlaylists": "Your Playlists",
|
||||||
@@ -562,6 +601,7 @@
|
|||||||
"MessageConfirmRemoveCollection": "Are you sure you want to remove collection \"{0}\"?",
|
"MessageConfirmRemoveCollection": "Are you sure you want to remove collection \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on",
|
"MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on",
|
||||||
"MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.<br /><br />Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.<br /><br />All clients using your server will be automatically refreshed.",
|
"MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.<br /><br />Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.<br /><br />All clients using your server will be automatically refreshed.",
|
||||||
"MessageSearchResultsFor": "Search results for",
|
"MessageSearchResultsFor": "Search results for",
|
||||||
|
"MessageSelected": "{0} selected",
|
||||||
"MessageServerCouldNotBeReached": "Server could not be reached",
|
"MessageServerCouldNotBeReached": "Server could not be reached",
|
||||||
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
||||||
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "Root user is the only user that can have an empty password",
|
"NoteChangeRootPassword": "Root user is the only user that can have an empty password",
|
||||||
"NoteChapterEditorTimes": "Note: First chapter start time must remain at 0:00 and the last chapter start time cannot exceed this audiobooks duration.",
|
"NoteChapterEditorTimes": "Note: First chapter start time must remain at 0:00 and the last chapter start time cannot exceed this audiobooks duration.",
|
||||||
"NoteFolderPicker": "Note: folders already mapped will not be shown",
|
"NoteFolderPicker": "Note: folders already mapped will not be shown",
|
||||||
"NoteFolderPickerDebian": "Note: Folder picker for the debian install is not fully implemented. You should enter the path to your library directly.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Warning: Most podcast apps will require the RSS feed URL is using HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Warning: Most podcast apps will require the RSS feed URL is using HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.",
|
"NoteRSSFeedPodcastAppsPubDate": "Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
||||||
|
|||||||
+41
-1
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "छुपाएं",
|
"ButtonHide": "छुपाएं",
|
||||||
"ButtonHome": "घर",
|
"ButtonHome": "घर",
|
||||||
"ButtonIssues": "समस्याएं",
|
"ButtonIssues": "समस्याएं",
|
||||||
|
"ButtonJumpBackward": "Jump Backward",
|
||||||
|
"ButtonJumpForward": "Jump Forward",
|
||||||
"ButtonLatest": "नवीनतम",
|
"ButtonLatest": "नवीनतम",
|
||||||
"ButtonLibrary": "पुस्तकालय",
|
"ButtonLibrary": "पुस्तकालय",
|
||||||
"ButtonLogout": "लॉग आउट",
|
"ButtonLogout": "लॉग आउट",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "सभी लेखकों को तलाश करें",
|
"ButtonMatchAllAuthors": "सभी लेखकों को तलाश करें",
|
||||||
"ButtonMatchBooks": "संबंधित पुस्तकों का मिलान करें",
|
"ButtonMatchBooks": "संबंधित पुस्तकों का मिलान करें",
|
||||||
"ButtonNevermind": "कोई बात नहीं",
|
"ButtonNevermind": "कोई बात नहीं",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Next Chapter",
|
||||||
"ButtonOk": "ठीक है",
|
"ButtonOk": "ठीक है",
|
||||||
"ButtonOpenFeed": "फ़ीड खोलें",
|
"ButtonOpenFeed": "फ़ीड खोलें",
|
||||||
"ButtonOpenManager": "मैनेजर खोलें",
|
"ButtonOpenManager": "मैनेजर खोलें",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "चलाएँ",
|
"ButtonPlay": "चलाएँ",
|
||||||
"ButtonPlaying": "चल रही है",
|
"ButtonPlaying": "चल रही है",
|
||||||
"ButtonPlaylists": "प्लेलिस्ट्स",
|
"ButtonPlaylists": "प्लेलिस्ट्स",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Previous Chapter",
|
||||||
"ButtonPurgeAllCache": "सभी Cache मिटाएं",
|
"ButtonPurgeAllCache": "सभी Cache मिटाएं",
|
||||||
"ButtonPurgeItemsCache": "आइटम Cache मिटाएं",
|
"ButtonPurgeItemsCache": "आइटम Cache मिटाएं",
|
||||||
"ButtonPurgeMediaProgress": "अभी तक सुना हुआ सब हटा दे",
|
"ButtonPurgeMediaProgress": "अभी तक सुना हुआ सब हटा दे",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "कतार से हटाएं",
|
"ButtonQueueRemoveItem": "कतार से हटाएं",
|
||||||
"ButtonQuickMatch": "जल्दी से समानता की तलाश करें",
|
"ButtonQuickMatch": "जल्दी से समानता की तलाश करें",
|
||||||
"ButtonRead": "पढ़ लिया",
|
"ButtonRead": "पढ़ लिया",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "हटाएं",
|
"ButtonRemove": "हटाएं",
|
||||||
"ButtonRemoveAll": "सभी हटाएं",
|
"ButtonRemoveAll": "सभी हटाएं",
|
||||||
"ButtonRemoveAllLibraryItems": "पुस्तकालय की सभी आइटम हटाएं",
|
"ButtonRemoveAllLibraryItems": "पुस्तकालय की सभी आइटम हटाएं",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "फ़ोल्डर का पथ चुनें",
|
"ButtonSelectFolderPath": "फ़ोल्डर का पथ चुनें",
|
||||||
"ButtonSeries": "सीरीज",
|
"ButtonSeries": "सीरीज",
|
||||||
"ButtonSetChaptersFromTracks": "ट्रैक्स से अध्याय बनाएं",
|
"ButtonSetChaptersFromTracks": "ट्रैक्स से अध्याय बनाएं",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "समय खिसकाए",
|
"ButtonShiftTimes": "समय खिसकाए",
|
||||||
"ButtonShow": "दिखाएं",
|
"ButtonShow": "दिखाएं",
|
||||||
"ButtonStartM4BEncode": "M4B एन्कोडिंग शुरू करें",
|
"ButtonStartM4BEncode": "M4B एन्कोडिंग शुरू करें",
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"ButtonUserEdit": "उपयोगकर्ता {0} को संपादित करें",
|
"ButtonUserEdit": "उपयोगकर्ता {0} को संपादित करें",
|
||||||
"ButtonViewAll": "सभी को देखें",
|
"ButtonViewAll": "सभी को देखें",
|
||||||
"ButtonYes": "हाँ",
|
"ButtonYes": "हाँ",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error fetching metadata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Could not fetch metadata - try updating title and/or author",
|
||||||
|
"ErrorUploadLacksTitle": "Must have a title",
|
||||||
"HeaderAccount": "खाता",
|
"HeaderAccount": "खाता",
|
||||||
"HeaderAdvanced": "विकसित",
|
"HeaderAdvanced": "विकसित",
|
||||||
"HeaderAppriseNotificationSettings": "Apprise अधिसूचना सेटिंग्स",
|
"HeaderAppriseNotificationSettings": "Apprise अधिसूचना सेटिंग्स",
|
||||||
@@ -101,6 +113,7 @@
|
|||||||
"HeaderCollectionItems": "Collection Items",
|
"HeaderCollectionItems": "Collection Items",
|
||||||
"HeaderCover": "Cover",
|
"HeaderCover": "Cover",
|
||||||
"HeaderCurrentDownloads": "Current Downloads",
|
"HeaderCurrentDownloads": "Current Downloads",
|
||||||
|
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
|
||||||
"HeaderDetails": "Details",
|
"HeaderDetails": "Details",
|
||||||
"HeaderDownloadQueue": "Download Queue",
|
"HeaderDownloadQueue": "Download Queue",
|
||||||
"HeaderEbookFiles": "Ebook Files",
|
"HeaderEbookFiles": "Ebook Files",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Update Details",
|
"HeaderUpdateDetails": "Update Details",
|
||||||
"HeaderUpdateLibrary": "Update Library",
|
"HeaderUpdateLibrary": "Update Library",
|
||||||
"HeaderUsers": "Users",
|
"HeaderUsers": "Users",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Your Stats",
|
"HeaderYourStats": "Your Stats",
|
||||||
"LabelAbridged": "Abridged",
|
"LabelAbridged": "Abridged",
|
||||||
"LabelAccountType": "Account Type",
|
"LabelAccountType": "Account Type",
|
||||||
@@ -196,6 +210,8 @@
|
|||||||
"LabelAuthorLastFirst": "Author (Last, First)",
|
"LabelAuthorLastFirst": "Author (Last, First)",
|
||||||
"LabelAuthors": "Authors",
|
"LabelAuthors": "Authors",
|
||||||
"LabelAutoDownloadEpisodes": "Auto Download Episodes",
|
"LabelAutoDownloadEpisodes": "Auto Download Episodes",
|
||||||
|
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Fetches metadata for title, author, and series to streamline uploading. Additional metadata may have to be matched after upload.",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoLaunch": "Auto Launch",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoRegister": "Auto Register",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Example",
|
"LabelExample": "Example",
|
||||||
"LabelExplicit": "Explicit",
|
"LabelExplicit": "Explicit",
|
||||||
"LabelFeedURL": "Feed URL",
|
"LabelFeedURL": "Feed URL",
|
||||||
|
"LabelFetchingMetadata": "Fetching Metadata",
|
||||||
"LabelFile": "File",
|
"LabelFile": "File",
|
||||||
"LabelFileBirthtime": "File Birthtime",
|
"LabelFileBirthtime": "File Birthtime",
|
||||||
"LabelFileModified": "File Modified",
|
"LabelFileModified": "File Modified",
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folders",
|
"LabelFolders": "Folders",
|
||||||
|
"LabelFontBold": "Bold",
|
||||||
"LabelFontFamily": "फुहारा परिवार",
|
"LabelFontFamily": "फुहारा परिवार",
|
||||||
|
"LabelFontItalic": "Italic",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
|
"LabelFontStrikethrough": "Strikethrough",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Genres",
|
"LabelGenres": "Genres",
|
||||||
@@ -336,7 +356,11 @@
|
|||||||
"LabelMetaTags": "Meta Tags",
|
"LabelMetaTags": "Meta Tags",
|
||||||
"LabelMinute": "Minute",
|
"LabelMinute": "Minute",
|
||||||
"LabelMissing": "Missing",
|
"LabelMissing": "Missing",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Missing Parts",
|
"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.",
|
||||||
"LabelMore": "More",
|
"LabelMore": "More",
|
||||||
"LabelMoreInfo": "More Info",
|
"LabelMoreInfo": "More Info",
|
||||||
"LabelName": "Name",
|
"LabelName": "Name",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Can Download",
|
"LabelPermissionsDownload": "Can Download",
|
||||||
"LabelPermissionsUpdate": "Can Update",
|
"LabelPermissionsUpdate": "Can Update",
|
||||||
"LabelPermissionsUpload": "Can Upload",
|
"LabelPermissionsUpload": "Can Upload",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Photo Path/URL",
|
"LabelPhotoPathURL": "Photo Path/URL",
|
||||||
"LabelPlaylists": "Playlists",
|
"LabelPlaylists": "Playlists",
|
||||||
"LabelPlayMethod": "Play Method",
|
"LabelPlayMethod": "Play Method",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "पॉडकास्ट खोज क्षेत्र",
|
||||||
"LabelPodcastType": "Podcast Type",
|
"LabelPodcastType": "Podcast Type",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Prefixes to Ignore (case insensitive)",
|
"LabelPrefixesToIgnore": "Prefixes to Ignore (case insensitive)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Recently Added",
|
"LabelRecentlyAdded": "Recently Added",
|
||||||
"LabelRecentSeries": "Recent Series",
|
"LabelRecentSeries": "Recent Series",
|
||||||
"LabelRecommended": "Recommended",
|
"LabelRecommended": "Recommended",
|
||||||
|
"LabelRedo": "Redo",
|
||||||
"LabelRegion": "Region",
|
"LabelRegion": "Region",
|
||||||
"LabelReleaseDate": "Release Date",
|
"LabelReleaseDate": "Release Date",
|
||||||
"LabelRemoveCover": "Remove cover",
|
"LabelRemoveCover": "Remove cover",
|
||||||
|
"LabelRowsPerPage": "Rows per page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
||||||
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
||||||
"LabelRSSFeedOpen": "RSS Feed Open",
|
"LabelRSSFeedOpen": "RSS Feed Open",
|
||||||
@@ -416,6 +444,7 @@
|
|||||||
"LabelSeries": "Series",
|
"LabelSeries": "Series",
|
||||||
"LabelSeriesName": "Series Name",
|
"LabelSeriesName": "Series Name",
|
||||||
"LabelSeriesProgress": "Series Progress",
|
"LabelSeriesProgress": "Series Progress",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Set as primary",
|
"LabelSetEbookAsPrimary": "Set as primary",
|
||||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
|
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
|
||||||
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
|
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
|
||||||
|
"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": "Parse subtitles",
|
"LabelSettingsParseSubtitles": "Parse subtitles",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by \" - \"<br>i.e. \"Book Title - A Subtitle Here\" has the subtitle \"A Subtitle Here\"",
|
"LabelSettingsParseSubtitlesHelp": "Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by \" - \"<br>i.e. \"Book Title - A Subtitle Here\" has the subtitle \"A Subtitle Here\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
"LabelSettingsPreferMatchedMetadata": "Prefer matched metadata",
|
||||||
@@ -482,6 +513,10 @@
|
|||||||
"LabelTagsAccessibleToUser": "Tags Accessible to User",
|
"LabelTagsAccessibleToUser": "Tags Accessible to User",
|
||||||
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
||||||
"LabelTasks": "Tasks Running",
|
"LabelTasks": "Tasks Running",
|
||||||
|
"LabelTextEditorBulletedList": "Bulleted list",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numbered list",
|
||||||
|
"LabelTextEditorUnlink": "Unlink",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Theme",
|
||||||
"LabelThemeDark": "Dark",
|
"LabelThemeDark": "Dark",
|
||||||
"LabelThemeLight": "Light",
|
"LabelThemeLight": "Light",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Single-track",
|
"LabelTracksSingleTrack": "Single-track",
|
||||||
"LabelType": "Type",
|
"LabelType": "Type",
|
||||||
"LabelUnabridged": "Unabridged",
|
"LabelUnabridged": "Unabridged",
|
||||||
|
"LabelUndo": "Undo",
|
||||||
"LabelUnknown": "Unknown",
|
"LabelUnknown": "Unknown",
|
||||||
"LabelUpdateCover": "Update Cover",
|
"LabelUpdateCover": "Update Cover",
|
||||||
"LabelUpdateCoverHelp": "Allow overwriting of existing covers for the selected books when a match is located",
|
"LabelUpdateCoverHelp": "Allow overwriting of existing covers for the selected books when a match is located",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
|
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
|
||||||
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
|
||||||
"LabelUploaderDropFiles": "Drop files",
|
"LabelUploaderDropFiles": "Drop files",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||||
"LabelUseChapterTrack": "Use chapter track",
|
"LabelUseChapterTrack": "Use chapter track",
|
||||||
"LabelUseFullTrack": "Use full track",
|
"LabelUseFullTrack": "Use full track",
|
||||||
"LabelUser": "User",
|
"LabelUser": "User",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "View player queue",
|
"LabelViewQueue": "View player queue",
|
||||||
"LabelVolume": "Volume",
|
"LabelVolume": "Volume",
|
||||||
"LabelWeekdaysToRun": "Weekdays to run",
|
"LabelWeekdaysToRun": "Weekdays to run",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Your audiobook duration",
|
"LabelYourAudiobookDuration": "Your audiobook duration",
|
||||||
"LabelYourBookmarks": "Your Bookmarks",
|
"LabelYourBookmarks": "Your Bookmarks",
|
||||||
"LabelYourPlaylists": "Your Playlists",
|
"LabelYourPlaylists": "Your Playlists",
|
||||||
@@ -562,6 +601,7 @@
|
|||||||
"MessageConfirmRemoveCollection": "Are you sure you want to remove collection \"{0}\"?",
|
"MessageConfirmRemoveCollection": "Are you sure you want to remove collection \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on",
|
"MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on",
|
||||||
"MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.<br /><br />Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.<br /><br />All clients using your server will be automatically refreshed.",
|
"MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.<br /><br />Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.<br /><br />All clients using your server will be automatically refreshed.",
|
||||||
"MessageSearchResultsFor": "Search results for",
|
"MessageSearchResultsFor": "Search results for",
|
||||||
|
"MessageSelected": "{0} selected",
|
||||||
"MessageServerCouldNotBeReached": "Server could not be reached",
|
"MessageServerCouldNotBeReached": "Server could not be reached",
|
||||||
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
||||||
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "रूट user is the only user that can have an empty password",
|
"NoteChangeRootPassword": "रूट user is the only user that can have an empty password",
|
||||||
"NoteChapterEditorTimes": "Note: First chapter start time must remain at 0:00 and the last chapter start time cannot exceed this audiobooks duration.",
|
"NoteChapterEditorTimes": "Note: First chapter start time must remain at 0:00 and the last chapter start time cannot exceed this audiobooks duration.",
|
||||||
"NoteFolderPicker": "Note: folders already mapped will not be shown",
|
"NoteFolderPicker": "Note: folders already mapped will not be shown",
|
||||||
"NoteFolderPickerDebian": "Note: Folder picker for the debian install is not fully implemented. You should enter the path to your library directly.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Warning: Most podcast apps will require the RSS feed URL is using HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Warning: Most podcast apps will require the RSS feed URL is using HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.",
|
"NoteRSSFeedPodcastAppsPubDate": "Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
|
||||||
|
|||||||
+41
-1
@@ -32,6 +32,8 @@
|
|||||||
"ButtonHide": "Sakrij",
|
"ButtonHide": "Sakrij",
|
||||||
"ButtonHome": "Početna stranica",
|
"ButtonHome": "Početna stranica",
|
||||||
"ButtonIssues": "Problemi",
|
"ButtonIssues": "Problemi",
|
||||||
|
"ButtonJumpBackward": "Jump Backward",
|
||||||
|
"ButtonJumpForward": "Jump Forward",
|
||||||
"ButtonLatest": "Najnovije",
|
"ButtonLatest": "Najnovije",
|
||||||
"ButtonLibrary": "Biblioteka",
|
"ButtonLibrary": "Biblioteka",
|
||||||
"ButtonLogout": "Odjavi se",
|
"ButtonLogout": "Odjavi se",
|
||||||
@@ -41,12 +43,17 @@
|
|||||||
"ButtonMatchAllAuthors": "Matchaj sve autore",
|
"ButtonMatchAllAuthors": "Matchaj sve autore",
|
||||||
"ButtonMatchBooks": "Matchaj knjige",
|
"ButtonMatchBooks": "Matchaj knjige",
|
||||||
"ButtonNevermind": "Nije bitno",
|
"ButtonNevermind": "Nije bitno",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Next Chapter",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
"ButtonOpenFeed": "Otvori feed",
|
"ButtonOpenFeed": "Otvori feed",
|
||||||
"ButtonOpenManager": "Otvori menadžera",
|
"ButtonOpenManager": "Otvori menadžera",
|
||||||
|
"ButtonPause": "Pause",
|
||||||
"ButtonPlay": "Pokreni",
|
"ButtonPlay": "Pokreni",
|
||||||
"ButtonPlaying": "Playing",
|
"ButtonPlaying": "Playing",
|
||||||
"ButtonPlaylists": "Playlists",
|
"ButtonPlaylists": "Playlists",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Previous Chapter",
|
||||||
"ButtonPurgeAllCache": "Isprazni sav cache",
|
"ButtonPurgeAllCache": "Isprazni sav cache",
|
||||||
"ButtonPurgeItemsCache": "Isprazni Items Cache",
|
"ButtonPurgeItemsCache": "Isprazni Items Cache",
|
||||||
"ButtonPurgeMediaProgress": "Purge Media Progress",
|
"ButtonPurgeMediaProgress": "Purge Media Progress",
|
||||||
@@ -54,6 +61,7 @@
|
|||||||
"ButtonQueueRemoveItem": "Remove from queue",
|
"ButtonQueueRemoveItem": "Remove from queue",
|
||||||
"ButtonQuickMatch": "Brzi match",
|
"ButtonQuickMatch": "Brzi match",
|
||||||
"ButtonRead": "Pročitaj",
|
"ButtonRead": "Pročitaj",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
"ButtonRemove": "Ukloni",
|
"ButtonRemove": "Ukloni",
|
||||||
"ButtonRemoveAll": "Ukloni sve",
|
"ButtonRemoveAll": "Ukloni sve",
|
||||||
"ButtonRemoveAllLibraryItems": "Ukloni sve stvari iz biblioteke",
|
"ButtonRemoveAllLibraryItems": "Ukloni sve stvari iz biblioteke",
|
||||||
@@ -73,6 +81,7 @@
|
|||||||
"ButtonSelectFolderPath": "Odaberi putanju do folder",
|
"ButtonSelectFolderPath": "Odaberi putanju do folder",
|
||||||
"ButtonSeries": "Serije",
|
"ButtonSeries": "Serije",
|
||||||
"ButtonSetChaptersFromTracks": "Set chapters from tracks",
|
"ButtonSetChaptersFromTracks": "Set chapters from tracks",
|
||||||
|
"ButtonShare": "Share",
|
||||||
"ButtonShiftTimes": "Pomakni vremena",
|
"ButtonShiftTimes": "Pomakni vremena",
|
||||||
"ButtonShow": "Prikaži",
|
"ButtonShow": "Prikaži",
|
||||||
"ButtonStartM4BEncode": "Pokreni M4B kodiranje",
|
"ButtonStartM4BEncode": "Pokreni M4B kodiranje",
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"ButtonUserEdit": "Edit user {0}",
|
"ButtonUserEdit": "Edit user {0}",
|
||||||
"ButtonViewAll": "Prikaži sve",
|
"ButtonViewAll": "Prikaži sve",
|
||||||
"ButtonYes": "Da",
|
"ButtonYes": "Da",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Error fetching metadata",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Could not fetch metadata - try updating title and/or author",
|
||||||
|
"ErrorUploadLacksTitle": "Must have a title",
|
||||||
"HeaderAccount": "Korisnički račun",
|
"HeaderAccount": "Korisnički račun",
|
||||||
"HeaderAdvanced": "Napredno",
|
"HeaderAdvanced": "Napredno",
|
||||||
"HeaderAppriseNotificationSettings": "Apprise Notification Settings",
|
"HeaderAppriseNotificationSettings": "Apprise Notification Settings",
|
||||||
@@ -101,6 +113,7 @@
|
|||||||
"HeaderCollectionItems": "Stvari u kolekciji",
|
"HeaderCollectionItems": "Stvari u kolekciji",
|
||||||
"HeaderCover": "Cover",
|
"HeaderCover": "Cover",
|
||||||
"HeaderCurrentDownloads": "Current Downloads",
|
"HeaderCurrentDownloads": "Current Downloads",
|
||||||
|
"HeaderCustomMetadataProviders": "Custom Metadata Providers",
|
||||||
"HeaderDetails": "Detalji",
|
"HeaderDetails": "Detalji",
|
||||||
"HeaderDownloadQueue": "Download Queue",
|
"HeaderDownloadQueue": "Download Queue",
|
||||||
"HeaderEbookFiles": "Ebook Files",
|
"HeaderEbookFiles": "Ebook Files",
|
||||||
@@ -171,6 +184,7 @@
|
|||||||
"HeaderUpdateDetails": "Aktualiziraj detalje",
|
"HeaderUpdateDetails": "Aktualiziraj detalje",
|
||||||
"HeaderUpdateLibrary": "Aktualiziraj biblioteku",
|
"HeaderUpdateLibrary": "Aktualiziraj biblioteku",
|
||||||
"HeaderUsers": "Korinici",
|
"HeaderUsers": "Korinici",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
"HeaderYourStats": "Tvoja statistika",
|
"HeaderYourStats": "Tvoja statistika",
|
||||||
"LabelAbridged": "Abridged",
|
"LabelAbridged": "Abridged",
|
||||||
"LabelAccountType": "Vrsta korisničkog računa",
|
"LabelAccountType": "Vrsta korisničkog računa",
|
||||||
@@ -196,6 +210,8 @@
|
|||||||
"LabelAuthorLastFirst": "Author (Last, First)",
|
"LabelAuthorLastFirst": "Author (Last, First)",
|
||||||
"LabelAuthors": "Autori",
|
"LabelAuthors": "Autori",
|
||||||
"LabelAutoDownloadEpisodes": "Automatski preuzmi epizode",
|
"LabelAutoDownloadEpisodes": "Automatski preuzmi epizode",
|
||||||
|
"LabelAutoFetchMetadata": "Auto Fetch Metadata",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Fetches metadata for title, author, and series to streamline uploading. Additional metadata may have to be matched after upload.",
|
||||||
"LabelAutoLaunch": "Auto Launch",
|
"LabelAutoLaunch": "Auto Launch",
|
||||||
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
"LabelAutoLaunchDescription": "Redirect to the auth provider automatically when navigating to the login page (manual override path <code>/login?autoLaunch=0</code>)",
|
||||||
"LabelAutoRegister": "Auto Register",
|
"LabelAutoRegister": "Auto Register",
|
||||||
@@ -266,6 +282,7 @@
|
|||||||
"LabelExample": "Example",
|
"LabelExample": "Example",
|
||||||
"LabelExplicit": "Explicit",
|
"LabelExplicit": "Explicit",
|
||||||
"LabelFeedURL": "Feed URL",
|
"LabelFeedURL": "Feed URL",
|
||||||
|
"LabelFetchingMetadata": "Fetching Metadata",
|
||||||
"LabelFile": "Datoteka",
|
"LabelFile": "Datoteka",
|
||||||
"LabelFileBirthtime": "File Birthtime",
|
"LabelFileBirthtime": "File Birthtime",
|
||||||
"LabelFileModified": "File Modified",
|
"LabelFileModified": "File Modified",
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folderi",
|
"LabelFolders": "Folderi",
|
||||||
|
"LabelFontBold": "Bold",
|
||||||
"LabelFontFamily": "Font family",
|
"LabelFontFamily": "Font family",
|
||||||
|
"LabelFontItalic": "Italic",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
|
"LabelFontStrikethrough": "Strikethrough",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Žanrovi",
|
"LabelGenres": "Žanrovi",
|
||||||
@@ -336,7 +356,11 @@
|
|||||||
"LabelMetaTags": "Meta Tags",
|
"LabelMetaTags": "Meta Tags",
|
||||||
"LabelMinute": "Minuta",
|
"LabelMinute": "Minuta",
|
||||||
"LabelMissing": "Nedostaje",
|
"LabelMissing": "Nedostaje",
|
||||||
|
"LabelMissingEbook": "Has no ebook",
|
||||||
"LabelMissingParts": "Nedostajali dijelovi",
|
"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.",
|
||||||
"LabelMore": "Više",
|
"LabelMore": "Više",
|
||||||
"LabelMoreInfo": "More Info",
|
"LabelMoreInfo": "More Info",
|
||||||
"LabelName": "Ime",
|
"LabelName": "Ime",
|
||||||
@@ -374,11 +398,13 @@
|
|||||||
"LabelPermissionsDownload": "Smije preuzimati",
|
"LabelPermissionsDownload": "Smije preuzimati",
|
||||||
"LabelPermissionsUpdate": "Smije aktualizirati",
|
"LabelPermissionsUpdate": "Smije aktualizirati",
|
||||||
"LabelPermissionsUpload": "Smije uploadati",
|
"LabelPermissionsUpload": "Smije uploadati",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
"LabelPhotoPathURL": "Slika putanja/URL",
|
"LabelPhotoPathURL": "Slika putanja/URL",
|
||||||
"LabelPlaylists": "Playlists",
|
"LabelPlaylists": "Playlists",
|
||||||
"LabelPlayMethod": "Vrsta reprodukcije",
|
"LabelPlayMethod": "Vrsta reprodukcije",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
|
"LabelPodcastSearchRegion": "Područje pretrage podcasta",
|
||||||
"LabelPodcastType": "Podcast Type",
|
"LabelPodcastType": "Podcast Type",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Port",
|
||||||
"LabelPrefixesToIgnore": "Prefiksi za ignorirati (mala i velika slova nisu bitna)",
|
"LabelPrefixesToIgnore": "Prefiksi za ignorirati (mala i velika slova nisu bitna)",
|
||||||
@@ -395,9 +421,11 @@
|
|||||||
"LabelRecentlyAdded": "Nedavno dodano",
|
"LabelRecentlyAdded": "Nedavno dodano",
|
||||||
"LabelRecentSeries": "Nedavne serije",
|
"LabelRecentSeries": "Nedavne serije",
|
||||||
"LabelRecommended": "Recommended",
|
"LabelRecommended": "Recommended",
|
||||||
|
"LabelRedo": "Redo",
|
||||||
"LabelRegion": "Regija",
|
"LabelRegion": "Regija",
|
||||||
"LabelReleaseDate": "Datum izlaska",
|
"LabelReleaseDate": "Datum izlaska",
|
||||||
"LabelRemoveCover": "Remove cover",
|
"LabelRemoveCover": "Remove cover",
|
||||||
|
"LabelRowsPerPage": "Rows per page",
|
||||||
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
|
||||||
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
"LabelRSSFeedCustomOwnerName": "Custom owner Name",
|
||||||
"LabelRSSFeedOpen": "RSS Feed Open",
|
"LabelRSSFeedOpen": "RSS Feed Open",
|
||||||
@@ -416,6 +444,7 @@
|
|||||||
"LabelSeries": "Serije",
|
"LabelSeries": "Serije",
|
||||||
"LabelSeriesName": "Ime serije",
|
"LabelSeriesName": "Ime serije",
|
||||||
"LabelSeriesProgress": "Series Progress",
|
"LabelSeriesProgress": "Series Progress",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
"LabelSetEbookAsPrimary": "Set as primary",
|
"LabelSetEbookAsPrimary": "Set as primary",
|
||||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
||||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
||||||
@@ -437,6 +466,8 @@
|
|||||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Koristi bookshelf pogled za početnu stranicu",
|
"LabelSettingsHomePageBookshelfView": "Koristi bookshelf pogled za početnu stranicu",
|
||||||
"LabelSettingsLibraryBookshelfView": "Koristi bookshelf pogled za biblioteku",
|
"LabelSettingsLibraryBookshelfView": "Koristi bookshelf pogled za biblioteku",
|
||||||
|
"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": "Parsaj podnapise",
|
"LabelSettingsParseSubtitles": "Parsaj podnapise",
|
||||||
"LabelSettingsParseSubtitlesHelp": "Izvadi podnapise iz imena od audiobook foldera.<br>Podnapis mora biti odvojen sa \" - \"<br>npr. \"Ime knjige - Podnapis ovdje\" ima podnapis \"Podnapis ovdje\"",
|
"LabelSettingsParseSubtitlesHelp": "Izvadi podnapise iz imena od audiobook foldera.<br>Podnapis mora biti odvojen sa \" - \"<br>npr. \"Ime knjige - Podnapis ovdje\" ima podnapis \"Podnapis ovdje\"",
|
||||||
"LabelSettingsPreferMatchedMetadata": "Preferiraj matchane metapodatke",
|
"LabelSettingsPreferMatchedMetadata": "Preferiraj matchane metapodatke",
|
||||||
@@ -482,6 +513,10 @@
|
|||||||
"LabelTagsAccessibleToUser": "Tags dostupni korisniku",
|
"LabelTagsAccessibleToUser": "Tags dostupni korisniku",
|
||||||
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
"LabelTagsNotAccessibleToUser": "Tags not Accessible to User",
|
||||||
"LabelTasks": "Tasks Running",
|
"LabelTasks": "Tasks Running",
|
||||||
|
"LabelTextEditorBulletedList": "Bulleted list",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Numbered list",
|
||||||
|
"LabelTextEditorUnlink": "Unlink",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Theme",
|
||||||
"LabelThemeDark": "Dark",
|
"LabelThemeDark": "Dark",
|
||||||
"LabelThemeLight": "Light",
|
"LabelThemeLight": "Light",
|
||||||
@@ -507,6 +542,7 @@
|
|||||||
"LabelTracksSingleTrack": "Single-track",
|
"LabelTracksSingleTrack": "Single-track",
|
||||||
"LabelType": "Tip",
|
"LabelType": "Tip",
|
||||||
"LabelUnabridged": "Unabridged",
|
"LabelUnabridged": "Unabridged",
|
||||||
|
"LabelUndo": "Undo",
|
||||||
"LabelUnknown": "Nepoznato",
|
"LabelUnknown": "Nepoznato",
|
||||||
"LabelUpdateCover": "Aktualiziraj Cover",
|
"LabelUpdateCover": "Aktualiziraj Cover",
|
||||||
"LabelUpdateCoverHelp": "Dozvoli postavljanje novog covera za odabrane knjige nakon što je match pronađen.",
|
"LabelUpdateCoverHelp": "Dozvoli postavljanje novog covera za odabrane knjige nakon što je match pronađen.",
|
||||||
@@ -515,6 +551,7 @@
|
|||||||
"LabelUpdateDetailsHelp": "Dozvoli postavljanje novih detalja za odabrane knjige nakon što je match pronađen",
|
"LabelUpdateDetailsHelp": "Dozvoli postavljanje novih detalja za odabrane knjige nakon što je match pronađen",
|
||||||
"LabelUploaderDragAndDrop": "Drag & Drop datoteke ili foldere",
|
"LabelUploaderDragAndDrop": "Drag & Drop datoteke ili foldere",
|
||||||
"LabelUploaderDropFiles": "Ubaci datoteke",
|
"LabelUploaderDropFiles": "Ubaci datoteke",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||||
"LabelUseChapterTrack": "Koristi poglavlja track",
|
"LabelUseChapterTrack": "Koristi poglavlja track",
|
||||||
"LabelUseFullTrack": "Koristi cijeli track",
|
"LabelUseFullTrack": "Koristi cijeli track",
|
||||||
"LabelUser": "Korisnik",
|
"LabelUser": "Korisnik",
|
||||||
@@ -526,6 +563,8 @@
|
|||||||
"LabelViewQueue": "View player queue",
|
"LabelViewQueue": "View player queue",
|
||||||
"LabelVolume": "Volume",
|
"LabelVolume": "Volume",
|
||||||
"LabelWeekdaysToRun": "Radnih dana da radi",
|
"LabelWeekdaysToRun": "Radnih dana da radi",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
"LabelYourAudiobookDuration": "Tvoje trajanje audiobooka",
|
"LabelYourAudiobookDuration": "Tvoje trajanje audiobooka",
|
||||||
"LabelYourBookmarks": "Tvoje knjižne oznake",
|
"LabelYourBookmarks": "Tvoje knjižne oznake",
|
||||||
"LabelYourPlaylists": "Your Playlists",
|
"LabelYourPlaylists": "Your Playlists",
|
||||||
@@ -562,6 +601,7 @@
|
|||||||
"MessageConfirmRemoveCollection": "AJeste li sigurni da želite obrisati kolekciju \"{0}\"?",
|
"MessageConfirmRemoveCollection": "AJeste li sigurni da želite obrisati kolekciju \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisode": "Jeste li sigurni da želite obrisati epizodu \"{0}\"?",
|
"MessageConfirmRemoveEpisode": "Jeste li sigurni da želite obrisati epizodu \"{0}\"?",
|
||||||
"MessageConfirmRemoveEpisodes": "Jeste li sigurni da želite obrisati {0} epizoda/-u?",
|
"MessageConfirmRemoveEpisodes": "Jeste li sigurni da želite obrisati {0} epizoda/-u?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
|
||||||
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{0}\"?",
|
||||||
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
"MessageConfirmRemovePlaylist": "Are you sure you want to remove your playlist \"{0}\"?",
|
||||||
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
"MessageConfirmRenameGenre": "Are you sure you want to rename genre \"{0}\" to \"{1}\" for all items?",
|
||||||
@@ -641,6 +681,7 @@
|
|||||||
"MessageRestoreBackupConfirm": "Jeste li sigurni da želite povratiti backup kreiran",
|
"MessageRestoreBackupConfirm": "Jeste li sigurni da želite povratiti backup kreiran",
|
||||||
"MessageRestoreBackupWarning": "Povračanje backupa će zamijeniti postoječu bazu podataka u /config i slike covera u /metadata/items i /metadata/authors.<br /><br />Backups ne modificiraju nikakve datoteke u folderu od biblioteke. Ako imate uključene server postavke da spremate cover i metapodtake u folderu od biblioteke, onda oni neće biti backupani ili overwritten.<br /><br />Svi klijenti koji koriste tvoj server će biti automatski osvježeni.",
|
"MessageRestoreBackupWarning": "Povračanje backupa će zamijeniti postoječu bazu podataka u /config i slike covera u /metadata/items i /metadata/authors.<br /><br />Backups ne modificiraju nikakve datoteke u folderu od biblioteke. Ako imate uključene server postavke da spremate cover i metapodtake u folderu od biblioteke, onda oni neće biti backupani ili overwritten.<br /><br />Svi klijenti koji koriste tvoj server će biti automatski osvježeni.",
|
||||||
"MessageSearchResultsFor": "Traži rezultate za",
|
"MessageSearchResultsFor": "Traži rezultate za",
|
||||||
|
"MessageSelected": "{0} selected",
|
||||||
"MessageServerCouldNotBeReached": "Server ne može biti kontaktiran",
|
"MessageServerCouldNotBeReached": "Server ne može biti kontaktiran",
|
||||||
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
"MessageSetChaptersFromTracksDescription": "Set chapters using each audio file as a chapter and chapter title as the audio file name",
|
||||||
"MessageStartPlaybackAtTime": "Pokreni reprodukciju za \"{0}\" na {1}?",
|
"MessageStartPlaybackAtTime": "Pokreni reprodukciju za \"{0}\" na {1}?",
|
||||||
@@ -656,7 +697,6 @@
|
|||||||
"NoteChangeRootPassword": "Root korisnik je jedini korisnik koji može imati praznu lozinku",
|
"NoteChangeRootPassword": "Root korisnik je jedini korisnik koji može imati praznu lozinku",
|
||||||
"NoteChapterEditorTimes": "Bilješka: Prvo početno vrijeme poglavlja mora ostati na 0:00 i posljednje vrijeme poglavlja ne smije preći vrijeme trajanja ove audio knjige.",
|
"NoteChapterEditorTimes": "Bilješka: Prvo početno vrijeme poglavlja mora ostati na 0:00 i posljednje vrijeme poglavlja ne smije preći vrijeme trajanja ove audio knjige.",
|
||||||
"NoteFolderPicker": "Bilješka: več mapirani folderi neće biti prikazani",
|
"NoteFolderPicker": "Bilješka: več mapirani folderi neće biti prikazani",
|
||||||
"NoteFolderPickerDebian": "Bilješka: Folder picker za debian instalaciju nije potpuno implementiran. Trebate unjeti direktnu putanju do biblioteke.",
|
|
||||||
"NoteRSSFeedPodcastAppsHttps": "Upozorenje: Večina podcasta će trebati RSS feed URL koji koristi HTTPS",
|
"NoteRSSFeedPodcastAppsHttps": "Upozorenje: Večina podcasta će trebati RSS feed URL koji koristi HTTPS",
|
||||||
"NoteRSSFeedPodcastAppsPubDate": "Upozorenje: 1 ili više vaših epizoda nemaju datum objavljivanja. Neke podcast aplikacije zahtjevaju to.",
|
"NoteRSSFeedPodcastAppsPubDate": "Upozorenje: 1 ili više vaših epizoda nemaju datum objavljivanja. Neke podcast aplikacije zahtjevaju to.",
|
||||||
"NoteUploaderFoldersWithMediaFiles": "Folderi sa media datotekama će biti tretirane kao odvojene stavke u biblioteki.",
|
"NoteUploaderFoldersWithMediaFiles": "Folderi sa media datotekama će biti tretirane kao odvojene stavke u biblioteki.",
|
||||||
|
|||||||
@@ -0,0 +1,781 @@
|
|||||||
|
{
|
||||||
|
"ButtonAdd": "Hozzáadás",
|
||||||
|
"ButtonAddChapters": "Fejezetek hozzáadása",
|
||||||
|
"ButtonAddDevice": "Eszköz hozzáadása",
|
||||||
|
"ButtonAddLibrary": "Könyvtár hozzáadása",
|
||||||
|
"ButtonAddPodcasts": "Podcastok hozzáadása",
|
||||||
|
"ButtonAddUser": "Felhasználó hozzáadása",
|
||||||
|
"ButtonAddYourFirstLibrary": "Az első könyvtár hozzáadása",
|
||||||
|
"ButtonApply": "Alkalmaz",
|
||||||
|
"ButtonApplyChapters": "Fejezetek alkalmazása",
|
||||||
|
"ButtonAuthors": "Szerzők",
|
||||||
|
"ButtonBrowseForFolder": "Mappa keresése",
|
||||||
|
"ButtonCancel": "Mégse",
|
||||||
|
"ButtonCancelEncode": "Kódolás megszakítása",
|
||||||
|
"ButtonChangeRootPassword": "Gyökérjelszó megváltoztatása",
|
||||||
|
"ButtonCheckAndDownloadNewEpisodes": "Új epizódok ellenőrzése és letöltése",
|
||||||
|
"ButtonChooseAFolder": "Válassz egy mappát",
|
||||||
|
"ButtonChooseFiles": "Fájlok kiválasztása",
|
||||||
|
"ButtonClearFilter": "Szűrő törlése",
|
||||||
|
"ButtonCloseFeed": "Hírcsatorna bezárása",
|
||||||
|
"ButtonCollections": "Gyűjtemény",
|
||||||
|
"ButtonConfigureScanner": "Szkenner konfigurálása",
|
||||||
|
"ButtonCreate": "Létrehozás",
|
||||||
|
"ButtonCreateBackup": "Biztonsági másolat készítése",
|
||||||
|
"ButtonDelete": "Törlés",
|
||||||
|
"ButtonDownloadQueue": "Sor",
|
||||||
|
"ButtonEdit": "Szerkesztés",
|
||||||
|
"ButtonEditChapters": "Fejezetek szerkesztése",
|
||||||
|
"ButtonEditPodcast": "Podcast szerkesztése",
|
||||||
|
"ButtonForceReScan": "Újraszkennelés kényszerítése",
|
||||||
|
"ButtonFullPath": "Teljes útvonal",
|
||||||
|
"ButtonHide": "Elrejtés",
|
||||||
|
"ButtonHome": "Kezdőlap",
|
||||||
|
"ButtonIssues": "Problémák",
|
||||||
|
"ButtonJumpBackward": "Ugrás vissza",
|
||||||
|
"ButtonJumpForward": "Ugrás előre",
|
||||||
|
"ButtonLatest": "Legújabb",
|
||||||
|
"ButtonLibrary": "Könyvtár",
|
||||||
|
"ButtonLogout": "Kijelentkezés",
|
||||||
|
"ButtonLookup": "Keresés",
|
||||||
|
"ButtonManageTracks": "Sávok kezelése",
|
||||||
|
"ButtonMapChapterTitles": "Fejezetcímek hozzárendelése",
|
||||||
|
"ButtonMatchAllAuthors": "Minden szerző egyeztetése",
|
||||||
|
"ButtonMatchBooks": "Könyvek egyeztetése",
|
||||||
|
"ButtonNevermind": "Mindegy",
|
||||||
|
"ButtonNext": "Next",
|
||||||
|
"ButtonNextChapter": "Következő fejezet",
|
||||||
|
"ButtonOk": "Oké",
|
||||||
|
"ButtonOpenFeed": "Hírcsatorna megnyitása",
|
||||||
|
"ButtonOpenManager": "Kezelő megnyitása",
|
||||||
|
"ButtonPause": "Szünet",
|
||||||
|
"ButtonPlay": "Lejátszás",
|
||||||
|
"ButtonPlaying": "Lejátszás folyamatban",
|
||||||
|
"ButtonPlaylists": "Lejátszási listák",
|
||||||
|
"ButtonPrevious": "Previous",
|
||||||
|
"ButtonPreviousChapter": "Előző fejezet",
|
||||||
|
"ButtonPurgeAllCache": "Összes gyorsítótár törlése",
|
||||||
|
"ButtonPurgeItemsCache": "Elemek gyorsítótárának törlése",
|
||||||
|
"ButtonPurgeMediaProgress": "Médialejátszás állapotának törlése",
|
||||||
|
"ButtonQueueAddItem": "Hozzáadás a sorhoz",
|
||||||
|
"ButtonQueueRemoveItem": "Eltávolítás a sorból",
|
||||||
|
"ButtonQuickMatch": "Gyors egyeztetés",
|
||||||
|
"ButtonRead": "Olvasás",
|
||||||
|
"ButtonRefresh": "Refresh",
|
||||||
|
"ButtonRemove": "Eltávolítás",
|
||||||
|
"ButtonRemoveAll": "Összes eltávolítása",
|
||||||
|
"ButtonRemoveAllLibraryItems": "Összes könyvtárelem eltávolítása",
|
||||||
|
"ButtonRemoveFromContinueListening": "Eltávolítás a Folytatás hallgatásából",
|
||||||
|
"ButtonRemoveFromContinueReading": "Eltávolítás a Folytatás olvasásából",
|
||||||
|
"ButtonRemoveSeriesFromContinueSeries": "Sorozat eltávolítása a Folytatás sorozatokból",
|
||||||
|
"ButtonReScan": "Újraszkennelés",
|
||||||
|
"ButtonReset": "Visszaállítás",
|
||||||
|
"ButtonResetToDefault": "Alapértelmezésre állítás",
|
||||||
|
"ButtonRestore": "Visszaállítás",
|
||||||
|
"ButtonSave": "Mentés",
|
||||||
|
"ButtonSaveAndClose": "Mentés és bezárás",
|
||||||
|
"ButtonSaveTracklist": "Sávlista mentése",
|
||||||
|
"ButtonScan": "Szkennelés",
|
||||||
|
"ButtonScanLibrary": "Könyvtár szkennelése",
|
||||||
|
"ButtonSearch": "Keresés",
|
||||||
|
"ButtonSelectFolderPath": "Mappa útvonalának kiválasztása",
|
||||||
|
"ButtonSeries": "Sorozatok",
|
||||||
|
"ButtonSetChaptersFromTracks": "Fejezetek beállítása sávokból",
|
||||||
|
"ButtonShare": "Share",
|
||||||
|
"ButtonShiftTimes": "Idők eltolása",
|
||||||
|
"ButtonShow": "Megjelenítés",
|
||||||
|
"ButtonStartM4BEncode": "M4B kódolás indítása",
|
||||||
|
"ButtonStartMetadataEmbed": "Metaadatok beágyazásának indítása",
|
||||||
|
"ButtonSubmit": "Beküldés",
|
||||||
|
"ButtonTest": "Teszt",
|
||||||
|
"ButtonUpload": "Feltöltés",
|
||||||
|
"ButtonUploadBackup": "Biztonsági másolat feltöltése",
|
||||||
|
"ButtonUploadCover": "Borító feltöltése",
|
||||||
|
"ButtonUploadOPMLFile": "OPML fájl feltöltése",
|
||||||
|
"ButtonUserDelete": "Felhasználó törlése {0}",
|
||||||
|
"ButtonUserEdit": "Felhasználó szerkesztése {0}",
|
||||||
|
"ButtonViewAll": "Összes megtekintése",
|
||||||
|
"ButtonYes": "Igen",
|
||||||
|
"ErrorUploadFetchMetadataAPI": "Hiba a metaadatok lekérésekor",
|
||||||
|
"ErrorUploadFetchMetadataNoResults": "Nem sikerült a metaadatok lekérése - próbálja meg frissíteni a címet és/vagy a szerzőt",
|
||||||
|
"ErrorUploadLacksTitle": "Cím szükséges",
|
||||||
|
"HeaderAccount": "Fiók",
|
||||||
|
"HeaderAdvanced": "Haladó",
|
||||||
|
"HeaderAppriseNotificationSettings": "Apprise értesítési beállítások",
|
||||||
|
"HeaderAudiobookTools": "Hangoskönyv fájlkezelő eszközök",
|
||||||
|
"HeaderAudioTracks": "Audiósávok",
|
||||||
|
"HeaderAuthentication": "Hitelesítés",
|
||||||
|
"HeaderBackups": "Biztonsági másolatok",
|
||||||
|
"HeaderChangePassword": "Jelszó megváltoztatása",
|
||||||
|
"HeaderChapters": "Fejezetek",
|
||||||
|
"HeaderChooseAFolder": "Válasszon egy mappát",
|
||||||
|
"HeaderCollection": "Gyűjtemény",
|
||||||
|
"HeaderCollectionItems": "Gyűjtemény elemek",
|
||||||
|
"HeaderCover": "Borító",
|
||||||
|
"HeaderCurrentDownloads": "Jelenlegi letöltések",
|
||||||
|
"HeaderCustomMetadataProviders": "Egyéni metaadat-szolgáltatók",
|
||||||
|
"HeaderDetails": "Részletek",
|
||||||
|
"HeaderDownloadQueue": "Letöltési sor",
|
||||||
|
"HeaderEbookFiles": "E-könyv fájlok",
|
||||||
|
"HeaderEmail": "E-mail",
|
||||||
|
"HeaderEmailSettings": "E-mail beállítások",
|
||||||
|
"HeaderEpisodes": "Epizódok",
|
||||||
|
"HeaderEreaderDevices": "E-olvasó eszközök",
|
||||||
|
"HeaderEreaderSettings": "E-olvasó beállítások",
|
||||||
|
"HeaderFiles": "Fájlok",
|
||||||
|
"HeaderFindChapters": "Fejezetek keresése",
|
||||||
|
"HeaderIgnoredFiles": "Figyelmen kívül hagyott fájlok",
|
||||||
|
"HeaderItemFiles": "Elemfájlok",
|
||||||
|
"HeaderItemMetadataUtils": "Elem metaadat eszközök",
|
||||||
|
"HeaderLastListeningSession": "Utolsó hallgatási munkamenet",
|
||||||
|
"HeaderLatestEpisodes": "Legújabb epizódok",
|
||||||
|
"HeaderLibraries": "Könyvtárak",
|
||||||
|
"HeaderLibraryFiles": "Könyvtárfájlok",
|
||||||
|
"HeaderLibraryStats": "Könyvtár statisztikák",
|
||||||
|
"HeaderListeningSessions": "Hallgatási munkamenetek",
|
||||||
|
"HeaderListeningStats": "Hallgatási statisztikák",
|
||||||
|
"HeaderLogin": "Bejelentkezés",
|
||||||
|
"HeaderLogs": "Naplók",
|
||||||
|
"HeaderManageGenres": "Műfajok kezelése",
|
||||||
|
"HeaderManageTags": "Címkék kezelése",
|
||||||
|
"HeaderMapDetails": "Részletek hozzárendelése",
|
||||||
|
"HeaderMatch": "Egyeztetés",
|
||||||
|
"HeaderMetadataOrderOfPrecedence": "Metaadatok előnyben részesítési sorrendje",
|
||||||
|
"HeaderMetadataToEmbed": "Beágyazandó metaadatok",
|
||||||
|
"HeaderNewAccount": "Új fiók",
|
||||||
|
"HeaderNewLibrary": "Új könyvtár",
|
||||||
|
"HeaderNotifications": "Értesítések",
|
||||||
|
"HeaderOpenIDConnectAuthentication": "OpenID Connect hitelesítés",
|
||||||
|
"HeaderOpenRSSFeed": "RSS hírcsatorna megnyitása",
|
||||||
|
"HeaderOtherFiles": "Egyéb fájlok",
|
||||||
|
"HeaderPasswordAuthentication": "Jelszó hitelesítés",
|
||||||
|
"HeaderPermissions": "Engedélyek",
|
||||||
|
"HeaderPlayerQueue": "Lejátszó sor",
|
||||||
|
"HeaderPlaylist": "Lejátszási lista",
|
||||||
|
"HeaderPlaylistItems": "Lejátszási lista elemek",
|
||||||
|
"HeaderPodcastsToAdd": "Hozzáadandó podcastok",
|
||||||
|
"HeaderPreviewCover": "Borító előnézete",
|
||||||
|
"HeaderRemoveEpisode": "Epizód eltávolítása",
|
||||||
|
"HeaderRemoveEpisodes": "{0} epizód eltávolítása",
|
||||||
|
"HeaderRSSFeedGeneral": "RSS részletek",
|
||||||
|
"HeaderRSSFeedIsOpen": "RSS hírcsatorna nyitva",
|
||||||
|
"HeaderRSSFeeds": "RSS hírcsatornák",
|
||||||
|
"HeaderSavedMediaProgress": "Mentett médialejátszási állapot",
|
||||||
|
"HeaderSchedule": "Ütemezés",
|
||||||
|
"HeaderScheduleLibraryScans": "Könyvtárak automatikus szkennelésének ütemezése",
|
||||||
|
"HeaderSession": "Munkamenet",
|
||||||
|
"HeaderSetBackupSchedule": "Biztonsági másolatok ütemezésének beállítása",
|
||||||
|
"HeaderSettings": "Beállítások",
|
||||||
|
"HeaderSettingsDisplay": "Kijelző",
|
||||||
|
"HeaderSettingsExperimental": "Kísérleti funkciók",
|
||||||
|
"HeaderSettingsGeneral": "Általános",
|
||||||
|
"HeaderSettingsScanner": "Szkenner",
|
||||||
|
"HeaderSleepTimer": "Alvásidőzítő",
|
||||||
|
"HeaderStatsLargestItems": "Legnagyobb elemek",
|
||||||
|
"HeaderStatsLongestItems": "Leghosszabb elemek (órákban)",
|
||||||
|
"HeaderStatsMinutesListeningChart": "Hallgatási percek (az utolsó 7 napban)",
|
||||||
|
"HeaderStatsRecentSessions": "Legutóbbi munkamenetek",
|
||||||
|
"HeaderStatsTop10Authors": "Top 10 szerzők",
|
||||||
|
"HeaderStatsTop5Genres": "Top 5 műfajok",
|
||||||
|
"HeaderTableOfContents": "Tartalomjegyzék",
|
||||||
|
"HeaderTools": "Eszközök",
|
||||||
|
"HeaderUpdateAccount": "Fiók frissítése",
|
||||||
|
"HeaderUpdateAuthor": "Szerző frissítése",
|
||||||
|
"HeaderUpdateDetails": "Részletek frissítése",
|
||||||
|
"HeaderUpdateLibrary": "Könyvtár frissítése",
|
||||||
|
"HeaderUsers": "Felhasználók",
|
||||||
|
"HeaderYearReview": "Year {0} in Review",
|
||||||
|
"HeaderYourStats": "Saját statisztikák",
|
||||||
|
"LabelAbridged": "Tömörített",
|
||||||
|
"LabelAccountType": "Fióktípus",
|
||||||
|
"LabelAccountTypeAdmin": "Admin",
|
||||||
|
"LabelAccountTypeGuest": "Vendég",
|
||||||
|
"LabelAccountTypeUser": "Felhasználó",
|
||||||
|
"LabelActivity": "Tevékenység",
|
||||||
|
"LabelAdded": "Hozzáadva",
|
||||||
|
"LabelAddedAt": "Hozzáadás ideje",
|
||||||
|
"LabelAddToCollection": "Hozzáadás a gyűjteményhez",
|
||||||
|
"LabelAddToCollectionBatch": "{0} könyv hozzáadása a gyűjteményhez",
|
||||||
|
"LabelAddToPlaylist": "Hozzáadás a lejátszási listához",
|
||||||
|
"LabelAddToPlaylistBatch": "{0} elem hozzáadása a lejátszási listához",
|
||||||
|
"LabelAdminUsersOnly": "Csak admin felhasználók",
|
||||||
|
"LabelAll": "Minden",
|
||||||
|
"LabelAllUsers": "Minden felhasználó",
|
||||||
|
"LabelAllUsersExcludingGuests": "Minden felhasználó, vendégek kivételével",
|
||||||
|
"LabelAllUsersIncludingGuests": "Minden felhasználó, beleértve a vendégeket is",
|
||||||
|
"LabelAlreadyInYourLibrary": "Már a könyvtárában van",
|
||||||
|
"LabelAppend": "Hozzáfűzés",
|
||||||
|
"LabelAuthor": "Szerző",
|
||||||
|
"LabelAuthorFirstLast": "Szerző (Keresztnév Vezetéknév)",
|
||||||
|
"LabelAuthorLastFirst": "Szerző (Vezetéknév, Keresztnév)",
|
||||||
|
"LabelAuthors": "Szerzők",
|
||||||
|
"LabelAutoDownloadEpisodes": "Epizódok automatikus letöltése",
|
||||||
|
"LabelAutoFetchMetadata": "Metaadatok automatikus lekérése",
|
||||||
|
"LabelAutoFetchMetadataHelp": "Cím, szerző és sorozat metaadatok automatikus lekérése a feltöltés megkönnyítése érdekében. További metaadatok egyeztetése szükséges lehet a feltöltés után.",
|
||||||
|
"LabelAutoLaunch": "Automatikus indítás",
|
||||||
|
"LabelAutoLaunchDescription": "Automatikus átirányítás az hitelesítő szolgáltatóhoz a bejelentkezési oldalra navigáláskor (kézi felülbírálás útvonala <code>/login?autoLaunch=0</code>)",
|
||||||
|
"LabelAutoRegister": "Automatikus regisztráció",
|
||||||
|
"LabelAutoRegisterDescription": "Új felhasználók automatikus létrehozása bejelentkezés után",
|
||||||
|
"LabelBackToUser": "Vissza a felhasználóhoz",
|
||||||
|
"LabelBackupLocation": "Biztonsági másolat helye",
|
||||||
|
"LabelBackupsEnableAutomaticBackups": "Automatikus biztonsági másolatok engedélyezése",
|
||||||
|
"LabelBackupsEnableAutomaticBackupsHelp": "Biztonsági másolatok mentése a /metadata/backups mappába",
|
||||||
|
"LabelBackupsMaxBackupSize": "Maximális biztonsági másolat méret (GB-ban)",
|
||||||
|
"LabelBackupsMaxBackupSizeHelp": "A rossz konfiguráció elleni védelem érdekében a biztonsági másolatok meghiúsulnak, ha meghaladják a beállított méretet.",
|
||||||
|
"LabelBackupsNumberToKeep": "Megtartandó biztonsági másolatok száma",
|
||||||
|
"LabelBackupsNumberToKeepHelp": "Egyszerre csak 1 biztonsági másolat kerül eltávolításra, tehát ha már több biztonsági másolat van, mint ez a szám, akkor manuálisan kell eltávolítani őket.",
|
||||||
|
"LabelBitrate": "Bitráta",
|
||||||
|
"LabelBooks": "Könyvek",
|
||||||
|
"LabelButtonText": "Gomb szövege",
|
||||||
|
"LabelChangePassword": "Jelszó megváltoztatása",
|
||||||
|
"LabelChannels": "Csatornák",
|
||||||
|
"LabelChapters": "Fejezetek",
|
||||||
|
"LabelChaptersFound": "fejezet található",
|
||||||
|
"LabelChapterTitle": "Fejezet címe",
|
||||||
|
"LabelClickForMoreInfo": "További információkért kattintson",
|
||||||
|
"LabelClosePlayer": "Lejátszó bezárása",
|
||||||
|
"LabelCodec": "Kodek",
|
||||||
|
"LabelCollapseSeries": "Sorozat összecsukása",
|
||||||
|
"LabelCollection": "Gyűjtemény",
|
||||||
|
"LabelCollections": "Gyűjtemények",
|
||||||
|
"LabelComplete": "Teljes",
|
||||||
|
"LabelConfirmPassword": "Jelszó megerősítése",
|
||||||
|
"LabelContinueListening": "Hallgatás folytatása",
|
||||||
|
"LabelContinueReading": "Olvasás folytatása",
|
||||||
|
"LabelContinueSeries": "Sorozat folytatása",
|
||||||
|
"LabelCover": "Borító",
|
||||||
|
"LabelCoverImageURL": "Borítókép URL",
|
||||||
|
"LabelCreatedAt": "Létrehozás ideje",
|
||||||
|
"LabelCronExpression": "Cron kifejezés",
|
||||||
|
"LabelCurrent": "Jelenlegi",
|
||||||
|
"LabelCurrently": "Jelenleg:",
|
||||||
|
"LabelCustomCronExpression": "Egyéni Cron kifejezés:",
|
||||||
|
"LabelDatetime": "Dátumidő",
|
||||||
|
"LabelDeleteFromFileSystemCheckbox": "Törlés a fájlrendszerről (ne jelölje be, ha csak az adatbázisból szeretné eltávolítani)",
|
||||||
|
"LabelDescription": "Leírás",
|
||||||
|
"LabelDeselectAll": "Minden kijelölés megszüntetése",
|
||||||
|
"LabelDevice": "Eszköz",
|
||||||
|
"LabelDeviceInfo": "Eszköz információ",
|
||||||
|
"LabelDeviceIsAvailableTo": "Eszköz elérhető a következő számára...",
|
||||||
|
"LabelDirectory": "Könyvtár",
|
||||||
|
"LabelDiscFromFilename": "Lemez a fájlnévből",
|
||||||
|
"LabelDiscFromMetadata": "Lemez a metaadatokból",
|
||||||
|
"LabelDiscover": "Felfedezés",
|
||||||
|
"LabelDownload": "Letöltés",
|
||||||
|
"LabelDownloadNEpisodes": "{0} epizód letöltése",
|
||||||
|
"LabelDuration": "Időtartam",
|
||||||
|
"LabelDurationFound": "Megtalált időtartam:",
|
||||||
|
"LabelEbook": "E-könyv",
|
||||||
|
"LabelEbooks": "E-könyvek",
|
||||||
|
"LabelEdit": "Szerkesztés",
|
||||||
|
"LabelEmail": "E-mail",
|
||||||
|
"LabelEmailSettingsFromAddress": "Feladó címe",
|
||||||
|
"LabelEmailSettingsSecure": "Biztonságos",
|
||||||
|
"LabelEmailSettingsSecureHelp": "Ha igaz, a kapcsolat TLS-t használ a szerverhez való csatlakozáskor. Ha hamis, akkor TLS-t használ, ha a szerver támogatja a STARTTLS kiterjesztést. A legtöbb esetben állítsa ezt az értéket igazra, ha a 465-ös portra csatlakozik. A 587-es vagy 25-ös port esetében tartsa hamis értéken. (a nodemailer.com/smtp/#authentication oldalról)",
|
||||||
|
"LabelEmailSettingsTestAddress": "Teszt cím",
|
||||||
|
"LabelEmbeddedCover": "Beágyazott borító",
|
||||||
|
"LabelEnable": "Engedélyezés",
|
||||||
|
"LabelEnd": "Vége",
|
||||||
|
"LabelEpisode": "Epizód",
|
||||||
|
"LabelEpisodeTitle": "Epizód címe",
|
||||||
|
"LabelEpisodeType": "Epizód típusa",
|
||||||
|
"LabelExample": "Példa",
|
||||||
|
"LabelExplicit": "Explicit",
|
||||||
|
"LabelFeedURL": "Hírcsatorna URL",
|
||||||
|
"LabelFetchingMetadata": "Metaadatok lekérése",
|
||||||
|
"LabelFile": "Fájl",
|
||||||
|
"LabelFileBirthtime": "Fájl létrehozásának ideje",
|
||||||
|
"LabelFileModified": "Fájl módosításának ideje",
|
||||||
|
"LabelFilename": "Fájlnév",
|
||||||
|
"LabelFilterByUser": "Szűrés felhasználó szerint",
|
||||||
|
"LabelFindEpisodes": "Epizódok keresése",
|
||||||
|
"LabelFinished": "Befejezett",
|
||||||
|
"LabelFolder": "Mappa",
|
||||||
|
"LabelFolders": "Mappák",
|
||||||
|
"LabelFontBold": "Félkövér",
|
||||||
|
"LabelFontFamily": "Betűtípus család",
|
||||||
|
"LabelFontItalic": "Dőlt",
|
||||||
|
"LabelFontScale": "Betűméret skála",
|
||||||
|
"LabelFontStrikethrough": "Áthúzott",
|
||||||
|
"LabelFormat": "Formátum",
|
||||||
|
"LabelGenre": "Műfaj",
|
||||||
|
"LabelGenres": "Műfajok",
|
||||||
|
"LabelHardDeleteFile": "Fájl végleges törlése",
|
||||||
|
"LabelHasEbook": "Van e-könyve",
|
||||||
|
"LabelHasSupplementaryEbook": "Van kiegészítő e-könyve",
|
||||||
|
"LabelHighestPriority": "Legmagasabb prioritás",
|
||||||
|
"LabelHost": "Hoszt",
|
||||||
|
"LabelHour": "Óra",
|
||||||
|
"LabelIcon": "Ikon",
|
||||||
|
"LabelImageURLFromTheWeb": "Kép URL a weben",
|
||||||
|
"LabelIncludeInTracklist": "Beleértve a sávlistába",
|
||||||
|
"LabelIncomplete": "Befejezetlen",
|
||||||
|
"LabelInProgress": "Folyamatban",
|
||||||
|
"LabelInterval": "Intervallum",
|
||||||
|
"LabelIntervalCustomDailyWeekly": "Egyéni napi/heti",
|
||||||
|
"LabelIntervalEvery12Hours": "Minden 12 órában",
|
||||||
|
"LabelIntervalEvery15Minutes": "Minden 15 percben",
|
||||||
|
"LabelIntervalEvery2Hours": "Minden 2 órában",
|
||||||
|
"LabelIntervalEvery30Minutes": "Minden 30 percben",
|
||||||
|
"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",
|
||||||
|
"LabelLanguageDefaultServer": "Szerver alapértelmezett nyelve",
|
||||||
|
"LabelLastBookAdded": "Utolsó hozzáadott könyv",
|
||||||
|
"LabelLastBookUpdated": "Utolsó frissített könyv",
|
||||||
|
"LabelLastSeen": "Utolsó látogatás",
|
||||||
|
"LabelLastTime": "Utolsó alkalom",
|
||||||
|
"LabelLastUpdate": "Utolsó frissítés",
|
||||||
|
"LabelLayout": "Elrendezés",
|
||||||
|
"LabelLayoutSinglePage": "Egyoldalas",
|
||||||
|
"LabelLayoutSplitPage": "Kétoldalas",
|
||||||
|
"LabelLess": "Kevesebb",
|
||||||
|
"LabelLibrariesAccessibleToUser": "A felhasználó számára elérhető könyvtárak",
|
||||||
|
"LabelLibrary": "Könyvtár",
|
||||||
|
"LabelLibraryItem": "Könyvtári elem",
|
||||||
|
"LabelLibraryName": "Könyvtár neve",
|
||||||
|
"LabelLimit": "Korlát",
|
||||||
|
"LabelLineSpacing": "Sorköz",
|
||||||
|
"LabelListenAgain": "Újrahallgatás",
|
||||||
|
"LabelLogLevelDebug": "Debug",
|
||||||
|
"LabelLogLevelInfo": "Információ",
|
||||||
|
"LabelLogLevelWarn": "Figyelmeztetés",
|
||||||
|
"LabelLookForNewEpisodesAfterDate": "Új epizódok keresése ezen a dátum után",
|
||||||
|
"LabelLowestPriority": "Legalacsonyabb prioritás",
|
||||||
|
"LabelMatchExistingUsersBy": "Meglévő felhasználók egyeztetése",
|
||||||
|
"LabelMatchExistingUsersByDescription": "Meglévő felhasználók összekapcsolására használt. Egyszer összekapcsolva, a felhasználók egyedülálló azonosítóval lesznek egyeztetve az Ön SSO szolgáltatójától",
|
||||||
|
"LabelMediaPlayer": "Médialejátszó",
|
||||||
|
"LabelMediaType": "Média típus",
|
||||||
|
"LabelMetadataOrderOfPrecedenceDescription": "A magasabb prioritású metaadat-források felülírják az alacsonyabb prioritásúakat",
|
||||||
|
"LabelMetadataProvider": "Metaadat-szolgáltató",
|
||||||
|
"LabelMetaTag": "Meta címke",
|
||||||
|
"LabelMetaTags": "Meta címkék",
|
||||||
|
"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.",
|
||||||
|
"LabelMore": "Több",
|
||||||
|
"LabelMoreInfo": "További információ",
|
||||||
|
"LabelName": "Név",
|
||||||
|
"LabelNarrator": "Előadó",
|
||||||
|
"LabelNarrators": "Előadók",
|
||||||
|
"LabelNew": "Új",
|
||||||
|
"LabelNewestAuthors": "Legújabb szerzők",
|
||||||
|
"LabelNewestEpisodes": "Legújabb epizódok",
|
||||||
|
"LabelNewPassword": "Új jelszó",
|
||||||
|
"LabelNextBackupDate": "Következő biztonsági másolat dátuma",
|
||||||
|
"LabelNextScheduledRun": "Következő ütemezett futtatás",
|
||||||
|
"LabelNoEpisodesSelected": "Nincsenek kiválasztott epizódok",
|
||||||
|
"LabelNotes": "Megjegyzések",
|
||||||
|
"LabelNotFinished": "Nem befejezett",
|
||||||
|
"LabelNotificationAppriseURL": "Apprise URL(ek)",
|
||||||
|
"LabelNotificationAvailableVariables": "Elérhető változók",
|
||||||
|
"LabelNotificationBodyTemplate": "Törzs sablon",
|
||||||
|
"LabelNotificationEvent": "Értesítési esemény",
|
||||||
|
"LabelNotificationsMaxFailedAttempts": "Maximális sikertelen próbálkozások",
|
||||||
|
"LabelNotificationsMaxFailedAttemptsHelp": "Az értesítések akkor kerülnek letiltásra, ha ennyiszer nem sikerül elküldeni őket",
|
||||||
|
"LabelNotificationsMaxQueueSize": "Maximális értesítési események sorának mérete",
|
||||||
|
"LabelNotificationsMaxQueueSizeHelp": "Az események korlátozva vannak, hogy másodpercenként 1-szer történjenek. Ha a sor maximális méretű, akkor az események figyelmen kívül lesznek hagyva. Ez megakadályozza az értesítések spamelését.",
|
||||||
|
"LabelNotificationTitleTemplate": "Cím sablon",
|
||||||
|
"LabelNotStarted": "Nem indult el",
|
||||||
|
"LabelNumberOfBooks": "Könyvek száma",
|
||||||
|
"LabelNumberOfEpisodes": "Epizódok száma",
|
||||||
|
"LabelOpenRSSFeed": "RSS hírcsatorna megnyitása",
|
||||||
|
"LabelOverwrite": "Felülírás",
|
||||||
|
"LabelPassword": "Jelszó",
|
||||||
|
"LabelPath": "Útvonal",
|
||||||
|
"LabelPermissionsAccessAllLibraries": "Hozzáférhet az összes könyvtárhoz",
|
||||||
|
"LabelPermissionsAccessAllTags": "Hozzáférhet az összes címkéhez",
|
||||||
|
"LabelPermissionsAccessExplicitContent": "Hozzáférhet explicit tartalomhoz",
|
||||||
|
"LabelPermissionsDelete": "Törölhet",
|
||||||
|
"LabelPermissionsDownload": "Letölthet",
|
||||||
|
"LabelPermissionsUpdate": "Frissíthet",
|
||||||
|
"LabelPermissionsUpload": "Feltölthet",
|
||||||
|
"LabelPersonalYearReview": "Your Year in Review ({0})",
|
||||||
|
"LabelPhotoPathURL": "Fénykép útvonal/URL",
|
||||||
|
"LabelPlaylists": "Lejátszási listák",
|
||||||
|
"LabelPlayMethod": "Lejátszási módszer",
|
||||||
|
"LabelPodcast": "Podcast",
|
||||||
|
"LabelPodcasts": "Podcastok",
|
||||||
|
"LabelPodcastSearchRegion": "Podcast keresési régió",
|
||||||
|
"LabelPodcastType": "Podcast típus",
|
||||||
|
"LabelPort": "Port",
|
||||||
|
"LabelPrefixesToIgnore": "Figyelmen kívül hagyandó előtagok (nem érzékeny a kis- és nagybetűkre)",
|
||||||
|
"LabelPreventIndexing": "A hírcsatorna indexelésének megakadályozása az iTunes és a Google podcast könyvtáraiban",
|
||||||
|
"LabelPrimaryEbook": "Elsődleges e-könyv",
|
||||||
|
"LabelProgress": "Haladás",
|
||||||
|
"LabelProvider": "Szolgáltató",
|
||||||
|
"LabelPubDate": "Kiadás dátuma",
|
||||||
|
"LabelPublisher": "Kiadó",
|
||||||
|
"LabelPublishYear": "Kiadás éve",
|
||||||
|
"LabelRead": "Olvasás",
|
||||||
|
"LabelReadAgain": "Újraolvasás",
|
||||||
|
"LabelReadEbookWithoutProgress": "E-könyv olvasása haladás nélkül",
|
||||||
|
"LabelRecentlyAdded": "Nemrég hozzáadva",
|
||||||
|
"LabelRecentSeries": "Legutóbbi sorozatok",
|
||||||
|
"LabelRecommended": "Ajánlott",
|
||||||
|
"LabelRedo": "Újra",
|
||||||
|
"LabelRegion": "Régió",
|
||||||
|
"LabelReleaseDate": "Megjelenés dátuma",
|
||||||
|
"LabelRemoveCover": "Borító eltávolítása",
|
||||||
|
"LabelRowsPerPage": "Sorok száma oldalanként",
|
||||||
|
"LabelRSSFeedCustomOwnerEmail": "Egyéni tulajdonos e-mail",
|
||||||
|
"LabelRSSFeedCustomOwnerName": "Egyéni tulajdonos neve",
|
||||||
|
"LabelRSSFeedOpen": "RSS hírcsatorna nyitva",
|
||||||
|
"LabelRSSFeedPreventIndexing": "Indexelés megakadályozása",
|
||||||
|
"LabelRSSFeedSlug": "RSS hírcsatorna slug",
|
||||||
|
"LabelRSSFeedURL": "RSS hírcsatorna URL",
|
||||||
|
"LabelSearchTerm": "Keresési kifejezés",
|
||||||
|
"LabelSearchTitle": "Cím keresése",
|
||||||
|
"LabelSearchTitleOrASIN": "Cím vagy ASIN keresése",
|
||||||
|
"LabelSeason": "Évad",
|
||||||
|
"LabelSelectAllEpisodes": "Összes epizód kiválasztása",
|
||||||
|
"LabelSelectEpisodesShowing": "Kiválasztás {0} megjelenített epizód",
|
||||||
|
"LabelSelectUsers": "Felhasználók kiválasztása",
|
||||||
|
"LabelSendEbookToDevice": "E-könyv küldése...",
|
||||||
|
"LabelSequence": "Sorozat",
|
||||||
|
"LabelSeries": "Sorozat",
|
||||||
|
"LabelSeriesName": "Sorozat neve",
|
||||||
|
"LabelSeriesProgress": "Sorozat haladása",
|
||||||
|
"LabelServerYearReview": "Server Year in Review ({0})",
|
||||||
|
"LabelSetEbookAsPrimary": "Beállítás elsődlegesként",
|
||||||
|
"LabelSetEbookAsSupplementary": "Beállítás kiegészítőként",
|
||||||
|
"LabelSettingsAudiobooksOnly": "Csak hangoskönyvek",
|
||||||
|
"LabelSettingsAudiobooksOnlyHelp": "Ennek a beállításnak az engedélyezése figyelmen kívül hagyja az e-könyv fájlokat, kivéve, ha azok egy hangoskönyv mappában vannak, ebben az esetben kiegészítő e-könyvként lesznek beállítva",
|
||||||
|
"LabelSettingsBookshelfViewHelp": "Skeuomorfikus dizájn fa polcokkal",
|
||||||
|
"LabelSettingsChromecastSupport": "Chromecast támogatás",
|
||||||
|
"LabelSettingsDateFormat": "Dátumformátum",
|
||||||
|
"LabelSettingsDisableWatcher": "Figyelő letiltása",
|
||||||
|
"LabelSettingsDisableWatcherForLibrary": "Mappafigyelő letiltása a könyvtárban",
|
||||||
|
"LabelSettingsDisableWatcherHelp": "Letiltja az automatikus elem hozzáadás/frissítés funkciót, amikor fájlváltozásokat észlel. *Szerver újraindítása szükséges",
|
||||||
|
"LabelSettingsEnableWatcher": "Figyelő engedélyezése",
|
||||||
|
"LabelSettingsEnableWatcherForLibrary": "Mappafigyelő engedélyezése a könyvtárban",
|
||||||
|
"LabelSettingsEnableWatcherHelp": "Engedélyezi az automatikus elem hozzáadás/frissítés funkciót, amikor fájlváltozásokat észlel. *Szerver újraindítása szükséges",
|
||||||
|
"LabelSettingsExperimentalFeatures": "Kísérleti funkciók",
|
||||||
|
"LabelSettingsExperimentalFeaturesHelp": "Fejlesztés alatt álló funkciók, amelyek visszajelzésre és tesztelésre szorulnak. Kattintson a github megbeszélés megnyitásához.",
|
||||||
|
"LabelSettingsFindCovers": "Borítók keresése",
|
||||||
|
"LabelSettingsFindCoversHelp": "Ha a hangoskönyvnek nincs beágyazott borítója vagy borítóképe a mappában, a szkenner megpróbálja megtalálni a borítót.<br>Megjegyzés: Ez meghosszabbítja a szkennelési időt",
|
||||||
|
"LabelSettingsHideSingleBookSeries": "Egykönyves sorozatok elrejtése",
|
||||||
|
"LabelSettingsHideSingleBookSeriesHelp": "A csak egy könyvet tartalmazó sorozatok el lesznek rejtve a sorozatok oldalról és a kezdőlap polcairól.",
|
||||||
|
"LabelSettingsHomePageBookshelfView": "Kezdőlap használja a könyvespolc nézetet",
|
||||||
|
"LabelSettingsLibraryBookshelfView": "Könyvtár használja a könyvespolc nézetet",
|
||||||
|
"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": "Feliratok elemzése",
|
||||||
|
"LabelSettingsParseSubtitlesHelp": "Feliratok kinyerése a hangoskönyv mappaneveiből.<br>A feliratnak el kell különülnie egy \" - \" jellel<br>például: \"Könyv címe - Egy felirat itt\" esetén a felirat \"Egy felirat itt\"",
|
||||||
|
"LabelSettingsPreferMatchedMetadata": "Preferált egyeztetett metaadatok",
|
||||||
|
"LabelSettingsPreferMatchedMetadataHelp": "Az egyeztetett adatok felülírják az elem részleteit a Gyors egyeztetés használatakor. Alapértelmezés szerint a Gyors egyeztetés csak a hiányzó részleteket tölti ki.",
|
||||||
|
"LabelSettingsSkipMatchingBooksWithASIN": "Már ASIN-nel rendelkező könyvek egyeztetésének kihagyása",
|
||||||
|
"LabelSettingsSkipMatchingBooksWithISBN": "Már ISBN-nel rendelkező könyvek egyeztetésének kihagyása",
|
||||||
|
"LabelSettingsSortingIgnorePrefixes": "Előtagok figyelmen kívül hagyása rendezéskor",
|
||||||
|
"LabelSettingsSortingIgnorePrefixesHelp": "például az \"a\" előtag esetén a \"A könyv címe\" könyv címe \"Könyv címe, A\" szerint rendeződik",
|
||||||
|
"LabelSettingsSquareBookCovers": "Négyzet alakú könyvborítók használata",
|
||||||
|
"LabelSettingsSquareBookCoversHelp": "Négyzet alakú borítók használata az 1,6:1 arányú standard könyvborítók helyett",
|
||||||
|
"LabelSettingsStoreCoversWithItem": "Borítók tárolása az elemmel",
|
||||||
|
"LabelSettingsStoreCoversWithItemHelp": "Alapértelmezés szerint a borítók a /metadata/items mappában vannak tárolva, ennek a beállításnak az engedélyezése a borítókat a könyvtári elem mappájában tárolja. Csak egy \"cover\" nevű fájl lesz megtartva",
|
||||||
|
"LabelSettingsStoreMetadataWithItem": "Metaadatok tárolása az elemmel",
|
||||||
|
"LabelSettingsStoreMetadataWithItemHelp": "Alapértelmezés szerint a metaadatfájlok a /metadata/items mappában vannak tárolva, ennek a beállításnak az engedélyezése a metaadatfájlokat a könyvtári elem mappáiban tárolja",
|
||||||
|
"LabelSettingsTimeFormat": "Időformátum",
|
||||||
|
"LabelShowAll": "Mindent mutat",
|
||||||
|
"LabelSize": "Méret",
|
||||||
|
"LabelSleepTimer": "Alvásidőzítő",
|
||||||
|
"LabelSlug": "Rövid cím",
|
||||||
|
"LabelStart": "Kezdés",
|
||||||
|
"LabelStarted": "Elkezdődött",
|
||||||
|
"LabelStartedAt": "Kezdés ideje",
|
||||||
|
"LabelStartTime": "Kezdési idő",
|
||||||
|
"LabelStatsAudioTracks": "Audiósávok",
|
||||||
|
"LabelStatsAuthors": "Szerzők",
|
||||||
|
"LabelStatsBestDay": "Legjobb nap",
|
||||||
|
"LabelStatsDailyAverage": "Napi átlag",
|
||||||
|
"LabelStatsDays": "Napok",
|
||||||
|
"LabelStatsDaysListened": "Hallgatott napok",
|
||||||
|
"LabelStatsHours": "Órák",
|
||||||
|
"LabelStatsInARow": "egymás után",
|
||||||
|
"LabelStatsItemsFinished": "Befejezett elemek",
|
||||||
|
"LabelStatsItemsInLibrary": "Elemek a könyvtárban",
|
||||||
|
"LabelStatsMinutes": "percek",
|
||||||
|
"LabelStatsMinutesListening": "Hallgatási percek",
|
||||||
|
"LabelStatsOverallDays": "Összes nap",
|
||||||
|
"LabelStatsOverallHours": "Összes óra",
|
||||||
|
"LabelStatsWeekListening": "Heti hallgatás",
|
||||||
|
"LabelSubtitle": "Felirat",
|
||||||
|
"LabelSupportedFileTypes": "Támogatott fájltípusok",
|
||||||
|
"LabelTag": "Címke",
|
||||||
|
"LabelTags": "Címkék",
|
||||||
|
"LabelTagsAccessibleToUser": "A felhasználó számára elérhető címkék",
|
||||||
|
"LabelTagsNotAccessibleToUser": "A felhasználó számára nem elérhető címkék",
|
||||||
|
"LabelTasks": "Futó feladatok",
|
||||||
|
"LabelTextEditorBulletedList": "Pontozott lista",
|
||||||
|
"LabelTextEditorLink": "Link",
|
||||||
|
"LabelTextEditorNumberedList": "Számozott lista",
|
||||||
|
"LabelTextEditorUnlink": "Link eltávolítása",
|
||||||
|
"LabelTheme": "Téma",
|
||||||
|
"LabelThemeDark": "Sötét",
|
||||||
|
"LabelThemeLight": "Világos",
|
||||||
|
"LabelTimeBase": "Időalap",
|
||||||
|
"LabelTimeListened": "Hallgatott idő",
|
||||||
|
"LabelTimeListenedToday": "Ma hallgatott idő",
|
||||||
|
"LabelTimeRemaining": "{0} maradt",
|
||||||
|
"LabelTimeToShift": "Eltolás ideje másodpercben",
|
||||||
|
"LabelTitle": "Cím",
|
||||||
|
"LabelToolsEmbedMetadata": "Metaadatok beágyazása",
|
||||||
|
"LabelToolsEmbedMetadataDescription": "Metaadatok beágyazása az audiofájlokba, beleértve a borítóképet és a fejezeteket.",
|
||||||
|
"LabelToolsMakeM4b": "M4B Hangoskönyv fájl készítése",
|
||||||
|
"LabelToolsMakeM4bDescription": ".M4B hangoskönyv fájl generálása beágyazott metaadatokkal, borítóképpel és fejezetekkel.",
|
||||||
|
"LabelToolsSplitM4b": "M4B felosztása MP3-ra",
|
||||||
|
"LabelToolsSplitM4bDescription": "MP3 fájlok létrehozása egy M4B-ből, fejezetenként felosztva, beágyazott metaadatokkal, borítóképpel és fejezetekkel.",
|
||||||
|
"LabelTotalDuration": "Teljes időtartam",
|
||||||
|
"LabelTotalTimeListened": "Teljes hallgatási idő",
|
||||||
|
"LabelTrackFromFilename": "Sáv a fájlnévből",
|
||||||
|
"LabelTrackFromMetadata": "Sáv a metaadatokból",
|
||||||
|
"LabelTracks": "Sávok",
|
||||||
|
"LabelTracksMultiTrack": "Többsávos",
|
||||||
|
"LabelTracksNone": "Nincsenek sávok",
|
||||||
|
"LabelTracksSingleTrack": "Egysávos",
|
||||||
|
"LabelType": "Típus",
|
||||||
|
"LabelUnabridged": "Nem tömörített",
|
||||||
|
"LabelUndo": "Visszavonás",
|
||||||
|
"LabelUnknown": "Ismeretlen",
|
||||||
|
"LabelUpdateCover": "Borító frissítése",
|
||||||
|
"LabelUpdateCoverHelp": "Lehetővé teszi a meglévő borítók felülírását a kiválasztott könyveknél, amikor találatot talál",
|
||||||
|
"LabelUpdatedAt": "Frissítve",
|
||||||
|
"LabelUpdateDetails": "Részletek frissítése",
|
||||||
|
"LabelUpdateDetailsHelp": "Lehetővé teszi a meglévő részletek felülírását a kiválasztott könyveknél, amikor találatot talál",
|
||||||
|
"LabelUploaderDragAndDrop": "Fájlok vagy mappák húzása és elengedése",
|
||||||
|
"LabelUploaderDropFiles": "Fájlok elengedése",
|
||||||
|
"LabelUploaderItemFetchMetadataHelp": "Cím, szerző és sorozat automatikus lekérése",
|
||||||
|
"LabelUseChapterTrack": "Fejezetsáv használata",
|
||||||
|
"LabelUseFullTrack": "Teljes sáv használata",
|
||||||
|
"LabelUser": "Felhasználó",
|
||||||
|
"LabelUsername": "Felhasználónév",
|
||||||
|
"LabelValue": "Érték",
|
||||||
|
"LabelVersion": "Verzió",
|
||||||
|
"LabelViewBookmarks": "Könyvjelzők megtekintése",
|
||||||
|
"LabelViewChapters": "Fejezetek megtekintése",
|
||||||
|
"LabelViewQueue": "Lejátszó sor megtekintése",
|
||||||
|
"LabelVolume": "Hangerő",
|
||||||
|
"LabelWeekdaysToRun": "Futás napjai",
|
||||||
|
"LabelYearReviewHide": "Hide Year in Review",
|
||||||
|
"LabelYearReviewShow": "See Year in Review",
|
||||||
|
"LabelYourAudiobookDuration": "Hangoskönyv időtartama",
|
||||||
|
"LabelYourBookmarks": "Könyvjelzőid",
|
||||||
|
"LabelYourPlaylists": "Lejátszási listáid",
|
||||||
|
"LabelYourProgress": "Haladásod",
|
||||||
|
"MessageAddToPlayerQueue": "Hozzáadás a lejátszó sorhoz",
|
||||||
|
"MessageAppriseDescription": "Ennek a funkció használatához futtatnia kell egy <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API</a> példányt vagy egy olyan API-t, amely kezeli ezeket a kéréseket. <br />Az Apprise API URL-nek a teljes URL útvonalat kell tartalmaznia az értesítés elküldéséhez, például, ha az API példánya a <code>http://192.168.1.1:8337</code> címen szolgáltatva, akkor <code>http://192.168.1.1:8337/notify</code> értéket kell megadnia.",
|
||||||
|
"MessageBackupsDescription": "A biztonsági másolatok tartalmazzák a felhasználókat, a felhasználói haladást, a könyvtári elem részleteit, a szerver beállításait és a képeket, amelyek a <code>/metadata/items</code> és <code>/metadata/authors</code> mappákban vannak tárolva. A biztonsági másolatok <strong>nem</strong> tartalmazzák a könyvtári mappákban tárolt fájlokat.",
|
||||||
|
"MessageBatchQuickMatchDescription": "A Gyors egyeztetés megpróbálja hozzáadni a hiányzó borítókat és metaadatokat a kiválasztott elemekhez. Engedélyezze az alábbi opciókat, hogy a Gyors egyeztetés felülírhassa a meglévő borítókat és/vagy metaadatokat.",
|
||||||
|
"MessageBookshelfNoCollections": "Még nem készített gyűjteményeket",
|
||||||
|
"MessageBookshelfNoResultsForFilter": "Nincs eredmény a \"{0}: {1}\" szűrőre",
|
||||||
|
"MessageBookshelfNoRSSFeeds": "Nincsenek nyitott RSS hírcsatornák",
|
||||||
|
"MessageBookshelfNoSeries": "Nincsenek sorozatai",
|
||||||
|
"MessageChapterEndIsAfter": "A fejezet vége a hangoskönyv végét követi",
|
||||||
|
"MessageChapterErrorFirstNotZero": "Az első fejezetnek 0:00-kor kell kezdődnie",
|
||||||
|
"MessageChapterErrorStartGteDuration": "Érvénytelen kezdési idő, kevesebbnek kell lennie, mint a hangoskönyv időtartama",
|
||||||
|
"MessageChapterErrorStartLtPrev": "Érvénytelen kezdési idő, nagyobbnak kell lennie, mint az előző fejezet kezdési ideje",
|
||||||
|
"MessageChapterStartIsAfter": "A fejezet kezdete a hangoskönyv végét követi",
|
||||||
|
"MessageCheckingCron": "Cron ellenőrzése...",
|
||||||
|
"MessageConfirmCloseFeed": "Biztosan be szeretné zárni ezt a hírcsatornát?",
|
||||||
|
"MessageConfirmDeleteBackup": "Biztosan törölni szeretné a(z) {0} biztonsági másolatot?",
|
||||||
|
"MessageConfirmDeleteFile": "Ez törölni fogja a fájlt a fájlrendszerből. Biztos benne?",
|
||||||
|
"MessageConfirmDeleteLibrary": "Biztosan véglegesen törölni szeretné a(z) \"{0}\" könyvtárat?",
|
||||||
|
"MessageConfirmDeleteLibraryItem": "Ez eltávolítja a könyvtári elemet az adatbázisból és a fájlrendszerből. Biztos benne?",
|
||||||
|
"MessageConfirmDeleteLibraryItems": "Ez eltávolítja a(z) {0} könyvtári elemet az adatbázisból és a fájlrendszerből. Biztos benne?",
|
||||||
|
"MessageConfirmDeleteSession": "Biztosan törölni szeretné ezt a munkamenetet?",
|
||||||
|
"MessageConfirmForceReScan": "Biztosan kényszeríteni szeretné az újraszkennelést?",
|
||||||
|
"MessageConfirmMarkAllEpisodesFinished": "Biztosan meg szeretné jelölni az összes epizódot befejezettnek?",
|
||||||
|
"MessageConfirmMarkAllEpisodesNotFinished": "Biztosan meg szeretné jelölni az összes epizódot nem befejezettnek?",
|
||||||
|
"MessageConfirmMarkSeriesFinished": "Biztosan meg szeretné jelölni a sorozat összes könyvét befejezettnek?",
|
||||||
|
"MessageConfirmMarkSeriesNotFinished": "Biztosan meg szeretné jelölni a sorozat összes könyvét nem befejezettnek?",
|
||||||
|
"MessageConfirmQuickEmbed": "Figyelem! A Gyors beágyazás nem készít biztonsági másolatot az audiofájlokról. Győződjön meg arról, hogy van biztonsági másolata az audiofájlokról. <br><br>Szeretné folytatni?",
|
||||||
|
"MessageConfirmRemoveAllChapters": "Biztosan eltávolítja az összes fejezetet?",
|
||||||
|
"MessageConfirmRemoveAuthor": "Biztosan eltávolítja a(z) \"{0}\" szerzőt?",
|
||||||
|
"MessageConfirmRemoveCollection": "Biztosan eltávolítja a(z) \"{0}\" gyűjteményt?",
|
||||||
|
"MessageConfirmRemoveEpisode": "Biztosan eltávolítja a(z) \"{0}\" epizódot?",
|
||||||
|
"MessageConfirmRemoveEpisodes": "Biztosan eltávolítja a(z) {0} epizódot?",
|
||||||
|
"MessageConfirmRemoveListeningSessions": "Biztosan eltávolítja a(z) {0} hallgatási munkamenetet?",
|
||||||
|
"MessageConfirmRemoveNarrator": "Biztosan eltávolítja a(z) \"{0}\" előadót?",
|
||||||
|
"MessageConfirmRemovePlaylist": "Biztosan eltávolítja a(z) \"{0}\" lejátszási listáját?",
|
||||||
|
"MessageConfirmRenameGenre": "Biztosan át szeretné nevezni a(z) \"{0}\" műfajt \"{1}\"-re az összes elemnél?",
|
||||||
|
"MessageConfirmRenameGenreMergeNote": "Megjegyzés: Ez a műfaj már létezik, így össze lesznek vonva.",
|
||||||
|
"MessageConfirmRenameGenreWarning": "Figyelem! Egy hasonló, de eltérő nagybetűkkel rendelkező műfaj már létezik \"{0}\".",
|
||||||
|
"MessageConfirmRenameTag": "Biztosan át szeretné nevezni a(z) \"{0}\" címkét \"{1}\"-re az összes elemnél?",
|
||||||
|
"MessageConfirmRenameTagMergeNote": "Megjegyzés: Ez a címke már létezik, így össze lesznek vonva.",
|
||||||
|
"MessageConfirmRenameTagWarning": "Figyelem! Egy hasonló, de eltérő nagybetűkkel rendelkező címke már létezik \"{0}\".",
|
||||||
|
"MessageConfirmReScanLibraryItems": "Biztosan újra szeretné szkennelni a(z) {0} elemet?",
|
||||||
|
"MessageConfirmSendEbookToDevice": "Biztosan el szeretné küldeni a(z) {0} e-könyvet a(z) \"{1}\" eszközre?",
|
||||||
|
"MessageDownloadingEpisode": "Epizód letöltése",
|
||||||
|
"MessageDragFilesIntoTrackOrder": "Húzza a fájlokat a helyes sávrendbe",
|
||||||
|
"MessageEmbedFinished": "Beágyazás befejeződött!",
|
||||||
|
"MessageEpisodesQueuedForDownload": "{0} Epizód letöltésre várakozik",
|
||||||
|
"MessageFeedURLWillBe": "A hírcsatorna URL-je {0} lesz",
|
||||||
|
"MessageFetching": "Lekérés...",
|
||||||
|
"MessageForceReScanDescription": "minden fájlt újra szkennel, mint egy friss szkennelés. Az audiofájlok ID3 címkéi, OPF fájlok és szövegfájlok újként lesznek szkennelve.",
|
||||||
|
"MessageImportantNotice": "Fontos közlemény!",
|
||||||
|
"MessageInsertChapterBelow": "Fejezet beszúrása alulra",
|
||||||
|
"MessageItemsSelected": "{0} kiválasztott elem",
|
||||||
|
"MessageItemsUpdated": "{0} frissített elem",
|
||||||
|
"MessageJoinUsOn": "Csatlakozzon hozzánk: ",
|
||||||
|
"MessageListeningSessionsInTheLastYear": "{0} hallgatási munkamenet az elmúlt évben",
|
||||||
|
"MessageLoading": "Betöltés...",
|
||||||
|
"MessageLoadingFolders": "Mappák betöltése...",
|
||||||
|
"MessageM4BFailed": "M4B sikertelen!",
|
||||||
|
"MessageM4BFinished": "M4B befejeződött!",
|
||||||
|
"MessageMapChapterTitles": "Fejezetcímek hozzárendelése a meglévő hangoskönyv fejezeteihez anélkül, hogy az időbélyegeket módosítaná",
|
||||||
|
"MessageMarkAllEpisodesFinished": "Az összes epizód megjelölése befejezettnek",
|
||||||
|
"MessageMarkAllEpisodesNotFinished": "Az összes epizód megjelölése nem befejezettnek",
|
||||||
|
"MessageMarkAsFinished": "Megjelölés befejezettnek",
|
||||||
|
"MessageMarkAsNotFinished": "Megjelölés nem befejezettnek",
|
||||||
|
"MessageMatchBooksDescription": "megpróbálja egyeztetni a könyvtár könyveit egy kiválasztott keresési szolgáltató könyvével, és kitölti az üres részleteket és a borítót. Nem írja felül a részleteket.",
|
||||||
|
"MessageNoAudioTracks": "Nincsenek audiósávok",
|
||||||
|
"MessageNoAuthors": "Nincsenek szerzők",
|
||||||
|
"MessageNoBackups": "Nincsenek biztonsági másolatok",
|
||||||
|
"MessageNoBookmarks": "Nincsenek könyvjelzők",
|
||||||
|
"MessageNoChapters": "Nincsenek fejezetek",
|
||||||
|
"MessageNoCollections": "Nincsenek gyűjtemények",
|
||||||
|
"MessageNoCoversFound": "Nem találhatóak borítók",
|
||||||
|
"MessageNoDescription": "Nincs leírás",
|
||||||
|
"MessageNoDownloadsInProgress": "Jelenleg nincsenek folyamatban lévő letöltések",
|
||||||
|
"MessageNoDownloadsQueued": "Nincsenek várakozó letöltések",
|
||||||
|
"MessageNoEpisodeMatchesFound": "Nincs találat az epizódokra",
|
||||||
|
"MessageNoEpisodes": "Nincsenek epizódok",
|
||||||
|
"MessageNoFoldersAvailable": "Nincsenek elérhető mappák",
|
||||||
|
"MessageNoGenres": "Nincsenek műfajok",
|
||||||
|
"MessageNoIssues": "Nincsenek problémák",
|
||||||
|
"MessageNoItems": "Nincsenek elemek",
|
||||||
|
"MessageNoItemsFound": "Nem találhatóak elemek",
|
||||||
|
"MessageNoListeningSessions": "Nincsenek hallgatási munkamenetek",
|
||||||
|
"MessageNoLogs": "Nincsenek naplók",
|
||||||
|
"MessageNoMediaProgress": "Nincs előrehaladás a médialejátszásban",
|
||||||
|
"MessageNoNotifications": "Nincsenek értesítések",
|
||||||
|
"MessageNoPodcastsFound": "Nem találhatóak podcastok",
|
||||||
|
"MessageNoResults": "Nincsenek eredmények",
|
||||||
|
"MessageNoSearchResultsFor": "Nincs keresési eredmény erre: \"{0}\"",
|
||||||
|
"MessageNoSeries": "Nincsenek sorozatok",
|
||||||
|
"MessageNoTags": "Nincsenek címkék",
|
||||||
|
"MessageNoTasksRunning": "Nincsenek futó feladatok",
|
||||||
|
"MessageNotYetImplemented": "Még nem implementált",
|
||||||
|
"MessageNoUpdateNecessary": "Nincs szükség frissítésre",
|
||||||
|
"MessageNoUpdatesWereNecessary": "Nem volt szükség frissítésekre",
|
||||||
|
"MessageNoUserPlaylists": "Nincsenek felhasználói lejátszási listák",
|
||||||
|
"MessageOr": "vagy",
|
||||||
|
"MessagePauseChapter": "Fejezet lejátszásának szüneteltetése",
|
||||||
|
"MessagePlayChapter": "Fejezet elejének meghallgatása",
|
||||||
|
"MessagePlaylistCreateFromCollection": "Lejátszási lista létrehozása gyűjteményből",
|
||||||
|
"MessagePodcastHasNoRSSFeedForMatching": "A podcastnak nincs RSS hírcsatorna URL-je az egyeztetéshez",
|
||||||
|
"MessageQuickMatchDescription": "Üres elem részletek és borító feltöltése az első találati eredménnyel a(z) '{0}'-ból. Nem írja felül a részleteket, kivéve, ha a 'Preferált egyeztetett metaadatok' szerverbeállítás engedélyezve van.",
|
||||||
|
"MessageRemoveChapter": "Fejezet eltávolítása",
|
||||||
|
"MessageRemoveEpisodes": "Epizód(ok) eltávolítása: {0}",
|
||||||
|
"MessageRemoveFromPlayerQueue": "Eltávolítás a lejátszási sorból",
|
||||||
|
"MessageRemoveUserWarning": "Biztosan véglegesen törölni szeretné a(z) \"{0}\" felhasználót?",
|
||||||
|
"MessageReportBugsAndContribute": "Hibák jelentése, funkciók kérése és hozzájárulás itt:",
|
||||||
|
"MessageResetChaptersConfirm": "Biztosan alaphelyzetbe szeretné állítani a fejezeteket és visszavonni a módosításokat?",
|
||||||
|
"MessageRestoreBackupConfirm": "Biztosan vissza szeretné állítani a biztonsági másolatot, amely ekkor készült:",
|
||||||
|
"MessageRestoreBackupWarning": "A biztonsági mentés visszaállítása felülírja az egész adatbázist, amely a /config mappában található, valamint a borítóképeket a /metadata/items és /metadata/authors mappákban.<br /><br />A biztonsági mentések nem módosítják a könyvtár mappáiban található fájlokat. Ha engedélyezte a szerverbeállításokat a borítóképek és a metaadatok könyvtármappákban való tárolására, akkor ezek nem kerülnek biztonsági mentésre vagy felülírásra.<br /><br />A szerver használó összes kliens automatikusan frissül.",
|
||||||
|
"MessageSearchResultsFor": "Keresési eredmények",
|
||||||
|
"MessageSelected": "{0} kiválasztva",
|
||||||
|
"MessageServerCouldNotBeReached": "A szervert nem lehet elérni",
|
||||||
|
"MessageSetChaptersFromTracksDescription": "Fejezetek beállítása minden egyes hangfájlt egy fejezetként használva, és a fejezet címét a hangfájl neveként",
|
||||||
|
"MessageStartPlaybackAtTime": "\"{0}\" lejátszásának kezdése {1} -tól?",
|
||||||
|
"MessageThinking": "Gondolkodás...",
|
||||||
|
"MessageUploaderItemFailed": "A feltöltés sikertelen",
|
||||||
|
"MessageUploaderItemSuccess": "Sikeresen feltöltve!",
|
||||||
|
"MessageUploading": "Feltöltés...",
|
||||||
|
"MessageValidCronExpression": "Érvényes cron kifejezés",
|
||||||
|
"MessageWatcherIsDisabledGlobally": "A megfigyelő globálisan le van tiltva a szerver beállításokban",
|
||||||
|
"MessageXLibraryIsEmpty": "{0} könyvtár üres!",
|
||||||
|
"MessageYourAudiobookDurationIsLonger": "Az Ön hangoskönyvének hossza hosszabb, mint a talált időtartam",
|
||||||
|
"MessageYourAudiobookDurationIsShorter": "Az Ön hangoskönyvének hossza rövidebb, mint a talált időtartam",
|
||||||
|
"NoteChangeRootPassword": "A Root felhasználó az egyetlen felhasználó, akinek lehet üres jelszava",
|
||||||
|
"NoteChapterEditorTimes": "Megjegyzés: Az első fejezet kezdőidejének 0:00 kell lennie, és az utolsó fejezet kezdőideje nem haladhatja meg a hangoskönyv időtartamát.",
|
||||||
|
"NoteFolderPicker": "Megjegyzés: azok a mappák, amelyek már hozzá vannak rendelve, nem jelennek meg",
|
||||||
|
"NoteRSSFeedPodcastAppsHttps": "Figyelem: A legtöbb podcast alkalmazás megköveteli, hogy az RSS feed URL HTTPS-t használjon",
|
||||||
|
"NoteRSSFeedPodcastAppsPubDate": "Figyelem: Az egy vagy több epizódnak nincs Közzétételi dátuma. Néhány podcast alkalmazás ezt megköveteli.",
|
||||||
|
"NoteUploaderFoldersWithMediaFiles": "A médiafájlokat tartalmazó mappák külön könyvtári tételekként lesznek kezelve.",
|
||||||
|
"NoteUploaderOnlyAudioFiles": "Ha csak hangfájlokat tölt fel, akkor minden egyes hangfájl külön hangoskönyvként lesz kezelve.",
|
||||||
|
"NoteUploaderUnsupportedFiles": "A nem támogatott fájlok figyelmen kívül hagyásra kerülnek. Mappa kiválasztása vagy elengedésekor az elem mappáján kívüli egyéb fájlok figyelmen kívül lesznek hagyva.",
|
||||||
|
"PlaceholderNewCollection": "Új gyűjtemény neve",
|
||||||
|
"PlaceholderNewFolderPath": "Új mappa útvonala",
|
||||||
|
"PlaceholderNewPlaylist": "Új lejátszási lista neve",
|
||||||
|
"PlaceholderSearch": "Keresés..",
|
||||||
|
"PlaceholderSearchEpisode": "Epizód keresése..",
|
||||||
|
"ToastAccountUpdateFailed": "A fiók frissítése sikertelen",
|
||||||
|
"ToastAccountUpdateSuccess": "Fiók frissítve",
|
||||||
|
"ToastAuthorImageRemoveFailed": "A kép eltávolítása sikertelen",
|
||||||
|
"ToastAuthorImageRemoveSuccess": "Szerző képe eltávolítva",
|
||||||
|
"ToastAuthorUpdateFailed": "A szerző frissítése sikertelen",
|
||||||
|
"ToastAuthorUpdateMerged": "Szerző összevonva",
|
||||||
|
"ToastAuthorUpdateSuccess": "Szerző frissítve",
|
||||||
|
"ToastAuthorUpdateSuccessNoImageFound": "Szerző frissítve (nem található kép)",
|
||||||
|
"ToastBackupCreateFailed": "A biztonsági mentés létrehozása sikertelen",
|
||||||
|
"ToastBackupCreateSuccess": "Biztonsági mentés létrehozva",
|
||||||
|
"ToastBackupDeleteFailed": "A biztonsági mentés törlése sikertelen",
|
||||||
|
"ToastBackupDeleteSuccess": "Biztonsági mentés törölve",
|
||||||
|
"ToastBackupRestoreFailed": "A biztonsági mentés visszaállítása sikertelen",
|
||||||
|
"ToastBackupUploadFailed": "A biztonsági mentés feltöltése sikertelen",
|
||||||
|
"ToastBackupUploadSuccess": "Biztonsági mentés feltöltve",
|
||||||
|
"ToastBatchUpdateFailed": "Kötegelt frissítés sikertelen",
|
||||||
|
"ToastBatchUpdateSuccess": "Kötegelt frissítés sikeres",
|
||||||
|
"ToastBookmarkCreateFailed": "Könyvjelző létrehozása sikertelen",
|
||||||
|
"ToastBookmarkCreateSuccess": "Könyvjelző hozzáadva",
|
||||||
|
"ToastBookmarkRemoveFailed": "Könyvjelző eltávolítása sikertelen",
|
||||||
|
"ToastBookmarkRemoveSuccess": "Könyvjelző eltávolítva",
|
||||||
|
"ToastBookmarkUpdateFailed": "Könyvjelző frissítése sikertelen",
|
||||||
|
"ToastBookmarkUpdateSuccess": "Könyvjelző frissítve",
|
||||||
|
"ToastChaptersHaveErrors": "A fejezetek hibákat tartalmaznak",
|
||||||
|
"ToastChaptersMustHaveTitles": "A fejezeteknek címekkel kell rendelkezniük",
|
||||||
|
"ToastCollectionItemsRemoveFailed": "Elem(ek) eltávolítása a gyűjteményből sikertelen",
|
||||||
|
"ToastCollectionItemsRemoveSuccess": "Elem(ek) eltávolítva a gyűjteményből",
|
||||||
|
"ToastCollectionRemoveFailed": "Gyűjtemény eltávolítása sikertelen",
|
||||||
|
"ToastCollectionRemoveSuccess": "Gyűjtemény eltávolítva",
|
||||||
|
"ToastCollectionUpdateFailed": "Gyűjtemény frissítése sikertelen",
|
||||||
|
"ToastCollectionUpdateSuccess": "Gyűjtemény frissítve",
|
||||||
|
"ToastItemCoverUpdateFailed": "Elem borítójának frissítése sikertelen",
|
||||||
|
"ToastItemCoverUpdateSuccess": "Elem borítója frissítve",
|
||||||
|
"ToastItemDetailsUpdateFailed": "Elem részleteinek frissítése sikertelen",
|
||||||
|
"ToastItemDetailsUpdateSuccess": "Elem részletei frissítve",
|
||||||
|
"ToastItemDetailsUpdateUnneeded": "Nincsenek szükséges frissítések a tétel részletein",
|
||||||
|
"ToastItemMarkedAsFinishedFailed": "Megjelölés Befejezettként sikertelen",
|
||||||
|
"ToastItemMarkedAsFinishedSuccess": "Elem megjelölve Befejezettként",
|
||||||
|
"ToastItemMarkedAsNotFinishedFailed": "Nem sikerült Nem Befejezettként megjelölni az elemet",
|
||||||
|
"ToastItemMarkedAsNotFinishedSuccess": "Elem megjelölve Nem Befejezettként",
|
||||||
|
"ToastLibraryCreateFailed": "Könyvtár létrehozása sikertelen",
|
||||||
|
"ToastLibraryCreateSuccess": "\"{0}\" könyvtár létrehozva",
|
||||||
|
"ToastLibraryDeleteFailed": "Könyvtár törlése sikertelen",
|
||||||
|
"ToastLibraryDeleteSuccess": "Könyvtár törölve",
|
||||||
|
"ToastLibraryScanFailedToStart": "A beolvasás elindítása sikertelen",
|
||||||
|
"ToastLibraryScanStarted": "Könyvtár beolvasása elindítva",
|
||||||
|
"ToastLibraryUpdateFailed": "Könyvtár frissítése sikertelen",
|
||||||
|
"ToastLibraryUpdateSuccess": "\"{0}\" könyvtár frissítve",
|
||||||
|
"ToastPlaylistCreateFailed": "Lejátszási lista létrehozása sikertelen",
|
||||||
|
"ToastPlaylistCreateSuccess": "Lejátszási lista létrehozva",
|
||||||
|
"ToastPlaylistRemoveFailed": "Lejátszási lista eltávolítása sikertelen",
|
||||||
|
"ToastPlaylistRemoveSuccess": "Lejátszási lista eltávolítva",
|
||||||
|
"ToastPlaylistUpdateFailed": "Lejátszási lista frissítése sikertelen",
|
||||||
|
"ToastPlaylistUpdateSuccess": "Lejátszási lista frissítve",
|
||||||
|
"ToastPodcastCreateFailed": "Podcast létrehozása sikertelen",
|
||||||
|
"ToastPodcastCreateSuccess": "Podcast sikeresen létrehozva",
|
||||||
|
"ToastRemoveItemFromCollectionFailed": "Tétel eltávolítása a gyűjteményből sikertelen",
|
||||||
|
"ToastRemoveItemFromCollectionSuccess": "Tétel eltávolítva a gyűjteményből",
|
||||||
|
"ToastRSSFeedCloseFailed": "RSS feed bezárása sikertelen",
|
||||||
|
"ToastRSSFeedCloseSuccess": "RSS feed bezárva",
|
||||||
|
"ToastSendEbookToDeviceFailed": "E-könyv küldése az eszközre sikertelen",
|
||||||
|
"ToastSendEbookToDeviceSuccess": "E-könyv elküldve az eszközre \"{0}\"",
|
||||||
|
"ToastSeriesUpdateFailed": "Sorozat frissítése sikertelen",
|
||||||
|
"ToastSeriesUpdateSuccess": "Sorozat frissítése sikeres",
|
||||||
|
"ToastSessionDeleteFailed": "Munkamenet törlése sikertelen",
|
||||||
|
"ToastSessionDeleteSuccess": "Munkamenet törölve",
|
||||||
|
"ToastSocketConnected": "Socket csatlakoztatva",
|
||||||
|
"ToastSocketDisconnected": "Socket lecsatlakoztatva",
|
||||||
|
"ToastSocketFailedToConnect": "A Socket csatlakoztatása sikertelen",
|
||||||
|
"ToastUserDeleteFailed": "Felhasználó törlése sikertelen",
|
||||||
|
"ToastUserDeleteSuccess": "Felhasználó törölve"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user