Compare commits

...

287 Commits

Author SHA1 Message Date
advplyr
e05cb0ef4d Version bump v2.16.2 2024-10-29 16:11:36 -05:00
advplyr
925c7f7dc7 Merge pull request #3566 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-29 16:04:27 -05:00
Charlie
c69e97ea24 Translated using Weblate (French)
Currently translated at 95.7% (1025 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-10-29 22:00:15 +01:00
advplyr
5e2aebc724 Merge pull request #3565 from mikiher/handle-download-errors-2
Fix incorrect call to handleDownloadError
2024-10-29 15:55:37 -05:00
advplyr
6eba467b91 Fix:Session sync for streaming podcast episodes using incorrect duration #3560 2024-10-29 15:41:31 -05:00
mikiher
524cf5ec5b Fix incorrect call to handleDownloadError 2024-10-29 21:42:44 +02:00
advplyr
50fd659749 Version bump v2.16.1 2024-10-28 17:05:47 -05:00
advplyr
8169afb59b Merge pull request #3554 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-28 17:01:24 -05:00
SunSpring
d40086fea1 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-10-28 23:00:51 +01:00
biuklija
399c40debd Translated using Weblate (Croatian)
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-28 23:00:50 +01:00
Vito0912
d986673dfd Translated using Weblate (German)
Currently translated at 99.8% (1069 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-28 23:00:50 +01:00
thehijacker
f83f4d41f1 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-28 23:00:49 +01:00
Dmitry
7ed711730e Translated using Weblate (Russian)
Currently translated at 100.0% (1071 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2024-10-28 23:00:49 +01:00
Frantisek Nagy
94e2ea9df3 Translated using Weblate (Hungarian)
Currently translated at 75.6% (810 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2024-10-28 23:00:48 +01:00
Bálint Kristóf
8c8c4a15c3 Translated using Weblate (Hungarian)
Currently translated at 75.6% (810 of 1071 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2024-10-28 23:00:48 +01:00
advplyr
2a9159f106 Merge pull request #3553 from mikiher/handle-download-errors
Add proper error handing for file downloads
2024-10-28 17:00:40 -05:00
advplyr
8f113d17c2 Fix:Ensure library has all settings defined when validating settings for update #3559 2024-10-28 16:57:37 -05:00
mikiher
9084055b95 Add proper error handing for file downloads 2024-10-28 08:03:31 +02:00
advplyr
fba9cce82e Version bump v2.16.0 2024-10-27 15:15:44 -05:00
advplyr
92cfb46c14 Merge pull request #3542 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-27 15:10:16 -05:00
thehijacker
449dc1a0e2 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1070 of 1070 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-26 20:34:51 +00:00
SunSpring
d9c345b0f3 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1070 of 1070 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-10-26 20:34:50 +00:00
Ahetek
69a639f76c Translated using Weblate (Polish)
Currently translated at 75.5% (806 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2024-10-26 20:34:50 +00:00
Mathias Franco
d576efe759 Translated using Weblate (Dutch)
Currently translated at 100.0% (1067 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-10-26 20:34:49 +00:00
biuklija
9ba2ecbc21 Translated using Weblate (Croatian)
Currently translated at 100.0% (1067 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-26 20:34:48 +00:00
Henning
84003cd67e Translated using Weblate (German)
Currently translated at 99.5% (1062 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-26 20:34:48 +00:00
kuci-JK
be8c447216 Translated using Weblate (Czech)
Currently translated at 83.5% (891 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2024-10-26 20:34:47 +00:00
SunSpring
e534daf5d4 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1067 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-10-26 20:34:47 +00:00
Mathias Franco
1fefc1af92 Translated using Weblate (Dutch)
Currently translated at 92.8% (991 of 1067 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-10-26 20:34:46 +00:00
thehijacker
e76c4ed2a4 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1064 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-26 20:34:46 +00:00
Mathias Franco
e1caf13233 Translated using Weblate (Dutch)
Currently translated at 83.7% (891 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-10-26 20:34:45 +00:00
Plazec
a7a2fbbca8 Translated using Weblate (Czech)
Currently translated at 83.3% (887 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2024-10-26 20:34:44 +00:00
Mathias Franco
28d93d9160 Translated using Weblate (Dutch)
Currently translated at 83.0% (884 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-10-26 20:34:44 +00:00
gallegonovato
4e90f90c28 Translated using Weblate (Spanish)
Currently translated at 97.0% (1033 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-26 20:34:43 +00:00
Plazec
2243fdddd3 Translated using Weblate (Czech)
Currently translated at 82.8% (882 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2024-10-26 20:34:43 +00:00
biuklija
39be3a2ef9 Translated using Weblate (Croatian)
Currently translated at 100.0% (1064 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-26 20:34:42 +00:00
Austin Spencer
ecc30b85bc Allow users to create ereaders (#3531)
* add create eReader permission toggle

* add english label for create EReader permission

* add ereader table to account with user specific modal

* add createEreader permission

* create api endpoint and logic for updating user eReader devices

* add translated label for createEreader permission

* handle name duplicates and remove helper func

* toast for duplicate name error caught on server

* restrict user ereader updates to devices with sole ownership

* remove label

* fix other devices logic and client socket emitter

* fix for deleting ereaders

* User create ereader endpoint validate accessibility

---------

Co-authored-by: advplyr <advplyr@protonmail.com>
2024-10-26 15:34:34 -05:00
advplyr
6905b288d2 Fix:Latest version displayed when update is available 2024-10-26 14:57:04 -05:00
advplyr
0782146682 Update:Pass mark as finished library settings to media progress update #837 2024-10-25 17:27:50 -05:00
advplyr
91aea4f754 Add:Library settings for mark as finished when time remaining or percent complete #837 2024-10-24 17:19:51 -05:00
advplyr
6ca277a21d Update:Library settings tab settings in 2 columns and cleanup 2024-10-23 17:11:41 -05:00
advplyr
c47c75aefe Update:More strings localized #3544 2024-10-22 17:24:31 -05:00
advplyr
9896e4381b Update:Setup variables to control when a media item is marked as finished. By time remaining or progress percentage #837 2024-10-21 17:48:02 -05:00
advplyr
953ffe889e Update:Book series embeds in grouping meta tag as semicolon deliminated, book meta tag parser falls back to using grouping tag for series if set #3473 2024-10-20 16:58:13 -05:00
advplyr
72e59e77a7 Merge pull request #3536 from nichwall/migration_indexes
Migration indexes
2024-10-19 15:51:12 -05:00
advplyr
35e2681ea9 Update index creation migration to be idempotent 2024-10-19 15:45:14 -05:00
Nicholas Wallace
84012d9090 Fix: podcast episode index name 2024-10-19 11:38:34 -07:00
Nicholas Wallace
e8a1ea3b54 Fix: table naming 2024-10-19 11:20:29 -07:00
Nicholas Wallace
ea6882d9ab Update changelog 2024-10-19 11:20:22 -07:00
Nicholas Wallace
1fa80e31d1 Add: migrations for authors, series, and podcast episodes 2024-10-19 10:40:17 -07:00
advplyr
d80752cc9d Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-10-18 16:25:12 -05:00
advplyr
b764e848c7 Version bump v2.15.1 2024-10-18 16:25:07 -05:00
advplyr
b037c4e8a3 Merge pull request #3532 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-18 16:23:32 -05:00
thehijacker
6ba2360790 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1064 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-18 23:10:42 +02:00
SunSpring
ca4eb507f0 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1064 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-10-18 23:10:41 +02:00
biuklija
965b094470 Translated using Weblate (Croatian)
Currently translated at 99.9% (1063 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-18 23:10:41 +02:00
gallegonovato
0fe313ecfd Translated using Weblate (Spanish)
Currently translated at 96.8% (1031 of 1064 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-18 23:10:40 +02:00
SunSpring
35a2f8d44f Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-10-18 23:10:40 +02:00
mikiher
50797879d5 Add a REINDEX NOCASE v2.15.1 migration and update v2.15.0 migration (#3533)
* Add REINDEX NOCASE migration and update v2.15.0 migration

* Update v2.15.0 migration test

* Fix typo
2024-10-18 16:10:29 -05:00
Nicholas W
9327331ee9 Localization updates for 2.15.0 (#3520)
* Add: episode edit dropdowns

* Update: lazy episode table and row

* Various string updates

* Batch quick match strings

* Author card strings

* Update translation key for quick match episodes confirm

---------

Co-authored-by: advplyr <advplyr@protonmail.com>
2024-10-17 17:03:08 -05:00
advplyr
1c15007e32 Merge pull request #3529 from asoluter/patch-1
Fix "Extract Cover Error" for files with multiple embedded covers
2024-10-17 16:44:58 -05:00
advplyr
2151ffa114 Merge pull request #3530 from mikiher/subdirectory-fixes-2
Add server proxies for all server paths
2024-10-17 16:00:48 -05:00
mikiher
49ed208a54 Add dev proxies for all server path 2024-10-17 11:25:57 +03:00
Ihor Sofiichenko
d668462529 Fix Extract Cover Error for files with multiple embedded covers 2024-10-17 00:27:21 -07:00
advplyr
f2102a0a23 Merge pull request #3512 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-16 17:42:46 -05:00
biuklija
5efc6b82c1 Translated using Weblate (Croatian)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-17 00:42:17 +02:00
thehijacker
1e4e9768da Translated using Weblate (Slovenian)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-17 00:42:17 +02:00
Daniel Schosser
cc5109c305 Translated using Weblate (German)
Currently translated at 98.8% (1011 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-17 00:42:16 +02:00
Mathias Franco
e858d6a1d5 Translated using Weblate (Dutch)
Currently translated at 68.7% (703 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-10-17 00:42:15 +02:00
biuklija
b4cd5d2862 Translated using Weblate (Croatian)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-17 00:42:14 +02:00
DiamondtipDR
0633a44cfb Translated using Weblate (Spanish)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-17 00:42:13 +02:00
Vito0912
5748126b83 Translated using Weblate (German)
Currently translated at 97.8% (1001 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-17 00:42:12 +02:00
thehijacker
06375743a3 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-17 00:42:12 +02:00
Ahetek
2a41c186aa Translated using Weblate (Polish)
Currently translated at 77.6% (794 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2024-10-17 00:42:11 +02:00
burghy86
af51b7254c Translated using Weblate (Italian)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-10-17 00:42:11 +02:00
Charlie
f63dfd769f Translated using Weblate (French)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-10-17 00:42:10 +02:00
apineiro97
a1512f3174 Translated using Weblate (Spanish)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-17 00:42:09 +02:00
gallegonovato
245751e2ce Translated using Weblate (Spanish)
Currently translated at 100.0% (1023 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-17 00:42:09 +02:00
Alexander Künzel
37001d9425 Translated using Weblate (German)
Currently translated at 97.0% (993 of 1023 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-17 00:42:08 +02:00
advplyr
9d1f51c6ba Add in /dev proxy for development 2024-10-16 17:42:00 -05:00
advplyr
cb234fe1fc Merge pull request #3521 from mikiher/subdirectory-fixes
Fixes and cleanup for subdirectory serving support
2024-10-15 16:55:54 -05:00
advplyr
cb85e0255b Fix share URLs on dev 2024-10-15 16:52:04 -05:00
advplyr
61b4cfdab7 Merge pull request #3518 from glorenzen/fix-decade-filter
Fix and simplify filter logic for publishedDecades
2024-10-15 16:19:36 -05:00
advplyr
d2c405c126 Fix decade filter and query by casting publishedYear to Int 2024-10-15 16:12:56 -05:00
mikiher
cbca560f92 server.js: add base path to all non-base-path requests 2024-10-15 06:40:14 +03:00
mikiher
2d7b63b4cf Add base path to socket.io connections on client and server 2024-10-15 05:50:23 +03:00
Greg Lorenzen
217038b085 Fix and simplify filter logic for publishedDecades 2024-10-14 20:58:09 +00:00
advplyr
13dd4edd6a Fix:Ignore dot files in migrations folder #3510 2024-10-14 14:46:55 -05:00
advplyr
a7288b4fbf Merge pull request #3514 from koralowiec/chore/docs-nginx-client-max-body-size
chore(docs): add client_max_body_size parameter in nginx config
2024-10-14 13:04:35 -05:00
koralowiec
3020e8104e chore(docs): change indentation in nginx config example 2024-10-14 17:22:39 +00:00
koralowiec
8fdeeaaf38 chore(docs): add client_max_body_size in nginx example 2024-10-14 17:20:08 +00:00
mikiher
42616b59de Cleanup: remove explicit localhost:3333 and remove unnessesary if(dev) blocks 2024-10-14 13:30:17 +03:00
mikiher
bf16681bea Merge branch 'subdirectory-fixes' of https://github.com/mikiher/audiobookshelf into subdirectory-fixes 2024-10-14 13:19:30 +03:00
mikiher
027190b5a4 Merge branch 'advplyr:master' into subdirectory-fixes 2024-10-14 13:18:04 +03:00
mikiher
241c02be30 nuxt.config.js: more cleanup and additional proxies 2024-10-14 13:12:10 +03:00
advplyr
dd87268848 Merge pull request #3508 from mikiher/fix-share-player-chapters
Fix next/previous chapter behavior on public share player
2024-10-13 14:29:57 -05:00
mikiher
f2ac24e623 Fix next/previous chapter behavior on public share player 2024-10-13 10:56:38 +03:00
advplyr
80e0cac474 Version bump v2.15.0 2024-10-12 16:18:45 -05:00
advplyr
37273dd51c Merge pull request #3486 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-12 16:06:27 -05:00
Languages add-on
926a85fff0 Added translation using Weblate (English (United States)) 2024-10-12 20:57:08 +00:00
J. Lavoie
70273ba2ba Translated using Weblate (Italian)
Currently translated at 99.7% (991 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-10-12 20:57:08 +00:00
J. Lavoie
158cdeed57 Translated using Weblate (French)
Currently translated at 100.0% (993 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-10-12 20:57:07 +00:00
J. Lavoie
ba9595a1be Translated using Weblate (German)
Currently translated at 100.0% (993 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-12 20:57:07 +00:00
Mathias Franco
347e3ff674 Translated using Weblate (Dutch)
Currently translated at 67.6% (672 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-10-12 20:57:06 +00:00
gallegonovato
2b6fb46cdb Translated using Weblate (Spanish)
Currently translated at 100.0% (993 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-12 20:57:06 +00:00
biuklija
465775bd55 Translated using Weblate (Croatian)
Currently translated at 100.0% (993 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-12 20:57:05 +00:00
thehijacker
44e82fc454 Translated using Weblate (Slovenian)
Currently translated at 100.0% (993 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-12 20:57:04 +00:00
thehijacker
c4963d0de8 Translated using Weblate (Slovenian)
Currently translated at 100.0% (993 of 993 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-12 20:57:04 +00:00
thehijacker
ff81d70cb1 Translated using Weblate (Slovenian)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-12 20:57:03 +00:00
Petras Šukys
d7a543e143 Translated using Weblate (Lithuanian)
Currently translated at 71.1% (705 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/lt/
2024-10-12 20:57:03 +00:00
biuklija
cba547083d Translated using Weblate (Croatian)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-12 20:57:02 +00:00
gallegonovato
47b1d2a2c2 Translated using Weblate (Spanish)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-12 20:57:02 +00:00
Daniel Schosser
abc378954c Translated using Weblate (German)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-12 20:57:01 +00:00
Soaibuzzaman
fdf871af17 Translated using Weblate (Bengali)
Currently translated at 99.8% (990 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bn/
2024-10-12 20:57:01 +00:00
thehijacker
83fcb0efdc Translated using Weblate (Slovenian)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-10-12 20:57:00 +00:00
DiamondtipDR
0c43f3d15a Translated using Weblate (Spanish)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-12 20:57:00 +00:00
Alexander Künzel
88e087d50f Translated using Weblate (German)
Currently translated at 99.8% (990 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-12 20:56:59 +00:00
gallegonovato
a9fb6eb8bc Translated using Weblate (Spanish)
Currently translated at 100.0% (991 of 991 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-10-12 20:56:58 +00:00
Charlie
08acfdcd24 Translated using Weblate (French)
Currently translated at 100.0% (990 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-10-12 20:56:58 +00:00
K. J
576eb9106f Translated using Weblate (German)
Currently translated at 100.0% (990 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-10-12 20:56:57 +00:00
advplyr
ddd2c0ae4e Add:Filter for missing chapters & alphabetize missing subitems #3497 2024-10-12 15:56:49 -05:00
advplyr
e58d7db03b Merge pull request #3417 from nichwall/series_cleanup_2
Add: series migration to be unique
2024-10-12 15:48:04 -05:00
advplyr
1cac42aec5 Add localization on logs page and confirm embed #3495 2024-10-12 15:32:51 -05:00
advplyr
f94449a659 Merge pull request #3500 from nichwall/2_14_0_strings
2.14.0 string localization
2024-10-12 15:25:41 -05:00
advplyr
df6afc957f Add localization for notification descriptions 2024-10-12 15:22:21 -05:00
mikiher
99ffd3050c Cleanup: Define routerBasePath constant in nuxt.config.js 2024-10-12 11:46:44 +03:00
mikiher
69dd82d329 Remove unneeded /dev routing 2024-10-12 11:18:49 +03:00
advplyr
076f71d490 Fix:Handle undefined page/limit in paginated library queries #3499 2024-10-11 17:15:16 -05:00
advplyr
33eae1e03a Fix:Server crash on podcast add page, adds API endpoint to get podcast titles #3499
- Instead of loading all podcast library items this page now loads only the needed data
2024-10-11 16:55:09 -05:00
Nicholas Wallace
8a20510cde Localize: subtitle books 2024-10-10 22:12:31 -07:00
Nicholas Wallace
c33b470fca Tools Manager strings 2024-10-10 21:58:17 -07:00
Nicholas Wallace
29db5f1990 Update: tools strings 2024-10-10 21:21:15 -07:00
Nicholas Wallace
f98f78a5bd Podcast search strings 2024-10-10 21:14:51 -07:00
advplyr
d258b42e01 Fix:Podcast episode batch mark as finished only showing for admin and up #3496 2024-10-10 08:03:47 -05:00
advplyr
a6da32430f Merge pull request #3492 from mikiher/author-image-ar
Use object-cover for author images unless AR is really high or low
2024-10-09 17:31:24 -05:00
advplyr
cfae607310 AuthorImage remove aspectRatio unused var 2024-10-09 17:22:38 -05:00
mikiher
7653e72e88 Use object-cover for author images unless AR is really high or low. 2024-10-09 15:04:25 +03:00
Greg Lorenzen
f38b6636e3 Add published decade filter option (#3489)
* Add strings for PublishedDecade and PublishedDecades

* Add publishedDecades filter options to LibraryFilterSelect

* Add publishedDecades to libraries store

* Add publishedDecades to getFilterData

* Add database method to add published decades to filter data

* Add published decade in BookScanner

* Add 'publishedDecades' to invalidFilters in user.js

* Add publishedDecades filter group to MediaGroupQuery

* Update client/strings/en-us.json

* Auto formatting

---------

Co-authored-by: advplyr <dev@advplyr.com>
Co-authored-by: advplyr <advplyr@protonmail.com>
2024-10-08 17:20:42 -05:00
advplyr
e42db121ea Merge pull request #3491 from thatguy7/unicode-author-series
retire unicode handling workaround for Author and Series title
2024-10-08 17:04:21 -05:00
advplyr
0adceaa3f0 Remove asciiOnlyToLowerCase 2024-10-08 16:59:45 -05:00
Oleg Ivasenko
e6db1495ab retire unicode handling workaround for Author and Series title 2024-10-08 19:52:26 +00:00
Nicholas Wallace
e6e494a92c Rename for next minor release 2024-10-07 18:52:14 -07:00
advplyr
549f95b259 Merge pull request #3488 from mikiher/nunicode-musl
Use musl-based libnusqlite3 in Docker
2024-10-07 16:43:38 -05:00
mikiher
d92626071e Use musl-based libnusqlite3 in Docker 2024-10-07 20:48:52 +03:00
advplyr
a7ac82b023 Merge pull request #3487 from mikiher/lazy-bookshelf-authors
Move authors to LazyBookshelf
2024-10-06 16:32:42 -05:00
advplyr
64b78b5822 Move pagination limit/page query param validation to middleware & check for positive integer 2024-10-06 16:29:30 -05:00
advplyr
8ba17db877 Fix authors button in SideRail selected 2024-10-06 15:58:23 -05:00
mikiher
6820d9ae4e Fix AuthorCard component test 2024-10-06 18:57:13 +03:00
mikiher
0bdc2fb05e Move authors to lazyBookshelf 2024-10-06 18:25:08 +03:00
advplyr
cf5598aeb9 Version bump v2.14.0 2024-10-05 16:10:07 -05:00
advplyr
8cf3d648ea Merge pull request #3478 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-10-05 15:58:50 -05:00
SunSpring
212311a980 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (990 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-10-05 06:45:04 +02:00
biuklija
c9522dc25d Translated using Weblate (Croatian)
Currently translated at 100.0% (990 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-10-03 23:34:54 +00:00
kuci-JK
37af753402 Translated using Weblate (Czech)
Currently translated at 88.8% (880 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2024-10-03 23:34:54 +00:00
advplyr
d8c5627cf8 Update audio player volume slider to be vertical 2024-10-03 18:34:43 -05:00
advplyr
4f926b37db Update:Remove server settings update toast on config page 2024-10-02 18:10:51 -05:00
advplyr
fefc16bd13 Fix:Use region for author search by name #3470 2024-10-01 16:14:51 -05:00
advplyr
1b1b71a9b6 Merge pull request #3468 from mikiher/nunicode-intergration
Nunicode integration
2024-10-01 15:17:54 -05:00
mikiher
086532652e Fix to NUSQLITE3_PATH in index.js 2024-10-01 17:15:44 +03:00
mikiher
4e8b4720a1 Merge branch 'nunicode-intergration' of https://github.com/mikiher/audiobookshelf into nunicode-intergration 2024-10-01 16:48:14 +03:00
mikiher
4a7ada28fb Switch to nunicode-binaries v1.1 2024-10-01 16:47:40 +03:00
advplyr
1710285674 Merge pull request #3460 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-09-30 17:19:04 -05:00
tonttula
a6bb61d998 Translated using Weblate (Finnish)
Currently translated at 44.0% (436 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-09-30 22:11:00 +00:00
gallegonovato
5ec05dfa84 Translated using Weblate (Spanish)
Currently translated at 100.0% (990 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-09-30 22:11:00 +00:00
Alexander Künzel
83e854aa13 Translated using Weblate (German)
Currently translated at 99.1% (982 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-09-30 22:10:59 +00:00
thehijacker
634f809159 Translated using Weblate (Slovenian)
Currently translated at 100.0% (990 of 990 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-09-30 22:10:59 +00:00
Hosted Weblate
e5cf141834 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/
2024-09-30 22:10:58 +00:00
biuklija
8610b68d3f Translated using Weblate (Croatian)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-30 22:10:57 +00:00
Alexander Künzel
f3e3bddc94 Translated using Weblate (German)
Currently translated at 99.2% (999 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-09-30 22:10:56 +00:00
tonttula
7ef3284cc5 Translated using Weblate (Finnish)
Currently translated at 42.9% (433 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-09-30 22:10:55 +00:00
Mihály Hunyady
3494586f77 Translated using Weblate (Hungarian)
Currently translated at 81.1% (817 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2024-09-30 22:10:55 +00:00
biuklija
faaf99e6bb Translated using Weblate (Croatian)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-30 22:10:54 +00:00
tonttula
1078ba2111 Translated using Weblate (Finnish)
Currently translated at 35.1% (354 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-09-30 22:10:53 +00:00
ti777777
2ad69300f5 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 74.9% (755 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hant/
2024-09-30 22:10:52 +00:00
Soaibuzzaman
d2f3fa7fdf Translated using Weblate (Bengali)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bn/
2024-09-30 22:10:52 +00:00
advplyr
64fcb6270b Fix i18n strings out of order 2024-09-30 17:10:42 -05:00
advplyr
562c30cff4 Replace failed to update toasts with one generic string 2024-09-29 17:53:52 -05:00
advplyr
7108501d24 Add libnusqlite3 to gitignore 2024-09-29 11:37:13 -05:00
mikiher
37eae3406c Remove debug messages 2024-09-29 12:27:30 +03:00
mikiher
501dc938e6 Add Nunicode sqlite extension integration 2024-09-29 09:22:39 +03:00
advplyr
c5ecd35fe9 Remove toasts after uploading #3352 2024-09-28 16:41:56 -05:00
advplyr
7cd8d7f44d Update NotificationManager to singleton 2024-09-27 17:33:23 -05:00
advplyr
567a9a4e58 Fix:API /libraries/:library/items validate limit and page are positive integers #3459 2024-09-26 16:48:38 -05:00
advplyr
58f4a0cfbb Merge pull request #3461 from mpgirro/oci-image-source
Add OpenContainers Annotations as Labels to Docker Image
2024-09-26 16:34:23 -05:00
Maximilian Irro
e6c0b697aa Add OpenContainer Image Format Annotations as Labels to Docker Image 2024-09-26 21:02:46 +02:00
advplyr
35f60d699d Merge pull request #3427 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-09-25 17:02:36 -05:00
Vili Kangas
c219be0970 Translated using Weblate (Finnish)
Currently translated at 31.7% (320 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-09-25 23:56:14 +02:00
Charlie
c72ce843fa Translated using Weblate (French)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-09-25 23:56:14 +02:00
Charlie
c606059a3a Translated using Weblate (French)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-09-25 23:56:14 +02:00
Philip Karlsson
049a8bdc6d Translated using Weblate (Swedish)
Currently translated at 70.5% (710 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2024-09-25 23:56:14 +02:00
burghy86
9752f744ca Translated using Weblate (Italian)
Currently translated at 96.8% (975 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-09-25 23:56:14 +02:00
biuklija
4be6fb789c Translated using Weblate (Croatian)
Currently translated at 98.2% (989 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-25 23:56:14 +02:00
thehijacker
afc56e5259 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-09-25 23:56:14 +02:00
SunSpring
d47f8521d5 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-09-25 23:56:14 +02:00
gallegonovato
7f853d426a Translated using Weblate (Spanish)
Currently translated at 100.0% (1007 of 1007 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-09-25 23:56:14 +02:00
burghy86
e9008c615d Translated using Weblate (Italian)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-09-25 23:56:14 +02:00
Charlie
01f081ef5a Translated using Weblate (French)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-09-25 23:56:14 +02:00
Charlie
7ee174e0d5 Translated using Weblate (French)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-09-25 23:56:14 +02:00
thehijacker
24439f86e0 Translated using Weblate (Slovenian)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2024-09-25 23:56:14 +02:00
Reimo Vellemaa
fbd3ce3b72 Translated using Weblate (Estonian)
Currently translated at 77.8% (759 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/et/
2024-09-25 23:56:14 +02:00
Reimo Vellemaa
96f8b54b51 Translated using Weblate (Estonian)
Currently translated at 77.7% (758 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/et/
2024-09-25 23:56:14 +02:00
SunSpring
9c94a78e29 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-09-25 23:56:14 +02:00
RafalHo
a14e3dd137 Translated using Weblate (Polish)
Currently translated at 82.2% (802 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2024-09-25 23:56:14 +02:00
Milo Ivir
e37673bd67 Translated using Weblate (Croatian)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-25 23:56:14 +02:00
Milo Ivir
6aa10d20a1 Translated using Weblate (Croatian)
Currently translated at 99.8% (974 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-25 23:56:14 +02:00
Lasse Slotmann
68a92acb7a Translated using Weblate (Danish)
Currently translated at 69.5% (678 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2024-09-25 23:56:14 +02:00
gallegonovato
8aa7cc9ca5 Translated using Weblate (Spanish)
Currently translated at 99.7% (973 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-09-25 23:56:14 +02:00
Lasse Slotmann
e6c087c3bb Translated using Weblate (Danish)
Currently translated at 69.2% (675 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2024-09-25 23:56:14 +02:00
Mario
39a2097152 Translated using Weblate (German)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-09-25 23:56:14 +02:00
Lasse Slotmann
6a8003917e Translated using Weblate (Danish)
Currently translated at 68.6% (669 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2024-09-25 23:56:14 +02:00
gfbdrgng
d5a17ddc8c Translated using Weblate (Russian)
Currently translated at 100.0% (975 of 975 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2024-09-25 23:56:14 +02:00
advplyr
48bbf0d649 Merge pull request #3453 from glorenzen/center-play-button
Center Play Button
2024-09-25 16:56:09 -05:00
advplyr
0bc58c254f Update playback speed to no longer use font-mono, adjust position of popup 2024-09-25 16:49:24 -05:00
Greg Lorenzen
b2d41f0583 Move playback speed control next to player volume control 2024-09-24 23:17:26 +00:00
Greg Lorenzen
0d31d20f0f Center align player chapter title 2024-09-24 23:00:19 +00:00
advplyr
5154e31c1c Update migration to v2.14.0 2024-09-24 17:06:00 -05:00
advplyr
c67b5e950e Update MigrationManager.test.js - moved migrations ensureDir to init() 2024-09-24 16:54:13 -05:00
advplyr
8a7b5cc87d Ensure series-column-unique migration is idempotent 2024-09-24 16:47:09 -05:00
advplyr
bb7938f66d Update:When merging embedded chapters from multiple files filter out ~0 duration chapters #3361 2024-09-24 10:54:25 -05:00
advplyr
5b22e945da Update:Format numbers on user listening stats chart #3441 2024-09-23 16:36:56 -05:00
advplyr
decde230aa Update:Some logs to include library item id #3440 2024-09-22 14:15:17 -05:00
advplyr
1dec8ae122 Update:Added string localization for tasks #3303 #3352 2024-09-21 14:02:57 -05:00
advplyr
8512d5e693 Update Task object to handle translation keys with subs 2024-09-20 17:18:29 -05:00
advplyr
bb481ccfb4 Update:Chapters page populate ASIN input in lookup modal after match #3428 2024-09-19 17:21:41 -05:00
advplyr
12bce48ef5 Update:Home page refetch items when scanning in first items 2024-09-18 15:30:24 -05:00
advplyr
013c7c776e Merge pull request #3436 from mikiher/create-playback-session-race-condition
Change PlaybackSession createFromOld to use upsert instead of create
2024-09-18 15:14:03 -05:00
advplyr
8f96d20a23 Merge pull request #3435 from mikiher/comic-book-extractors
Move to node-unrar-js for cbr and node-stream-zip for cbz
2024-09-18 14:52:32 -05:00
advplyr
1a8811b69a Remove unused requires 2024-09-18 14:26:10 -05:00
mikiher
d796849d74 Small change to logging of unhandled rejections 2024-09-18 18:44:16 +03:00
mikiher
942bd0859f Change PlaybackSession createFromOld to use upsert instead of create 2024-09-18 18:01:36 +03:00
mikiher
072028c740 Cleanup empty directiories inside the temp extraction dir 2024-09-18 10:16:46 +03:00
mikiher
0d08aecd56 Move from libarchive to node-unrar-js for cbr and node-stream-zip for cbz 2024-09-18 08:28:15 +03:00
Nicholas Wallace
66b290577c Clean up unused parts of statement 2024-09-17 20:00:06 -07:00
advplyr
22ad16e11b Fix:Server crash on scan for library with no metadataPrecedence set #3434 2024-09-17 16:10:32 -05:00
advplyr
2f49a08c7d Merge pull request #3425 from wommy/postcss-options
added postcssOptions to remove npm warning
2024-09-16 14:22:42 -05:00
wommy
fcacda74cb added postcssOptions to remove npm warning 2024-09-15 18:29:23 -04:00
advplyr
fa0c90de70 Merge pull request #3422 from mikiher/parse-comic-metadata-try-catch
Catch file extraction errors in parseComicMetadata
2024-09-15 16:10:13 -05:00
advplyr
c1197314ac Merge pull request #3397 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-09-15 15:34:29 -05:00
mikiher
0b31792660 catch file extraction errors in parseComicMetadata 2024-09-15 11:48:33 +03:00
Nicholas Wallace
8b95dd65d9 Fix: test cases checking the wrong bookSeriesId 2024-09-14 15:43:10 -07:00
Nicholas Wallace
691ed88096 Add more logging, clean up typo 2024-09-14 15:34:38 -07:00
Nicholas Wallace
836d772cd4 Update: remove the same book if occurs multiple times in duplicate series 2024-09-14 15:23:29 -07:00
Nicholas Wallace
999ada03d1 Fix: missing variables 2024-09-14 14:36:47 -07:00
advplyr
b35fabbe55 Update:Collection & playlist Play button renamed to Play All #3320 2024-09-14 16:04:50 -05:00
biuklija
8cd8a157a6 Translated using Weblate (Croatian)
Currently translated at 100.0% (974 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-14 23:04:50 +02:00
burghy86
86aece6828 Translated using Weblate (Italian)
Currently translated at 92.8% (904 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-09-14 23:04:50 +02:00
gfbdrgng
f9edadbafd Translated using Weblate (Russian)
Currently translated at 100.0% (974 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2024-09-14 23:04:49 +02:00
biuklija
6a388cd4fe Translated using Weblate (Croatian)
Currently translated at 100.0% (974 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2024-09-14 23:04:49 +02:00
J. Lavoie
9d17e9ff48 Translated using Weblate (Italian)
Currently translated at 89.6% (873 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-09-14 23:04:49 +02:00
J. Lavoie
662b7d01b8 Translated using Weblate (French)
Currently translated at 100.0% (974 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-09-14 23:04:49 +02:00
J. Lavoie
a19bc4b4e4 Translated using Weblate (German)
Currently translated at 100.0% (974 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-09-14 23:04:49 +02:00
Charlie
a545aa5c39 Translated using Weblate (French)
Currently translated at 100.0% (974 of 974 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-09-14 23:04:49 +02:00
Nicholas Wallace
fa451f362b Add: tests for one book in duplicate series 2024-09-14 12:11:31 -07:00
Nicholas Wallace
868659a2f1 Add: unique constraint on bookseries table 2024-09-14 11:44:19 -07:00
advplyr
8ae62da138 Update migration unit test name 2024-09-14 10:40:01 -05:00
advplyr
bedba39af9 Merge branch 'master' into series_cleanup_2 2024-09-14 10:11:16 -05:00
advplyr
8493e56b11 Merge pull request #3418 from mikiher/fix-database-version-init
Fix MigrationManager initial run behavior
2024-09-14 10:09:46 -05:00
mikiher
21c77dccce Add server migration scripts to pkg assets 2024-09-14 13:05:21 +03:00
mikiher
55164803b0 Fix migrationMeta database version initial value, and move isDatabaseNew logic inside MigrationManager 2024-09-14 08:01:32 +03:00
Nicholas Wallace
c163f84aec Update migration changelog for series name unique 2024-09-13 17:01:48 -07:00
Nicholas Wallace
2711b989e1 Add: series migration to be unique 2024-09-13 16:55:48 -07:00
advplyr
5c49a8ce6a Merge pull request #3407 from agraubert/patch-1
Default deny explicit content to users
2024-09-13 13:24:12 -05:00
advplyr
854f308eae Merge pull request #3410 from mikiher/library-scan-try-catch
Handle library scan failure gracefully
2024-09-13 13:10:46 -05:00
advplyr
16ba6b53ba Merge pull request #3414 from thatguy7/master
Improved handling of Authors and Series with names containing non-ASCII characters
2024-09-13 12:59:01 -05:00
Oleg Ivasenko
0af29a378a use asciiOnlyToLowerCase to match lower function behaviour of SQLite 2024-09-13 17:09:32 +00:00
Oleg Ivasenko
def34a860b when checking if series/author is alread in DB, use case insensitive match only for ASCII names 2024-09-13 16:23:25 +00:00
mikiher
f8034e1b78 scanLibrary fail and cancel handling round 2 2024-09-13 09:23:48 +03:00
advplyr
01fbea02f1 Clean out old unused functions, Device updates for replacing DeviceInfo 2024-09-12 16:36:39 -05:00
advplyr
3d9af89e24 Merge pull request #3411 from justcallmelarry/feature/add-duration-when-creating-sessions
Add duration to local sessions on creation
2024-09-12 15:23:10 -05:00
Lauri Vuorela
d430d9f3ed add new setDuration and use that 2024-09-12 20:05:08 +02:00
Lauri Vuorela
0c24a1e626 add duration to session when creating 2024-09-12 19:46:08 +02:00
mikiher
1099dbe642 Handle library scan failure gracefully 2024-09-12 18:56:52 +03:00
Aaron Graubert
2df3277dcd Server side change to enable default explicit acces for admins 2024-09-11 23:09:04 -06:00
Aaron Graubert
6ae14213f5 Related ui changes for removing default explicit access 2024-09-11 23:08:00 -06:00
Aaron Graubert
61bd029303 Default deny explicit content to users 2024-09-11 22:42:21 -06:00
advplyr
5b09bd8242 Merge pull request #3374 from wommy/update-nuxt-2.18.1
client update: nuxt 2.17.3 -> 2.18.1
2024-09-11 16:28:08 -05:00
advplyr
703477b157 Merge pull request #3405 from mikiher/logger-fixes
Log non-strings into log file like console.log does
2024-09-11 14:31:05 -05:00
mikiher
03ff5d8ae1 Disregard socketListener.level if level >= FATAL 2024-09-11 22:05:38 +03:00
mikiher
220f7ef7cd Resolve some weird unrelated flakiness in BookFinder test 2024-09-11 21:40:31 +03:00
mikiher
682a99dd43 Log non-strings into log file like console.log does 2024-09-11 19:58:30 +03:00
advplyr
fac5de582d Merge pull request #3378 from mikiher/migration-manager
Add db migration management infratructure
2024-09-10 16:50:39 -05:00
advplyr
7cbf9de8ca Update migrations jsdocs 2024-09-10 15:57:07 -05:00
mikiher
6fb1202c1c Put umzug in server/libs and remove unneeded dependencies from it 2024-09-08 21:33:32 +03:00
mikiher
8a28029809 Make migration management more robust 2024-09-07 22:24:19 +03:00
mikiher
b3ce300d32 Fix some packaging and dependency issues 2024-09-04 23:55:16 +03:00
mikiher
3f93b93d9e Add db migration management infratructure 2024-09-04 12:48:10 +03:00
wommy
e32c83db63 npm update nuxt: 2.17.3 -> 2.18.1 2024-09-03 19:36:58 -04:00
198 changed files with 10966 additions and 4231 deletions

View File

@@ -70,6 +70,7 @@ jobs:
uses: docker/build-push-action@v3
with:
tags: ${{ github.event.inputs.tags || steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
context: .
platforms: linux/amd64,linux/arm64
push: true

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@
/ffmpeg*
/ffprobe*
/unicode*
/libnusqlite3*
sw.*
.DS_STORE

View File

@@ -11,20 +11,35 @@ FROM node:20-alpine
ENV NODE_ENV=production
RUN apk update && \
apk add --no-cache --update \
curl \
tzdata \
ffmpeg \
make \
gcompat \
python3 \
g++ \
tini
apk add --no-cache --update \
curl \
tzdata \
ffmpeg \
make \
python3 \
g++ \
tini \
unzip
COPY --from=build /client/dist /client/dist
COPY index.js package* /
COPY server server
ARG TARGETPLATFORM
ENV NUSQLITE3_DIR="/usr/local/lib/nusqlite3"
ENV NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so"
RUN case "$TARGETPLATFORM" in \
"linux/amd64") \
curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-x64.zip" ;; \
"linux/arm64") \
curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-arm64.zip" ;; \
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
esac && \
unzip /tmp/library.zip -d $NUSQLITE3_DIR && \
rm /tmp/library.zip
RUN npm ci --only=production
RUN apk del make python3 g++

View File

@@ -347,6 +347,13 @@ export default {
libraryItemsAdded(libraryItems) {
console.log('libraryItems added', libraryItems)
// First items added to library
const isThisLibrary = libraryItems.some((li) => li.libraryId === this.currentLibraryId)
if (!this.shelves.length && !this.search && isThisLibrary) {
this.fetchCategories()
return
}
const recentlyAddedShelf = this.shelves.find((shelf) => shelf.id === 'recently-added')
if (!recentlyAddedShelf) return

View File

@@ -24,7 +24,7 @@
</div>
<div v-if="shelf.type === 'authors'" class="flex items-center">
<template v-for="entity in shelf.entities">
<cards-author-card :key="entity.id" :author="entity" @hook:updated="updatedBookCard" class="mx-2e" @edit="editAuthor" />
<cards-author-card :key="entity.id" :authorMount="entity" @hook:updated="updatedBookCard" class="mx-2e" @edit="editAuthor" />
</template>
</div>
<div v-if="shelf.type === 'narrators'" class="flex items-center">

View File

@@ -30,7 +30,7 @@
<p v-if="isCollectionsPage" class="text-sm">{{ $strings.ButtonCollections }}</p>
<span v-else class="material-symbols text-lg">&#xe431;</span>
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/authors`" class="flex-grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="flex-grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<p v-if="isAuthorsPage" class="text-sm">{{ $strings.ButtonAuthors }}</p>
<svg v-else class="w-5 h-5" viewBox="0 0 24 24">
<path
@@ -62,7 +62,7 @@
<ui-context-menu-dropdown v-if="!isBatchSelecting && seriesContextMenuItems.length" :items="seriesContextMenuItems" class="mx-px" @action="seriesContextMenuAction" />
</template>
<!-- library & collections page -->
<template v-else-if="page !== 'search' && page !== 'podcast-search' && page !== 'recent-episodes' && !isHome">
<template v-else-if="page !== 'search' && page !== 'podcast-search' && page !== 'recent-episodes' && !isHome && !isAuthorsPage">
<p class="hidden md:block">{{ $formatNumber(numShowing) }} {{ entityName }}</p>
<div class="flex-grow hidden sm:inline-block" />
@@ -92,12 +92,14 @@
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template>
<!-- authors page -->
<template v-else-if="page === 'authors'">
<div class="flex-grow" />
<ui-btn v-if="userCanUpdate && authors?.length && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
<template v-else-if="isAuthorsPage">
<p class="hidden md:block">{{ $formatNumber(numShowing) }} {{ entityName }}</p>
<div class="flex-grow hidden sm:inline-block" />
<ui-btn v-if="userCanUpdate && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
<!-- author sort select -->
<controls-sort-select v-if="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" />
<controls-sort-select 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>
<!-- home page -->
<template v-else-if="isHome">
@@ -117,11 +119,7 @@ export default {
type: Object,
default: () => null
},
searchQuery: String,
authors: {
type: Array,
default: () => []
}
searchQuery: String
},
data() {
return {
@@ -268,7 +266,7 @@ export default {
return this.$route.name === 'library-library-podcast-latest'
},
isAuthorsPage() {
return this.$route.name === 'library-library-authors'
return this.page === 'authors'
},
isAlbumsPage() {
return this.page === 'albums'
@@ -284,6 +282,7 @@ export default {
if (this.isSeriesPage) return this.$strings.LabelSeries
if (this.isCollectionsPage) return this.$strings.LabelCollections
if (this.isPlaylistsPage) return this.$strings.LabelPlaylists
if (this.isAuthorsPage) return this.$strings.LabelAuthors
return ''
},
seriesId() {
@@ -473,42 +472,54 @@ export default {
})
.catch((error) => {
console.error('Failed to re-add series to continue listening', error)
this.$toast.error(this.$strings.ToastItemUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.processingSeries = false
})
},
async fetchAllAuthors() {
// fetch all authors from the server, in the order that they are currently displayed
const response = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/authors?sort=${this.settings.authorSortBy}&desc=${this.settings.authorSortDesc}`)
return response.authors
},
async matchAllAuthors() {
this.processingAuthors = true
for (const author of this.authors) {
const payload = {}
if (author.asin) payload.asin = author.asin
else payload.q = author.name
try {
const authors = await this.fetchAllAuthors()
payload.region = 'us'
if (this.libraryProvider.startsWith('audible.')) {
payload.region = this.libraryProvider.split('.').pop() || 'us'
for (const author of authors) {
const payload = {}
if (author.asin) payload.asin = author.asin
else payload.q = author.name
payload.region = 'us'
if (this.libraryProvider.startsWith('audible.')) {
payload.region = this.libraryProvider.split('.').pop() || 'us'
}
this.$eventBus.$emit(`searching-author-${author.id}`, true)
var response = await this.$axios.$post(`/api/authors/${author.id}/match`, payload).catch((error) => {
console.error('Failed', error)
return null
})
if (!response) {
console.error(`Author ${author.name} not found`)
this.$toast.error(this.$getString('ToastAuthorNotFound', [author.name]))
} else if (response.updated) {
if (response.author.imagePath) console.log(`Author ${response.author.name} was updated`)
else console.log(`Author ${response.author.name} was updated (no image found)`)
} else {
console.log(`No updates were made for Author ${response.author.name}`)
}
this.$eventBus.$emit(`searching-author-${author.id}`, false)
}
this.$eventBus.$emit(`searching-author-${author.id}`, true)
var response = await this.$axios.$post(`/api/authors/${author.id}/match`, payload).catch((error) => {
console.error('Failed', error)
return null
})
if (!response) {
console.error(`Author ${author.name} not found`)
this.$toast.error(this.$getString('ToastAuthorNotFound', [author.name]))
} else if (response.updated) {
if (response.author.imagePath) console.log(`Author ${response.author.name} was updated`)
else console.log(`Author ${response.author.name} was updated (no image found)`)
} else {
console.log(`No updates were made for Author ${response.author.name}`)
}
this.$eventBus.$emit(`searching-author-${author.id}`, false)
} catch (error) {
console.error('Failed to match all authors', error)
this.$toast.error(this.$strings.ToastMatchAllAuthorsFailed)
}
this.processingAuthors = false
},

View File

@@ -19,7 +19,7 @@
<p class="text-xs text-gray-300 italic">{{ Source }}</p>
</div>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xs">Latest: {{ $config.version }}</a>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xs">Latest: {{ versionData.latestVersion }}</a>
</div>
</div>
</template>

View File

@@ -91,6 +91,7 @@ export default {
if (this.page === 'series') return this.$strings.MessageBookshelfNoSeries
if (this.page === 'collections') return this.$strings.MessageBookshelfNoCollections
if (this.page === 'playlists') return this.$strings.MessageNoUserPlaylists
if (this.page === 'authors') return this.$strings.MessageNoAuthors
if (this.hasFilter) {
if (this.filterName === 'Issues') return this.$strings.MessageNoIssues
else if (this.filterName === 'Feed-open') return this.$strings.MessageBookshelfNoRSSFeeds
@@ -111,6 +112,12 @@ export default {
seriesFilterBy() {
return this.$store.getters['user/getUserSetting']('seriesFilterBy')
},
authorSortBy() {
return this.$store.getters['user/getUserSetting']('authorSortBy')
},
authorSortDesc() {
return !!this.$store.getters['user/getUserSetting']('authorSortDesc')
},
orderBy() {
return this.$store.getters['user/getUserSetting']('orderBy')
},
@@ -217,6 +224,8 @@ export default {
this.$store.commit('globals/setEditCollection', entity)
} else if (this.entityName === 'playlists') {
this.$store.commit('globals/setEditPlaylist', entity)
} else if (this.entityName === 'authors') {
this.$store.commit('globals/showEditAuthorModal', entity)
}
},
clearSelectedEntities() {
@@ -457,6 +466,9 @@ export default {
if (this.collapseBookSeries) {
searchParams.set('collapseseries', 1)
}
} else if (this.page === 'authors') {
searchParams.set('sort', this.authorSortBy)
searchParams.set('desc', this.authorSortDesc ? 1 : 0)
} else {
if (this.filterBy && this.filterBy !== 'all') {
searchParams.set('filter', this.filterBy)
@@ -601,6 +613,34 @@ export default {
this.executeRebuild()
}
},
authorAdded(author) {
if (this.entityName !== 'authors') return
console.log(`[LazyBookshelf] authorAdded ${author.id}`, author)
this.resetEntities()
},
authorUpdated(author) {
if (this.entityName !== 'authors') return
console.log(`[LazyBookshelf] authorUpdated ${author.id}`, author)
const indexOf = this.entities.findIndex((ent) => ent && ent.id === author.id)
if (indexOf >= 0) {
this.entities[indexOf] = author
if (this.entityComponentRefs[indexOf]) {
this.entityComponentRefs[indexOf].setEntity(author)
}
}
},
authorRemoved(author) {
if (this.entityName !== 'authors') return
console.log(`[LazyBookshelf] authorRemoved ${author.id}`, author)
const indexOf = this.entities.findIndex((ent) => ent && ent.id === author.id)
if (indexOf >= 0) {
this.entities = this.entities.filter((ent) => ent.id !== author.id)
this.totalEntities--
this.$eventBus.$emit('bookshelf-total-entities', this.totalEntities)
this.executeRebuild()
}
},
shareOpen(mediaItemShare) {
if (this.entityName === 'items' || this.entityName === 'series-books') {
var indexOf = this.entities.findIndex((ent) => ent?.media?.id === mediaItemShare.mediaItemId)
@@ -727,6 +767,9 @@ export default {
this.$root.socket.on('playlist_added', this.playlistAdded)
this.$root.socket.on('playlist_updated', this.playlistUpdated)
this.$root.socket.on('playlist_removed', this.playlistRemoved)
this.$root.socket.on('author_added', this.authorAdded)
this.$root.socket.on('author_updated', this.authorUpdated)
this.$root.socket.on('author_removed', this.authorRemoved)
this.$root.socket.on('share_open', this.shareOpen)
this.$root.socket.on('share_closed', this.shareClosed)
} else {
@@ -756,6 +799,9 @@ export default {
this.$root.socket.off('playlist_added', this.playlistAdded)
this.$root.socket.off('playlist_updated', this.playlistUpdated)
this.$root.socket.off('playlist_removed', this.playlistRemoved)
this.$root.socket.off('author_added', this.authorAdded)
this.$root.socket.off('author_updated', this.authorUpdated)
this.$root.socket.off('author_removed', this.authorRemoved)
this.$root.socket.off('share_open', this.shareOpen)
this.$root.socket.off('share_closed', this.shareClosed)
} else {

View File

@@ -167,7 +167,7 @@ export default {
},
podcastAuthor() {
if (!this.isPodcast) return null
return this.mediaMetadata.author || 'Unknown'
return this.mediaMetadata.author || this.$strings.LabelUnknown
},
hasNextItemInQueue() {
return this.currentPlayerQueueIndex < this.playerQueueItems.length - 1
@@ -251,7 +251,7 @@ export default {
sleepTimerEnd() {
this.clearSleepTimer()
this.playerHandler.pause()
this.$toast.info('Sleep Timer Done.. zZzzZz')
this.$toast.info(this.$strings.ToastSleepTimerDone)
},
cancelSleepTimer() {
this.showSleepTimerModal = false
@@ -525,7 +525,7 @@ export default {
},
showFailedProgressSyncs() {
if (!isNaN(this.syncFailedToast)) this.$toast.dismiss(this.syncFailedToast)
this.syncFailedToast = this.$toast('Progress is not being synced. Restart playback', { timeout: false, type: 'error' })
this.syncFailedToast = this.$toast(this.$strings.ToastProgressIsNotBeingSynced, { timeout: false, type: 'error' })
},
sessionClosedEvent(sessionId) {
if (this.playerHandler.currentSessionId === sessionId) {

View File

@@ -58,7 +58,7 @@
<div v-show="isPlaylistsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/authors`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg class="w-6 h-6" viewBox="0 0 24 24">
<path
fill="currentColor"
@@ -180,7 +180,7 @@ export default {
return this.$route.name === 'library-library-series-id' || this.paramId === 'series'
},
isAuthorsPage() {
return this.$route.name === 'library-library-authors'
return this.libraryBookshelfPage && this.paramId === 'authors'
},
isNarratorsPage() {
return this.$route.name === 'library-library-narrators'

View File

@@ -1,6 +1,6 @@
<template>
<div :style="{ minWidth: cardWidth + 'px', maxWidth: cardWidth + 'px' }">
<nuxt-link :to="`/author/${author.id}`">
<div class="pb-3e" :style="{ minWidth: cardWidth + 'px', maxWidth: cardWidth + 'px' }">
<nuxt-link :to="`/author/${author?.id}`">
<div cy-id="card" @mouseover="mouseover" @mouseleave="mouseleave">
<div cy-id="imageArea" :style="{ height: cardHeight + 'px' }" class="bg-primary box-shadow-book rounded-md relative overflow-hidden">
<!-- Image or placeholder -->
@@ -40,7 +40,7 @@
<script>
export default {
props: {
author: {
authorMount: {
type: Object,
default: () => {}
},
@@ -57,7 +57,8 @@ export default {
data() {
return {
searching: false,
isHovering: false
isHovering: false,
author: null
}
},
computed: {
@@ -68,34 +69,37 @@ export default {
return this.height * this.sizeMultiplier
},
userToken() {
return this.$store.getters['user/getToken']
return this.store.getters['user/getToken']
},
_author() {
return this.author || {}
},
authorId() {
return this._author.id
return this._author?.id || ''
},
name() {
return this._author.name || ''
return this._author?.name || ''
},
asin() {
return this._author.asin || ''
return this._author?.asin || ''
},
numBooks() {
return this._author.numBooks || 0
return this._author?.numBooks || 0
},
store() {
return this.$store || this.$nuxt.$store
},
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
return this.store.getters['user/getUserCanUpdate']
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId
return this.store.state.libraries.currentLibraryId
},
libraryProvider() {
return this.$store.getters['libraries/getLibraryProvider'](this.currentLibraryId) || 'google'
return this.store.getters['libraries/getLibraryProvider'](this.currentLibraryId) || 'google'
},
sizeMultiplier() {
return this.$store.getters['user/getSizeMultiplier']
return this.store.getters['user/getSizeMultiplier']
}
},
methods: {
@@ -121,24 +125,54 @@ export default {
return null
})
if (!response) {
this.$toast.error(`Author ${this.name} not found`)
this.$toast.error(this.$getString('ToastAuthorNotFound', [this.name]))
} else if (response.updated) {
if (response.author.imagePath) this.$toast.success(`Author ${response.author.name} was updated`)
else this.$toast.success(`Author ${response.author.name} was updated (no image found)`)
if (response.author.imagePath) {
this.$toast.success(this.$strings.ToastAuthorUpdateSuccess)
} else {
this.$toast.success(this.$strings.ToastAuthorUpdateSuccessNoImageFound)
}
} else {
this.$toast.info(`No updates were made for Author ${response.author.name}`)
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
}
this.searching = false
},
setSearching(isSearching) {
this.searching = isSearching
}
},
setEntity(author) {
this.removeListeners()
this.author = author
this.addListeners()
},
addListeners() {
if (this.author) {
this.$eventBus.$on(`searching-author-${this.authorId}`, this.setSearching)
}
},
removeListeners() {
if (this.author) {
this.$eventBus.$off(`searching-author-${this.authorId}`, this.setSearching)
}
},
destroy() {
// destroy the vue listeners, etc
this.$destroy()
// remove the element from the DOM
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el)
} else if (this.$el && this.$el.remove) {
this.$el.remove()
}
},
setSelectionMode(val) {}
},
mounted() {
this.$eventBus.$on(`searching-author-${this.authorId}`, this.setSearching)
if (this.authorMount) this.setEntity(this.authorMount)
},
beforeDestroy() {
this.$eventBus.$off(`searching-author-${this.authorId}`, this.setSearching)
this.removeListeners()
}
}
</script>

View File

@@ -8,6 +8,7 @@
<p class="truncate text-sm">{{ title }}</p>
<p class="truncate text-xs text-gray-300">{{ description }}</p>
<p v-if="specialMessage" class="truncate text-xs text-gray-300">{{ specialMessage }}</p>
<p v-if="isFailed && failedMessage" class="text-xs truncate text-red-500">{{ failedMessage }}</p>
<p v-else-if="!isFinished && cancelingScan" class="text-xs truncate">Canceling...</p>
@@ -26,7 +27,16 @@ export default {
},
data() {
return {
cancelingScan: false
cancelingScan: false,
specialMessage: ''
}
},
watch: {
task: {
immediate: true,
handler() {
this.initTask()
}
}
},
computed: {
@@ -34,14 +44,17 @@ export default {
return this.$store.getters['user/getIsAdminOrUp']
},
title() {
if (this.task.titleKey && this.$strings[this.task.titleKey]) {
return this.$getString(this.task.titleKey, this.task.titleSubs)
}
return this.task.title || 'No Title'
},
description() {
if (this.task.descriptionKey && this.$strings[this.task.descriptionKey]) {
return this.$getString(this.task.descriptionKey, this.task.descriptionSubs)
}
return this.task.description || ''
},
details() {
return this.task.details || 'Unknown'
},
isFinished() {
return !!this.task.isFinished
},
@@ -52,6 +65,9 @@ export default {
return this.isFinished && !this.isFailed
},
failedMessage() {
if (this.task.errorKey && this.$strings[this.task.errorKey]) {
return this.$getString(this.task.errorKey, this.task.errorSubs)
}
return this.task.error || ''
},
action() {
@@ -87,6 +103,21 @@ export default {
}
},
methods: {
initTask() {
// special message for library scan tasks
if (this.task?.data?.scanResults) {
const scanResults = this.task.data.scanResults
const strs = []
if (scanResults.added) strs.push(this.$getString('MessageTaskScanItemsAdded', [scanResults.added]))
if (scanResults.updated) strs.push(this.$getString('MessageTaskScanItemsUpdated', [scanResults.updated]))
if (scanResults.missing) strs.push(this.$getString('MessageTaskScanItemsMissing', [scanResults.missing]))
const changesDetected = strs.length > 0 ? strs.join(', ') : this.$strings.MessageTaskScanNoChangesNeeded
const timeElapsed = scanResults.elapsed ? ` (${this.$elapsedPretty(scanResults.elapsed / 1000, false, true)})` : ''
this.specialMessage = `${changesDetected}${timeElapsed}`
} else {
this.specialMessage = ''
}
},
cancelScan() {
const libraryId = this.task?.data?.libraryId
if (!libraryId) {

View File

@@ -325,7 +325,7 @@ export default {
},
displaySubtitle() {
if (!this.libraryItem) return '\u00A0'
if (this.collapsedSeries) return this.collapsedSeries.numBooks === 1 ? '1 book' : `${this.collapsedSeries.numBooks} books`
if (this.collapsedSeries) return `${this.collapsedSeries.numBooks} ${this.$strings.LabelBooks}`
if (this.mediaMetadata.subtitle) return this.mediaMetadata.subtitle
if (this.mediaMetadata.seriesName) return this.mediaMetadata.seriesName
return ''
@@ -816,7 +816,7 @@ export default {
})
.catch((error) => {
console.error('Failed to remove series from home', error)
this.$toast.error(this.$strings.ToastFailedToUpdateUser)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.processing = false
@@ -834,7 +834,7 @@ export default {
})
.catch((error) => {
console.error('Failed to hide item from home', error)
this.$toast.error(this.$strings.ToastFailedToUpdateUser)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.processing = false

View File

@@ -130,7 +130,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update notification', error)
this.$toast.error(this.$strings.ToastNotificationUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.enabling = false

View File

@@ -189,6 +189,12 @@ export default {
value: 'publishers',
sublist: true
},
{
text: this.$strings.LabelPublishedDecade,
textPlural: this.$strings.LabelPublishedDecades,
value: 'publishedDecades',
sublist: true
},
{
text: this.$strings.LabelLanguage,
textPlural: this.$strings.LabelLanguages,
@@ -338,6 +344,9 @@ export default {
publishers() {
return this.filterData.publishers || []
},
publishedDecades() {
return this.filterData.publishedDecades || []
},
progress() {
return [
{
@@ -404,21 +413,17 @@ export default {
id: 'isbn',
name: 'ISBN'
},
{
id: 'subtitle',
name: this.$strings.LabelSubtitle
},
{
id: 'authors',
name: this.$strings.LabelAuthor
},
{
id: 'publishedYear',
name: this.$strings.LabelPublishYear
id: 'chapters',
name: this.$strings.LabelChapters
},
{
id: 'series',
name: this.$strings.LabelSeries
id: 'cover',
name: this.$strings.LabelCover
},
{
id: 'description',
@@ -429,24 +434,32 @@ export default {
name: this.$strings.LabelGenres
},
{
id: 'tags',
name: this.$strings.LabelTags
id: 'language',
name: this.$strings.LabelLanguage
},
{
id: 'narrators',
name: this.$strings.LabelNarrator
},
{
id: 'publishedYear',
name: this.$strings.LabelPublishYear
},
{
id: 'publisher',
name: this.$strings.LabelPublisher
},
{
id: 'language',
name: this.$strings.LabelLanguage
id: 'series',
name: this.$strings.LabelSeries
},
{
id: 'cover',
name: this.$strings.LabelCover
id: 'subtitle',
name: this.$strings.LabelSubtitle
},
{
id: 'tags',
name: this.$strings.LabelTags
}
]
},

View File

@@ -1,9 +1,9 @@
<template>
<div ref="wrapper" class="relative ml-4 sm:ml-8" v-click-outside="clickOutside">
<div class="flex items-center justify-center text-gray-300 cursor-pointer h-full" @mousedown.prevent @mouseup.prevent @click="setShowMenu(true)">
<span class="font-mono uppercase text-gray-200 text-sm sm:text-base">{{ playbackRate.toFixed(1) }}<span class="text-base">x</span></span>
<span class="text-gray-200 text-sm sm:text-base">{{ playbackRate.toFixed(1) }}<span class="text-base">x</span></span>
</div>
<div v-show="showMenu" class="absolute -top-20 z-20 bg-bg border-black-200 border shadow-xl rounded-lg" :style="{ left: menuLeft + 'px' }">
<div v-show="showMenu" class="absolute -top-[5.5rem] z-20 bg-bg border-black-200 border shadow-xl rounded-lg" :style="{ left: menuLeft + 'px' }">
<div class="absolute -bottom-1.5 right-0 w-full flex justify-center" :style="{ left: arrowLeft + 'px' }">
<div class="arrow-down" />
</div>
@@ -11,12 +11,12 @@
<template v-for="rate in rates">
<div :key="rate" class="h-full border-black-300 w-11 cursor-pointer border rounded-sm" :class="value === rate ? 'bg-black-100' : 'hover:bg-black hover:bg-opacity-10'" style="min-width: 44px; max-width: 44px" @click="set(rate)">
<div class="w-full h-full flex justify-center items-center">
<p class="text-xs text-center font-mono">{{ rate }}<span class="text-sm">x</span></p>
<p class="text-xs text-center">{{ rate }}<span class="text-sm">x</span></p>
</div>
</div>
</template>
</div>
<div class="w-full py-1 px-4">
<div class="w-full py-1 px-1">
<div class="flex items-center justify-between">
<ui-icon-btn :disabled="!canDecrement" icon="remove" @click="decrement" />
<p class="px-2 text-2xl sm:text-3xl">{{ playbackRate }}<span class="text-2xl">x</span></p>
@@ -41,7 +41,7 @@ export default {
currentPlaybackRate: 0,
MIN_SPEED: 0.5,
MAX_SPEED: 10,
menuLeft: -92,
menuLeft: -96,
arrowLeft: 0
}
},
@@ -89,9 +89,9 @@ export default {
if (boundingBox.left + 110 > window.innerWidth - 10) {
this.menuLeft = window.innerWidth - 230 - boundingBox.left
this.arrowLeft = Math.abs(this.menuLeft) - 92
this.arrowLeft = Math.abs(this.menuLeft) - 96
} else {
this.menuLeft = -92
this.menuLeft = -96
this.arrowLeft = 0
}
},
@@ -109,4 +109,4 @@ export default {
this.currentPlaybackRate = this.playbackRate
}
}
</script>
</script>

View File

@@ -4,10 +4,10 @@
<span class="material-symbols text-2xl sm:text-3xl">{{ volumeIcon }}</span>
</button>
<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 ref="volumeTrack" class="h-1 w-full bg-gray-500 my-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
<div class="bg-gray-100 h-full absolute left-0 top-0 pointer-events-none rounded-full" :style="{ width: volume * trackWidth + 'px' }" />
<div class="w-2.5 h-2.5 bg-white shadow-sm rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ left: cursorLeft + 'px', top: '-3px' }" />
<div v-show="isOpen" class="volumeMenu h-28 absolute bottom-2 w-6 py-2 bg-bg shadow-sm rounded-lg" style="top: -116px">
<div ref="volumeTrack" class="w-1 h-full bg-gray-500 mx-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
<div class="bg-gray-100 w-full absolute left-0 bottom-0 pointer-events-none rounded-full" :style="{ height: volume * trackHeight + 'px' }" />
<div class="w-2.5 h-2.5 bg-white shadow-sm rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ bottom: cursorBottom + 'px', left: '-3px' }" />
</div>
</div>
</transition>
@@ -24,10 +24,10 @@ export default {
isOpen: false,
isDragging: false,
isHovering: false,
posX: 0,
posY: 0,
lastValue: 0.5,
isMute: false,
trackWidth: 112 - 20,
trackHeight: 112 - 20,
openTimeout: null
}
},
@@ -45,9 +45,9 @@ export default {
this.$emit('input', val)
}
},
cursorLeft() {
var left = this.trackWidth * this.volume
return left - 3
cursorBottom() {
var bottom = this.trackHeight * this.volume
return bottom - 3
},
volumeIcon() {
if (this.volume <= 0) return 'volume_mute'
@@ -89,17 +89,10 @@ export default {
}, 600)
},
mousemove(e) {
var diff = this.posX - e.x
this.posX = e.x
var volShift = 0
if (diff < 0) {
// Volume up
volShift = diff / this.trackWidth
} else {
// volume down
volShift = diff / this.trackWidth
}
var newVol = this.volume - volShift
var diff = this.posY - e.y
this.posY = e.y
var volShift = diff / this.trackHeight
var newVol = this.volume + volShift
newVol = Math.min(Math.max(0, newVol), 1)
this.volume = newVol
e.preventDefault()
@@ -113,8 +106,8 @@ export default {
},
mousedownTrack(e) {
this.isDragging = true
this.posX = e.x
var vol = e.offsetX / this.trackWidth
this.posY = e.y
var vol = 1 - e.offsetY / this.trackHeight
vol = Math.min(Math.max(vol, 0), 1)
this.volume = vol
document.body.addEventListener('mousemove', this.mousemove)
@@ -137,7 +130,7 @@ export default {
this.clickVolumeIcon()
},
clickVolumeTrack(e) {
var vol = e.offsetX / this.trackWidth
var vol = 1 - e.offsetY / this.trackHeight
vol = Math.min(Math.max(vol, 0), 1)
this.volume = vol
}
@@ -147,7 +140,7 @@ export default {
this.isMute = true
}
const storageVolume = localStorage.getItem('volume')
if (storageVolume) {
if (storageVolume && !isNaN(storageVolume)) {
this.volume = parseFloat(storageVolume)
}
},
@@ -157,4 +150,4 @@ export default {
document.body.removeEventListener('mouseup', this.mouseup)
}
}
</script>
</script>

View File

@@ -56,24 +56,15 @@ export default {
},
imgSrc() {
if (!this.imagePath) return null
if (process.env.NODE_ENV !== 'production') {
// Testing
return `http://localhost:3333${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
}
return `/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
}
},
methods: {
imageLoaded() {
var aspectRatio = 1.25
if (this.$refs.wrapper) {
aspectRatio = this.$refs.wrapper.clientHeight / this.$refs.wrapper.clientWidth
}
if (this.$refs.img) {
var { naturalWidth, naturalHeight } = this.$refs.img
var imgAr = naturalHeight / naturalWidth
var arDiff = Math.abs(imgAr - aspectRatio)
if (arDiff > 0.15) {
if (imgAr < 0.5 || imgAr > 2) {
this.showCoverBg = true
} else {
this.showCoverBg = false

View File

@@ -69,6 +69,15 @@
</div>
</div>
<div class="flex items-center my-2 max-w-md">
<div class="w-1/2">
<p id="ereader-permissions-toggle">{{ $strings.LabelPermissionsCreateEreader }}</p>
</div>
<div class="w-1/2">
<ui-toggle-switch labeledBy="ereader-permissions-toggle" v-model="newUser.permissions.createEreader" />
</div>
</div>
<div class="flex items-center my-2 max-w-md">
<div class="w-1/2">
<p id="explicit-content-permissions-toggle">{{ $strings.LabelPermissionsAccessExplicitContent }}</p>
@@ -296,7 +305,7 @@ export default {
.then((data) => {
this.processing = false
if (data.error) {
this.$toast.error(`${this.$strings.ToastAccountUpdateFailed}: ${data.error}`)
this.$toast.error(`${this.$strings.ToastFailedToUpdate}: ${data.error}`)
} else {
console.log('Account updated', data.user)
@@ -313,7 +322,7 @@ export default {
this.processing = false
console.error('Failed to update account', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(errMsg || this.$strings.ToastFailedToUpdateAccount)
this.$toast.error(errMsg || this.$strings.ToastFailedToUpdate)
})
},
submitCreateAccount() {
@@ -351,10 +360,11 @@ export default {
update: type === 'admin',
delete: type === 'admin',
upload: type === 'admin',
accessExplicitContent: true,
accessExplicitContent: type === 'admin',
accessAllLibraries: true,
accessAllTags: true,
selectedTagsNotAccessible: false
selectedTagsNotAccessible: false,
createEreader: type === 'admin'
}
},
init() {
@@ -386,8 +396,9 @@ export default {
upload: false,
accessAllLibraries: true,
accessAllTags: true,
accessExplicitContent: true,
selectedTagsNotAccessible: false
accessExplicitContent: false,
selectedTagsNotAccessible: false,
createEreader: false
},
librariesAccessible: [],
itemTagsSelected: []

View File

@@ -116,10 +116,10 @@ export default {
libraryItemIds: this.selectedBookIds
})
.then(() => {
this.$toast.info('Batch quick match of ' + this.selectedBookIds.length + ' books started!')
this.$toast.info(this.$getString('ToastBatchQuickMatchStarted', [this.selectedBookIds.length]))
})
.catch((error) => {
this.$toast.error('Batch quick match failed')
this.$toast.error(this.$strings.ToastBatchQuickMatchFailed)
console.error('Failed to batch quick match', error)
})
.finally(() => {

View File

@@ -110,7 +110,7 @@ export default {
this.$toast.success(this.$strings.ToastBookmarkUpdateSuccess)
})
.catch((error) => {
this.$toast.error(this.$strings.ToastBookmarkUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
console.error(error)
})
this.show = false

View File

@@ -112,11 +112,11 @@ export default {
return this.$store.state.user.user
},
demoShareUrl() {
return `${window.origin}/share/${this.newShareSlug}`
return `${window.origin}${this.$config.routerBasePath}/share/${this.newShareSlug}`
},
currentShareUrl() {
if (!this.currentShare) return ''
return `${window.origin}/share/${this.currentShare.slug}`
return `${window.origin}${this.$config.routerBasePath}/share/${this.currentShare.slug}`
},
currentShareTimeRemaining() {
if (!this.currentShare) return 'Error'

View File

@@ -148,7 +148,7 @@ export default {
var result = await this.$axios.$patch(`/api/authors/${this.authorId}`, updatePayload).catch((error) => {
console.error('Failed', error)
const errorMsg = error.response ? error.response.data : null
this.$toast.error(errorMsg || this.$strings.ToastAuthorUpdateFailed)
this.$toast.error(errorMsg || this.$strings.ToastFailedToUpdate)
return null
})
if (result) {

View File

@@ -135,7 +135,7 @@ export default {
.catch((error) => {
console.error('Failed to update collection', error)
this.processing = false
this.$toast.error(this.$strings.ToastCollectionUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
}
},

View File

@@ -178,7 +178,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update device', error)
this.$toast.error(this.$strings.ToastDeviceUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.processing = false

View File

@@ -0,0 +1,188 @@
<template>
<modals-modal ref="modal" v-model="show" name="ereader-device-edit" :width="800" :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">{{ title }}</p>
</div>
</template>
<form @submit.prevent="submitForm">
<div class="w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300">
<div class="w-full px-3 py-5 md:p-12">
<div class="flex items-center -mx-1 mb-4">
<div class="w-full md:w-1/2 px-1">
<ui-text-input-with-label ref="ereaderNameInput" v-model="newDevice.name" :disabled="processing" :label="$strings.LabelName" />
</div>
<div class="w-full md:w-1/2 px-1">
<ui-text-input-with-label ref="ereaderEmailInput" v-model="newDevice.email" :disabled="processing" :label="$strings.LabelEmail" />
</div>
</div>
<div class="flex items-center pt-4">
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
</form>
</modals-modal>
</template>
<script>
export default {
props: {
value: Boolean,
existingDevices: {
type: Array,
default: () => []
},
ereaderDevice: {
type: Object,
default: () => null
}
},
data() {
return {
processing: false,
newDevice: {
name: '',
email: '',
availabilityOption: 'adminAndUp',
users: []
}
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.init()
}
}
}
},
computed: {
show: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
user() {
return this.$store.state.user.user
},
title() {
return !this.ereaderDevice ? 'Create Device' : 'Update Device'
}
},
methods: {
submitForm() {
this.$refs.ereaderNameInput.blur()
this.$refs.ereaderEmailInput.blur()
if (!this.newDevice.name?.trim() || !this.newDevice.email?.trim()) {
this.$toast.error(this.$strings.ToastNameEmailRequired)
return
}
this.newDevice.name = this.newDevice.name.trim()
this.newDevice.email = this.newDevice.email.trim()
// Only catches duplicate names for the current user
// Duplicates with other users caught on server side
if (!this.ereaderDevice) {
if (this.existingDevices.some((d) => d.name === this.newDevice.name)) {
this.$toast.error(this.$strings.ToastDeviceNameAlreadyExists)
return
}
this.submitCreate()
} else {
if (this.ereaderDevice.name !== this.newDevice.name && this.existingDevices.some((d) => d.name === this.newDevice.name)) {
this.$toast.error(this.$strings.ToastDeviceNameAlreadyExists)
return
}
this.submitUpdate()
}
},
submitUpdate() {
this.processing = true
const existingDevicesWithoutThisOne = this.existingDevices.filter((d) => d.name !== this.ereaderDevice.name)
const payload = {
ereaderDevices: [
...existingDevicesWithoutThisOne,
{
...this.newDevice
}
]
}
this.$axios
.$post(`/api/me/ereader-devices`, payload)
.then((data) => {
this.$emit('update', data.ereaderDevices)
this.show = false
})
.catch((error) => {
console.error('Failed to update device', error)
if (error.response?.data?.toLowerCase().includes('duplicate')) {
this.$toast.error(this.$strings.ToastDeviceNameAlreadyExists)
} else {
this.$toast.error(this.$strings.ToastDeviceAddFailed)
}
})
.finally(() => {
this.processing = false
})
},
submitCreate() {
this.processing = true
const payload = {
ereaderDevices: [
...this.existingDevices,
{
...this.newDevice
}
]
}
this.$axios
.$post('/api/me/ereader-devices', payload)
.then((data) => {
this.$emit('update', data.ereaderDevices || [])
this.show = false
})
.catch((error) => {
console.error('Failed to add device', error)
if (error.response?.data?.toLowerCase().includes('duplicate')) {
this.$toast.error(this.$strings.ToastDeviceNameAlreadyExists)
} else {
this.$toast.error(this.$strings.ToastDeviceAddFailed)
}
})
.finally(() => {
this.processing = false
})
},
init() {
if (this.ereaderDevice) {
this.newDevice.name = this.ereaderDevice.name
this.newDevice.email = this.ereaderDevice.email
this.newDevice.availabilityOption = this.ereaderDevice.availabilityOption || 'specificUsers'
this.newDevice.users = this.ereaderDevice.users || [this.user.id]
} else {
this.newDevice.name = ''
this.newDevice.email = ''
this.newDevice.availabilityOption = 'specificUsers'
this.newDevice.users = [this.user.id]
}
}
},
mounted() {}
}
</script>

View File

@@ -6,7 +6,7 @@
<ui-text-input-with-label ref="maxEpisodesInput" v-model="maxEpisodesToDownload" :disabled="checkingNewEpisodes" type="number" :label="$strings.LabelLimit" class="w-16 mr-2" input-class="h-10">
<div class="flex -mb-0.5">
<p class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': checkingNewEpisodes }">{{ $strings.LabelLimit }}</p>
<ui-tooltip direction="top" text="Max # of episodes to download. Use 0 for unlimited.">
<ui-tooltip direction="top" :text="$strings.LabelMaxEpisodesToDownload">
<span class="material-symbols text-base">info</span>
</ui-tooltip>
</div>
@@ -99,7 +99,7 @@ export default {
if (this.maxEpisodesToDownload < 0) {
this.maxEpisodesToDownload = 3
this.$toast.error('Invalid max episodes to download')
this.$toast.error(this.$strings.ToastInvalidMaxEpisodesToDownload)
return
}
@@ -120,9 +120,9 @@ export default {
.then((response) => {
if (response.episodes && response.episodes.length) {
console.log('New episodes', response.episodes.length)
this.$toast.success(`${response.episodes.length} new episodes found!`)
this.$toast.success(this.$getString('ToastNewEpisodesFound', [response.episodes.length]))
} else {
this.$toast.info('No new episodes found')
this.$toast.info(this.$strings.ToastNoNewEpisodesFound)
}
this.checkingNewEpisodes = false
})
@@ -141,4 +141,4 @@ export default {
this.setLastEpisodeCheckInput()
}
}
</script>
</script>

View File

@@ -60,7 +60,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.title" :disabled="!selectedMatchUsage.title" :label="$strings.LabelTitle" />
<p v-if="mediaMetadata.title" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('title', mediaMetadata.title)">{{ mediaMetadata.title || '' }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('title', mediaMetadata.title)">{{ mediaMetadata.title || '' }}</a>
</p>
</div>
</div>
@@ -69,7 +69,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.subtitle" :disabled="!selectedMatchUsage.subtitle" :label="$strings.LabelSubtitle" />
<p v-if="mediaMetadata.subtitle" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('subtitle', mediaMetadata.subtitle)">{{ mediaMetadata.subtitle }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('subtitle', mediaMetadata.subtitle)">{{ mediaMetadata.subtitle }}</a>
</p>
</div>
</div>
@@ -78,7 +78,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.author" :disabled="!selectedMatchUsage.author" :label="$strings.LabelAuthor" />
<p v-if="mediaMetadata.authorName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', mediaMetadata.authorName)">{{ mediaMetadata.authorName }}</a>
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', mediaMetadata.authorName)">{{ mediaMetadata.authorName }}</a>
</p>
</div>
</div>
@@ -87,7 +87,7 @@
<div class="flex-grow ml-4">
<ui-multi-select v-model="selectedMatch.narrator" :items="narrators" :disabled="!selectedMatchUsage.narrator" :label="$strings.LabelNarrators" />
<p v-if="mediaMetadata.narratorName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('narrator', mediaMetadata.narrators)">{{ mediaMetadata.narratorName }}</a>
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('narrator', mediaMetadata.narrators)">{{ mediaMetadata.narratorName }}</a>
</p>
</div>
</div>
@@ -96,7 +96,7 @@
<div class="flex-grow ml-4">
<ui-textarea-with-label v-model="selectedMatch.description" :rows="3" :disabled="!selectedMatchUsage.description" :label="$strings.LabelDescription" />
<p v-if="mediaMetadata.description" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.description.substr(0, 100) + (mediaMetadata.description.length > 100 ? '...' : '') }}</a>
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.description.substr(0, 100) + (mediaMetadata.description.length > 100 ? '...' : '') }}</a>
</p>
</div>
</div>
@@ -105,7 +105,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.publisher" :disabled="!selectedMatchUsage.publisher" :label="$strings.LabelPublisher" />
<p v-if="mediaMetadata.publisher" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publisher', mediaMetadata.publisher)">{{ mediaMetadata.publisher }}</a>
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publisher', mediaMetadata.publisher)">{{ mediaMetadata.publisher }}</a>
</p>
</div>
</div>
@@ -114,7 +114,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.publishedYear" :disabled="!selectedMatchUsage.publishedYear" :label="$strings.LabelPublishYear" />
<p v-if="mediaMetadata.publishedYear" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
</p>
</div>
</div>
@@ -124,7 +124,7 @@
<div class="flex-grow ml-4">
<widgets-series-input-widget v-model="selectedMatch.series" :disabled="!selectedMatchUsage.series" />
<p v-if="mediaMetadata.seriesName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('series', mediaMetadata.series)">{{ mediaMetadata.seriesName }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('series', mediaMetadata.series)">{{ mediaMetadata.seriesName }}</a>
</p>
</div>
</div>
@@ -133,7 +133,7 @@
<div class="flex-grow ml-4">
<ui-multi-select v-model="selectedMatch.genres" :items="genres" :disabled="!selectedMatchUsage.genres" :label="$strings.LabelGenres" />
<p v-if="mediaMetadata.genres?.length" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('genres', mediaMetadata.genres)">{{ mediaMetadata.genres.join(', ') }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('genres', mediaMetadata.genres)">{{ mediaMetadata.genres.join(', ') }}</a>
</p>
</div>
</div>
@@ -142,7 +142,7 @@
<div class="flex-grow ml-4">
<ui-multi-select v-model="selectedMatch.tags" :items="tags" :disabled="!selectedMatchUsage.tags" :label="$strings.LabelTags" />
<p v-if="media.tags?.length" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('tags', media.tags)">{{ media.tags.join(', ') }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('tags', media.tags)">{{ media.tags.join(', ') }}</a>
</p>
</div>
</div>
@@ -151,7 +151,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.language" :disabled="!selectedMatchUsage.language" :label="$strings.LabelLanguage" />
<p v-if="mediaMetadata.language" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('language', mediaMetadata.language)">{{ mediaMetadata.language }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('language', mediaMetadata.language)">{{ mediaMetadata.language }}</a>
</p>
</div>
</div>
@@ -160,7 +160,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.isbn" :disabled="!selectedMatchUsage.isbn" label="ISBN" />
<p v-if="mediaMetadata.isbn" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('isbn', mediaMetadata.isbn)">{{ mediaMetadata.isbn }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('isbn', mediaMetadata.isbn)">{{ mediaMetadata.isbn }}</a>
</p>
</div>
</div>
@@ -169,7 +169,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.asin" :disabled="!selectedMatchUsage.asin" label="ASIN" />
<p v-if="mediaMetadata.asin" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('asin', mediaMetadata.asin)">{{ mediaMetadata.asin }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('asin', mediaMetadata.asin)">{{ mediaMetadata.asin }}</a>
</p>
</div>
</div>
@@ -179,7 +179,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.itunesId" type="number" :disabled="!selectedMatchUsage.itunesId" label="iTunes ID" />
<p v-if="mediaMetadata.itunesId" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesId', mediaMetadata.itunesId)">{{ mediaMetadata.itunesId }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesId', mediaMetadata.itunesId)">{{ mediaMetadata.itunesId }}</a>
</p>
</div>
</div>
@@ -188,7 +188,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.feedUrl" :disabled="!selectedMatchUsage.feedUrl" label="RSS Feed URL" />
<p v-if="mediaMetadata.feedUrl" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('feedUrl', mediaMetadata.feedUrl)">{{ mediaMetadata.feedUrl }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('feedUrl', mediaMetadata.feedUrl)">{{ mediaMetadata.feedUrl }}</a>
</p>
</div>
</div>
@@ -197,7 +197,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.itunesPageUrl" :disabled="!selectedMatchUsage.itunesPageUrl" label="iTunes Page URL" />
<p v-if="mediaMetadata.itunesPageUrl" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesPageUrl', mediaMetadata.itunesPageUrl)">{{ mediaMetadata.itunesPageUrl }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesPageUrl', mediaMetadata.itunesPageUrl)">{{ mediaMetadata.itunesPageUrl }}</a>
</p>
</div>
</div>
@@ -206,7 +206,7 @@
<div class="flex-grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.releaseDate" :disabled="!selectedMatchUsage.releaseDate" :label="$strings.LabelReleaseDate" />
<p v-if="mediaMetadata.releaseDate" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('releaseDate', mediaMetadata.releaseDate)">{{ mediaMetadata.releaseDate }}</a>
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('releaseDate', mediaMetadata.releaseDate)">{{ mediaMetadata.releaseDate }}</a>
</p>
</div>
</div>
@@ -623,7 +623,7 @@ export default {
this.clearSelectedMatch()
this.$emit('selectTab', 'details')
} else {
this.$toast.error(this.$strings.ToastItemDetailsUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
}
} else {
this.clearSelectedMatch()

View File

@@ -2,28 +2,28 @@
<div class="w-full h-full relative">
<div id="scheduleWrapper" class="w-full overflow-y-auto px-2 py-4 md:px-6 md:py-6">
<template v-if="!feedUrl">
<widgets-alert type="warning" class="text-base mb-4">No RSS feed URL is set for this podcast</widgets-alert>
<widgets-alert type="warning" class="text-base mb-4">{{ $strings.ToastPodcastNoRssFeed }}</widgets-alert>
</template>
<template v-if="feedUrl || autoDownloadEpisodes">
<div class="flex items-center justify-between mb-4">
<p class="text-base md:text-xl font-semibold">Schedule Automatic Episode Downloads</p>
<ui-checkbox v-model="enableAutoDownloadEpisodes" label="Enable" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" />
<p class="text-base md:text-xl font-semibold">{{ $strings.HeaderScheduleEpisodeDownloads }}</p>
<ui-checkbox v-model="enableAutoDownloadEpisodes" :label="$strings.LabelEnable" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" />
</div>
<div v-if="enableAutoDownloadEpisodes" class="flex items-center py-2">
<ui-text-input ref="maxEpisodesInput" type="number" v-model="newMaxEpisodesToKeep" no-spinner :padding-x="1" text-center class="w-10 text-base" @change="updatedMaxEpisodesToKeep" />
<ui-tooltip text="Value of 0 sets no max limit. After a new episode is auto-downloaded this will delete the oldest episode if you have more than X episodes. <br>This will only delete 1 episode per new download.">
<ui-tooltip :text="$strings.LabelMaxEpisodesToKeepHelp">
<p class="pl-4 text-base">
Max episodes to keep
{{ $strings.LabelMaxEpisodesToKeep }}
<span class="material-symbols icon-text">info</span>
</p>
</ui-tooltip>
</div>
<div v-if="enableAutoDownloadEpisodes" class="flex items-center py-2">
<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="$strings.LabelUseZeroForUnlimited">
<p class="pl-4 text-base">
Max new episodes to download per check
{{ $strings.LabelMaxEpisodesToDownloadPerCheck }}
<span class="material-symbols icon-text">info</span>
</p>
</ui-tooltip>
@@ -36,7 +36,7 @@
<div v-if="feedUrl || autoDownloadEpisodes" class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg border-t border-white border-opacity-5">
<div class="flex items-center px-2 md:px-4">
<div class="flex-grow" />
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'success' : 'primary'" class="mx-2">{{ isUpdated ? 'Save' : 'No update necessary' }}</ui-btn>
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'success' : 'primary'" class="mx-2">{{ isUpdated ? $strings.ButtonSave : $strings.MessageNoUpdatesWereNecessary }}</ui-btn>
</div>
</div>
</div>

View File

@@ -33,18 +33,18 @@
<span class="material-symbols text-lg ml-2">launch</span>
</ui-btn>
<ui-btn v-if="!isMetadataEmbedQueued && !isEmbedTaskRunning" class="w-full mt-4" small @click.stop="quickEmbed">Quick Embed</ui-btn>
<ui-btn v-if="!isMetadataEmbedQueued && !isEmbedTaskRunning" class="w-full mt-4" small @click.stop="quickEmbed">{{ $strings.ButtonQuickEmbed }}</ui-btn>
</div>
</div>
<!-- queued alert -->
<widgets-alert v-if="isMetadataEmbedQueued" type="warning" class="mt-4">
<p class="text-lg">Queued for metadata embed ({{ queuedEmbedLIds.length }} in queue)</p>
<p class="text-lg">{{ $getString('MessageQuickEmbedQueue', [queuedEmbedLIds.length]) }}</p>
</widgets-alert>
<!-- processing alert -->
<widgets-alert v-if="isEmbedTaskRunning" type="warning" class="mt-4">
<p class="text-lg">Currently embedding metadata</p>
<p class="text-lg">{{ $strings.MessageQuickEmbedInProgress }}</p>
</widgets-alert>
</div>
@@ -113,7 +113,7 @@ export default {
methods: {
quickEmbed() {
const payload = {
message: '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?',
message: this.$strings.MessageConfirmQuickEmbed,
callback: (confirmed) => {
if (confirmed) {
this.$axios

View File

@@ -111,7 +111,6 @@ export default {
},
updateLibrary(library) {
this.mapLibraryToCopy(library)
console.log('Updated library', this.libraryCopy)
},
getNewLibraryData() {
return {
@@ -128,7 +127,9 @@ export default {
autoScanCronExpression: null,
hideSingleBookSeries: false,
onlyShowLaterBooksInContinueSeries: false,
metadataPrecedence: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata']
metadataPrecedence: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata'],
markAsFinishedPercentComplete: null,
markAsFinishedTimeRemaining: 10
}
}
},
@@ -160,7 +161,7 @@ export default {
return false
}
if (!this.libraryCopy.folders.length) {
this.$toast.error('Library must have at least 1 path')
this.$toast.error(this.$strings.ToastMustHaveAtLeastOnePath)
return false
}
@@ -222,7 +223,7 @@ export default {
if (error.response && error.response.data) {
this.$toast.error(error.response.data)
} else {
this.$toast.error(this.$strings.ToastLibraryUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
}
this.processing = false
})
@@ -236,7 +237,6 @@ export default {
this.show = false
this.$toast.success(this.$getString('ToastLibraryCreateSuccess', [res.name]))
if (!this.$store.state.libraries.currentLibraryId) {
console.log('Setting initially library id', res.id)
// First library added
this.$store.dispatch('libraries/fetch', res.id)
}

View File

@@ -1,78 +1,94 @@
<template>
<div class="w-full h-full px-1 md:px-4 py-1 mb-4">
<div class="flex items-center py-3">
<ui-toggle-switch v-model="useSquareBookCovers" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsSquareBookCoversHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsSquareBookCovers }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
<div class="py-3">
<div class="flex items-center">
<ui-toggle-switch v-if="!globalWatcherDisabled" v-model="enableWatcher" @input="formUpdated" />
<ui-toggle-switch v-else disabled :value="false" />
<p class="pl-4 text-base">{{ $strings.LabelSettingsEnableWatcherForLibrary }}</p>
</div>
<p v-if="globalWatcherDisabled" class="text-xs text-warning">*{{ $strings.MessageWatcherIsDisabledGlobally }}</p>
</div>
<div v-if="isBookLibrary" class="flex items-center py-3">
<ui-toggle-switch v-model="audiobooksOnly" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsAudiobooksOnlyHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsAudiobooksOnly }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
<div v-if="isBookLibrary" class="py-3">
<div class="flex items-center">
<ui-toggle-switch v-model="skipMatchingMediaWithAsin" @input="formUpdated" />
<p class="pl-4 text-base">{{ $strings.LabelSettingsSkipMatchingBooksWithASIN }}</p>
</div>
</div>
<div v-if="isBookLibrary" class="py-3">
<div class="flex items-center">
<ui-toggle-switch v-model="skipMatchingMediaWithIsbn" @input="formUpdated" />
<p class="pl-4 text-base">{{ $strings.LabelSettingsSkipMatchingBooksWithISBN }}</p>
</div>
</div>
<div v-if="isBookLibrary" class="py-3">
<div class="flex items-center">
<ui-toggle-switch v-model="hideSingleBookSeries" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsHideSingleBookSeriesHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsHideSingleBookSeries }}
<div class="flex flex-wrap">
<div class="flex items-center p-2 w-full md:w-1/2">
<ui-toggle-switch v-model="useSquareBookCovers" size="sm" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsSquareBookCoversHelp">
<p class="pl-4 text-sm">
{{ $strings.LabelSettingsSquareBookCovers }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</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 }}
<div class="p-2 w-full md:w-1/2">
<div class="flex items-center">
<ui-toggle-switch v-if="!globalWatcherDisabled" v-model="enableWatcher" size="sm" @input="formUpdated" />
<ui-toggle-switch v-else disabled size="sm" :value="false" />
<p class="pl-4 text-sm">{{ $strings.LabelSettingsEnableWatcherForLibrary }}</p>
</div>
<p v-if="globalWatcherDisabled" class="text-xs text-warning">*{{ $strings.MessageWatcherIsDisabledGlobally }}</p>
</div>
<div v-if="isBookLibrary" class="flex items-center p-2 w-full md:w-1/2">
<ui-toggle-switch v-model="audiobooksOnly" size="sm" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsAudiobooksOnlyHelp">
<p class="pl-4 text-sm">
{{ $strings.LabelSettingsAudiobooksOnly }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
</div>
<div v-if="isBookLibrary" class="py-3">
<div class="flex items-center">
<ui-toggle-switch v-model="epubsAllowScriptedContent" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsEpubsAllowScriptedContentHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsEpubsAllowScriptedContent }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
<div v-if="isBookLibrary" class="p-2 w-full md:w-1/2">
<div class="flex items-center">
<ui-toggle-switch v-model="skipMatchingMediaWithAsin" size="sm" @input="formUpdated" />
<p class="pl-4 text-sm">{{ $strings.LabelSettingsSkipMatchingBooksWithASIN }}</p>
</div>
</div>
<div v-if="isBookLibrary" class="p-2 w-full md:w-1/2">
<div class="flex items-center">
<ui-toggle-switch v-model="skipMatchingMediaWithIsbn" size="sm" @input="formUpdated" />
<p class="pl-4 text-sm">{{ $strings.LabelSettingsSkipMatchingBooksWithISBN }}</p>
</div>
</div>
<div v-if="isBookLibrary" class="p-2 w-full md:w-1/2">
<div class="flex items-center">
<ui-toggle-switch v-model="hideSingleBookSeries" size="sm" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsHideSingleBookSeriesHelp">
<p class="pl-4 text-sm">
{{ $strings.LabelSettingsHideSingleBookSeries }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
</div>
<div v-if="isBookLibrary" class="p-2 w-full md:w-1/2">
<div class="flex items-center">
<ui-toggle-switch v-model="onlyShowLaterBooksInContinueSeries" size="sm" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp">
<p class="pl-4 text-sm">
{{ $strings.LabelSettingsOnlyShowLaterBooksInContinueSeries }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
</div>
<div v-if="isBookLibrary" class="p-2 w-full md:w-1/2">
<div class="flex items-center">
<ui-toggle-switch v-model="epubsAllowScriptedContent" size="sm" @input="formUpdated" />
<ui-tooltip :text="$strings.LabelSettingsEpubsAllowScriptedContentHelp">
<p class="pl-4 text-sm">
{{ $strings.LabelSettingsEpubsAllowScriptedContent }}
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
</div>
<div v-if="isPodcastLibrary" class="p-2 w-full md:w-1/2">
<ui-dropdown :label="$strings.LabelPodcastSearchRegion" v-model="podcastSearchRegion" :items="$podcastSearchRegionOptions" small class="max-w-72" menu-max-height="200px" @input="formUpdated" />
</div>
<div class="p-2 w-full flex items-center space-x-2 flex-wrap">
<div>
<ui-dropdown v-model="markAsFinishedWhen" :items="maskAsFinishedWhenItems" :label="$strings.LabelSettingsLibraryMarkAsFinishedWhen" small class="w-72 min-w-72 text-sm" menu-max-height="200px" @input="markAsFinishedWhenChanged" />
</div>
<div class="w-16">
<div>
<label class="px-1 text-sm font-semibold"></label>
<div class="relative">
<ui-text-input v-model="markAsFinishedValue" type="number" label="" no-spinner custom-input-class="pr-5" @input="markAsFinishedChanged" />
<div class="absolute top-0 bottom-0 right-4 flex items-center">{{ markAsFinishedWhen === 'timeRemaining' ? '' : '%' }}</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="isPodcastLibrary" class="py-3">
<ui-dropdown :label="$strings.LabelPodcastSearchRegion" v-model="podcastSearchRegion" :items="$podcastSearchRegionOptions" small class="max-w-72" menu-max-height="200px" @input="formUpdated" />
</div>
</div>
</template>
@@ -97,7 +113,9 @@ export default {
epubsAllowScriptedContent: false,
hideSingleBookSeries: false,
onlyShowLaterBooksInContinueSeries: false,
podcastSearchRegion: 'us'
podcastSearchRegion: 'us',
markAsFinishedWhen: 'timeRemaining',
markAsFinishedValue: 10
}
},
computed: {
@@ -119,10 +137,34 @@ export default {
providers() {
if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
},
maskAsFinishedWhenItems() {
return [
{
text: this.$strings.LabelSettingsLibraryMarkAsFinishedTimeRemaining,
value: 'timeRemaining'
},
{
text: this.$strings.LabelSettingsLibraryMarkAsFinishedPercentComplete,
value: 'percentComplete'
}
]
}
},
methods: {
markAsFinishedWhenChanged(val) {
if (val === 'percentComplete' && this.markAsFinishedValue > 100) {
this.markAsFinishedValue = 100
}
this.formUpdated()
},
markAsFinishedChanged(val) {
this.formUpdated()
},
getLibraryData() {
let markAsFinishedTimeRemaining = this.markAsFinishedWhen === 'timeRemaining' ? Number(this.markAsFinishedValue) : null
let markAsFinishedPercentComplete = this.markAsFinishedWhen === 'percentComplete' ? Number(this.markAsFinishedValue) : null
return {
settings: {
coverAspectRatio: this.useSquareBookCovers ? this.$constants.BookCoverAspectRatio.SQUARE : this.$constants.BookCoverAspectRatio.STANDARD,
@@ -133,7 +175,9 @@ export default {
epubsAllowScriptedContent: !!this.epubsAllowScriptedContent,
hideSingleBookSeries: !!this.hideSingleBookSeries,
onlyShowLaterBooksInContinueSeries: !!this.onlyShowLaterBooksInContinueSeries,
podcastSearchRegion: this.podcastSearchRegion
podcastSearchRegion: this.podcastSearchRegion,
markAsFinishedTimeRemaining: markAsFinishedTimeRemaining,
markAsFinishedPercentComplete: markAsFinishedPercentComplete
}
}
},
@@ -150,6 +194,11 @@ export default {
this.hideSingleBookSeries = !!this.librarySettings.hideSingleBookSeries
this.onlyShowLaterBooksInContinueSeries = !!this.librarySettings.onlyShowLaterBooksInContinueSeries
this.podcastSearchRegion = this.librarySettings.podcastSearchRegion || 'us'
this.markAsFinishedWhen = this.librarySettings.markAsFinishedTimeRemaining ? 'timeRemaining' : 'percentComplete'
if (!this.librarySettings.markAsFinishedTimeRemaining && !this.librarySettings.markAsFinishedPercentComplete) {
this.markAsFinishedWhen = 'timeRemaining'
}
this.markAsFinishedValue = this.librarySettings.markAsFinishedTimeRemaining || this.librarySettings.markAsFinishedPercentComplete || 10
}
},
mounted() {

View File

@@ -3,13 +3,13 @@
<div class="w-full border border-black-200 p-4 my-8">
<div class="flex flex-wrap items-center">
<div>
<p class="text-lg">Remove metadata files in library item folders</p>
<p class="max-w-sm text-sm pt-2 text-gray-300">Remove all metadata.json or metadata.abs files in your {{ mediaType }} folders</p>
<p class="text-lg">{{ $strings.LabelRemoveMetadataFile }}</p>
<p class="max-w-sm text-sm pt-2 text-gray-300">{{ $getString('LabelRemoveMetadataFileHelp', [mediaType]) }}</p>
</div>
<div class="flex-grow" />
<div>
<ui-btn class="mb-4 block" @click.stop="removeAllMetadataClick('json')">Remove all metadata.json</ui-btn>
<ui-btn @click.stop="removeAllMetadataClick('abs')">Remove all metadata.abs</ui-btn>
<ui-btn class="mb-4 block" @click.stop="removeAllMetadataClick('json')">{{ $strings.LabelRemoveAllMetadataJson }}</ui-btn>
<ui-btn @click.stop="removeAllMetadataClick('abs')">{{ $strings.LabelRemoveAllMetadataAbs }}</ui-btn>
</div>
</div>
</div>
@@ -43,7 +43,7 @@ export default {
methods: {
removeAllMetadataClick(ext) {
const payload = {
message: `Are you sure you want to remove all metadata.${ext} files in your library item folders?`,
message: this.$getString('MessageConfirmRemoveMetadataFiles', [ext]),
persistent: true,
callback: (confirmed) => {
if (confirmed) {
@@ -60,16 +60,16 @@ export default {
.$post(`/api/libraries/${this.libraryId}/remove-metadata?ext=${ext}`)
.then((data) => {
if (!data.found) {
this.$toast.info(`No metadata.${ext} files were found in library`)
this.$toast.info(this.$getString('ToastMetadataFilesRemovedNoneFound', [ext]))
} else if (!data.removed) {
this.$toast.success(`No metadata.${ext} files removed`)
this.$toast.success(this.$getString('ToastMetadataFilesRemovedNoneRemoved', [ext]))
} else {
this.$toast.success(`Successfully removed ${data.removed} metadata.${ext} files`)
this.$toast.success(this.$getString('ToastMetadataFilesRemovedSuccess', [data.removed, ext]))
}
})
.catch((error) => {
console.error('Failed to remove metadata files', error)
this.$toast.error('Failed to remove metadata files')
this.$toast.error(this.$getString('ToastMetadataFilesRemovedError', [ext]))
})
.finally(() => {
this.$emit('update:processing', false)

View File

@@ -77,7 +77,13 @@ export default {
return this.notificationData.events || []
},
eventOptions() {
return this.notificationEvents.map((e) => ({ value: e.name, text: e.name, subtext: e.description }))
return this.notificationEvents.map((e) => {
return {
value: e.name,
text: e.name,
subtext: this.$strings[e.descriptionKey] || e.description
}
})
},
selectedEventData() {
return this.notificationEvents.find((e) => e.name === this.newNotification.eventName)
@@ -132,7 +138,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update notification', error)
this.$toast.error(this.$strings.ToastNotificationUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.processing = false

View File

@@ -135,7 +135,7 @@ export default {
})
.catch((error) => {
console.error('Failed to remove items from playlist', error)
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
this.processing = false
})
},
@@ -153,7 +153,7 @@ export default {
})
.catch((error) => {
console.error('Failed to add items to playlist', error)
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
this.processing = false
})
},

View File

@@ -115,7 +115,7 @@ export default {
.catch((error) => {
console.error('Failed to update playlist', error)
this.processing = false
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
}
},

View File

@@ -156,7 +156,12 @@ export default {
return this.selectedFolder.fullPath
},
podcastTypes() {
return this.$store.state.globals.podcastTypes || []
return this.$store.state.globals.podcastTypes.map((e) => {
return {
text: this.$strings[e.descriptionKey] || e.text,
value: e.value
}
})
}
},
methods: {

View File

@@ -33,11 +33,11 @@
</div>
<div v-if="enclosureUrl" class="pb-4 pt-6">
<ui-text-input-with-label :value="enclosureUrl" readonly class="text-xs">
<label class="px-1 text-xs text-gray-200 font-semibold">Episode URL from RSS feed</label>
<label class="px-1 text-xs text-gray-200 font-semibold">{{ $strings.LabelEpisodeUrlFromRssFeed }}</label>
</ui-text-input-with-label>
</div>
<div v-else class="py-4">
<p class="text-xs text-gray-300 font-semibold">Episode not linked to RSS feed episode</p>
<p class="text-xs text-gray-300 font-semibold">{{ $strings.LabelEpisodeNotLinkedToRssFeed }}</p>
</div>
</div>
</template>
@@ -97,7 +97,12 @@ export default {
return this.enclosure.url
},
episodeTypes() {
return this.$store.state.globals.episodeTypes || []
return this.$store.state.globals.episodeTypes.map((e) => {
return {
text: this.$strings[e.descriptionKey] || e.text,
value: e.value
}
})
}
},
methods: {
@@ -152,14 +157,14 @@ export default {
const updateResult = await this.$axios.$patch(`/api/podcasts/${this.libraryItem.id}/episode/${this.episodeId}`, updatedDetails).catch((error) => {
console.error('Failed update episode', error)
this.isProcessing = false
this.$toast.error(error?.response?.data || 'Failed to update episode')
this.$toast.error(error?.response?.data || this.$strings.ToastFailedToUpdate)
return false
})
this.isProcessing = false
if (updateResult) {
if (updateResult) {
this.$toast.success('Podcast episode updated')
this.$toast.success(this.$strings.ToastItemUpdateSuccess)
return true
} else {
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)

View File

@@ -139,7 +139,7 @@ export default {
slug: this.newFeedSlug,
metadataDetails: this.metadataDetails
}
if (this.$isDev) payload.serverAddress = `http://localhost:3333${this.$config.routerBasePath}`
if (this.$isDev) payload.serverAddress = process.env.serverUrl
console.log('Payload', payload)
this.$axios

View File

@@ -1,38 +1,37 @@
<template>
<div class="flex items-center pt-4 pb-2 lg:pt-0 lg:pb-2">
<div class="flex-grow" />
<template v-if="!loading">
<ui-tooltip direction="top" :text="$strings.ButtonPreviousChapter" class="mr-4 lg:mr-8">
<button :aria-label="$strings.ButtonPreviousChapter" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
<span class="material-symbols text-2xl sm:text-3xl">first_page</span>
<div class="flex justify-center pt-4 pb-2 lg:pt-0 lg:pb-2">
<div class="flex items-center justify-center flex-grow">
<template v-if="!loading">
<ui-tooltip direction="top" :text="$strings.ButtonPreviousChapter" class="mr-4 lg:mr-8">
<button :aria-label="$strings.ButtonPreviousChapter" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
<span class="material-symbols text-2xl sm:text-3xl">first_page</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="jumpBackwardText">
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
<span class="material-symbols text-2xl sm:text-3xl">replay</span>
</button>
</ui-tooltip>
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
<span class="material-symbols fill text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="jumpBackwardText">
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
<span class="material-symbols text-2xl sm:text-3xl">replay</span>
</button>
</ui-tooltip>
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
<span class="material-symbols fill text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
</button>
<ui-tooltip direction="top" :text="jumpForwardText">
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
<span class="material-symbols text-2xl sm:text-3xl">forward_media</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="hasNextLabel" class="ml-4 lg:ml-8">
<button :aria-label="hasNextLabel" :disabled="!hasNext" class="text-gray-300 disabled:text-gray-500" @mousedown.prevent @mouseup.prevent @click.stop="next">
<span class="material-symbols text-2xl sm:text-3xl">last_page</span>
</button>
</ui-tooltip>
<controls-playback-speed-control v-model="playbackRateInput" @input="playbackRateUpdated" @change="playbackRateChanged" />
</template>
<template v-else>
<div class="cursor-pointer p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin">
<span class="material-symbols text-2xl">autorenew</span>
</div>
</template>
<div class="flex-grow" />
<ui-tooltip direction="top" :text="jumpForwardText">
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
<span class="material-symbols text-2xl sm:text-3xl">forward_media</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="hasNextLabel" class="ml-4 lg:ml-8">
<button :aria-label="hasNextLabel" :disabled="!hasNext" class="text-gray-300 disabled:text-gray-500" @mousedown.prevent @mouseup.prevent @click.stop="next">
<span class="material-symbols text-2xl sm:text-3xl">last_page</span>
</button>
</ui-tooltip>
</template>
<template v-else>
<div class="cursor-pointer p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin">
<span class="material-symbols text-2xl">autorenew</span>
</div>
</template>
</div>
</div>
</template>
@@ -41,7 +40,6 @@ export default {
props: {
loading: Boolean,
seekLoading: Boolean,
playbackRate: Number,
paused: Boolean,
hasNextChapter: Boolean,
hasNextItemInQueue: Boolean
@@ -50,14 +48,6 @@ export default {
return {}
},
computed: {
playbackRateInput: {
get() {
return this.playbackRate
},
set(val) {
this.$emit('update:playbackRate', val)
}
},
jumpForwardText() {
return this.getJumpText('jumpForwardAmount', this.$strings.ButtonJumpForward)
},
@@ -89,15 +79,6 @@ export default {
jumpForward() {
this.$emit('jumpForward')
},
playbackRateUpdated(playbackRate) {
this.$emit('setPlaybackRate', playbackRate)
},
playbackRateChanged(playbackRate) {
this.$emit('setPlaybackRate', playbackRate)
this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => {
console.error('Failed to update settings', err)
})
},
getJumpText(setting, prefix) {
const amount = this.$store.getters['user/getUserSetting'](setting)
if (!amount) return prefix

View File

@@ -2,9 +2,9 @@
<div class="w-full -mt-6">
<div class="w-full relative mb-1">
<div class="absolute -top-10 lg:top-0 right-0 lg:right-2 flex items-center h-full">
<!-- <span class="material-symbols text-2xl cursor-pointer" @click="toggleFullscreen(true)">expand_less</span> -->
<controls-playback-speed-control v-model="playbackRate" @input="setPlaybackRate" @change="playbackRateChanged" class="mx-2 block" />
<ui-tooltip direction="top" :text="$strings.LabelVolume">
<ui-tooltip direction="left" :text="$strings.LabelVolume">
<controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden sm:block" />
</ui-tooltip>
@@ -13,7 +13,7 @@
<span v-if="!sleepTimerSet" class="material-symbols text-2xl">snooze</span>
<div v-else class="flex items-center">
<span class="material-symbols text-lg text-warning">snooze</span>
<p class="text-sm sm:text-lg text-warning font-mono font-semibold text-center px-0.5 sm:pb-0.5 sm:min-w-8">{{ sleepTimerRemainingString }}</p>
<p class="text-sm sm:text-lg text-warning font-semibold text-center px-0.5 sm:pb-0.5 sm:min-w-8">{{ sleepTimerRemainingString }}</p>
</div>
</button>
</ui-tooltip>
@@ -48,15 +48,19 @@
<player-track-bar ref="trackbar" :loading="loading" :chapters="chapters" :duration="duration" :current-chapter="currentChapter" :playback-rate="playbackRate" @seek="seek" />
<div class="flex">
<p ref="currentTimestamp" class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">00:00:00</p>
<p class="font-mono text-sm hidden sm:block text-gray-100 pointer-events-auto">&nbsp;/&nbsp;{{ progressPercent }}%</p>
<div class="flex-grow" />
<p class="text-xs sm:text-sm text-gray-300 pt-0.5 px-2 truncate">
{{ currentChapterName }} <span v-if="useChapterTrack" class="text-xs text-gray-400">&nbsp;({{ $getString('LabelPlayerChapterNumberMarker', [currentChapterIndex + 1, chapters.length]) }})</span>
</p>
<div class="flex-grow" />
<p class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">{{ timeRemainingPretty }}</p>
<div class="relative flex items-center justify-between">
<div class="flex-grow flex items-center">
<p ref="currentTimestamp" class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">00:00:00</p>
<p class="font-mono text-sm hidden sm:block text-gray-100 pointer-events-auto">&nbsp;/&nbsp;{{ progressPercent }}%</p>
</div>
<div class="absolute left-1/2 transform -translate-x-1/2">
<p class="text-xs sm:text-sm text-gray-300 pt-0.5 px-2 truncate">
{{ currentChapterName }} <span v-if="useChapterTrack" class="text-xs text-gray-400">&nbsp;({{ $getString('LabelPlayerChapterNumberMarker', [currentChapterIndex + 1, chapters.length]) }})</span>
</p>
</div>
<div class="flex-grow flex items-center justify-end">
<p class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">{{ timeRemainingPretty }}</p>
</div>
</div>
<modals-chapters-modal v-model="showChaptersModal" :current-chapter="currentChapter" :playback-rate="playbackRate" :chapters="chapters" @select="selectChapter" />
@@ -224,6 +228,12 @@ export default {
this.playbackRate = Number((this.playbackRate - 0.1).toFixed(1))
this.setPlaybackRate(this.playbackRate)
},
playbackRateChanged(playbackRate) {
this.setPlaybackRate(playbackRate)
this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => {
console.error('Failed to update settings', err)
})
},
setPlaybackRate(playbackRate) {
this.$emit('setPlaybackRate', playbackRate)
},

View File

@@ -35,22 +35,22 @@
<div class="flex justify-between pt-12">
<div>
<p class="text-sm text-center">{{ $strings.LabelStatsWeekListening }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ totalMinutesListeningThisWeek }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(totalMinutesListeningThisWeek) }}</p>
<p class="text-sm text-center">{{ $strings.LabelStatsMinutes }}</p>
</div>
<div>
<p class="text-sm text-center">{{ $strings.LabelStatsDailyAverage }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ averageMinutesPerDay }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(averageMinutesPerDay) }}</p>
<p class="text-sm text-center">{{ $strings.LabelStatsMinutes }}</p>
</div>
<div>
<p class="text-sm text-center">{{ $strings.LabelStatsBestDay }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ mostListenedDay }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(mostListenedDay) }}</p>
<p class="text-sm text-center">{{ $strings.LabelStatsMinutes }}</p>
</div>
<div>
<p class="text-sm text-center">{{ $strings.LabelStatsDays }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ daysInARow }}</p>
<p class="text-5xl font-semibold text-center" style="line-height: 0.85">{{ $formatNumber(daysInARow) }}</p>
<p class="text-sm text-center">{{ $strings.LabelStatsInARow }}</p>
</div>
</div>

View File

@@ -78,7 +78,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update collection', error)
this.$toast.error(this.$strings.ToastCollectionUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
},
editBook(book) {

View File

@@ -92,7 +92,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update playlist', error)
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
},
init() {

View File

@@ -223,7 +223,7 @@ export default {
})
.catch((error) => {
console.error('Failed to remove item from playlist', error)
this.$toast.error(this.$strings.ToastPlaylistUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.processingRemove = false

View File

@@ -12,10 +12,10 @@
</div>
<div class="h-8 flex items-center">
<div class="w-full inline-flex justify-between max-w-xl">
<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>
<p v-if="episode?.season" class="text-sm text-gray-300">{{ $getString('LabelSeasonNumber', [episode.season]) }}</p>
<p v-if="episode?.episode" class="text-sm text-gray-300">{{ $getString('LabelEpisodeNumber', [episode.episode]) }}</p>
<p v-if="episode?.chapters?.length" class="text-sm text-gray-300">{{ $getString('LabelChapterCount', [episode.chapters.length]) }}</p>
<p v-if="publishedAt" class="text-sm text-gray-300">{{ $getString('LabelPublishedDate', [$formatDate(publishedAt, dateFormat)]) }}</p>
</div>
</div>
@@ -132,13 +132,13 @@ export default {
return this.store.state.streamIsPlaying && this.isStreaming
},
timeRemaining() {
if (this.streamIsPlaying) return 'Playing'
if (this.streamIsPlaying) return this.$strings.ButtonPlaying
if (!this.itemProgress) return this.$elapsedPretty(this.episode?.duration || 0)
if (this.userIsFinished) return 'Finished'
if (this.userIsFinished) return this.$strings.LabelFinished
const duration = this.itemProgress.duration || this.episode?.duration || 0
const remaining = Math.floor(duration - this.itemProgress.currentTime)
return `${this.$elapsedPretty(remaining)} left`
return this.$getString('LabelTimeLeft', [this.$elapsedPretty(remaining)])
}
},
methods: {
@@ -182,7 +182,7 @@ export default {
toggleFinished(confirmed = false) {
if (!this.userIsFinished && this.itemProgressPercent > 0 && !confirmed) {
const payload = {
message: `Are you sure you want to mark "${this.episodeTitle}" as finished?`,
message: this.$getString('MessageConfirmMarkItemFinished', [this.episodeTitle]),
callback: (confirmed) => {
if (confirmed) {
this.toggleFinished(true)

View File

@@ -93,17 +93,18 @@ export default {
},
computed: {
contextMenuItems() {
if (!this.userIsAdminOrUp) return []
return [
{
text: 'Quick match all episodes',
const menuItems = []
if (this.userIsAdminOrUp) {
menuItems.push({
text: this.$strings.MessageQuickMatchAllEpisodes,
action: 'quick-match-episodes'
},
{
text: this.allEpisodesFinished ? this.$strings.MessageMarkAllEpisodesNotFinished : this.$strings.MessageMarkAllEpisodesFinished,
action: 'batch-mark-as-finished'
}
]
})
}
menuItems.push({
text: this.allEpisodesFinished ? this.$strings.MessageMarkAllEpisodesNotFinished : this.$strings.MessageMarkAllEpisodesFinished,
action: 'batch-mark-as-finished'
})
return menuItems
},
sortItems() {
return [
@@ -261,21 +262,21 @@ export default {
this.processing = true
const payload = {
message: 'Quick matching episodes will overwrite details if a match is found. Only unmatched episodes will be updated. Are you sure?',
message: this.$strings.MessageConfirmQuickMatchEpisodes,
callback: (confirmed) => {
if (confirmed) {
this.$axios
.$post(`/api/podcasts/${this.libraryItem.id}/match-episodes?override=1`)
.then((data) => {
if (data.numEpisodesUpdated) {
this.$toast.success(`${data.numEpisodesUpdated} episodes updated`)
this.$toast.success(this.$getString('ToastEpisodeUpdateSuccess', [data.numEpisodesUpdated]))
} else {
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
}
})
.catch((error) => {
console.error('Failed to request match episodes', error)
this.$toast.error('Failed to match episodes')
this.$toast.error(this.$strings.ToastFailedToMatch)
})
}
this.processing = false

View File

@@ -57,7 +57,8 @@ export default {
inputName: String,
showCopy: Boolean,
step: [String, Number],
min: [String, Number]
min: [String, Number],
customInputClass: String
},
data() {
return {
@@ -82,6 +83,7 @@ export default {
_list.push(`py-${this.paddingY}`)
if (this.noSpinner) _list.push('no-spinner')
if (this.textCenter) _list.push('text-center')
if (this.customInputClass) _list.push(this.customInputClass)
return _list.join(' ')
},
actualType() {

View File

@@ -1,7 +1,7 @@
<template>
<div>
<button :aria-labelledby="labeledBy" role="checkbox" type="button" class="border rounded-full border-black-100 flex items-center cursor-pointer w-10 justify-start" :aria-checked="toggleValue" :class="className" @click="clickToggle">
<span class="rounded-full border w-5 h-5 border-black-50 shadow transform transition-transform duration-100" :class="switchClassName"></span>
<button :aria-labelledby="labeledBy" role="checkbox" type="button" class="border rounded-full border-black-100 flex items-center cursor-pointer justify-start" :style="{ width: buttonWidth + 'px' }" :aria-checked="toggleValue" :class="className" @click="clickToggle">
<span class="rounded-full border border-black-50 shadow transform transition-transform duration-100" :style="{ width: cursorHeightWidth + 'px', height: cursorHeightWidth + 'px' }" :class="switchClassName"></span>
</button>
</div>
</template>
@@ -19,7 +19,11 @@ export default {
default: 'primary'
},
disabled: Boolean,
labeledBy: String
labeledBy: String,
size: {
type: String,
default: 'md'
}
},
computed: {
toggleValue: {
@@ -37,6 +41,13 @@ export default {
switchClassName() {
var bgColor = this.disabled ? 'bg-gray-300' : 'bg-white'
return this.toggleValue ? 'translate-x-5 ' + bgColor : bgColor
},
cursorHeightWidth() {
if (this.size === 'sm') return 16
return 20
},
buttonWidth() {
return this.cursorHeightWidth * 2
}
},
methods: {

View File

@@ -1,14 +1,11 @@
<template>
<ui-tooltip v-if="alreadyInLibrary" :text="$strings.LabelAlreadyInYourLibrary" direction="top">
<span class="material-symbols ml-1 text-success" style="font-size: 0.8rem">check_circle</span>
<ui-tooltip :text="$strings.LabelAlreadyInYourLibrary" direction="top" class="inline-flex">
<span class="material-symbols ml-1 text-sm text-success">check_circle</span>
</ui-tooltip>
</template>
<script>
export default {
props: {
alreadyInLibrary: Boolean
},
data() {
return {}
},

View File

@@ -65,7 +65,7 @@ export default {
},
authors: {
component: 'cards-author-card',
itemPropName: 'author',
itemPropName: 'author-mount',
itemIdFunc: (item) => item.id
},
narrators: {

View File

@@ -101,7 +101,12 @@ export default {
return this.$store.state.libraries.filterData || {}
},
podcastTypes() {
return this.$store.state.globals.podcastTypes || []
return this.$store.state.globals.podcastTypes.map((e) => {
return {
text: this.$strings[e.descriptionKey] || e.text,
value: e.value
}
})
}
},
methods: {

View File

@@ -5,14 +5,14 @@ import Tooltip from '@/components/ui/Tooltip.vue'
import LoadingSpinner from '@/components/widgets/LoadingSpinner.vue'
describe('AuthorCard', () => {
const author = {
const authorMount = {
id: 1,
name: 'John Doe',
numBooks: 5
}
const propsData = {
author,
authorMount,
nameBelow: false
}

View File

@@ -357,7 +357,8 @@ export default {
teardown: false,
transports: ['websocket'],
upgrade: false,
reconnection: true
reconnection: true,
path: `${this.$config.routerBasePath}/socket.io`
})
this.$root.socket = this.socket
console.log('Socket initialized')

View File

@@ -4,6 +4,7 @@ import LazySeriesCard from '@/components/cards/LazySeriesCard'
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
import LazyPlaylistCard from '@/components/cards/LazyPlaylistCard'
import LazyAlbumCard from '@/components/cards/LazyAlbumCard'
import AuthorCard from '@/components/cards/AuthorCard'
export default {
data() {
@@ -20,6 +21,7 @@ export default {
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
if (this.entityName === 'playlists') return Vue.extend(LazyPlaylistCard)
if (this.entityName === 'albums') return Vue.extend(LazyAlbumCard)
if (this.entityName === 'authors') return Vue.extend(AuthorCard)
return Vue.extend(LazyBookCard)
},
getComponentName() {
@@ -27,6 +29,7 @@ export default {
if (this.entityName === 'collections') return 'cards-lazy-collection-card'
if (this.entityName === 'playlists') return 'cards-lazy-playlist-card'
if (this.entityName === 'albums') return 'cards-lazy-album-card'
if (this.entityName === 'authors') return 'cards-author-card'
return 'cards-lazy-book-card'
},
async setCardSize() {
@@ -46,13 +49,14 @@ export default {
props.orderBy = this.seriesSortBy
}
const instance = new ComponentClass({
propsData: props
propsData: props,
parent: this
})
instance.$mount()
this.resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
this.cardWidth = entry.contentRect.width
this.cardHeight = entry.contentRect.height
this.cardWidth = entry.borderBoxSize[0].inlineSize
this.cardHeight = entry.borderBoxSize[0].blockSize
this.resizeObserver.disconnect()
this.$refs.bookshelf.removeChild(instance.$el)
}
@@ -72,7 +76,7 @@ export default {
})
const timeAfter = performance.now()
},
async mountEntityCard(index) {
mountEntityCard(index) {
var shelf = Math.floor(index / this.entitiesPerShelf)
var shelfEl = document.getElementById(`shelf-${shelf}`)
if (!shelfEl) {
@@ -114,6 +118,7 @@ export default {
const _this = this
const instance = new ComponentClass({
propsData: props,
parent: this,
created() {
this.$on('edit', (entity) => {
if (_this.editEntity) _this.editEntity(entity)

View File

@@ -1,19 +1,24 @@
const pkg = require('./package.json')
const routerBasePath = process.env.ROUTER_BASE_PATH || ''
const serverHostUrl = process.env.NODE_ENV === 'production' ? '' : 'http://localhost:3333'
const serverPaths = ['api/', 'public/', 'hls/', 'auth/', 'feed/', 'status', 'login', 'logout', 'init']
const proxy = Object.fromEntries(serverPaths.map((path) => [`${routerBasePath}/${path}`, { target: process.env.NODE_ENV !== 'production' ? serverHostUrl : '/' }]))
module.exports = {
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
ssr: false,
target: 'static',
dev: process.env.NODE_ENV !== 'production',
env: {
serverUrl: process.env.NODE_ENV === 'production' ? process.env.ROUTER_BASE_PATH || '' : 'http://localhost:3333',
serverUrl: serverHostUrl + routerBasePath,
chromecastReceiver: 'FD1F76C5'
},
telemetry: false,
publicRuntimeConfig: {
version: pkg.version,
routerBasePath: process.env.ROUTER_BASE_PATH || ''
routerBasePath
},
// Global page headers: https://go.nuxtjs.dev/config-head
@@ -22,38 +27,23 @@ module.exports = {
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ hid: 'robots', name: 'robots', content: 'noindex' }
],
meta: [{ charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: '' }, { hid: 'robots', name: 'robots', content: 'noindex' }],
script: [],
link: [
{ 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' }
{ rel: 'icon', type: 'image/x-icon', href: routerBasePath + '/favicon.ico' },
{ rel: 'apple-touch-icon', href: routerBasePath + '/ios_icon.png' }
]
},
router: {
base: process.env.ROUTER_BASE_PATH || ''
base: routerBasePath
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
'@/assets/tailwind.css',
'@/assets/app.css'
],
css: ['@/assets/tailwind.css', '@/assets/app.css'],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
'@/plugins/constants.js',
'@/plugins/init.client.js',
'@/plugins/axios.js',
'@/plugins/toast.js',
'@/plugins/utils.js',
'@/plugins/i18n.js'
],
plugins: ['@/plugins/constants.js', '@/plugins/init.client.js', '@/plugins/axios.js', '@/plugins/toast.js', '@/plugins/utils.js', '@/plugins/i18n.js'],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
@@ -65,30 +55,25 @@ module.exports = {
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'nuxt-socket-io',
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
modules: ['nuxt-socket-io', '@nuxtjs/axios', '@nuxtjs/proxy'],
proxy: {
'/api/': { target: process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '/' },
'/dev/': { target: 'http://localhost:3333', pathRewrite: { '^/dev/': '' } }
},
proxy,
io: {
sockets: [{
name: 'dev',
url: 'http://localhost:3333'
},
{
name: 'prod'
}]
sockets: [
{
name: 'dev',
url: serverHostUrl
},
{
name: 'prod'
}
]
},
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
baseURL: process.env.ROUTER_BASE_PATH || ''
baseURL: routerBasePath
},
// nuxt/pwa https://pwa.nuxtjs.org
@@ -108,11 +93,11 @@ module.exports = {
background_color: '#232323',
icons: [
{
src: (process.env.ROUTER_BASE_PATH || '') + '/icon.svg',
src: routerBasePath + '/icon.svg',
sizes: 'any'
},
{
src: (process.env.ROUTER_BASE_PATH || '') + '/icon192.png',
src: routerBasePath + '/icon192.png',
type: 'image/png',
sizes: 'any'
}
@@ -129,10 +114,12 @@ module.exports = {
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
postcss: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
postcssOptions: {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}
}
},
watchers: {
@@ -147,12 +134,12 @@ module.exports = {
},
/**
* Temporary workaround for @nuxt-community/tailwindcss-module.
*
* Reported: 2022-05-23
* See: [Issue tracker](https://github.com/nuxt-community/tailwindcss-module/issues/480)
*/
* Temporary workaround for @nuxt-community/tailwindcss-module.
*
* Reported: 2022-05-23
* See: [Issue tracker](https://github.com/nuxt-community/tailwindcss-module/issues/480)
*/
devServerHandlers: [],
ignore: ["**/*.test.*", "**/*.cy.*"]
ignore: ['**/*.test.*', '**/*.cy.*']
}

5126
client/package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
"version": "2.13.4",
"version": "2.16.2",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast client",
"main": "index.js",
@@ -27,7 +27,7 @@
"fast-average-color": "^9.4.0",
"hls.js": "^1.5.7",
"libarchive.js": "^1.3.0",
"nuxt": "^2.17.3",
"nuxt": "^2.18.1",
"nuxt-socket-io": "^1.1.18",
"trix": "^1.3.1",
"v-click-outside": "^3.1.2",

View File

@@ -32,9 +32,48 @@
</form>
</div>
<div v-if="showEreaderTable">
<div class="w-full h-px bg-white/10 my-4" />
<app-settings-content :header-text="$strings.HeaderEreaderDevices">
<template #header-items>
<div class="flex-grow" />
<ui-btn color="primary" small @click="addNewDeviceClick">{{ $strings.ButtonAddDevice }}</ui-btn>
</template>
<table v-if="ereaderDevices.length" class="tracksTable mt-4">
<tr>
<th class="text-left">{{ $strings.LabelName }}</th>
<th class="text-left">{{ $strings.LabelEmail }}</th>
<th class="w-40"></th>
</tr>
<tr v-for="device in ereaderDevices" :key="device.name">
<td>
<p class="text-sm md:text-base text-gray-100">{{ device.name }}</p>
</td>
<td class="text-left">
<p class="text-sm md:text-base text-gray-100">{{ device.email }}</p>
</td>
<td class="w-40">
<div class="flex justify-end items-center h-10">
<ui-icon-btn icon="edit" borderless :size="8" icon-font-size="1.1rem" :disabled="deletingDeviceName === device.name || device.users?.length !== 1" class="mx-1" @click="editDeviceClick(device)" />
<ui-icon-btn icon="delete" borderless :size="8" icon-font-size="1.1rem" :disabled="deletingDeviceName === device.name || device.users?.length !== 1" @click="deleteDeviceClick(device)" />
</div>
</td>
</tr>
</table>
<div v-else-if="!loading" class="text-center py-4">
<p class="text-lg text-gray-100">{{ $strings.MessageNoDevices }}</p>
</div>
</app-settings-content>
</div>
<div class="py-4 mt-8 flex">
<ui-btn color="primary flex items-center text-lg" @click="logout"><span class="material-symbols mr-4 icon-text">logout</span>{{ $strings.ButtonLogout }}</ui-btn>
</div>
<modals-emails-user-e-reader-device-modal v-model="showEReaderDeviceModal" :existing-devices="revisedEreaderDevices" :ereader-device="selectedEReaderDevice" @update="ereaderDevicesUpdated" />
</div>
</div>
</template>
@@ -43,11 +82,20 @@
export default {
data() {
return {
loading: false,
password: null,
newPassword: null,
confirmPassword: null,
changingPassword: false,
selectedLanguage: ''
selectedLanguage: '',
newEReaderDevice: {
name: '',
email: ''
},
ereaderDevices: [],
deletingDeviceName: null,
selectedEReaderDevice: null,
showEReaderDeviceModal: false
}
},
computed: {
@@ -75,6 +123,12 @@ export default {
},
showChangePasswordForm() {
return !this.isGuest && this.isPasswordAuthEnabled
},
showEreaderTable() {
return this.usertype !== 'root' && this.usertype !== 'admin' && this.user.permissions?.createEreader
},
revisedEreaderDevices() {
return this.ereaderDevices.filter((device) => device.users?.length === 1)
}
},
methods: {
@@ -142,10 +196,52 @@ export default {
this.$toast.error(this.$strings.ToastUnknownError)
this.changingPassword = false
})
},
addNewDeviceClick() {
this.selectedEReaderDevice = null
this.showEReaderDeviceModal = true
},
editDeviceClick(device) {
this.selectedEReaderDevice = device
this.showEReaderDeviceModal = true
},
deleteDeviceClick(device) {
const payload = {
message: this.$getString('MessageConfirmDeleteDevice', [device.name]),
callback: (confirmed) => {
if (confirmed) {
this.deleteDevice(device)
}
},
type: 'yesNo'
}
this.$store.commit('globals/setConfirmPrompt', payload)
},
deleteDevice(device) {
const payload = {
ereaderDevices: this.revisedEreaderDevices.filter((d) => d.name !== device.name)
}
this.deletingDeviceName = device.name
this.$axios
.$post(`/api/me/ereader-devices`, payload)
.then((data) => {
this.ereaderDevicesUpdated(data.ereaderDevices)
})
.catch((error) => {
console.error('Failed to delete device', error)
this.$toast.error(this.$strings.ToastRemoveFailed)
})
.finally(() => {
this.deletingDeviceName = null
})
},
ereaderDevicesUpdated(ereaderDevices) {
this.ereaderDevices = ereaderDevices
}
},
mounted() {
this.selectedLanguage = this.$languageCodes.current
this.ereaderDevices = this.$store.state.libraries.ereaderDevices || []
}
}
</script>

View File

@@ -415,7 +415,7 @@ export default {
const audioEl = this.audioEl || document.createElement('audio')
var src = audioTrack.contentUrl + `?token=${this.userToken}`
if (this.$isDev) {
src = `http://localhost:3333${this.$config.routerBasePath}${src}`
src = `${process.env.serverUrl}${src}`
}
audioEl.src = src
@@ -486,7 +486,7 @@ export default {
.then((data) => {
this.saving = false
if (data.updated) {
this.$toast.success('Chapters updated')
this.$toast.success(this.$strings.ToastChaptersUpdated)
if (this.previousRoute) {
this.$router.push(this.previousRoute)
} else {
@@ -499,7 +499,7 @@ export default {
.catch((error) => {
this.saving = false
console.error('Failed to update chapters', error)
this.$toast.error('Failed to update chapters')
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
},
applyChapterNamesOnly() {
@@ -533,7 +533,7 @@ export default {
},
findChapters() {
if (!this.asinInput) {
this.$toast.error('Must input an ASIN')
this.$toast.error(this.$strings.ToastAsinRequired)
return
}
@@ -628,15 +628,27 @@ export default {
.finally(() => {
this.saving = false
})
},
libraryItemUpdated(libraryItem) {
if (libraryItem.id === this.libraryItem.id) {
if (!!libraryItem.media.metadata.asin && this.mediaMetadata.asin !== libraryItem.media.metadata.asin) {
this.asinInput = libraryItem.media.metadata.asin
}
this.libraryItem = libraryItem
}
}
},
mounted() {
this.regionInput = localStorage.getItem('audibleRegion') || 'US'
this.asinInput = this.mediaMetadata.asin || null
this.initChapters()
this.$eventBus.$on(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
},
beforeDestroy() {
this.destroyAudioEl()
this.$eventBus.$off(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
}
}
</script>

View File

@@ -63,11 +63,11 @@
<div class="w-full max-w-4xl mx-auto">
<!-- queued alert -->
<widgets-alert v-if="isMetadataEmbedQueued" type="warning" class="mb-4">
<p class="text-lg">Audiobook is queued for metadata embed ({{ queuedEmbedLIds.length }} in queue)</p>
<p class="text-lg">{{ $getString('MessageEmbedQueue', [queuedEmbedLIds.length]) }}</p>
</widgets-alert>
<!-- metadata embed action buttons -->
<div v-else-if="isEmbedTool" class="w-full flex justify-end items-center mb-4">
<ui-checkbox v-if="!isTaskFinished" v-model="shouldBackupAudioFiles" :disabled="processing" label="Backup audio files" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" @input="toggleBackupAudioFiles" />
<ui-checkbox v-if="!isTaskFinished" v-model="shouldBackupAudioFiles" :disabled="processing" :label="$strings.LabelBackupAudioFiles" medium checkbox-bg="bg" label-class="pl-2 text-base md:text-lg" @input="toggleBackupAudioFiles" />
<div class="flex-grow" />
@@ -78,7 +78,7 @@
<!-- m4b embed action buttons -->
<div v-else class="w-full flex items-center mb-4">
<button :disabled="processing" class="text-sm uppercase text-gray-200 flex items-center pt-px pl-1 pr-2 hover:bg-white/5 rounded-md" @click="showEncodeOptions = !showEncodeOptions">
<span class="material-symbols text-xl">{{ showEncodeOptions || usingCustomEncodeOptions ? 'check_box' : 'check_box_outline_blank' }}</span> <span class="pl-1">Use Advanced Options</span>
<span class="material-symbols text-xl">{{ showEncodeOptions || usingCustomEncodeOptions ? 'check_box' : 'check_box_outline_blank' }}</span> <span class="pl-1">{{ $strings.LabelUseAdvancedOptions }}</span>
</button>
<div class="flex-grow" />
@@ -94,11 +94,11 @@
<transition name="slide">
<div v-if="showEncodeOptions || usingCustomEncodeOptions" class="mb-4 pb-4 border-b border-white/10">
<div class="flex flex-wrap -mx-2">
<ui-text-input-with-label ref="bitrateInput" v-model="encodingOptions.bitrate" :disabled="processing || isTaskFinished" :label="'Audio Bitrate (e.g. 128k)'" class="m-2 max-w-40" @input="bitrateChanged" />
<ui-text-input-with-label ref="channelsInput" v-model="encodingOptions.channels" :disabled="processing || isTaskFinished" :label="'Audio Channels (1 or 2)'" class="m-2 max-w-40" @input="channelsChanged" />
<ui-text-input-with-label ref="codecInput" v-model="encodingOptions.codec" :disabled="processing || isTaskFinished" :label="'Audio Codec'" class="m-2 max-w-40" @input="codecChanged" />
<ui-text-input-with-label ref="bitrateInput" v-model="encodingOptions.bitrate" :disabled="processing || isTaskFinished" :label="$strings.LabelAudioBitrate" class="m-2 max-w-40" @input="bitrateChanged" />
<ui-text-input-with-label ref="channelsInput" v-model="encodingOptions.channels" :disabled="processing || isTaskFinished" :label="$strings.LabelAudioChannels" class="m-2 max-w-40" @input="channelsChanged" />
<ui-text-input-with-label ref="codecInput" v-model="encodingOptions.codec" :disabled="processing || isTaskFinished" :label="$strings.LabelAudioCodec" class="m-2 max-w-40" @input="codecChanged" />
</div>
<p class="text-sm text-warning">Warning: Do not update these settings unless you are familiar with ffmpeg encoding options.</p>
<p class="text-sm text-warning">{{ $strings.LabelEncodingWarningAdvancedSettings }}</p>
</div>
</transition>
</div>
@@ -106,36 +106,36 @@
<div class="mb-4">
<div v-if="isEmbedTool" class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">Metadata will be embedded in the audio tracks inside your audiobook folder.</p>
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingInfoEmbedded }}</p>
</div>
<div v-else class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">
Finished M4B will be put into your audiobook folder at <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">.../{{ libraryItemRelPath }}/</span>.
{{ $strings.LabelEncodingFinishedM4B }} <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">.../{{ libraryItemRelPath }}/</span>.
</p>
</div>
<div v-if="shouldBackupAudioFiles || isM4BTool" class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">
A backup of your original audio files will be stored in <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">/metadata/cache/items/{{ libraryItemId }}/</span>. Make sure to periodically purge items cache.
{{ $strings.LabelEncodingBackupLocation }} <span class="rounded-md bg-neutral-600 text-sm text-white py-0.5 px-1 font-mono">/metadata/cache/items/{{ libraryItemId }}/</span>. {{ $strings.LabelEncodingClearItemCache }}
</p>
</div>
<div v-if="isEmbedTool && audioFiles.length > 1" class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">Chapters are not embedded in multi-track audiobooks.</p>
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingChaptersNotEmbedded }}</p>
</div>
<div v-if="isM4BTool" class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">Encoding can take up to 30 minutes.</p>
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingTimeWarning }}</p>
</div>
<div v-if="isM4BTool" class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">If you have the watcher disabled you will need to re-scan this audiobook afterwards.</p>
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingWatcherDisabled }}</p>
</div>
<div class="flex items-start mb-2">
<span class="material-symbols text-base text-warning pt-1">star</span>
<p class="text-gray-200 ml-2">Once the task is started you can navigate away from this page.</p>
<p class="text-gray-200 ml-2">{{ $strings.LabelEncodingStartedNavigation }}</p>
</div>
</div>
</div>
@@ -269,11 +269,11 @@ export default {
},
availableTools() {
if (this.isSingleM4b) {
return [{ value: 'embed', text: 'Embed Metadata' }]
return [{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata }]
} else {
return [
{ value: 'embed', text: 'Embed Metadata' },
{ value: 'm4b', text: 'M4B Encoder' }
{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata },
{ value: 'm4b', text: this.$strings.LabelToolsM4bEncoder }
]
}
},
@@ -370,7 +370,7 @@ export default {
},
embedClick() {
const payload = {
message: `Are you sure you want to embed metadata in ${this.audioFiles.length} audio files?`,
message: this.$getString('MessageConfirmEmbedMetadataInAudioFiles', [this.audioFiles.length]),
callback: (confirmed) => {
if (confirmed) {
this.updateAudioFileMetadata()

View File

@@ -53,7 +53,7 @@ export default {
})
if (!author) {
return redirect(`/library/${store.state.libraries.currentLibraryId}/authors`)
return redirect(`/library/${store.state.libraries.currentLibraryId}/bookshelf/authors`)
}
if (store.state.libraries.currentLibraryId !== author.libraryId || !store.state.libraries.filterData) {
@@ -109,7 +109,7 @@ export default {
authorRemoved(author) {
if (author.id === this.author.id) {
console.warn('Author was removed')
this.$router.replace(`/library/${this.currentLibraryId}/authors`)
this.$router.replace(`/library/${this.currentLibraryId}/bookshelf/authors`)
}
}
},

View File

@@ -16,7 +16,7 @@
<ui-btn v-if="showPlayButton" :disabled="streaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="clickPlay">
<span v-show="!streaming" class="material-symbols fill text-2xl -ml-2 pr-1 text-white">play_arrow</span>
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }}
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlayAll }}
</ui-btn>
<!-- RSS feed -->

View File

@@ -317,7 +317,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update server settings', error)
this.$toast.error(this.$strings.ToastServerSettingsUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.savingSettings = false

View File

@@ -162,7 +162,7 @@ export default {
})
.catch((error) => {
console.error('Failed to save backup path', error)
const errorMsg = error.response?.data || this.$strings.ToastBackupPathUpdateFailed
const errorMsg = error.response?.data || this.$strings.ToastFailedToUpdate
this.$toast.error(errorMsg)
})
.finally(() => {

View File

@@ -292,7 +292,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update email settings', error)
this.$toast.error(this.$strings.ToastEmailSettingsUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.savingSettings = false

View File

@@ -290,7 +290,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update prefixes', error)
this.$toast.error(this.$strings.ToastSortingPrefixesUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.savingPrefixes = false
@@ -328,7 +328,6 @@ export default {
.dispatch('updateServerSettings', payload)
.then(() => {
this.updatingServerSettings = false
this.$toast.success(this.$strings.ToastServerSettingsUpdateSuccess)
if (payload.language) {
// Updating language after save allows for re-rendering
@@ -338,7 +337,7 @@ export default {
.catch((error) => {
console.error('Failed to update server settings', error)
this.updatingServerSettings = false
this.$toast.error(this.$strings.ToastServerSettingsUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
},
initServerSettings() {

View File

@@ -10,9 +10,9 @@
</template>
<div class="flex justify-between mb-2 place-items-end">
<ui-text-input ref="input" v-model="search" placeholder="Search filter.." @input="inputUpdate" clearable class="w-full sm:w-40 h-8 text-sm sm:mb-0" />
<ui-text-input ref="input" v-model="search" :placeholder="$strings.PlaceholderSearch" @input="inputUpdate" clearable class="w-full sm:w-40 h-8 text-sm sm:mb-0" />
<ui-dropdown v-model="newServerSettings.logLevel" label="Server Log Level" :items="logLevelItems" @input="logLevelUpdated" class="w-full sm:w-44" />
<ui-dropdown v-model="newServerSettings.logLevel" :label="$strings.LabelServerLogLevel" :items="logLevelItems" @input="logLevelUpdated" class="w-full sm:w-44" />
</div>
<div class="relative">

View File

@@ -132,7 +132,7 @@ export default {
})
.catch((error) => {
console.error('Failed to update notification settings', error)
this.$toast.error(this.$strings.ToastNotificationSettingsUpdateFailed)
this.$toast.error(this.$strings.ToastFailedToUpdate)
})
.finally(() => {
this.savingSettings = false

View File

@@ -88,7 +88,7 @@
<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>
<p class="text-sm mx-2">{{ $getString('LabelPaginationPageXOfY', [currentPage + 1, 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>
@@ -103,7 +103,7 @@
<div v-if="openListeningSessions.length" class="w-full my-8 h-px bg-white/10" />
<!-- open listening sessions table -->
<p v-if="openListeningSessions.length" class="text-lg my-4">Open Listening Sessions</p>
<p v-if="openListeningSessions.length" class="text-lg my-4">{{ $strings.HeaderOpenListeningSessions }}</p>
<div v-if="openListeningSessions.length" class="block max-w-full">
<table class="userSessionsTable">
<tr class="bg-primary bg-opacity-40">

View File

@@ -14,7 +14,7 @@
<h1 class="text-xl pl-2">{{ username }}</h1>
</div>
<div v-if="userToken" class="flex text-xs mt-4">
<ui-text-input-with-label label="API Token" :value="userToken" readonly />
<ui-text-input-with-label :label="$strings.LabelApiToken" :value="userToken" readonly />
<div class="px-1 mt-8 cursor-pointer" @click="copyToClipboard(userToken)">
<span class="material-symbols pl-2 text-base">content_copy</span>

View File

@@ -54,7 +54,7 @@
</table>
<div class="flex items-center justify-end py-1">
<ui-icon-btn icon="arrow_back_ios_new" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage === 0" @click="prevPage" />
<p class="text-sm mx-1">Page {{ currentPage + 1 }} of {{ numPages }}</p>
<p class="text-sm mx-1">{{ $getString('LabelPaginationPageXOfY', [currentPage + 1, numPages]) }}</p>
<ui-icon-btn icon="arrow_forward_ios" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage >= numPages - 1" @click="nextPage" />
</div>
</div>

View File

@@ -1,115 +0,0 @@
<template>
<div class="page" :class="streamLibraryItem ? 'streaming' : ''">
<app-book-shelf-toolbar page="authors" is-home :authors="authors" />
<div id="bookshelf" class="w-full h-full p-8e overflow-y-auto" :style="{ fontSize: sizeMultiplier + 'rem' }">
<!-- Cover size widget -->
<widgets-cover-size-widget class="fixed right-4 z-50" :style="{ bottom: streamLibraryItem ? '181px' : '16px' }" />
<div class="flex flex-wrap justify-center">
<template v-for="author in authorsSorted">
<cards-author-card :key="author.id" :author="author" class="p-3e" @edit="editAuthor" />
</template>
</div>
</div>
</div>
</template>
<script>
export default {
async asyncData({ store, params, redirect, query, app }) {
var libraryId = params.library
var libraryData = await store.dispatch('libraries/fetch', libraryId)
if (!libraryData) {
return redirect('/oops?message=Library not found')
}
const library = libraryData.library
if (library.mediaType === 'podcast') {
return redirect(`/library/${libraryId}`)
}
return {
libraryId
}
},
data() {
return {
loading: true,
authors: []
}
},
computed: {
sizeMultiplier() {
return this.$store.getters['user/getSizeMultiplier']
},
streamLibraryItem() {
return this.$store.state.streamLibraryItem
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId
},
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') {
// Fallback to name sort if equal
if (a[sortProp] === b[sortProp]) return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) * bDesc
return a[sortProp] > b[sortProp] ? bDesc : -bDesc
}
return a[sortProp]?.localeCompare(b[sortProp], undefined, { sensitivity: 'base' }) * bDesc
})
}
},
methods: {
async init() {
this.authors = await this.$axios
.$get(`/api/libraries/${this.currentLibraryId}/authors`)
.then((response) => response.authors)
.catch((error) => {
console.error('Failed to load authors', error)
return []
})
this.loading = false
},
authorAdded(author) {
if (!this.authors.some((au) => au.id === author.id)) {
this.authors.push(author)
}
},
authorUpdated(author) {
this.authors = this.authors.map((au) => {
if (au.id === author.id) {
return author
}
return au
})
},
authorRemoved(author) {
this.authors = this.authors.filter((au) => au.id !== author.id)
},
editAuthor(author) {
this.$store.commit('globals/showEditAuthorModal', author)
}
},
mounted() {
this.init()
this.$root.socket.on('author_added', this.authorAdded)
this.$root.socket.on('author_updated', this.authorUpdated)
this.$root.socket.on('author_removed', this.authorRemoved)
},
beforeDestroy() {
this.$root.socket.off('author_added', this.authorAdded)
this.$root.socket.off('author_updated', this.authorUpdated)
this.$root.socket.off('author_removed', this.authorRemoved)
}
}
</script>

View File

@@ -27,7 +27,7 @@ export default {
// Redirect podcast libraries
const library = libraryData.library
if (library.mediaType === 'podcast' && (params.id === 'collections' || params.id === 'series')) {
if (library.mediaType === 'podcast' && (params.id === 'collections' || params.id === 'series' || params.id === 'authors')) {
return redirect(`/library/${libraryId}`)
}

View File

@@ -120,7 +120,7 @@ export default {
})
.catch((error) => {
console.error('Failed to updated narrator', error)
this.$toast.error('Failed to update narrator')
this.$toast.error(this.$strings.ToastFailedToUpdate)
this.loading = false
})
},

View File

@@ -5,7 +5,7 @@
<div id="bookshelf" class="w-full overflow-y-auto px-2 py-6 sm:px-4 md:p-12 relative">
<div class="w-full max-w-4xl mx-auto flex">
<form @submit.prevent="submit" class="flex flex-grow">
<ui-text-input v-model="searchInput" type="search" :disabled="processing" placeholder="Enter search term or RSS feed URL" class="flex-grow mr-2 text-sm md:text-base" />
<ui-text-input v-model="searchInput" type="search" :disabled="processing" :placeholder="$strings.MessagePodcastSearchField" class="flex-grow mr-2 text-sm md:text-base" />
<ui-btn type="submit" :disabled="processing" class="hidden md:block">{{ $strings.ButtonSubmit }}</ui-btn>
<ui-btn type="submit" :disabled="processing" class="block md:hidden" small>{{ $strings.ButtonSubmit }}</ui-btn>
</form>
@@ -22,7 +22,7 @@
<div class="flex items-center">
<a :href="podcast.pageUrl" class="text-base md:text-lg text-gray-200 hover:underline" target="_blank" @click.stop>{{ podcast.title }}</a>
<widgets-explicit-indicator v-if="podcast.explicit" />
<widgets-already-in-library-indicator :already-in-library="podcast.alreadyInLibrary" />
<widgets-already-in-library-indicator v-if="podcast.alreadyInLibrary" />
</div>
<p class="text-sm md:text-base text-gray-300 whitespace-nowrap truncate">{{ $getString('LabelByAuthor', [podcast.artistName]) }}</p>
<p class="text-xs text-gray-400 leading-5">{{ podcast.genres.join(', ') }}</p>
@@ -108,7 +108,7 @@ export default {
if (!txt || !txt.includes('<opml') || !txt.includes('<outline ')) {
// Quick lazy check for valid OPML
this.$toast.error('Invalid OPML file <opml> tag not found OR an <outline> tag was not found')
this.$toast.error(this.$strings.MessageTaskOpmlParseFastFail)
this.processing = false
return
}
@@ -117,7 +117,7 @@ export default {
.$post(`/api/podcasts/opml/parse`, { opmlText: txt })
.then((data) => {
if (!data.feeds?.length) {
this.$toast.error('No feeds found in OPML file')
this.$toast.error(this.$strings.MessageTaskOpmlParseNoneFound)
} else {
this.opmlFeeds = data.feeds || []
this.showOPMLFeedsModal = true
@@ -125,7 +125,7 @@ export default {
})
.catch((error) => {
console.error('Failed', error)
this.$toast.error('Failed to parse OPML file')
this.$toast.error(this.$strings.MessageTaskOpmlParseFailed)
})
.finally(() => {
this.processing = false
@@ -191,7 +191,7 @@ export default {
return
}
if (!podcast.feedUrl) {
this.$toast.error('Invalid podcast - no feed')
this.$toast.error(this.$strings.MessageNoPodcastFeed)
return
}
this.processing = true
@@ -211,15 +211,15 @@ export default {
async fetchExistentPodcastsInYourLibrary() {
this.processing = true
const podcasts = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/items?page=0&minified=1`).catch((error) => {
const podcastsResponse = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/podcast-titles`).catch((error) => {
console.error('Failed to fetch podcasts', error)
return []
})
this.existentPodcasts = podcasts.results.map((p) => {
this.existentPodcasts = podcastsResponse.podcasts.map((p) => {
return {
title: p.media.metadata.title.toLowerCase(),
itunesId: p.media.metadata.itunesId,
id: p.id
title: p.title.toLowerCase(),
itunesId: p.itunesId,
id: p.libraryItemId
}
})
this.processing = false

View File

@@ -16,7 +16,7 @@
<ui-btn v-if="showPlayButton" :disabled="streaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="clickPlay">
<span v-show="!streaming" class="material-symbols fill text-2xl -ml-2 pr-1 text-white">play_arrow</span>
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }}
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlayAll }}
</ui-btn>
<ui-icon-btn icon="edit" class="mx-0.5" @click="editClick" />

View File

@@ -10,7 +10,7 @@
<p v-if="mediaItemShare.playbackSession.displayAuthor" class="text-lg lg:text-xl text-slate-400 font-semibold text-center mb-1 truncate">{{ mediaItemShare.playbackSession.displayAuthor }}</p>
<div class="w-full pt-16">
<player-ui ref="audioPlayer" :chapters="chapters" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
<player-ui ref="audioPlayer" :chapters="chapters" :current-chapter="currentChapter" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
</div>
</div>
</div>
@@ -51,7 +51,8 @@ export default {
windowHeight: 0,
listeningTimeSinceSync: 0,
coverRgb: null,
coverBgIsLight: false
coverBgIsLight: false,
currentTime: 0
}
},
computed: {
@@ -60,16 +61,10 @@ export default {
},
coverUrl() {
if (!this.playbackSession.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg`
if (process.env.NODE_ENV === 'development') {
return `http://localhost:3333/public/share/${this.mediaItemShare.slug}/cover`
}
return `/public/share/${this.mediaItemShare.slug}/cover`
return `${this.$config.routerBasePath}/public/share/${this.mediaItemShare.slug}/cover`
},
audioTracks() {
return (this.playbackSession.audioTracks || []).map((track) => {
if (process.env.NODE_ENV === 'development') {
track.contentUrl = `${process.env.serverUrl}${track.contentUrl}`
}
track.relativeContentUrl = track.contentUrl
return track
})
@@ -83,6 +78,9 @@ export default {
chapters() {
return this.playbackSession.chapters || []
},
currentChapter() {
return this.chapters.find((chapter) => chapter.start <= this.currentTime && this.currentTime < chapter.end)
},
coverAspectRatio() {
const coverAspectRatio = this.playbackSession.coverAspectRatio
return coverAspectRatio === this.$constants.BookCoverAspectRatio.STANDARD ? 1.6 : 1
@@ -154,6 +152,7 @@ export default {
// Update UI
this.$refs.audioPlayer.setCurrentTime(time)
this.currentTime = time
},
setDuration() {
if (!this.localAudioPlayer) return

View File

@@ -384,12 +384,6 @@ export default {
else itemsFailed++
this.updateItemCardStatus(item.index, result ? 'success' : 'failed')
}
if (itemsUploaded) {
this.$toast.success(`Successfully uploaded ${itemsUploaded} item${itemsUploaded > 1 ? 's' : ''}`)
}
if (itemsFailed) {
this.$toast.success(`Failed to upload ${itemsFailed} item${itemsFailed > 1 ? 's' : ''}`)
}
this.processing = false
this.uploadFinished = true
}

View File

@@ -23,10 +23,6 @@ export default class AudioTrack {
get relativeContentUrl() {
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
if (process.env.NODE_ENV === 'development') {
return `${process.env.serverUrl}${this.contentUrl}?token=${this.userToken}`
}
return this.contentUrl + `?token=${this.userToken}`
}
}

View File

@@ -297,7 +297,6 @@ export default class PlayerHandler {
if (listeningTimeToAdd > 20) {
syncData = {
timeListened: listeningTimeToAdd,
duration: this.getDuration(),
currentTime: this.getCurrentTime()
}
}
@@ -317,7 +316,6 @@ export default class PlayerHandler {
const listeningTimeToAdd = Math.max(0, Math.floor(this.listeningTimeSinceSync))
const syncData = {
timeListened: listeningTimeToAdd,
duration: this.getDuration(),
currentTime
}

View File

@@ -1,5 +1,5 @@
export default function ({ $axios, store, $config }) {
$axios.onRequest(config => {
$axios.onRequest((config) => {
if (!config.url) {
console.error('Axios request invalid config', config)
return
@@ -13,14 +13,13 @@ export default function ({ $axios, store, $config }) {
}
if (process.env.NODE_ENV === 'development') {
config.url = `/dev${config.url}`
console.log('Making request to ' + config.url)
}
})
$axios.onError(error => {
$axios.onError((error) => {
const code = parseInt(error.response && error.response.status)
const message = error.response ? error.response.data || 'Unknown Error' : 'Unknown Error'
console.error('Axios error', code, message)
})
}
}

View File

@@ -89,10 +89,10 @@ Vue.prototype.$strings = { ...enUsStrings }
* Get string and substitute
*
* @param {string} key
* @param {string[]} subs
* @param {string[]} [subs=[]]
* @returns {string}
*/
Vue.prototype.$getString = (key, subs) => {
Vue.prototype.$getString = (key, subs = []) => {
if (!Vue.prototype.$strings[key]) return ''
if (subs?.length && Array.isArray(subs)) {
return supplant(Vue.prototype.$strings[key], subs)

View File

@@ -18,7 +18,10 @@ Vue.prototype.$bytesPretty = (bytes, decimals = 2) => {
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
}
Vue.prototype.$elapsedPretty = (seconds, useFullNames = false) => {
Vue.prototype.$elapsedPretty = (seconds, useFullNames = false, useMilliseconds = false) => {
if (useMilliseconds && seconds > 0 && seconds < 1) {
return `${Math.floor(seconds * 1000)} ms`
}
if (seconds < 60) {
return `${Math.floor(seconds)} sec${useFullNames ? 'onds' : ''}`
}

View File

@@ -72,13 +72,13 @@ export const state = () => ({
}
],
podcastTypes: [
{ text: 'Episodic', value: 'episodic' },
{ text: 'Serial', value: 'serial' }
{ text: 'Episodic', value: 'episodic', descriptionKey: 'LabelEpisodic' },
{ text: 'Serial', value: 'serial', descriptionKey: 'LabelSerial' }
],
episodeTypes: [
{ text: 'Full', value: 'full' },
{ text: 'Trailer', value: 'trailer' },
{ text: 'Bonus', value: 'bonus' }
{ text: 'Full', value: 'full', descriptionKey: 'LabelFull' },
{ text: 'Trailer', value: 'trailer', descriptionKey: 'LabelTrailer' },
{ text: 'Bonus', value: 'bonus', descriptionKey: 'LabelBonus' }
],
libraryIcons: ['database', 'audiobookshelf', 'books-1', 'books-2', 'book-1', 'microphone-1', 'microphone-3', 'radio', 'podcast', 'rss', 'headphones', 'music', 'file-picture', 'rocket', 'power', 'star', 'heart']
})
@@ -98,12 +98,6 @@ export const getters = {
const userToken = rootGetters['user/getToken']
const lastUpdate = libraryItem.updatedAt || Date.now()
const libraryItemId = libraryItem.libraryItemId || libraryItem.id // Workaround for /users/:id page showing media progress covers
if (process.env.NODE_ENV !== 'production') {
// Testing
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}${raw ? '&raw=1' : ''}`
},
getLibraryItemCoverSrcById:
@@ -112,10 +106,6 @@ export const getters = {
const placeholder = `${rootState.routerBasePath}/book_placeholder.jpg`
if (!libraryItemId) return placeholder
const userToken = rootGetters['user/getToken']
if (process.env.NODE_ENV !== 'production') {
// Testing
return `http://localhost:3333${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}${raw ? '&raw=1' : ''}${timestamp ? `&ts=${timestamp}` : ''}`
}
return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}${raw ? '&raw=1' : ''}${timestamp ? `&ts=${timestamp}` : ''}`
},
getIsBatchSelectingMediaItems: (state) => {

View File

@@ -240,7 +240,8 @@ export const mutations = {
series: [],
narrators: [],
languages: [],
publishers: []
publishers: [],
publishedDecades: []
}
*/
const mediaMetadata = libraryItem.media.metadata
@@ -307,6 +308,16 @@ export const mutations = {
state.filterData.publishers.sort((a, b) => a.localeCompare(b))
}
// Add publishedDecades
if (mediaMetadata.publishedYear && !isNaN(mediaMetadata.publishedYear)) {
const publishedYear = parseInt(mediaMetadata.publishedYear, 10)
const decade = (Math.floor(publishedYear / 10) * 10).toString()
if (!state.filterData.publishedDecades.includes(decade)) {
state.filterData.publishedDecades.push(decade)
state.filterData.publishedDecades.sort((a, b) => a - b)
}
}
// Add language
if (mediaMetadata.language && !state.filterData.languages.includes(mediaMetadata.language)) {
state.filterData.languages.push(mediaMetadata.language)

View File

@@ -90,7 +90,7 @@ export const actions = {
if (state.settings.orderBy == 'media.metadata.publishedYear') {
settingsUpdate.orderBy = 'media.metadata.title'
}
const invalidFilters = ['series', 'authors', 'narrators', 'publishers', 'languages', 'progress', 'issues', 'ebooks', 'abridged']
const invalidFilters = ['series', 'authors', 'narrators', 'publishers', 'publishedDecades', 'languages', 'progress', 'issues', 'ebooks', 'abridged']
const filterByFirstPart = (state.settings.filterBy || '').split('.').shift()
if (invalidFilters.includes(filterByFirstPart)) {
settingsUpdate.filterBy = 'all'

View File

@@ -711,10 +711,8 @@
"PlaceholderNewPlaylist": "Ново име на плейлиста",
"PlaceholderSearch": "Търсене...",
"PlaceholderSearchEpisode": "Търсене на Епизоди...",
"ToastAccountUpdateFailed": "Неуспешно обновяване на акаунта",
"ToastAccountUpdateSuccess": "Успешно обновяване на акаунта",
"ToastAuthorImageRemoveSuccess": "Авторската снимка е премахната",
"ToastAuthorUpdateFailed": "Неуспешно обновяване на автора",
"ToastAuthorUpdateMerged": "Обновяване на автора сливано",
"ToastAuthorUpdateSuccess": "Автора обновен",
"ToastAuthorUpdateSuccessNoImageFound": "Автор обновен (не е намерена снимка)",
@@ -728,17 +726,13 @@
"ToastBookmarkCreateFailed": "Неуспешно създаване на отметка",
"ToastBookmarkCreateSuccess": "Отметката е създадена",
"ToastBookmarkRemoveSuccess": "Отметката е премахната",
"ToastBookmarkUpdateFailed": "Неуспешно обновяване на отметка",
"ToastBookmarkUpdateSuccess": "Отметката е обновена",
"ToastChaptersHaveErrors": "Главите имат грешки",
"ToastChaptersMustHaveTitles": "Главите трябва да имат заглавия",
"ToastCollectionItemsRemoveSuccess": "Елемент(и) премахнати от колекция",
"ToastCollectionRemoveSuccess": "Колекцията е премахната",
"ToastCollectionUpdateFailed": "Неуспешно обновяване на колекция",
"ToastCollectionUpdateSuccess": "Колекцията е обновена",
"ToastItemCoverUpdateFailed": "Неуспешно обновяване на корица на елемент",
"ToastItemCoverUpdateSuccess": "Корицата на елемента е обновена",
"ToastItemDetailsUpdateFailed": "Неуспешно обновяване на детайли на елемент",
"ToastItemDetailsUpdateSuccess": "Детайлите на елемента са обновени",
"ToastItemMarkedAsFinishedFailed": "Неуспешно маркиране като завършено",
"ToastItemMarkedAsFinishedSuccess": "Елементът е маркиран като завършен",
@@ -750,12 +744,10 @@
"ToastLibraryDeleteSuccess": "Библиотеката е изтрита",
"ToastLibraryScanFailedToStart": "Неуспешно стартиране на сканиране",
"ToastLibraryScanStarted": "Сканирането на библиотеката е стартирано",
"ToastLibraryUpdateFailed": "Неуспешно обновяване на библиотека",
"ToastLibraryUpdateSuccess": "Библиотеката \"{0}\" е обновена",
"ToastPlaylistCreateFailed": "Неуспешно създаване на плейлист",
"ToastPlaylistCreateSuccess": "Плейлистът е създаден",
"ToastPlaylistRemoveSuccess": "Плейлистът е премахнат",
"ToastPlaylistUpdateFailed": "Неуспешно обновяване на плейлист",
"ToastPlaylistUpdateSuccess": "Плейлистът е обновен",
"ToastPodcastCreateFailed": "Неуспешно създаване на подкаст",
"ToastPodcastCreateSuccess": "Подкастът е създаден",

View File

@@ -8,7 +8,7 @@
"ButtonAddYourFirstLibrary": "আপনার প্রথম লাইব্রেরি যোগ করুন",
"ButtonApply": "প্রয়োগ করুন",
"ButtonApplyChapters": "অধ্যায় প্রয়োগ করুন",
"ButtonAuthors": "লেখক",
"ButtonAuthors": "লেখকগণ",
"ButtonBack": "পেছনে যান",
"ButtonBrowseForFolder": "ফোল্ডারের জন্য ব্রাউজ করুন",
"ButtonCancel": "বাতিল করুন",
@@ -56,6 +56,7 @@
"ButtonOpenManager": "ম্যানেজার খুলুন",
"ButtonPause": "বিরতি",
"ButtonPlay": "বাজান",
"ButtonPlayAll": "সব চালান",
"ButtonPlaying": "বাজছে",
"ButtonPlaylists": "প্লেলিস্ট",
"ButtonPrevious": "পূর্ববর্তী",
@@ -549,7 +550,7 @@
"LabelSleepTimer": "স্লিপ টাইমার",
"LabelSlug": "স্লাগ",
"LabelStart": "শুরু",
"LabelStartTime": "শুরু করার সময়",
"LabelStartTime": "শুরুর সময়",
"LabelStarted": "শুরু হয়েছে",
"LabelStartedAt": "এতে শুরু হয়েছে",
"LabelStatsAudioTracks": "অডিও ট্র্যাক",
@@ -702,7 +703,7 @@
"MessageEpisodesQueuedForDownload": "{0} পর্ব(গুলি) ডাউনলোডের জন্য সারিবদ্ধ",
"MessageEreaderDevices": "ই-বুক সরবরাহ নিশ্চিত করতে, আপনাকে নীচে তালিকাভুক্ত প্রতিটি ডিভাইসের জন্য একটি বৈধ প্রেরক হিসাবে উপরের ইমেল ঠিকানাটি যুক্ত করতে হতে পারে।",
"MessageFeedURLWillBe": "ফিড URL হবে {0}",
"MessageFetching": "আনয় হচ্ছে...",
"MessageFetching": "আনয় হচ্ছে.",
"MessageForceReScanDescription": "সকল ফাইল আবার নতুন স্ক্যানের মত স্ক্যান করবে। অডিও ফাইল ID3 ট্যাগ, OPF ফাইল, এবং টেক্সট ফাইলগুলি নতুন হিসাবে স্ক্যান করা হবে।",
"MessageImportantNotice": "গুরুত্বপূর্ণ বিজ্ঞপ্তি!",
"MessageInsertChapterBelow": "নীচে অধ্যায় ঢোকান",
@@ -710,7 +711,7 @@
"MessageItemsUpdated": "{0}টি আইটেম আপডেট করা হয়েছে",
"MessageJoinUsOn": "আমাদের সাথে যোগ দিন",
"MessageListeningSessionsInTheLastYear": "গত বছরে {0}টি শোনার সেশন",
"MessageLoading": "লোড হচ্ছে...",
"MessageLoading": "লোড হচ্ছে.",
"MessageLoadingFolders": "ফোল্ডার লোড হচ্ছে...",
"MessageLogsDescription": "লগগুলি JSON ফাইল হিসাবে <code>/metadata/logs</code>-এ সংরক্ষণ করা হয়। ক্র্যাশ লগগুলি <code>/metadata/logs/crash_logs.txt</code>-এ সংরক্ষণ করা হয়।",
"MessageM4BFailed": "M4B ব্যর্থ!",
@@ -776,6 +777,38 @@
"MessageShareExpiresIn": "মেয়াদ শেষ হবে {0}",
"MessageShareURLWillBe": "শেয়ার করা ইউআরএল হবে <strong>{0}</strong>",
"MessageStartPlaybackAtTime": "\"{0}\" এর জন্য {1} এ প্লেব্যাক শুরু করবেন?",
"MessageTaskAudioFileNotWritable": "অডিও ফাইল \"{0}\" লেখার যোগ্য নয়",
"MessageTaskCanceledByUser": "ব্যবহারকারী দ্বারা টাস্ক বাতিল করা হয়েছে",
"MessageTaskDownloadingEpisodeDescription": "\"{0}\" পর্ব ডাউনলোড করা হচ্ছে",
"MessageTaskEmbeddingMetadata": "মেটাডেটা এম্বেড করা হচ্ছে",
"MessageTaskEmbeddingMetadataDescription": "অডিওবুক \"{0}\" এ মেটাডেটা এম্বেড করা হচ্ছে",
"MessageTaskEncodingM4b": "এনকোডিং M4B",
"MessageTaskEncodingM4bDescription": "একটি একক m4b ফাইলে অডিওবুক \"{0}\" এনকোড করা হচ্ছে",
"MessageTaskFailed": "ব্যর্থ হয়েছে",
"MessageTaskFailedToBackupAudioFile": "অডিও ফাইল \"{0}\" ব্যাকআপ করতে ব্যর্থ হয়েছে",
"MessageTaskFailedToCreateCacheDirectory": "ক্যাশে ডিরেক্টরি তৈরি করতে ব্যর্থ হয়েছে",
"MessageTaskFailedToEmbedMetadataInFile": "\"{0}\" ফাইলে মেটাডেটা এম্বেড করতে ব্যর্থ হয়েছে",
"MessageTaskFailedToMergeAudioFiles": "অডিও ফাইল মার্জ করতে ব্যর্থ হয়েছে",
"MessageTaskFailedToMoveM4bFile": "m4b ফাইল সরাতে ব্যর্থ হয়েছে",
"MessageTaskFailedToWriteMetadataFile": "মেটাডেটা ফাইল লিখতে ব্যর্থ হয়েছে",
"MessageTaskMatchingBooksInLibrary": "লাইব্রেরি \"{0}\"-এ বই মিলানো হচ্ছে",
"MessageTaskNoFilesToScan": "স্ক্যান করার জন্য কোন ফাইল নেই",
"MessageTaskOpmlImport": "OPML আমদানি",
"MessageTaskOpmlImportDescription": "{0} RSS ফিড থেকে পডকাস্ট তৈরি করা হচ্ছে",
"MessageTaskOpmlImportFeed": "OPML ফিড আমদানি",
"MessageTaskOpmlImportFeedDescription": "RSS ফিড \"{0}\" আমদানি করা হচ্ছে",
"MessageTaskOpmlImportFeedFailed": "পডকাস্ট ফিড পেতে ব্যর্থ হয়েছে",
"MessageTaskOpmlImportFeedPodcastDescription": "পডকাস্ট তৈরি করা হচ্ছে \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "পডকাস্ট আগে থেকেই পাথে বিদ্যমান",
"MessageTaskOpmlImportFeedPodcastFailed": "পডকাস্ট তৈরি করতে ব্যর্থ",
"MessageTaskOpmlImportFinished": "{0}টি পডকাস্ট যোগ করা হয়েছে",
"MessageTaskScanItemsAdded": "{0}টি করা হয়েছে",
"MessageTaskScanItemsMissing": "{0}টি অনুপস্থিত",
"MessageTaskScanItemsUpdated": "{0} টি আপডেট করা হয়েছে",
"MessageTaskScanNoChangesNeeded": "কোন পরিবর্তন প্রয়োজন নেই",
"MessageTaskScanningFileChanges": "\"{0}\" এ ফাইলের পরিবর্তন স্ক্যান করা হচ্ছে",
"MessageTaskScanningLibrary": "\"{0}\" লাইব্রেরি স্ক্যান করা হচ্ছে",
"MessageTaskTargetDirectoryNotWritable": "টার্গেট ডিরেক্টরি লেখার যোগ্য নয়",
"MessageThinking": "চিন্তা করছি...",
"MessageUploaderItemFailed": "আপলোড করতে ব্যর্থ",
"MessageUploaderItemSuccess": "সফলভাবে আপলোড হয়েছে!",
@@ -816,14 +849,12 @@
"StatsTopNarrators": "শীর্ষ কথকগণ",
"StatsTotalDuration": "মোট সময়কাল…",
"StatsYearInReview": "বাৎসরিক পর্যালোচনা",
"ToastAccountUpdateFailed": "অ্যাকাউন্ট আপডেট করতে ব্যর্থ",
"ToastAccountUpdateSuccess": "অ্যাকাউন্ট আপডেট করা হয়েছে",
"ToastAppriseUrlRequired": "একটি Apprise ইউআরএল লিখতে হবে",
"ToastAuthorImageRemoveSuccess": "লেখকের ছবি সরানো হয়েছে",
"ToastAuthorNotFound": "লেখক \"{0}\" খুঁজে পাওয়া যায়নি",
"ToastAuthorRemoveSuccess": "লেখক সরানো হয়েছে",
"ToastAuthorSearchNotFound": "লেখক পাওয়া যায়নি",
"ToastAuthorUpdateFailed": "লেখক আপডেট করতে ব্যর্থ",
"ToastAuthorUpdateMerged": "লেখক একত্রিত হয়েছে",
"ToastAuthorUpdateSuccess": "লেখক আপডেট করেছেন",
"ToastAuthorUpdateSuccessNoImageFound": "লেখক আপডেট করেছেন (কোন ছবি পাওয়া যায়নি)",
@@ -834,7 +865,6 @@
"ToastBackupDeleteSuccess": "ব্যাকআপ মুছে ফেলা হয়েছে",
"ToastBackupInvalidMaxKeep": "রাখার জন্য অকার্যকর ব্যাকআপের সংখ্যা",
"ToastBackupInvalidMaxSize": "অকার্যকর সর্বোচ্চ ব্যাকআপ আকার",
"ToastBackupPathUpdateFailed": "ব্যাকআপ পথ আপডেট করতে ব্যর্থ হয়েছে",
"ToastBackupRestoreFailed": "ব্যাকআপ পুনরুদ্ধার করতে ব্যর্থ",
"ToastBackupUploadFailed": "ব্যাকআপ আপলোড করতে ব্যর্থ",
"ToastBackupUploadSuccess": "ব্যাকআপ আপলোড হয়েছে",
@@ -845,7 +875,6 @@
"ToastBookmarkCreateFailed": "বুকমার্ক তৈরি করতে ব্যর্থ",
"ToastBookmarkCreateSuccess": "বুকমার্ক যোগ করা হয়েছে",
"ToastBookmarkRemoveSuccess": "বুকমার্ক সরানো হয়েছে",
"ToastBookmarkUpdateFailed": "বুকমার্ক আপডেট করতে ব্যর্থ",
"ToastBookmarkUpdateSuccess": "বুকমার্ক আপডেট করা হয়েছে",
"ToastCachePurgeFailed": "ক্যাশে পরিষ্কার করতে ব্যর্থ হয়েছে",
"ToastCachePurgeSuccess": "ক্যাশে সফলভাবে পরিষ্কার করা হয়েছে",
@@ -856,7 +885,6 @@
"ToastCollectionItemsAddSuccess": "আইটেম(গুলি) সংগ্রহে যোগ করা সফল হয়েছে",
"ToastCollectionItemsRemoveSuccess": "আইটেম(গুলি) সংগ্রহ থেকে সরানো হয়েছে",
"ToastCollectionRemoveSuccess": "সংগ্রহ সরানো হয়েছে",
"ToastCollectionUpdateFailed": "সংগ্রহ আপডেট করতে ব্যর্থ",
"ToastCollectionUpdateSuccess": "সংগ্রহ আপডেট করা হয়েছে",
"ToastCoverUpdateFailed": "কভার আপডেট ব্যর্থ হয়েছে",
"ToastDeleteFileFailed": "ফাইল মুছে ফেলতে ব্যর্থ হয়েছে",
@@ -865,8 +893,6 @@
"ToastDeviceNameAlreadyExists": "এই নামের ইরিডার ডিভাইস ইতিমধ্যেই বিদ্যমান",
"ToastDeviceTestEmailFailed": "পরীক্ষামূলক ইমেল পাঠাতে ব্যর্থ হয়েছে",
"ToastDeviceTestEmailSuccess": "পরীক্ষামূলক ইমেল পাঠানো হয়েছে",
"ToastDeviceUpdateFailed": "ডিভাইস আপডেট করতে ব্যর্থ হয়েছে",
"ToastEmailSettingsUpdateFailed": "ইমেল সেটিংস আপডেট করতে ব্যর্থ হয়েছে",
"ToastEmailSettingsUpdateSuccess": "ইমেল সেটিংস আপডেট করা হয়েছে",
"ToastEncodeCancelFailed": "এনকোড বাতিল করতে ব্যর্থ হয়েছে",
"ToastEncodeCancelSucces": "এনকোড বাতিল করা হয়েছে",
@@ -875,21 +901,17 @@
"ToastErrorCannotShare": "এই ডিভাইসে স্থানীয়ভাবে শেয়ার করা যাবে না",
"ToastFailedToLoadData": "ডেটা লোড করা যায়নি",
"ToastFailedToShare": "শেয়ার করতে ব্যর্থ",
"ToastFailedToUpdateAccount": "অ্যাকাউন্ট আপডেট করতে ব্যর্থ",
"ToastFailedToUpdateUser": "ব্যবহারকারী আপডেট করতে ব্যর্থ",
"ToastFailedToUpdate": "আপডেট করতে ব্যর্থ হয়েছে",
"ToastInvalidImageUrl": "অকার্যকর ছবির ইউআরএল",
"ToastInvalidUrl": "অকার্যকর ইউআরএল",
"ToastItemCoverUpdateFailed": "আইটেম কভার আপডেট করতে ব্যর্থ হয়েছে",
"ToastItemCoverUpdateSuccess": "আইটেম কভার আপডেট করা হয়েছে",
"ToastItemDeletedFailed": "আইটেম মুছে ফেলতে ব্যর্থ",
"ToastItemDeletedSuccess": "মুছে ফেলা আইটেম",
"ToastItemDetailsUpdateFailed": "আইটেমের বিবরণ আপডেট করতে ব্যর্থ",
"ToastItemDetailsUpdateSuccess": "আইটেমের বিবরণ আপডেট করা হয়েছে",
"ToastItemMarkedAsFinishedFailed": "সমাপ্ত হিসাবে চিহ্নিত করতে ব্যর্থ",
"ToastItemMarkedAsFinishedSuccess": "আইটেম সমাপ্ত হিসাবে চিহ্নিত",
"ToastItemMarkedAsNotFinishedFailed": "সমাপ্ত হয়নি হিসাবে চিহ্নিত করতে ব্যর্থ",
"ToastItemMarkedAsNotFinishedSuccess": "আইটেম সমাপ্ত হয়নি বলে চিহ্নিত",
"ToastItemUpdateFailed": "আইটেম আপডেট করতে ব্যর্থ",
"ToastItemUpdateSuccess": "আইটেম আপডেট করা হয়েছে",
"ToastLibraryCreateFailed": "লাইব্রেরি তৈরি করতে ব্যর্থ",
"ToastLibraryCreateSuccess": "লাইব্রেরি \"{0}\" তৈরি করা হয়েছে",
@@ -897,7 +919,6 @@
"ToastLibraryDeleteSuccess": "লাইব্রেরি মুছে ফেলা হয়েছে",
"ToastLibraryScanFailedToStart": "স্ক্যান শুরু করতে ব্যর্থ",
"ToastLibraryScanStarted": "লাইব্রেরি স্ক্যান শুরু হয়েছে",
"ToastLibraryUpdateFailed": "লাইব্রেরি আপডেট করতে ব্যর্থ",
"ToastLibraryUpdateSuccess": "লাইব্রেরি \"{0}\" আপডেট করা হয়েছে",
"ToastNameEmailRequired": "নাম এবং ইমেইল আবশ্যক",
"ToastNameRequired": "নাম আবশ্যক",
@@ -912,16 +933,13 @@
"ToastNotificationDeleteFailed": "বিজ্ঞপ্তি মুছে ফেলতে ব্যর্থ",
"ToastNotificationFailedMaximum": "সর্বাধিক ব্যর্থ প্রচেষ্টা >= 0 হতে হবে",
"ToastNotificationQueueMaximum": "সর্বাধিক বিজ্ঞপ্তি সারি >= 0 হতে হবে",
"ToastNotificationSettingsUpdateFailed": "বিজ্ঞপ্তি সেটিংস আপডেট করতে ব্যর্থ",
"ToastNotificationSettingsUpdateSuccess": "বিজ্ঞপ্তি সেটিংস আপডেট করা হয়েছে",
"ToastNotificationTestTriggerFailed": "পরীক্ষামূলক বিজ্ঞপ্তি ট্রিগার করতে ব্যর্থ হয়েছে",
"ToastNotificationTestTriggerSuccess": "পরীক্ষামুলক বিজ্ঞপ্তি ট্রিগার হয়েছে",
"ToastNotificationUpdateFailed": "বিজ্ঞপ্তি আপডেট করতে ব্যর্থ",
"ToastNotificationUpdateSuccess": "বিজ্ঞপ্তি আপডেট হয়েছে",
"ToastPlaylistCreateFailed": "প্লেলিস্ট তৈরি করতে ব্যর্থ",
"ToastPlaylistCreateSuccess": "প্লেলিস্ট তৈরি করা হয়েছে",
"ToastPlaylistRemoveSuccess": "প্লেলিস্ট সরানো হয়েছে",
"ToastPlaylistUpdateFailed": "প্লেলিস্ট আপডেট করতে ব্যর্থ",
"ToastPlaylistUpdateSuccess": "প্লেলিস্ট আপডেট করা হয়েছে",
"ToastPodcastCreateFailed": "পডকাস্ট তৈরি করতে ব্যর্থ",
"ToastPodcastCreateSuccess": "পডকাস্ট সফলভাবে তৈরি করা হয়েছে",
@@ -950,7 +968,6 @@
"ToastSendEbookToDeviceSuccess": "ইবুক \"{0}\" ডিভাইসে পাঠানো হয়েছে",
"ToastSeriesUpdateFailed": "সিরিজ আপডেট ব্যর্থ হয়েছে",
"ToastSeriesUpdateSuccess": "সিরিজ আপডেট সাফল্য",
"ToastServerSettingsUpdateFailed": "সার্ভার সেটিংস আপডেট করতে ব্যর্থ হয়েছে",
"ToastServerSettingsUpdateSuccess": "সার্ভার সেটিংস আপডেট করা হয়েছে",
"ToastSessionCloseFailed": "অধিবেশন বন্ধ করতে ব্যর্থ হয়েছে",
"ToastSessionDeleteFailed": "সেশন মুছে ফেলতে ব্যর্থ",
@@ -961,7 +978,6 @@
"ToastSocketDisconnected": "সকেট সংযোগ বিচ্ছিন্ন",
"ToastSocketFailedToConnect": "সকেট সংযোগ করতে ব্যর্থ হয়েছে",
"ToastSortingPrefixesEmptyError": "কমপক্ষে ১ টি সাজানোর উপসর্গ থাকতে হবে",
"ToastSortingPrefixesUpdateFailed": "বাছাই উপসর্গ আপডেট করতে ব্যর্থ হয়েছে",
"ToastSortingPrefixesUpdateSuccess": "বাছাই করা উপসর্গ আপডেট করা হয়েছে ({0}টি আইটেম)",
"ToastTitleRequired": "শিরোনাম আবশ্যক",
"ToastUnknownError": "অজানা ত্রুটি",

View File

@@ -19,6 +19,7 @@
"ButtonChooseFiles": "Vybrat soubory",
"ButtonClearFilter": "Vymazat filtr",
"ButtonCloseFeed": "Zavřít kanál",
"ButtonCloseSession": "Zavřít otevřenou relaci",
"ButtonCollections": "Kolekce",
"ButtonConfigureScanner": "Konfigurovat Prohledávání",
"ButtonCreate": "Vytvořit",
@@ -28,6 +29,9 @@
"ButtonEdit": "Upravit",
"ButtonEditChapters": "Upravit kapitoly",
"ButtonEditPodcast": "Upravit podcast",
"ButtonEnable": "Povolit",
"ButtonFireAndFail": "Spustit a selhat",
"ButtonFireOnTest": "Spustit událost onTest",
"ButtonForceReScan": "Vynutit opětovné prohledání",
"ButtonFullPath": "Úplná cesta",
"ButtonHide": "Skrýt",
@@ -44,18 +48,25 @@
"ButtonMatchAllAuthors": "Spárovat všechny autory",
"ButtonMatchBooks": "Spárovat Knihy",
"ButtonNevermind": "Nevadí",
"ButtonNext": "Další",
"ButtonNextChapter": "Další Kapitola",
"ButtonNextItemInQueue": "Žádná další položka ve frontě",
"ButtonOk": "Ok",
"ButtonOpenFeed": "Otevřít kanál",
"ButtonOpenManager": "Otevřít správce",
"ButtonPause": "Pozastavit",
"ButtonPlay": "Přehrát",
"ButtonPlayAll": "Přehrát vše",
"ButtonPlaying": "Hraje",
"ButtonPlaylists": "Seznamy skladeb",
"ButtonPrevious": "Předchozí",
"ButtonPreviousChapter": "Předchozí Kapitola",
"ButtonProbeAudioFile": "Prozkoumat audio soubor",
"ButtonPurgeAllCache": "Vyčistit veškerou mezipaměť",
"ButtonPurgeItemsCache": "Vyčistit mezipaměť položek",
"ButtonQueueAddItem": "Přidat do fronty",
"ButtonQueueRemoveItem": "Odstranit z fronty",
"ButtonQuickEmbed": "Rychle Zapsat",
"ButtonQuickEmbedMetadata": "Rychle Zapsat Metadata",
"ButtonQuickMatch": "Rychlé přiřazení",
"ButtonReScan": "Znovu prohledat",
@@ -88,6 +99,8 @@
"ButtonStartMetadataEmbed": "Spustit vkládání metadat",
"ButtonStats": "Statistiky",
"ButtonSubmit": "Odeslat",
"ButtonTest": "Test",
"ButtonUnlinkOpenId": "Odpojit OpenID",
"ButtonUpload": "Nahrát",
"ButtonUploadBackup": "Nahrát zálohu",
"ButtonUploadCover": "Nahrát obálku",
@@ -100,10 +113,12 @@
"ErrorUploadFetchMetadataNoResults": "Nepodařilo se načíst metadata - zkuste aktualizovat název a/nebo autora",
"ErrorUploadLacksTitle": "Musí mít titul",
"HeaderAccount": "Účet",
"HeaderAddCustomMetadataProvider": "Přidat vlastního poskytovatele metadat",
"HeaderAdvanced": "Pokročilé",
"HeaderAppriseNotificationSettings": "Nastavení oznámení Apprise",
"HeaderAudioTracks": "Zvukové stopy",
"HeaderAudiobookTools": "Nástroje pro správu souborů audioknih",
"HeaderAuthentication": "Autentizace",
"HeaderBackups": "Zálohy",
"HeaderChangePassword": "Změnit heslo",
"HeaderChapters": "Kapitoly",
@@ -144,10 +159,13 @@
"HeaderMetadataToEmbed": "Metadata k vložení",
"HeaderNewAccount": "Nový účet",
"HeaderNewLibrary": "Nová knihovna",
"HeaderNotificationCreate": "Vytvořit notifikaci",
"HeaderNotificationUpdate": "Aktualizovat notifikaci",
"HeaderNotifications": "Oznámení",
"HeaderOpenIDConnectAuthentication": "Ověřování pomocí OpenID Connect",
"HeaderOpenRSSFeed": "Otevřít RSS kanál",
"HeaderOtherFiles": "Ostatní soubory",
"HeaderPasswordAuthentication": "Autentizace heslem",
"HeaderPermissions": "Oprávnění",
"HeaderPlayerQueue": "Fronta přehrávače",
"HeaderPlayerSettings": "Nastavení přehrávače",
@@ -162,6 +180,7 @@
"HeaderRemoveEpisodes": "Odstranit {0} epizody",
"HeaderSavedMediaProgress": "Průběh uložených médií",
"HeaderSchedule": "Plán",
"HeaderScheduleEpisodeDownloads": "Naplánovat automatické stahování epizod",
"HeaderScheduleLibraryScans": "Naplánovat automatické prohledávání knihoven",
"HeaderSession": "Relace",
"HeaderSetBackupSchedule": "Nastavit plán zálohování",
@@ -200,13 +219,18 @@
"LabelAddToPlaylist": "Přidat do seznamu přehrávání",
"LabelAddToPlaylistBatch": "Přidat {0} položky do seznamu přehrávání",
"LabelAddedAt": "Přidáno v",
"LabelAddedDate": "Přidáno {0}",
"LabelAdminUsersOnly": "Pouze administrátoři",
"LabelAll": "Vše",
"LabelAllUsers": "Všichni uživatelé",
"LabelAllUsersExcludingGuests": "Všichni uživatelé kromě hostů",
"LabelAllUsersIncludingGuests": "Všichni uživatelé včetně hostů",
"LabelAlreadyInYourLibrary": "Již ve vaší knihovně",
"LabelApiToken": "API Token",
"LabelAppend": "Připojit",
"LabelAudioBitrate": "Bitový tok zvuku (např. 128k)",
"LabelAudioChannels": "Zvukové kanály (1 nebo 2)",
"LabelAudioCodec": "Kodek audia",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (jméno a příjmení)",
"LabelAuthorLastFirst": "Autor (příjmení a jméno)",
@@ -219,6 +243,7 @@
"LabelAutoRegister": "Automatická registrace",
"LabelAutoRegisterDescription": "Automaticky vytvářet nové uživatele po přihlášení",
"LabelBackToUser": "Zpět k uživateli",
"LabelBackupAudioFiles": "Zálohovat zvukové soubory",
"LabelBackupLocation": "Umístění zálohy",
"LabelBackupsEnableAutomaticBackups": "Povolit automatické zálohování",
"LabelBackupsEnableAutomaticBackupsHelp": "Zálohy uložené v /metadata/backups",
@@ -227,8 +252,10 @@
"LabelBackupsNumberToKeep": "Počet záloh, které se mají uchovat",
"LabelBackupsNumberToKeepHelp": "Najednou bude odstraněna pouze 1 záloha, takže pokud již máte více záloh, měli byste je odstranit ručně.",
"LabelBitrate": "Datový tok",
"LabelBonus": "Bonus",
"LabelBooks": "Knihy",
"LabelButtonText": "Text tlačítka",
"LabelByAuthor": "od {0}",
"LabelChangePassword": "Změnit heslo",
"LabelChannels": "Kanály",
"LabelChapterTitle": "Název kapitoly",
@@ -238,6 +265,7 @@
"LabelClosePlayer": "Zavřít přehrávač",
"LabelCodec": "Kodek",
"LabelCollapseSeries": "Sbalit sérii",
"LabelCollapseSubSeries": "Sbalit podsérie",
"LabelCollection": "Kolekce",
"LabelCollections": "Kolekce",
"LabelComplete": "Dokončeno",
@@ -288,16 +316,21 @@
"LabelEpisode": "Epizoda",
"LabelEpisodeTitle": "Název epizody",
"LabelEpisodeType": "Typ epizody",
"LabelEpisodes": "Epizody",
"LabelExample": "Příklad",
"LabelExpandSeries": "Rozbalit série",
"LabelExpandSubSeries": "Rozbalit podsérie",
"LabelExplicit": "Explicitní",
"LabelExplicitChecked": "Explicitní (zaškrtnuto)",
"LabelExplicitUnchecked": "Není explicitní (nezaškrtnuto)",
"LabelExportOPML": "Export OPML",
"LabelFeedURL": "URL zdroje",
"LabelFetchingMetadata": "Získávání metadat",
"LabelFile": "Soubor",
"LabelFileBirthtime": "Čas vzniku souboru",
"LabelFileBornDate": "Vytvořeno {0}",
"LabelFileModified": "Soubor změněn",
"LabelFileModifiedDate": "Změněno {0}",
"LabelFilename": "Název souboru",
"LabelFilterByUser": "Filtrovat podle uživatele",
"LabelFindEpisodes": "Najít epizody",
@@ -307,6 +340,7 @@
"LabelFontBold": "Tučně",
"LabelFontBoldness": "Výraznost písma",
"LabelFontFamily": "Rodina písem",
"LabelFontItalic": "Kurzíva",
"LabelFontScale": "Měřítko písma",
"LabelFontStrikethrough": "Přeškrtnutí",
"LabelFormat": "Formát",
@@ -325,6 +359,7 @@
"LabelInProgress": "Probíhá",
"LabelIncludeInTracklist": "Zahrnout do seznamu stop",
"LabelIncomplete": "Neúplné",
"LabelInterval": "Interval",
"LabelIntervalCustomDailyWeekly": "Vlastní denně/týdně",
"LabelIntervalEvery12Hours": "Každých 12 hodin",
"LabelIntervalEvery15Minutes": "Každých 15 minut",
@@ -421,17 +456,22 @@
"LabelPersonalYearReview": "Váš přehled roku ({0})",
"LabelPhotoPathURL": "Cesta k fotografii/URL",
"LabelPlayMethod": "Metoda přehrávání",
"LabelPlayerChapterNumberMarker": "{0} z {1}",
"LabelPlaylists": "Seznamy skladeb",
"LabelPodcast": "Podcast",
"LabelPodcastSearchRegion": "Oblast vyhledávání podcastu",
"LabelPodcastType": "Typ podcastu",
"LabelPodcasts": "Podcasty",
"LabelPort": "Port",
"LabelPrefixesToIgnore": "Předpony, které se mají ignorovat (nerozlišují se malá a velká písmena)",
"LabelPreventIndexing": "Zabránit indexování vašeho kanálu v adresářích podcastů iTunes a Google",
"LabelPrimaryEbook": "Hlavní e-kniha",
"LabelProgress": "Průběh",
"LabelProvider": "Poskytovatel",
"LabelProviderAuthorizationValue": "Hodnota autorizačního headeru",
"LabelPubDate": "Datum vydání",
"LabelPublishYear": "Rok vydání",
"LabelPublishedDate": "Vydáno {0}",
"LabelPublisher": "Vydavatel",
"LabelPublishers": "Vydavatelé",
"LabelRSSFeedCustomOwnerEmail": "Vlastní e-mail vlastníka",
@@ -441,6 +481,7 @@
"LabelRSSFeedSlug": "RSS kanál Slug",
"LabelRSSFeedURL": "URL RSS kanálu",
"LabelRandomly": "Náhodně",
"LabelReAddSeriesToContinueListening": "Znovu přidat sérii k pokračování poslechu",
"LabelRead": "Číst",
"LabelReadAgain": "Číst znovu",
"LabelReadEbookWithoutProgress": "Číst e-knihu bez zachování průběhu",
@@ -448,6 +489,7 @@
"LabelRecentlyAdded": "Nedávno přidané",
"LabelRecommended": "Doporučeno",
"LabelRedo": "Přepracovat",
"LabelRegion": "Region",
"LabelReleaseDate": "Datum vydání",
"LabelRemoveCover": "Odstranit obálku",
"LabelRowsPerPage": "Řádky na stránku",
@@ -539,6 +581,7 @@
"LabelTagsNotAccessibleToUser": "Značky nepřístupné uživateli",
"LabelTasks": "Spuštěné Úlohy",
"LabelTextEditorBulletedList": "Seznam s odrážkami",
"LabelTextEditorLink": "Odkaz",
"LabelTextEditorNumberedList": "Seznam s čísly",
"LabelTextEditorUnlink": "Zrušit odkaz",
"LabelTheme": "Téma",
@@ -572,6 +615,7 @@
"LabelUnabridged": "Nezkráceno",
"LabelUndo": "Zpět",
"LabelUnknown": "Neznámý",
"LabelUnknownPublishDate": "Neznámé datum vydání",
"LabelUpdateCover": "Aktualizovat obálku",
"LabelUpdateCoverHelp": "Povolit přepsání existujících obálek pro vybrané knihy, pokud je nalezena shoda",
"LabelUpdateDetails": "Aktualizovat podrobnosti",
@@ -620,14 +664,19 @@
"MessageCheckingCron": "Kontrola cronu...",
"MessageConfirmCloseFeed": "Opravdu chcete zavřít tento kanál?",
"MessageConfirmDeleteBackup": "Opravdu chcete smazat zálohu pro {0}?",
"MessageConfirmDeleteDevice": "Opravdu chcete vymazat zařízení e-reader \"{0}\"?",
"MessageConfirmDeleteFile": "Tento krok smaže soubor ze souborového systému. Jsi si jisti?",
"MessageConfirmDeleteLibrary": "Opravdu chcete trvale smazat knihovnu \"{0}\"?",
"MessageConfirmDeleteLibraryItem": "Tento krok odstraní položku knihovny z databáze a vašeho souborového systému. Jste si jisti?",
"MessageConfirmDeleteLibraryItems": "Tímto smažete {0} položkek knihovny z databáze a vašeho souborového systému. Jsi si jisti?",
"MessageConfirmDeleteMetadataProvider": "Opravdu chcete vymazat vlastního poskytovatele metadat \"{0}\"?",
"MessageConfirmDeleteNotification": "Opravdu chcete vymazat tuto notifikaci?",
"MessageConfirmDeleteSession": "Opravdu chcete smazat tuto relaci?",
"MessageConfirmForceReScan": "Opravdu chcete vynutit opětovné prohledání?",
"MessageConfirmMarkAllEpisodesFinished": "Opravdu chcete označit všechny epizody jako dokončené?",
"MessageConfirmMarkAllEpisodesNotFinished": "Opravdu chcete označit všechny epizody jako nedokončené?",
"MessageConfirmMarkItemFinished": "Opravdu chcete označit \"{0}\" jako dokončené?",
"MessageConfirmMarkItemNotFinished": "Opravdu chcete označit \"{0}\" jako nedokončené?",
"MessageConfirmMarkSeriesFinished": "Opravdu chcete označit všechny knihy z této série jako dokončené?",
"MessageConfirmMarkSeriesNotFinished": "Opravdu chcete označit všechny knihy z této série jako nedokončené?",
"MessageConfirmPurgeCache": "Vyčistit mezipaměť odstraní celý adresář na adrese <code>/metadata/cache</code>. <br /><br />Určitě chcete odstranit adresář mezipaměti?",
@@ -648,7 +697,9 @@
"MessageConfirmRenameTag": "Opravdu chcete přejmenovat tag \"{0}\" na \"{1}\" pro všechny položky?",
"MessageConfirmRenameTagMergeNote": "Poznámka: Tato značka již existuje, takže budou sloučeny.",
"MessageConfirmRenameTagWarning": "Varování! Podobná značka s jinými velkými a malými písmeny již existuje \"{0}\".",
"MessageConfirmResetProgress": "Opravdu chcete zahodit svůj pokrok?",
"MessageConfirmSendEbookToDevice": "Opravdu chcete odeslat e-knihu {0} {1}\" do zařízení \"{2}\"?",
"MessageConfirmUnlinkOpenId": "Opravdu chcete odpojit tohoto uživatele z OpenID?",
"MessageDownloadingEpisode": "Stahuji epizodu",
"MessageDragFilesIntoTrackOrder": "Přetáhněte soubory do správného pořadí stop",
"MessageEmbedFailed": "Vložení selhalo!",
@@ -656,7 +707,7 @@
"MessageEpisodesQueuedForDownload": "{0} Epizody zařazené do fronty ke stažení",
"MessageEreaderDevices": "Aby bylo zajištěno doručení elektronických knih, může být nutné přidat výše uvedenou e-mailovou adresu jako platného odesílatele pro každé zařízení uvedené níže.",
"MessageFeedURLWillBe": "URL zdroje bude {0}",
"MessageFetching": "Stahování...",
"MessageFetching": "Načítání...",
"MessageForceReScanDescription": "znovu prohledá všechny soubory jako při novém skenování. ID3 tagy zvukových souborů OPF soubory a textové soubory budou skenovány jako nové.",
"MessageImportantNotice": "Důležité upozornění!",
"MessageInsertChapterBelow": "Vložit kapitolu níže",
@@ -683,6 +734,7 @@
"MessageNoCollections": "Žádné kolekce",
"MessageNoCoversFound": "Nebyly nalezeny žádné obálky",
"MessageNoDescription": "Bez popisu",
"MessageNoDevices": "Žádná zařízení",
"MessageNoDownloadsInProgress": "Momentálně neprobíhá žádné stahování",
"MessageNoDownloadsQueued": "Žádné stahování ve frontě",
"MessageNoEpisodeMatchesFound": "Nebyly nalezeny žádné odpovídající epizody",
@@ -710,6 +762,7 @@
"MessagePauseChapter": "Pozastavit přehrávání kapitoly",
"MessagePlayChapter": "Poslechnout si začátek kapitoly",
"MessagePlaylistCreateFromCollection": "Vytvořit seznam skladeb z kolekce",
"MessagePleaseWait": "Čekejte prosím...",
"MessagePodcastHasNoRSSFeedForMatching": "Podcast nemá žádnou adresu URL kanálu RSS, kterou by mohl použít pro porovnávání",
"MessageQuickMatchDescription": "Vyplňte prázdné detaily položky a obálku prvním výsledkem shody z '{0}'. Nepřepisuje podrobnosti, pokud není povoleno nastavení serveru \"Preferovat párování metadata\".",
"MessageRemoveChapter": "Odstranit kapitolu",
@@ -728,17 +781,46 @@
"MessageShareExpiresIn": "Expiruje za {0}",
"MessageShareURLWillBe": "Sdílené URL bude <strong>{0}</strong>",
"MessageStartPlaybackAtTime": "Spustit přehrávání pro \"{0}\" v {1}?",
"MessageTaskAudioFileNotWritable": "Nelze zapisovat do audio souboru \"{0}\"",
"MessageTaskCanceledByUser": "Task zrušen uživatelem",
"MessageTaskDownloadingEpisodeDescription": "Stahování epizody \"{0}\"",
"MessageTaskEmbeddingMetadata": "Vkládání metadat",
"MessageTaskEmbeddingMetadataDescription": "Vkládání metadat do audioknihy \"{0}\"",
"MessageTaskEncodingM4b": "Kódování M4B",
"MessageTaskEncodingM4bDescription": "Kódování audioknihy \"{0}\" do jednoho m4b souboru",
"MessageTaskFailed": "Selhalo",
"MessageTaskFailedToBackupAudioFile": "Zálohování audio souboru \"{0}\" se selhalo",
"MessageTaskFailedToCreateCacheDirectory": "Vytvoření cache adresáře selhalo",
"MessageTaskFailedToEmbedMetadataInFile": "Vkládání metadat do souboru \"{0}\" selhalo",
"MessageTaskFailedToMergeAudioFiles": "Spojení audio souborů selhalo",
"MessageTaskFailedToMoveM4bFile": "Přesunutí m4b souboru selhalo",
"MessageTaskFailedToWriteMetadataFile": "Zápis souboru metadat selhal",
"MessageTaskNoFilesToScan": "Žádné soubory ke skenování",
"MessageTaskOpmlImport": "Import OPML",
"MessageTaskOpmlImportDescription": "Vytváření podcastů z {0} RSS feedů",
"MessageTaskOpmlImportFeedDescription": "Importování RSS feedu \"{0}\"",
"MessageTaskOpmlImportFeedPodcastDescription": "Vytváření podcastu \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Podcast se stejnou cestou již existuje",
"MessageTaskOpmlImportFeedPodcastFailed": "Vytváření podcastu selhalo",
"MessageTaskOpmlImportFinished": "Přidáno {0} podcastů",
"MessageTaskScanItemsAdded": "{0} přidáno",
"MessageTaskScanItemsMissing": "{0} chybí",
"MessageTaskScanItemsUpdated": "{0} aktualizováno",
"MessageTaskScanNoChangesNeeded": "Žádné změny nejsou nutné",
"MessageTaskScanningFileChanges": "Skenování změn souborů v \"{0}\"",
"MessageTaskScanningLibrary": "Skenování \"{0}\" knihovny",
"MessageTaskTargetDirectoryNotWritable": "Do cílové složky nelze zapisovat",
"MessageThinking": "Přemýšlení...",
"MessageUploaderItemFailed": "Nahrávání se nezdařilo",
"MessageUploaderItemSuccess": "Nahráno bylo úspěšně!",
"MessageUploading": "Odesílám...",
"MessageUploaderItemFailed": "Nahrávání selhalo",
"MessageUploaderItemSuccess": "Úspěšně nahráno!",
"MessageUploading": "Nahrávám...",
"MessageValidCronExpression": "Platný výraz cronu",
"MessageWatcherIsDisabledGlobally": "Hlídač je globálně zakázán v nastavení serveru",
"MessageXLibraryIsEmpty": "{0} knihovna je prázdná!",
"MessageYourAudiobookDurationIsLonger": "Doba trvání audioknihy je delší než nalezená délka",
"MessageYourAudiobookDurationIsLonger": "Délka audioknihy je delší, než byla nalezena",
"MessageYourAudiobookDurationIsShorter": "Délka audioknihy je kratší, než byla nalezena",
"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 na 0:00 a čas začátku poslední kapitoly nesmí překročit dobu trvání audioknihy.",
"NoteFolderPicker": "Poznámka: složky, které jsou již namapovány, nebudou zobrazeny",
"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í.",
@@ -752,8 +834,10 @@
"PlaceholderSearchEpisode": "Hledat epizodu..",
"StatsAuthorsAdded": "autoři přidáni",
"StatsBooksAdded": "knihy přidány",
"StatsBooksAdditional": "Některé další zahrnují…",
"StatsBooksFinished": "dokončené knihy",
"StatsBooksFinishedThisYear": "Některé knihy dokončené tento rok…",
"StatsCollectionGrewTo": "Vaše kolekce knih se rozrostla na…",
"StatsSessions": "sezení",
"StatsSpentListening": "stráveno posloucháním",
"StatsTopAuthor": "TOP AUTOR",
@@ -763,59 +847,75 @@
"StatsTopMonth": "TOP MĚSÍC",
"StatsTotalDuration": "S celkovou dobou…",
"StatsYearInReview": "ROK V PŘEHLEDU",
"ToastAccountUpdateFailed": "Aktualizace účtu se nezdařila",
"ToastAccountUpdateSuccess": "Účet aktualizován",
"ToastAppriseUrlRequired": "Je nutné zadat Apprise URL",
"ToastAuthorImageRemoveSuccess": "Obrázek autora odstraněn",
"ToastAuthorUpdateFailed": "Aktualizace autora se nezdařila",
"ToastAuthorNotFound": "Author \"{0}\" nenalezen",
"ToastAuthorRemoveSuccess": "Autor odstraněn",
"ToastAuthorSearchNotFound": "Autor nenalezen",
"ToastAuthorUpdateMerged": "Autor sloučen",
"ToastAuthorUpdateSuccess": "Autor aktualizován",
"ToastAuthorUpdateSuccessNoImageFound": "Autor aktualizován (nebyl nalezen žádný obrázek)",
"ToastBackupAppliedSuccess": "Záloha obnovena",
"ToastBackupCreateFailed": "Vytvoření zálohy se nezdařilo",
"ToastBackupCreateSuccess": "Záloha vytvořena",
"ToastBackupDeleteFailed": "Nepodařilo se smazat zálohu",
"ToastBackupDeleteSuccess": "Záloha smazána",
"ToastBackupInvalidMaxKeep": "Neplatný počet záloh k zachování",
"ToastBackupInvalidMaxSize": "Neplatná maximální velikost zálohy",
"ToastBackupRestoreFailed": "Nepodařilo se obnovit zálohu",
"ToastBackupUploadFailed": "Nepodařilo se nahrát zálohu",
"ToastBackupUploadSuccess": "Záloha nahrána",
"ToastBatchDeleteFailed": "Hromadné smazání selhalo",
"ToastBatchDeleteSuccess": "Hromadné smazání proběhlo úspěšně",
"ToastBatchUpdateFailed": "Dávková aktualizace se nezdařila",
"ToastBatchUpdateSuccess": "Dávková aktualizace proběhla úspěšně",
"ToastBookmarkCreateFailed": "Vytvoření záložky se nezdařilo",
"ToastBookmarkCreateSuccess": "Přidána záložka",
"ToastBookmarkRemoveSuccess": "Záložka odstraněna",
"ToastBookmarkUpdateFailed": "Aktualizace záložky se nezdařila",
"ToastBookmarkUpdateSuccess": "Záložka aktualizována",
"ToastCachePurgeFailed": "Nepodařilo se vyčistit mezipaměť",
"ToastCachePurgeSuccess": "Vyrovnávací paměť úspěšně vyčištěna",
"ToastChaptersHaveErrors": "Kapitoly obsahují chyby",
"ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy",
"ToastChaptersRemoved": "Kapitoly odstraněny",
"ToastCollectionItemsRemoveSuccess": "Položky odstraněny z kolekce",
"ToastCollectionRemoveSuccess": "Kolekce odstraněna",
"ToastCollectionUpdateFailed": "Aktualizace kolekce se nezdařila",
"ToastCollectionUpdateSuccess": "Kolekce aktualizována",
"ToastCoverUpdateFailed": "Aktualizace obálky selhala",
"ToastDeleteFileFailed": "Nepodařilo se smazat soubor",
"ToastDeleteFileSuccess": "Soubor smazán",
"ToastDeviceAddFailed": "Přidání zařízení selhalo",
"ToastDeviceNameAlreadyExists": "Zařízení se stejným jménem již existuje",
"ToastDeviceTestEmailFailed": "Odeslání testovacího emailu selhalo",
"ToastDeviceTestEmailSuccess": "Testovací email byl odeslán",
"ToastEmailSettingsUpdateSuccess": "Nastavení emailu aktualizována",
"ToastEpisodeDownloadQueueClearFailed": "Vyčištění fronty selhalo",
"ToastErrorCannotShare": "Na tomto zařízení nelze nativně sdílet",
"ToastFailedToLoadData": "Nepodařilo se načíst data",
"ToastItemCoverUpdateFailed": "Aktualizace obálky se nezdařila",
"ToastFailedToShare": "Sdílení selhalo",
"ToastFailedToUpdate": "Aktualizace selhala",
"ToastInvalidImageUrl": "Neplatná URL obrázku",
"ToastInvalidUrl": "Neplatná URL",
"ToastItemCoverUpdateSuccess": "Obálka předmětu byl aktualizována",
"ToastItemDetailsUpdateFailed": "Nepodařilo se aktualizovat podrobnosti o položce",
"ToastItemDeletedFailed": "Smazání položky selhalo",
"ToastItemDeletedSuccess": "Položka smazána",
"ToastItemDetailsUpdateSuccess": "Podrobnosti o položce byly aktualizovány",
"ToastItemMarkedAsFinishedFailed": "Nepodařilo se označit jako dokončené",
"ToastItemMarkedAsFinishedSuccess": "Položka označena jako dokončená",
"ToastItemMarkedAsNotFinishedFailed": "Nepodařilo se označit jako nedokončené",
"ToastItemMarkedAsNotFinishedSuccess": "Položka označena jako nedokončená",
"ToastItemUpdateSuccess": "Položka aktualizována",
"ToastLibraryCreateFailed": "Vytvoření knihovny se nezdařilo",
"ToastLibraryCreateSuccess": "Knihovna \"{0}\" vytvořena",
"ToastLibraryDeleteFailed": "Nepodařilo se smazat knihovnu",
"ToastLibraryDeleteSuccess": "Knihovna smazána",
"ToastLibraryScanFailedToStart": "Nepodařilo se spustit kontrolu",
"ToastLibraryScanStarted": "Kontrola knihovny spuštěna",
"ToastLibraryUpdateFailed": "Aktualizace knihovny se nezdařila",
"ToastLibraryUpdateSuccess": "Knihovna \"{0}\" aktualizována",
"ToastPlaylistCreateFailed": "Vytvoření seznamu přehrávání se nezdařilo",
"ToastPlaylistCreateSuccess": "Seznam přehrávání vytvořen",
"ToastPlaylistRemoveSuccess": "Seznam přehrávání odstraněn",
"ToastPlaylistUpdateFailed": "Aktualizace seznamu přehrávání se nezdařila",
"ToastPlaylistUpdateSuccess": "Seznam přehrávání aktualizován",
"ToastPodcastCreateFailed": "Vytvoření podcastu se nezdařilo",
"ToastPodcastCreateSuccess": "Podcast byl úspěšně vytvořen",
@@ -827,7 +927,6 @@
"ToastSendEbookToDeviceSuccess": "E-kniha odeslána do zařízení \"{0}\"",
"ToastSeriesUpdateFailed": "Aktualizace série se nezdařila",
"ToastSeriesUpdateSuccess": "Aktualizace série byla úspěšná",
"ToastServerSettingsUpdateFailed": "Nepodařilo se aktualizovat nastavení serveru",
"ToastServerSettingsUpdateSuccess": "Nastavení serveru aktualizováno",
"ToastSessionDeleteFailed": "Nepodařilo se smazat relaci",
"ToastSessionDeleteSuccess": "Relace smazána",
@@ -835,7 +934,6 @@
"ToastSocketDisconnected": "Socket odpojen",
"ToastSocketFailedToConnect": "Socket se nepodařilo připojit",
"ToastSortingPrefixesEmptyError": "Musí mít alespoň 1 třídicí předponu",
"ToastSortingPrefixesUpdateFailed": "Nepodařilo se aktualizovat třídicí předpony",
"ToastSortingPrefixesUpdateSuccess": "Aktualizovány předpony třídění ({0} položek)",
"ToastUserDeleteFailed": "Nepodařilo se smazat uživatele",
"ToastUserDeleteSuccess": "Uživatel smazán"

View File

@@ -1,7 +1,10 @@
{
"ButtonAdd": "Tilføj",
"ButtonAddChapters": "Tilføj kapitler",
"ButtonAddDevice": "Tilføj enhed",
"ButtonAddLibrary": "Tilføj Bibliotek",
"ButtonAddPodcasts": "Tilføj podcasts",
"ButtonAddUser": "Tilføj bruger",
"ButtonAddYourFirstLibrary": "Tilføj din første bibliotek",
"ButtonApply": "Anvend",
"ButtonApplyChapters": "Anvend kapitler",
@@ -25,6 +28,7 @@
"ButtonEdit": "Rediger",
"ButtonEditChapters": "Rediger kapitler",
"ButtonEditPodcast": "Rediger podcast",
"ButtonEnable": "Aktiver",
"ButtonForceReScan": "Tvungen genindlæsning",
"ButtonFullPath": "Fuld sti",
"ButtonHide": "Skjul",
@@ -42,6 +46,7 @@
"ButtonOk": "OK",
"ButtonOpenFeed": "Åbn feed",
"ButtonOpenManager": "Åbn manager",
"ButtonPause": "Pause",
"ButtonPlay": "Afspil",
"ButtonPlaying": "Afspiller",
"ButtonPlaylists": "Afspilningslister",
@@ -66,7 +71,7 @@
"ButtonScanLibrary": "Scan Bibliotek",
"ButtonSearch": "Søg",
"ButtonSelectFolderPath": "Vælg Mappen Sti",
"ButtonSeries": "Serie",
"ButtonSeries": "Serier",
"ButtonSetChaptersFromTracks": "Sæt kapitler fra spor",
"ButtonShiftTimes": "Skift Tider",
"ButtonShow": "Vis",
@@ -188,14 +193,14 @@
"LabelChapters": "Kapitler",
"LabelChaptersFound": "fundne kapitler",
"LabelClosePlayer": "Luk afspiller",
"LabelCollapseSeries": "Fold Serie Sammen",
"LabelCollapseSeries": "Fold Serier Sammen",
"LabelCollection": "Samling",
"LabelCollections": "Samlinger",
"LabelComplete": "Fuldfør",
"LabelConfirmPassword": "Bekræft Adgangskode",
"LabelContinueListening": "Fortsæt Lytning",
"LabelContinueReading": "Fortsæt Læsning",
"LabelContinueSeries": "Fortsæt Serie",
"LabelContinueListening": "Fortsæt med at lytte",
"LabelContinueReading": "Fortsæt med at læse",
"LabelContinueSeries": "Fortsæt Serien",
"LabelCover": "Omslag",
"LabelCoverImageURL": "Omslagsbillede URL",
"LabelCreatedAt": "Oprettet Kl.",
@@ -212,6 +217,7 @@
"LabelDiscFromFilename": "Disk fra Filnavn",
"LabelDiscFromMetadata": "Disk fra Metadata",
"LabelDiscover": "Opdag",
"LabelDownload": "Download",
"LabelDownloadNEpisodes": "Download {0} episoder",
"LabelDuration": "Varighed",
"LabelDurationFound": "Fundet varighed:",
@@ -225,12 +231,15 @@
"LabelEmbeddedCover": "Indlejret Omslag",
"LabelEnable": "Aktivér",
"LabelEnd": "Slut",
"LabelEndOfChapter": "Slutningen af kapitel",
"LabelEpisode": "Episode",
"LabelEpisodeTitle": "Episodetitel",
"LabelEpisodeType": "Episodetype",
"LabelExample": "Eksempel",
"LabelExplicit": "Eksplisit",
"LabelFeedURL": "Feed URL",
"LabelFile": "Fil",
"LabelFileBirthtime": "Fødselstidspunkt for fil",
"LabelFileBirthtime": "Oprettelsestidspunkt for fil",
"LabelFileModified": "Fil ændret",
"LabelFilename": "Filnavn",
"LabelFilterByUser": "Filtrér efter bruger",
@@ -238,8 +247,10 @@
"LabelFinished": "Færdig",
"LabelFolder": "Mappe",
"LabelFolders": "Mapper",
"LabelFontBoldness": "Skrift tykkelse",
"LabelFontFamily": "Fontfamilie",
"LabelFontScale": "Skriftstørrelse",
"LabelGenre": "Genre",
"LabelGenres": "Genrer",
"LabelHardDeleteFile": "Permanent slet fil",
"LabelHasEbook": "Har e-bog",
@@ -267,6 +278,7 @@
"LabelLastSeen": "Sidst set",
"LabelLastTime": "Sidste gang",
"LabelLastUpdate": "Seneste opdatering",
"LabelLayout": "Layout",
"LabelLayoutSinglePage": "Enkeltside",
"LabelLayoutSplitPage": "Opdelt side",
"LabelLess": "Mindre",
@@ -344,10 +356,11 @@
"LabelRSSFeedPreventIndexing": "Forhindrer indeksering",
"LabelRSSFeedSlug": "RSS-feed-slug",
"LabelRSSFeedURL": "RSS-feed-URL",
"LabelRandomly": "Tilfældigt",
"LabelRead": "Læst",
"LabelReadAgain": "Læs igen",
"LabelReadEbookWithoutProgress": "Læs e-bog uden at følge fremskridt",
"LabelRecentSeries": "Seneste serie",
"LabelRecentSeries": "Seneste serier",
"LabelRecentlyAdded": "Senest tilføjet",
"LabelRecommended": "Anbefalet",
"LabelReleaseDate": "Udgivelsesdato",
@@ -604,10 +617,8 @@
"PlaceholderNewPlaylist": "Nyt afspilningslistnavn",
"PlaceholderSearch": "Søg..",
"PlaceholderSearchEpisode": "Søg efter episode..",
"ToastAccountUpdateFailed": "Mislykkedes opdatering af konto",
"ToastAccountUpdateSuccess": "Konto opdateret",
"ToastAuthorImageRemoveSuccess": "Forfatterbillede fjernet",
"ToastAuthorUpdateFailed": "Mislykkedes opdatering af forfatter",
"ToastAuthorUpdateMerged": "Forfatter fusioneret",
"ToastAuthorUpdateSuccess": "Forfatter opdateret",
"ToastAuthorUpdateSuccessNoImageFound": "Forfatter opdateret (ingen billede fundet)",
@@ -623,17 +634,13 @@
"ToastBookmarkCreateFailed": "Mislykkedes oprettelse af bogmærke",
"ToastBookmarkCreateSuccess": "Bogmærke tilføjet",
"ToastBookmarkRemoveSuccess": "Bogmærke fjernet",
"ToastBookmarkUpdateFailed": "Mislykkedes opdatering af bogmærke",
"ToastBookmarkUpdateSuccess": "Bogmærke opdateret",
"ToastChaptersHaveErrors": "Kapitler har fejl",
"ToastChaptersMustHaveTitles": "Kapitler skal have titler",
"ToastCollectionItemsRemoveSuccess": "Element(er) fjernet fra samlingen",
"ToastCollectionRemoveSuccess": "Samling fjernet",
"ToastCollectionUpdateFailed": "Mislykkedes opdatering af samling",
"ToastCollectionUpdateSuccess": "Samling opdateret",
"ToastItemCoverUpdateFailed": "Mislykkedes opdatering af varens omslag",
"ToastItemCoverUpdateSuccess": "Varens omslag opdateret",
"ToastItemDetailsUpdateFailed": "Mislykkedes opdatering af varedetaljer",
"ToastItemDetailsUpdateSuccess": "Varedetaljer opdateret",
"ToastItemMarkedAsFinishedFailed": "Mislykkedes markering som afsluttet",
"ToastItemMarkedAsFinishedSuccess": "Vare markeret som afsluttet",
@@ -645,12 +652,10 @@
"ToastLibraryDeleteSuccess": "Bibliotek slettet",
"ToastLibraryScanFailedToStart": "Mislykkedes start af skanning",
"ToastLibraryScanStarted": "Biblioteksskanning startet",
"ToastLibraryUpdateFailed": "Mislykkedes opdatering af bibliotek",
"ToastLibraryUpdateSuccess": "Bibliotek \"{0}\" opdateret",
"ToastPlaylistCreateFailed": "Mislykkedes oprettelse af afspilningsliste",
"ToastPlaylistCreateSuccess": "Afspilningsliste oprettet",
"ToastPlaylistRemoveSuccess": "Afspilningsliste fjernet",
"ToastPlaylistUpdateFailed": "Mislykkedes opdatering af afspilningsliste",
"ToastPlaylistUpdateSuccess": "Afspilningsliste opdateret",
"ToastPodcastCreateFailed": "Mislykkedes oprettelse af podcast",
"ToastPodcastCreateSuccess": "Podcast oprettet med succes",

View File

@@ -19,7 +19,7 @@
"ButtonChooseFiles": "Wähle eine Datei",
"ButtonClearFilter": "Filter löschen",
"ButtonCloseFeed": "Feed schließen",
"ButtonCloseSession": "Offene Session schließen",
"ButtonCloseSession": "Offene Sitzung schließen",
"ButtonCollections": "Sammlungen",
"ButtonConfigureScanner": "Scannereinstellungen",
"ButtonCreate": "Erstellen",
@@ -51,11 +51,12 @@
"ButtonNext": "Vor",
"ButtonNextChapter": "Nächstes Kapitel",
"ButtonNextItemInQueue": "Das nächste Element in der Warteschlange",
"ButtonOk": "Ok",
"ButtonOk": "OK",
"ButtonOpenFeed": "Feed öffnen",
"ButtonOpenManager": "Manager öffnen",
"ButtonPause": "Pausieren",
"ButtonPlay": "Abspielen",
"ButtonPlayAll": "Alles abspielen",
"ButtonPlaying": "Spielt",
"ButtonPlaylists": "Wiedergabelisten",
"ButtonPrevious": "Zurück",
@@ -65,6 +66,7 @@
"ButtonPurgeItemsCache": "Lösche Medien-Cache",
"ButtonQueueAddItem": "Zur Warteschlange hinzufügen",
"ButtonQueueRemoveItem": "Aus der Warteschlange entfernen",
"ButtonQuickEmbed": "Schnelles Hinzufügen",
"ButtonQuickEmbedMetadata": "Schnelles Hinzufügen von Metadaten",
"ButtonQuickMatch": "Schnellabgleich",
"ButtonReScan": "Neu scannen",
@@ -115,7 +117,7 @@
"HeaderAdvanced": "Erweitert",
"HeaderAppriseNotificationSettings": "Apprise Benachrichtigungseinstellungen",
"HeaderAudioTracks": "Audiodateien",
"HeaderAudiobookTools": "Hörbuch-Dateiverwaltungstools",
"HeaderAudiobookTools": "Hörbuch-Dateiverwaltungswerkzeuge",
"HeaderAuthentication": "Authentifizierung",
"HeaderBackups": "Sicherungen",
"HeaderChangePassword": "Passwort ändern",
@@ -125,13 +127,13 @@
"HeaderCollectionItems": "Sammlungseinträge",
"HeaderCover": "Titelbild",
"HeaderCurrentDownloads": "Aktuelle Downloads",
"HeaderCustomMessageOnLogin": "Benutzerdefinierte Nachricht für den Login",
"HeaderCustomMetadataProviders": "Benutzerdefinierte Metadata Anbieter",
"HeaderCustomMessageOnLogin": "Benutzerdefinierte Nachricht für die Anmeldung",
"HeaderCustomMetadataProviders": "Benutzerdefinierte Metadatenanbieter",
"HeaderDetails": "Details",
"HeaderDownloadQueue": "Download Warteschlange",
"HeaderEbookFiles": "E-Buch-Dateien",
"HeaderEmail": "Email",
"HeaderEmailSettings": "Email Einstellungen",
"HeaderEmail": "E-Mail",
"HeaderEmailSettings": "E-Mail-Einstellungen",
"HeaderEpisodes": "Episoden",
"HeaderEreaderDevices": "E-Reader Geräte",
"HeaderEreaderSettings": "Einstellungen zum Lesen",
@@ -158,12 +160,13 @@
"HeaderNewAccount": "Neues Konto",
"HeaderNewLibrary": "Neue Bibliothek",
"HeaderNotificationCreate": "Benachrichtigung erstellen",
"HeaderNotificationUpdate": "Benachrichtigung updaten",
"HeaderNotificationUpdate": "Benachrichtigung bearbeiten",
"HeaderNotifications": "Benachrichtigungen",
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentifizierung",
"HeaderOpenListeningSessions": "Aktive Hörbuch-Sitzungen",
"HeaderOpenRSSFeed": "RSS-Feed öffnen",
"HeaderOtherFiles": "Sonstige Dateien",
"HeaderPasswordAuthentication": "Passwort Authentifizierung",
"HeaderPasswordAuthentication": "Passwortauthentifizierung",
"HeaderPermissions": "Berechtigungen",
"HeaderPlayerQueue": "Player Warteschlange",
"HeaderPlayerSettings": "Player Einstellungen",
@@ -178,6 +181,7 @@
"HeaderRemoveEpisodes": "Entferne {0} Episoden",
"HeaderSavedMediaProgress": "Gespeicherte Hörfortschritte",
"HeaderSchedule": "Zeitplan",
"HeaderScheduleEpisodeDownloads": "Automatische Episoden-Downloads planen",
"HeaderScheduleLibraryScans": "Automatische Bibliotheksscans",
"HeaderSession": "Sitzung",
"HeaderSetBackupSchedule": "Zeitplan für die Datensicherung festlegen",
@@ -223,7 +227,11 @@
"LabelAllUsersExcludingGuests": "Alle Benutzer außer Gästen",
"LabelAllUsersIncludingGuests": "Alle Benutzer und Gäste",
"LabelAlreadyInYourLibrary": "Bereits in der Bibliothek",
"LabelApiToken": "API Schlüssel",
"LabelAppend": "Anhängen",
"LabelAudioBitrate": "Audiobitrate (z. B. 128 kbit/s)",
"LabelAudioChannels": "Audiokanäle (1 oder 2)",
"LabelAudioCodec": "Audiocodec",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (Vorname Nachname)",
"LabelAuthorLastFirst": "Autor (Nachname, Vorname)",
@@ -236,6 +244,7 @@
"LabelAutoRegister": "Automatische Registrierung",
"LabelAutoRegisterDescription": "Automatische neue Neutzer anlegen nach dem Registrieren",
"LabelBackToUser": "Zurück zum Benutzer",
"LabelBackupAudioFiles": "Audio-Dateien sichern",
"LabelBackupLocation": "Backup-Ort",
"LabelBackupsEnableAutomaticBackups": "Automatische Sicherung aktivieren",
"LabelBackupsEnableAutomaticBackupsHelp": "Backups werden in /metadata/backups gespeichert",
@@ -244,15 +253,18 @@
"LabelBackupsNumberToKeep": "Anzahl der aufzubewahrenden Sicherungen",
"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",
"LabelBonus": "Bonus",
"LabelBooks": "Bücher",
"LabelButtonText": "Button Text",
"LabelButtonText": "Knopftext",
"LabelByAuthor": "von {0}",
"LabelChangePassword": "Passwort ändern",
"LabelChannels": "Kanäle",
"LabelChapterCount": "{0} Kapitel",
"LabelChapterTitle": "Kapitelüberschrift",
"LabelChapters": "Kapitel",
"LabelChaptersFound": "Gefundene Kapitel",
"LabelClickForMoreInfo": "Klicken für mehr Informationen",
"LabelClickToUseCurrentValue": "Anklicken um aktuellen Wert zu verwenden",
"LabelClosePlayer": "Player schließen",
"LabelCodec": "Codec",
"LabelCollapseSeries": "Serien einklappen",
@@ -293,21 +305,34 @@
"LabelEbook": "E-Buch",
"LabelEbooks": "E-Bücher",
"LabelEdit": "Bearbeiten",
"LabelEmail": "Email",
"LabelEmailSettingsFromAddress": "Von Adresse",
"LabelEmail": "E-Mail",
"LabelEmailSettingsFromAddress": "Sender",
"LabelEmailSettingsRejectUnauthorized": "Nicht autorisierte Zertifikate ablehnen",
"LabelEmailSettingsRejectUnauthorizedHelp": "Durch das Deaktivieren der SSL-Zertifikatsüberprüfung kann deine Verbindung Sicherheitsrisiken wie Man-in-the-Middle-Angriffen ausgesetzt sein. Deaktiviere diese Option nur, wenn due die Auswirkungen verstehst und dem Mailserver vertraust, mit dem eine Verbindung hergestellt wird.",
"LabelEmailSettingsRejectUnauthorizedHelp": "Durch das Deaktivieren der SSL-Zertifikatsüberprüfung kann deine Verbindung Sicherheitsrisiken wie Man-in-the-Middle-Angriffen ausgesetzt sein. Deaktiviere diese Option nur, wenn due die Auswirkungen verstehst und dem E-Mail-Server vertraust, mit dem eine Verbindung hergestellt wird.",
"LabelEmailSettingsSecure": "Sicher",
"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 Adresse",
"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-Adresse",
"LabelEmbeddedCover": "Eingebettetes Cover",
"LabelEnable": "Aktivieren",
"LabelEncodingBackupLocation": "Eine Sicherungskopie der originalen Audiodateien wird gespeichert in:",
"LabelEncodingChaptersNotEmbedded": "Kapitel sind in mehrspurigen Hörbüchern nicht eingebettet.",
"LabelEncodingClearItemCache": "Stelle sicher, dass der Cache regelmäßig geleert wird.",
"LabelEncodingFinishedM4B": "Die fertige M4B-Datei wird im Hörbuch-Ordner unter folgendem Pfad abgelegt:",
"LabelEncodingInfoEmbedded": "Metadaten werden in die Audiodateien innerhalb des Audiobook Ordners eingebunden.",
"LabelEncodingStartedNavigation": "Sobald die Aufgabe gestartet ist, kann die Seite verlassen werden.",
"LabelEncodingTimeWarning": "Kodierung kann bis zu 30 Minuten dauern.",
"LabelEncodingWarningAdvancedSettings": "Achtung: Ändere diese Einstellungen nur, wenn du dich mit ffmpeg Kodierung auskennst.",
"LabelEncodingWatcherDisabled": "Wenn der Watcher deaktiviert ist musst du das Hörbuch danach erneut scannen.",
"LabelEnd": "Ende",
"LabelEndOfChapter": "Ende des Kapitels",
"LabelEpisode": "Episode",
"LabelEpisodeNotLinkedToRssFeed": "Episode nicht mit RSS-Feed verknüpft",
"LabelEpisodeNumber": "Episode #{0}",
"LabelEpisodeTitle": "Episodentitel",
"LabelEpisodeType": "Episodentyp",
"LabelEpisodeUrlFromRssFeed": "Episoden URL vom RSS-Feed",
"LabelEpisodes": "Episoden",
"LabelEpisodic": "Episodisch",
"LabelExample": "Beispiel",
"LabelExpandSeries": "Serie ausklappen",
"LabelExpandSubSeries": "Unterserie ausklappen",
@@ -315,7 +340,7 @@
"LabelExplicitChecked": "Explicit (Altersbeschränkung) (angehakt)",
"LabelExplicitUnchecked": "Not Explicit (Altersbeschränkung) (nicht angehakt)",
"LabelExportOPML": "OPML exportieren",
"LabelFeedURL": "Feed URL",
"LabelFeedURL": "Feed-URL",
"LabelFetchingMetadata": "Abholen der Metadaten",
"LabelFile": "Datei",
"LabelFileBirthtime": "Datei erstellt",
@@ -335,14 +360,15 @@
"LabelFontScale": "Schriftgröße",
"LabelFontStrikethrough": "Durchgestrichen",
"LabelFormat": "Format",
"LabelFull": "Voll",
"LabelGenre": "Kategorie",
"LabelGenres": "Kategorien",
"LabelHardDeleteFile": "Datei dauerhaft löschen",
"LabelHasEbook": "E-Book verfügbar",
"LabelHasSupplementaryEbook": "Ergänzendes E-Book verfügbar",
"LabelHasEbook": "E-Buch verfügbar",
"LabelHasSupplementaryEbook": "Ergänzendes E-Buch verfügbar",
"LabelHideSubtitles": "Untertitel ausblenden",
"LabelHighestPriority": "Höchste Priorität",
"LabelHost": "Host",
"LabelHost": "Anbieter",
"LabelHour": "Stunde",
"LabelHours": "Stunden",
"LabelIcon": "Symbol",
@@ -371,13 +397,13 @@
"LabelLastSeen": "Zuletzt gesehen",
"LabelLastTime": "Letztes Mal",
"LabelLastUpdate": "Letzte Aktualisierung",
"LabelLayout": "Layout",
"LabelLayout": "Ansicht",
"LabelLayoutSinglePage": "Eine Seite",
"LabelLayoutSplitPage": "Geteilte Seite",
"LabelLess": "Weniger",
"LabelLibrariesAccessibleToUser": "Für Benutzer zugängliche Bibliotheken",
"LabelLibrary": "Bibliothek",
"LabelLibraryFilterSublistEmpty": "Nr. {0}",
"LabelLibraryFilterSublistEmpty": "Keine {0}",
"LabelLibraryItem": "Bibliothekseintrag",
"LabelLibraryName": "Bibliotheksname",
"LabelLimit": "Begrenzung",
@@ -390,6 +416,10 @@
"LabelLowestPriority": "Niedrigste Priorität",
"LabelMatchExistingUsersBy": "Zuordnen existierender Benutzer mit",
"LabelMatchExistingUsersByDescription": "Wird zum Verbinden vorhandener Benutzer verwendet. Sobald die Verbindung hergestellt ist, wird den Benutzern eine eindeutige ID vom SSO-Anbieter zugeordnet",
"LabelMaxEpisodesToDownload": "Max. Anzahl an Episoden zum Herunterladen, 0 für unbegrenzte Episoden.",
"LabelMaxEpisodesToDownloadPerCheck": "Max. Anzahl neuer Episoden zum Herunterladen pro Abfrage",
"LabelMaxEpisodesToKeep": "Max. Anzahl zu behaltender Episoden",
"LabelMaxEpisodesToKeepHelp": "0 setzt keine Begrenzung. Wenn eine neue Episode automatisch heruntergeladen wird, wird die älteste Episode gelöscht, wenn du mehr als X Episoden gespeichert hast. Es wird nur eine Episode pro neuem Download gelöscht.",
"LabelMediaPlayer": "Mediaplayer",
"LabelMediaType": "Medientyp",
"LabelMetaTag": "Meta Schlagwort",
@@ -399,10 +429,10 @@
"LabelMinute": "Minute",
"LabelMinutes": "Minuten",
"LabelMissing": "Fehlend",
"LabelMissingEbook": "E-Book fehlt",
"LabelMissingSupplementaryEbook": "Ergänzendes E-Book fehlt",
"LabelMissingEbook": "E-Buch fehlt",
"LabelMissingSupplementaryEbook": "Ergänzendes E-Buch 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.",
"LabelMobileRedirectURIsDescription": "Dies ist eine weiße Liste 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",
"LabelMoreInfo": "Mehr Infos",
"LabelName": "Name",
@@ -419,7 +449,7 @@
"LabelNotFinished": "Nicht beendet",
"LabelNotStarted": "Nicht begonnen",
"LabelNotes": "Notizen",
"LabelNotificationAppriseURL": "Apprise URL(s)",
"LabelNotificationAppriseURL": "Apprise-URL(s)",
"LabelNotificationAvailableVariables": "Verfügbare Variablen",
"LabelNotificationBodyTemplate": "Textvorlage",
"LabelNotificationEvent": "Benachrichtigungs Event",
@@ -435,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Name des OpenID-Claims, der eine Liste der Benutzergruppen enthält. Wird häufig als <code>groups</code> bezeichnet. <b>Wenn konfiguriert</b>, wird die Anwendung automatisch Rollen basierend auf den Gruppenmitgliedschaften des Benutzers zuweisen, vorausgesetzt, dass diese Gruppen im Claim als 'admin', 'user' oder 'guest' benannt sind (Groß/Kleinschreibung ist irrelevant). Der Claim eine Liste sein, und wenn ein Benutzer mehreren Gruppen angehört, wird die Anwendung die Rolle zuordnen, die dem höchsten Zugriffslevel entspricht. Wenn keine Gruppe übereinstimmt, wird der Zugang verweigert.",
"LabelOpenRSSFeed": "Öffne RSS-Feed",
"LabelOverwrite": "Überschreiben",
"LabelPaginationPageXOfY": "Seite {0} von {1}",
"LabelPassword": "Passwort",
"LabelPath": "Pfad",
"LabelPermanent": "Dauerhaft",
"LabelPermissionsAccessAllLibraries": "Zugriff auf alle Bibliotheken",
"LabelPermissionsAccessAllTags": "Zugriff auf alle Schlagwörter",
"LabelPermissionsAccessExplicitContent": "Zugriff auf explizite (alterbeschränkte) Inhalte",
"LabelPermissionsCreateEreader": "Kann E-Reader erstellen",
"LabelPermissionsDelete": "Darf Löschen",
"LabelPermissionsDownload": "Herunterladen",
"LabelPermissionsUpdate": "Aktualisieren",
@@ -457,52 +489,60 @@
"LabelPort": "Port",
"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",
"LabelPrimaryEbook": "Primäres E-Book",
"LabelPrimaryEbook": "Primäres E-Buch",
"LabelProgress": "Fortschritt",
"LabelProvider": "Anbieter",
"LabelProviderAuthorizationValue": "Autorisierungsheader-Wert",
"LabelPubDate": "Veröffentlichungsdatum",
"LabelPublishYear": "Jahr",
"LabelPublishedDate": "Veröffentlicht {0}",
"LabelPublishedDecade": "Jahrzehnt",
"LabelPublishedDecades": "Jahrzehnte",
"LabelPublisher": "Herausgeber",
"LabelPublishers": "Herausgeber",
"LabelRSSFeedCustomOwnerEmail": "Benutzerdefinierte Eigentümer-E-Mail",
"LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers",
"LabelRSSFeedOpen": "RSS Feed Offen",
"LabelRSSFeedOpen": "RSS Feed offen",
"LabelRSSFeedPreventIndexing": "Indizierung verhindern",
"LabelRSSFeedSlug": "RSS-Feed-Schlagwort",
"LabelRSSFeedURL": "RSS Feed URL",
"LabelRSSFeedURL": "RSS-Feed-URL",
"LabelRandomly": "Zufällig",
"LabelReAddSeriesToContinueListening": "Serien erneut zur Fortsetzungsliste hinzufügen",
"LabelRead": "Lesen",
"LabelReadAgain": "Noch einmal Lesen",
"LabelReadEbookWithoutProgress": "E-Book lesen und Fortschritt verwerfen",
"LabelReadEbookWithoutProgress": "E-Buch lesen und Fortschritt verwerfen",
"LabelRecentSeries": "Aktuelle Serien",
"LabelRecentlyAdded": "Kürzlich hinzugefügt",
"LabelRecommended": "Empfohlen",
"LabelRedo": "Wiederholen",
"LabelRegion": "Region",
"LabelReleaseDate": "Veröffentlichungsdatum",
"LabelRemoveAllMetadataAbs": "Alle metadata.abs Dateien löschen",
"LabelRemoveAllMetadataJson": "Alle metadata.json Dateien löschen",
"LabelRemoveCover": "Entferne Titelbild",
"LabelRemoveMetadataFile": "Metadaten-Dateien in Bibliotheksordnern löschen",
"LabelRemoveMetadataFileHelp": "Alle metadata.json und metadata.abs Dateien aus den Ordnern {0} löschen.",
"LabelRowsPerPage": "Zeilen pro Seite",
"LabelSearchTerm": "Begriff suchen",
"LabelSearchTitle": "Titel suchen",
"LabelSearchTitleOrASIN": "Titel oder ASIN suchen",
"LabelSeason": "Staffel",
"LabelSeasonNumber": "Staffel #{0}",
"LabelSelectAll": "Alles auswählen",
"LabelSelectAllEpisodes": "Alle Episoden auswählen",
"LabelSelectEpisodesShowing": "{0} ausgewählte Episoden werden angezeigt",
"LabelSelectUsers": "Benutzer auswählen",
"LabelSendEbookToDevice": "E-Book senden an...",
"LabelSendEbookToDevice": "E-Buch senden an …",
"LabelSequence": "Reihenfolge",
"LabelSeries": "Serien",
"LabelSeriesName": "Serienname",
"LabelSeriesProgress": "Serienfortschritt",
"LabelServerLogLevel": "Server Log Level",
"LabelServerYearReview": "Server Jahr in Übersicht ({0})",
"LabelSetEbookAsPrimary": "Als Hauptbuch setzen",
"LabelSetEbookAsSupplementary": "Als Ergänzung setzen",
"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",
"LabelSettingsAudiobooksOnlyHelp": "Wenn du diese Einstellung aktivierst, werden E-Buch-Dateien ignoriert, es sei denn, sie befinden sich in einem Hörbuchordner. In diesem Fall werden sie als zusätzliche E-Bücher festgelegt",
"LabelSettingsBookshelfViewHelp": "Skeumorphes Design mit Holzeinlegeböden",
"LabelSettingsChromecastSupport": "Chromecastunterstützung",
"LabelSettingsDateFormat": "Datumsformat",
@@ -522,6 +562,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Serien, die nur ein einzelnes Buch enthalten, werden auf der Startseite und in der Serienansicht ausgeblendet.",
"LabelSettingsHomePageBookshelfView": "Startseite verwendet die Bücherregalansicht",
"LabelSettingsLibraryBookshelfView": "Bibliothek verwendet die Bücherregalansicht",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "In Prozent gehört größer als",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Verbleibende Zeit ist weniger als (Sekunden)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Markiere Mediendateien als fertig, wenn",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Überspringe vorherige Bücher in fortführender Serie",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Die Startseite von \"Fortführende Serien\" zeigt das erste noch nicht begonnene Buch in Serien an, die mindestens ein Buch abgeschlossen und keine Bücher begonnen haben. Wenn diese Einstellung aktiviert wird, werden Serien ab dem letzten abgeschlossenen Buch fortgesetzt und nicht ab dem ersten nicht begonnenen Buch.",
"LabelSettingsParseSubtitles": "Analysiere Untertitel",
@@ -566,7 +609,7 @@
"LabelStatsMinutesListening": "Gehörte Minuten",
"LabelStatsOverallDays": "Gesamte Tage",
"LabelStatsOverallHours": "Gesamte Stunden",
"LabelStatsWeekListening": "Gehörte Wochen",
"LabelStatsWeekListening": "Wochenhördauer",
"LabelSubtitle": "Untertitel",
"LabelSupportedFileTypes": "Unterstützte Dateitypen",
"LabelTag": "Schlagwort",
@@ -586,6 +629,7 @@
"LabelTimeDurationXMinutes": "{0} Minuten",
"LabelTimeDurationXSeconds": "{0} Sekunden",
"LabelTimeInMinutes": "Zeit in Minuten",
"LabelTimeLeft": "{0} verbleibend",
"LabelTimeListened": "Gehörte Zeit",
"LabelTimeListenedToday": "Heute gehörte Zeit",
"LabelTimeRemaining": "{0} verbleibend",
@@ -593,9 +637,10 @@
"LabelTitle": "Titel",
"LabelToolsEmbedMetadata": "Metadaten einbetten",
"LabelToolsEmbedMetadataDescription": "Bettet die Metadaten einschließlich des Titelbildes und der Kapitel in die Audiodatein ein.",
"LabelToolsM4bEncoder": "M4B Kodierer",
"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.",
"LabelToolsSplitM4b": "M4B in MP3's aufteilen",
"LabelToolsSplitM4b": "M4B in MP3s aufteilen",
"LabelToolsSplitM4bDescription": "Erstellt aus einer mit Metadaten und nach Kapiteln aufgeteilten M4B-Datei seperate MP3's mit eingebetteten Metadaten, Coverbild und Kapiteln.",
"LabelTotalDuration": "Gesamtdauer",
"LabelTotalTimeListened": "Gehörte Gesamtzeit",
@@ -605,6 +650,7 @@
"LabelTracksMultiTrack": "Mehrfachdatei",
"LabelTracksNone": "Keine Dateien",
"LabelTracksSingleTrack": "Einzeldatei",
"LabelTrailer": "Vorschau",
"LabelType": "Typ",
"LabelUnabridged": "Ungekürzt",
"LabelUndo": "Rückgängig machen",
@@ -618,8 +664,10 @@
"LabelUploaderDragAndDrop": "Ziehen und Ablegen von Dateien oder Ordnern",
"LabelUploaderDropFiles": "Dateien löschen",
"LabelUploaderItemFetchMetadataHelp": "Automatisches Aktualisieren von Titel, Autor und Serie",
"LabelUseAdvancedOptions": "Nutze Erweiterte Optionen",
"LabelUseChapterTrack": "Kapiteldatei verwenden",
"LabelUseFullTrack": "Gesamte Datei verwenden",
"LabelUseZeroForUnlimited": "0 für unbegrenzt",
"LabelUser": "Benutzer",
"LabelUsername": "Benutzername",
"LabelValue": "Wert",
@@ -658,7 +706,7 @@
"MessageCheckingCron": "Überprüfe Cron...",
"MessageConfirmCloseFeed": "Feed wird geschlossen! Bist du dir sicher?",
"MessageConfirmDeleteBackup": "Sicherung für {0} wird gelöscht! Bist du dir sicher?",
"MessageConfirmDeleteDevice": "Möchtest Du das E-Reader-Gerät „{0}“ wirklich löschen?",
"MessageConfirmDeleteDevice": "Möchtest du das Lesegerät „{0}“ wirklich löschen?",
"MessageConfirmDeleteFile": "Datei wird vom System gelöscht! Bist du dir sicher?",
"MessageConfirmDeleteLibrary": "Bibliothek \"{0}\" wird dauerhaft gelöscht! Bist du dir sicher?",
"MessageConfirmDeleteLibraryItem": "Bibliothekselement wird aus der Datenbank + Festplatte gelöscht? Bist du dir sicher?",
@@ -666,6 +714,7 @@
"MessageConfirmDeleteMetadataProvider": "Möchtest du den benutzerdefinierten Metadatenanbieter \"{0}\" wirklich löschen?",
"MessageConfirmDeleteNotification": "Möchtest du diese Benachrichtigung wirklich löschen?",
"MessageConfirmDeleteSession": "Sitzung wird gelöscht! Bist du dir sicher?",
"MessageConfirmEmbedMetadataInAudioFiles": "Bist du dir sicher, dass die Metadaten in {0} Audiodateien eingebettet werden sollen?",
"MessageConfirmForceReScan": "Scanvorgang erzwingen! Bist du dir sicher?",
"MessageConfirmMarkAllEpisodesFinished": "Alle Episoden werden als abgeschlossen markiert! Bist du dir sicher?",
"MessageConfirmMarkAllEpisodesNotFinished": "Alle Episoden werden als nicht abgeschlossen markiert! Bist du dir sicher?",
@@ -677,6 +726,7 @@
"MessageConfirmPurgeCache": "Cache leeren wird das ganze Verzeichnis <code>/metadata/cache</code> löschen. <br /><br />Bist du dir sicher, dass das Cache Verzeichnis gelöscht werden soll?",
"MessageConfirmPurgeItemsCache": "Durch Elementcache leeren wird das gesamte Verzeichnis unter <code>/metadata/cache/items</code> gelöscht.<br />Bist du dir sicher?",
"MessageConfirmQuickEmbed": "Warnung! Audiodateien werden bei der Schnelleinbettung nicht gesichert! Achte darauf, dass du eine Sicherungskopie der Audiodateien besitzt. <br><br>Möchtest du fortfahren?",
"MessageConfirmQuickMatchEpisodes": "Schnelles Zuordnen von Episoden überschreibt die Details, wenn eine Übereinstimmung gefunden wird. Nur nicht zugeordnete Episoden werden aktualisiert. Bist du sicher?",
"MessageConfirmReScanLibraryItems": "{0} Elemente werden erneut gescannt! Bist du dir sicher?",
"MessageConfirmRemoveAllChapters": "Alle Kapitel werden entfernt! Bist du dir sicher?",
"MessageConfirmRemoveAuthor": "Autor \"{0}\" wird enfernt! Bist du dir sicher?",
@@ -684,6 +734,7 @@
"MessageConfirmRemoveEpisode": "Episode \"{0}\" wird entfernt! Bist du dir sicher?",
"MessageConfirmRemoveEpisodes": "{0} Episoden werden entfernt! Bist du dir sicher?",
"MessageConfirmRemoveListeningSessions": "Bist du dir sicher, dass du {0} Hörsitzungen enfernen möchtest?",
"MessageConfirmRemoveMetadataFiles": "Bist du sicher, dass du alle metadata.{0} Dateien in deinen Bibliotheksordnern löschen willst?",
"MessageConfirmRemoveNarrator": "Erzähler \"{0}\" wird entfernt! Bist du dir 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?",
@@ -693,14 +744,15 @@
"MessageConfirmRenameTagMergeNote": "Hinweis: Tag existiert bereits -> Tags werden zusammengelegt.",
"MessageConfirmRenameTagWarning": "Warnung! Ein ähnlicher Tag mit einem anderen Wortlaut existiert bereits: \"{0}\".",
"MessageConfirmResetProgress": "Möchtest du Ihren Fortschritt wirklich zurücksetzen?",
"MessageConfirmSendEbookToDevice": "{0} E-Book \"{1}\" wird auf das Gerät \"{2}\" gesendet! Bist du dir sicher?",
"MessageConfirmSendEbookToDevice": "{0} E-Buch „{1} wird auf das Gerät {2} gesendet! Bist du dir sicher?",
"MessageConfirmUnlinkOpenId": "Möchtest du die Verknüpfung dieses Benutzers mit OpenID wirklich löschen?",
"MessageDownloadingEpisode": "Episode wird heruntergeladen",
"MessageDragFilesIntoTrackOrder": "Verschiebe die Dateien in die richtige Reihenfolge",
"MessageEmbedFailed": "Einbetten fehlgeschlagen!",
"MessageEmbedFinished": "Einbettung abgeschlossen!",
"MessageEmbedQueue": "Eingereiht zum einbinden von Metadaten ({0} in Warteschlange)",
"MessageEpisodesQueuedForDownload": "{0} Episode(n) in der Warteschlange zum Herunterladen",
"MessageEreaderDevices": "Um die Zustellung von E-Books sicherzustellen, musst du eventuell die oben genannte E-Mail-Adresse als gültigen Absender für jedes unten aufgeführte Gerät hinzufügen.",
"MessageEreaderDevices": "Um die Zustellung von E-Büchern sicherzustellen, musst du eventuell die oben genannte E-Mail-Adresse als gültigen Absender für jedes unten aufgeführte Gerät hinzufügen.",
"MessageFeedURLWillBe": "Feed-URL wird {0} sein",
"MessageFetching": "Wird abgerufen …",
"MessageForceReScanDescription": "Durchsucht alle Dateien erneut, wie bei einem frischen Scan. ID3-Tags von Audiodateien, OPF-Dateien und Textdateien werden neu durchsucht.",
@@ -743,6 +795,7 @@
"MessageNoLogs": "Keine Protokolle",
"MessageNoMediaProgress": "Kein Medienfortschritt",
"MessageNoNotifications": "Keine Benachrichtigungen",
"MessageNoPodcastFeed": "Ungültiger Podcast: Kein Feed",
"MessageNoPodcastsFound": "Keine Podcasts gefunden",
"MessageNoResults": "Keine Ergebnisse",
"MessageNoSearchResultsFor": "Keine Suchergebnisse für \"{0}\"",
@@ -759,6 +812,10 @@
"MessagePlaylistCreateFromCollection": "Erstelle eine Wiedergabeliste aus der Sammlung",
"MessagePleaseWait": "Bitte warten...",
"MessagePodcastHasNoRSSFeedForMatching": "Der Podcast hat keine RSS-Feed-Url welche für den Online-Abgleich verwendet werden kann",
"MessagePodcastSearchField": "Suchbegriff oder RSS-Feed URL eingeben",
"MessageQuickEmbedInProgress": "Schnellabgleich läuft",
"MessageQuickEmbedQueue": "In Warteschlange für Schnelles einbinden ({0} eingereiht)",
"MessageQuickMatchAllEpisodes": "Quick Match aller Episoden",
"MessageQuickMatchDescription": "Füllt leere Details und Titelbilder mit dem ersten Treffer aus '{0}'. Überschreibt keine Details, es sei denn, die Server-Einstellung \"Passende Metadaten bevorzugen\" ist aktiviert.",
"MessageRemoveChapter": "Kapitel entfernen",
"MessageRemoveEpisodes": "Entferne {0} Episode(n)",
@@ -776,6 +833,41 @@
"MessageShareExpiresIn": "Läuft in {0} ab",
"MessageShareURLWillBe": "Der Freigabe Link wird <strong>{0}</strong> sein.",
"MessageStartPlaybackAtTime": "Start der Wiedergabe für \"{0}\" bei {1}?",
"MessageTaskAudioFileNotWritable": "Die Audiodatei \"{0}\" ist schreibgeschützt",
"MessageTaskCanceledByUser": "Aufgabe vom Benutzer abgebrochen",
"MessageTaskDownloadingEpisodeDescription": "Folge \"{0}\" wird heruntergeladen",
"MessageTaskEmbeddingMetadata": "Metadaten werden eingebettet",
"MessageTaskEmbeddingMetadataDescription": "Metadaten werden in Hörbuch \"{0}\" eingebettet",
"MessageTaskEncodingM4b": "M4B wird encodiert",
"MessageTaskEncodingM4bDescription": "Hörbuch \"{0}\" wird in eine einzelne m4b Datei encodiert",
"MessageTaskFailed": "Fehlgeschlagen",
"MessageTaskFailedToBackupAudioFile": "Sicherung der Audiodatei \"{0}\" fehlgeschlagen",
"MessageTaskFailedToCreateCacheDirectory": "Fehler beim erstellen des Cache-Verzeichnisses",
"MessageTaskFailedToEmbedMetadataInFile": "Einbetten der Metadaten in die Datei \"{0}\" fehlgeschlagen",
"MessageTaskFailedToMergeAudioFiles": "Fehler beim zusammenführen der Audiodateien",
"MessageTaskFailedToMoveM4bFile": "Fehler beim verschieben der m4b Datei",
"MessageTaskFailedToWriteMetadataFile": "Fehler beim schreiben der Metadaten-Datei",
"MessageTaskMatchingBooksInLibrary": "Vergleiche Bücher in Bibliothek \"{0}\"",
"MessageTaskNoFilesToScan": "Keine Dateien zum scannen",
"MessageTaskOpmlImport": "OPML-Import",
"MessageTaskOpmlImportDescription": "Podcasts von {0} RSS-Feeds werden ersrtellt",
"MessageTaskOpmlImportFeed": "OPML-Feed importieren",
"MessageTaskOpmlImportFeedDescription": "RSS-Feed \"{0}\" wird importiert",
"MessageTaskOpmlImportFeedFailed": "Podcast Feed konnte nicht geladen werden",
"MessageTaskOpmlImportFeedPodcastDescription": "Podcast \"{0}\" wird erstellt",
"MessageTaskOpmlImportFeedPodcastExists": "Der Podcast ist bereits im Pfad vorhanden",
"MessageTaskOpmlImportFeedPodcastFailed": "Erstellen des Podcasts fehlgeschlagen",
"MessageTaskOpmlImportFinished": "{0} Podcasts hinzugefügt",
"MessageTaskOpmlParseFailed": "Fehler beim lesen der OPML Datei",
"MessageTaskOpmlParseFastFail": "Ungültie OPML Datei: <opml> ODER <outline> tag wurde nicht gefunden",
"MessageTaskOpmlParseNoneFound": "Keine feeds in der OPML Datei gefunden",
"MessageTaskScanItemsAdded": "{0} hinzugefügt",
"MessageTaskScanItemsMissing": "{0} fehlend",
"MessageTaskScanItemsUpdated": "{0} aktualisiert",
"MessageTaskScanNoChangesNeeded": "Keine Änderungen nötig",
"MessageTaskScanningFileChanges": "Überprüfe \"{0}\" nach geänderten Dateien",
"MessageTaskScanningLibrary": "Bibliothek \"{0}\" wird durchsucht",
"MessageTaskTargetDirectoryNotWritable": "Das Zielverzeichnis ist schreibgeschützt",
"MessageThinking": "Nachdenken...",
"MessageUploaderItemFailed": "Hochladen fehlgeschlagen",
"MessageUploaderItemSuccess": "Erfolgreich hochgeladen!",
@@ -793,6 +885,10 @@
"NoteUploaderFoldersWithMediaFiles": "Ordner mit Mediendateien werden als separate Bibliothekselemente 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.",
"NotificationOnBackupCompletedDescription": "Wird ausgeführt wenn ein Backup erstellt wurde",
"NotificationOnBackupFailedDescription": "Wird ausgeführt wenn ein Backup fehlgeschlagen ist",
"NotificationOnEpisodeDownloadedDescription": "Wird ausgeführt wenn eine Podcast Folge automatisch heruntergeladen wird",
"NotificationOnTestDescription": "Wird ausgeführt wenn das Benachrichtigungssystem getestet wird",
"PlaceholderNewCollection": "Neuer Sammlungsname",
"PlaceholderNewFolderPath": "Neuer Ordnerpfad",
"PlaceholderNewPlaylist": "Neuer Wiedergabelistenname",
@@ -816,14 +912,13 @@
"StatsTopNarrators": "TOP SPRECHER",
"StatsTotalDuration": "Mit einer totalen Dauer von…",
"StatsYearInReview": "DAS JAHR IM RÜCKBLICK",
"ToastAccountUpdateFailed": "Aktualisierung des Kontos fehlgeschlagen",
"ToastAccountUpdateSuccess": "Konto aktualisiert",
"ToastAppriseUrlRequired": "Eine Apprise-URL ist notwendig",
"ToastAsinRequired": "ASIN ist erforderlich",
"ToastAuthorImageRemoveSuccess": "Autorenbild entfernt",
"ToastAuthorNotFound": "Autor \"{0}\" nicht gefunden",
"ToastAuthorRemoveSuccess": "Autor entfernt",
"ToastAuthorSearchNotFound": "Autor nicht gefunden",
"ToastAuthorUpdateFailed": "Aktualisierung des Autors fehlgeschlagen",
"ToastAuthorUpdateMerged": "Autor zusammengeführt",
"ToastAuthorUpdateSuccess": "Autor aktualisiert",
"ToastAuthorUpdateSuccessNoImageFound": "Autor aktualisiert (kein Bild gefunden)",
@@ -834,29 +929,29 @@
"ToastBackupDeleteSuccess": "Sicherung gelöscht",
"ToastBackupInvalidMaxKeep": "Ungültige Anzahl aufzubewahrender Backups",
"ToastBackupInvalidMaxSize": "Ungültige maximale Backupgröße",
"ToastBackupPathUpdateFailed": "Der Backuppfad konnte nicht aktualisiert werden",
"ToastBackupRestoreFailed": "Sicherung konnte nicht wiederhergestellt werden",
"ToastBackupUploadFailed": "Sicherung konnte nicht hochgeladen werden",
"ToastBackupUploadSuccess": "Sicherung hochgeladen",
"ToastBatchDeleteFailed": "Batch-Löschen fehlgeschlagen",
"ToastBatchDeleteSuccess": "Batch-Löschung erfolgreich",
"ToastBatchQuickMatchFailed": "Batch-Schnellabgleich fehlgeschlagen!",
"ToastBatchQuickMatchStarted": "Batch-Schnellabgleich für {0} Bücher gestartet!",
"ToastBatchUpdateFailed": "Stapelaktualisierung fehlgeschlagen",
"ToastBatchUpdateSuccess": "Stapelaktualisierung erfolgreich",
"ToastBookmarkCreateFailed": "Lesezeichen konnte nicht erstellt werden",
"ToastBookmarkCreateSuccess": "Lesezeichen hinzugefügt",
"ToastBookmarkRemoveSuccess": "Lesezeichen entfernt",
"ToastBookmarkUpdateFailed": "Lesezeichenaktualisierung fehlgeschlagen",
"ToastBookmarkUpdateSuccess": "Lesezeichen aktualisiert",
"ToastCachePurgeFailed": "Cache leeren fehlgeschlagen",
"ToastCachePurgeSuccess": "Cache geleert",
"ToastChaptersHaveErrors": "Kapitel sind fehlerhaft",
"ToastChaptersMustHaveTitles": "Kapitel benötigen eindeutige Namen",
"ToastChaptersRemoved": "Kapitel entfernt",
"ToastChaptersUpdated": "Kapitel aktualisiert",
"ToastCollectionItemsAddFailed": "Das Hinzufügen von Element(en) zur Sammlung ist fehlgeschlagen",
"ToastCollectionItemsAddSuccess": "Element(e) erfolgreich zur Sammlung hinzugefügt",
"ToastCollectionItemsRemoveSuccess": "Medien aus der Sammlung entfernt",
"ToastCollectionRemoveSuccess": "Sammlung entfernt",
"ToastCollectionUpdateFailed": "Sammlung konnte nicht aktualisiert werden",
"ToastCollectionUpdateSuccess": "Sammlung aktualisiert",
"ToastCoverUpdateFailed": "Cover-Update fehlgeschlagen",
"ToastDeleteFileFailed": "Die Datei konnte nicht gelöscht werden",
@@ -864,32 +959,29 @@
"ToastDeviceAddFailed": "Gerät konnte nicht hinzugefügt werden",
"ToastDeviceNameAlreadyExists": "E-Reader-Gerät mit diesem Namen existiert bereits",
"ToastDeviceTestEmailFailed": "Senden der Test-E-Mail fehlgeschlagen",
"ToastDeviceTestEmailSuccess": "Test-E-Mail versand",
"ToastDeviceUpdateFailed": "Das Gerät konnte nicht aktualisiert werden",
"ToastEmailSettingsUpdateFailed": "E-Mail-Einstellungen konnten nicht aktualisiert werden",
"ToastDeviceTestEmailSuccess": "Test-E-Mail gesendet",
"ToastEmailSettingsUpdateSuccess": "E-Mail-Einstellungen aktualisiert",
"ToastEncodeCancelFailed": "Das Encoding konnte nicht abgebrochen werden",
"ToastEncodeCancelSucces": "Encoding abgebrochen",
"ToastEpisodeDownloadQueueClearFailed": "Warteschlange konnte nicht gelöscht werden",
"ToastEpisodeDownloadQueueClearSuccess": "Warteschlange für Episoden-Downloads gelöscht",
"ToastEpisodeUpdateSuccess": "{0} Episoden aktualisiert",
"ToastErrorCannotShare": "Das kann nicht nativ auf diesem Gerät freigegeben werden",
"ToastFailedToLoadData": "Daten laden fehlgeschlagen",
"ToastFailedToMatch": "Fehler beim Abgleich",
"ToastFailedToShare": "Fehler beim Teilen",
"ToastFailedToUpdateAccount": "Fehler beim ändern des Accounts",
"ToastFailedToUpdateUser": "Fehler beim ändern des Benutzers",
"ToastFailedToUpdate": "Aktualisierung ist fehlgeschlagen",
"ToastInvalidImageUrl": "Ungültiger Bild URL",
"ToastInvalidMaxEpisodesToDownload": "Ungültige Max. Anzahl an Episoden zum Herunterladen",
"ToastInvalidUrl": "Ungültiger URL",
"ToastItemCoverUpdateFailed": "Fehler bei der Aktualisierung des Titelbildes",
"ToastItemCoverUpdateSuccess": "Titelbild aktualisiert",
"ToastItemDeletedFailed": "Fehler beim löschen des Artikels",
"ToastItemDeletedSuccess": "Artikel gelöscht",
"ToastItemDetailsUpdateFailed": "Fehler bei der Aktualisierung der Artikeldetails",
"ToastItemDetailsUpdateSuccess": "Artikeldetails aktualisiert",
"ToastItemMarkedAsFinishedFailed": "Fehler bei der Markierung des Artikels als \"Beendet\"",
"ToastItemMarkedAsFinishedSuccess": "Artikel als \"Beendet\" markiert",
"ToastItemMarkedAsNotFinishedFailed": "Fehler bei der Markierung des Artikels als \"Nicht Beendet\"",
"ToastItemMarkedAsNotFinishedSuccess": "Artikel als \"Nicht Beendet\" markiert",
"ToastItemUpdateFailed": "Fehler beim ändern des Artikels",
"ToastItemUpdateSuccess": "Artikel wurde verändert",
"ToastLibraryCreateFailed": "Bibliothek konnte nicht erstellt werden",
"ToastLibraryCreateSuccess": "Bibliothek \"{0}\" erstellt",
@@ -897,37 +989,42 @@
"ToastLibraryDeleteSuccess": "Bibliothek gelöscht",
"ToastLibraryScanFailedToStart": "Scan konnte nicht gestartet werden",
"ToastLibraryScanStarted": "Bibliotheksscan gestartet",
"ToastLibraryUpdateFailed": "Aktualisierung der Bibliothek fehlgeschlagen",
"ToastLibraryUpdateSuccess": "Bibliothek \"{0}\" aktualisiert",
"ToastNameEmailRequired": "Name und Email sind erforderlich",
"ToastMatchAllAuthorsFailed": "Nicht alle Autoren konnten zugeordnet werden",
"ToastMetadataFilesRemovedError": "Fehler beim löschen von metadata.{0} Dateien",
"ToastMetadataFilesRemovedNoneFound": "Keine metadata.{0} Dateien in Bibliothek gefunden",
"ToastMetadataFilesRemovedNoneRemoved": "Keine metadata.{0} Dateien gelöscht",
"ToastMetadataFilesRemovedSuccess": "{0} metadata.{1} Datei(en) gelöscht",
"ToastMustHaveAtLeastOnePath": "Es muss mindestens ein Pfad angegeben werden",
"ToastNameEmailRequired": "Name und E-Mail sind erforderlich",
"ToastNameRequired": "Name ist erforderlich",
"ToastNewEpisodesFound": "{0} neue Episoden gefunden",
"ToastNewUserCreatedFailed": "Fehler beim erstellen des Accounts: \"{ 0}\"",
"ToastNewUserCreatedSuccess": "Neuer Account erstellt",
"ToastNewUserLibraryError": "Mindestens eine Bibliothek muss ausgewählt werden",
"ToastNewUserPasswordError": "Passwort erforderlich, nur der root Benutzer darf ein leeres Passwort haben",
"ToastNewUserTagError": "Mindestens ein Tag muss ausgewählt sein",
"ToastNewUserUsernameError": "Nutzername eingeben",
"ToastNoNewEpisodesFound": "Keine neuen Episoden gefunden",
"ToastNoUpdatesNecessary": "Keine Änderungen nötig",
"ToastNotificationCreateFailed": "Fehler beim erstellen der Benachrichtig",
"ToastNotificationDeleteFailed": "Fehler beim löschen der Benachrichtigung",
"ToastNotificationFailedMaximum": "Maximale Fehlversuche muss >= 0 sein",
"ToastNotificationQueueMaximum": "Maximale Benachrichtigungswarteschlange muss >= 0 sein",
"ToastNotificationSettingsUpdateFailed": "Fehler beim ändern der Benachrichtigungseinstellungen",
"ToastNotificationSettingsUpdateSuccess": "Benachrichtigungseinstellungen geändert",
"ToastNotificationTestTriggerFailed": "Fehler beim Auslösen der Testbenachrichtigung",
"ToastNotificationTestTriggerSuccess": "Testbenachrichtigung ausgelöst",
"ToastNotificationUpdateFailed": "Fehler bein ändern der Benachrichtigung",
"ToastNotificationUpdateSuccess": "Benachrichtigung geändert",
"ToastPlaylistCreateFailed": "Erstellen der Wiedergabeliste fehlgeschlagen",
"ToastPlaylistCreateSuccess": "Wiedergabeliste erstellt",
"ToastPlaylistRemoveSuccess": "Wiedergabeliste gelöscht",
"ToastPlaylistUpdateFailed": "Aktualisieren der Wiedergabeliste fehlgeschlagen",
"ToastPlaylistUpdateSuccess": "Wiedergabeliste aktualisiert",
"ToastPodcastCreateFailed": "Podcast konnte nicht erstellt werden",
"ToastPodcastCreateSuccess": "Podcast erstellt",
"ToastPodcastGetFeedFailed": "Fehler beim abrufen des Podcast Feeds",
"ToastPodcastNoEpisodesInFeed": "Keine Episoden in RSS Feed gefunden",
"ToastPodcastNoRssFeed": "Podcast enthält keinen RSS Feed",
"ToastProgressIsNotBeingSynced": "Fortschritt wird nicht synchronisiert, Wiedergabe wird neu gestartet",
"ToastProviderCreatedFailed": "Fehler beim hinzufügen des Anbieters",
"ToastProviderCreatedSuccess": "Neuer Anbieter hinzugefügt",
"ToastProviderNameAndUrlRequired": "Name und URL notwendig",
@@ -946,22 +1043,21 @@
"ToastRescanUpdated": "Erneut scannen erledigt, Artikel wurde verändert",
"ToastScanFailed": "Fehler beim scannen des Artikels der Bibliothek",
"ToastSelectAtLeastOneUser": "Wähle mindestens einen Benutzer aus",
"ToastSendEbookToDeviceFailed": "E-Book konnte nicht auf Gerät übertragen werden",
"ToastSendEbookToDeviceSuccess": "E-Book an Gerät \"{0}\" gesendet",
"ToastSendEbookToDeviceFailed": "E-Buch konnte nicht auf Gerät übertragen werden",
"ToastSendEbookToDeviceSuccess": "E-Buch an Gerät {0} gesendet",
"ToastSeriesUpdateFailed": "Aktualisierung der Serien fehlgeschlagen",
"ToastSeriesUpdateSuccess": "Serien aktualisiert",
"ToastServerSettingsUpdateFailed": "Die Server-Einstellungen wurden nicht gespeichert",
"ToastServerSettingsUpdateSuccess": "Die Server-Einstellungen wurden geupdated",
"ToastSessionCloseFailed": "Fehler beim schließen der Sitzung",
"ToastSessionDeleteFailed": "Sitzung konnte nicht gelöscht werden",
"ToastSessionDeleteSuccess": "Sitzung gelöscht",
"ToastSleepTimerDone": "Einschlaf-Timer aktiviert... zZzzZz",
"ToastSlugMustChange": "URL-Schlüssel enthält ungültige Zeichen",
"ToastSlugRequired": "URL-Schlüssel erforderlich",
"ToastSocketConnected": "Verbindung zum WebSocket hergestellt",
"ToastSocketDisconnected": "Verbindung zum WebSocket verloren",
"ToastSocketFailedToConnect": "Verbindung zum WebSocket fehlgeschlagen",
"ToastSortingPrefixesEmptyError": "Es muss mindestens ein Sortier-Prefix vorhanden sein",
"ToastSortingPrefixesUpdateFailed": "Update der Sortier-Prefixe ist fehlgeschlagen",
"ToastSortingPrefixesUpdateSuccess": "Die Sortier-Prefixe wirden geupdated ({0} Einträge)",
"ToastTitleRequired": "Titel erforderlich",
"ToastUnknownError": "Unbekannter Fehler",

View File

@@ -56,6 +56,7 @@
"ButtonOpenManager": "Open Manager",
"ButtonPause": "Pause",
"ButtonPlay": "Play",
"ButtonPlayAll": "Play All",
"ButtonPlaying": "Playing",
"ButtonPlaylists": "Playlists",
"ButtonPrevious": "Previous",
@@ -65,6 +66,7 @@
"ButtonPurgeItemsCache": "Purge Items Cache",
"ButtonQueueAddItem": "Add to queue",
"ButtonQueueRemoveItem": "Remove from queue",
"ButtonQuickEmbed": "Quick Embed",
"ButtonQuickEmbedMetadata": "Quick Embed Metadata",
"ButtonQuickMatch": "Quick Match",
"ButtonReScan": "Re-Scan",
@@ -161,6 +163,7 @@
"HeaderNotificationUpdate": "Update Notification",
"HeaderNotifications": "Notifications",
"HeaderOpenIDConnectAuthentication": "OpenID Connect Authentication",
"HeaderOpenListeningSessions": "Open Listening Sessions",
"HeaderOpenRSSFeed": "Open RSS Feed",
"HeaderOtherFiles": "Other Files",
"HeaderPasswordAuthentication": "Password Authentication",
@@ -178,6 +181,7 @@
"HeaderRemoveEpisodes": "Remove {0} Episodes",
"HeaderSavedMediaProgress": "Saved Media Progress",
"HeaderSchedule": "Schedule",
"HeaderScheduleEpisodeDownloads": "Schedule Automatic Episode Downloads",
"HeaderScheduleLibraryScans": "Schedule Automatic Library Scans",
"HeaderSession": "Session",
"HeaderSetBackupSchedule": "Set Backup Schedule",
@@ -223,7 +227,11 @@
"LabelAllUsersExcludingGuests": "All users excluding guests",
"LabelAllUsersIncludingGuests": "All users including guests",
"LabelAlreadyInYourLibrary": "Already in your library",
"LabelApiToken": "API Token",
"LabelAppend": "Append",
"LabelAudioBitrate": "Audio Bitrate (e.g. 128k)",
"LabelAudioChannels": "Audio Channels (1 or 2)",
"LabelAudioCodec": "Audio Codec",
"LabelAuthor": "Author",
"LabelAuthorFirstLast": "Author (First Last)",
"LabelAuthorLastFirst": "Author (Last, First)",
@@ -236,6 +244,7 @@
"LabelAutoRegister": "Auto Register",
"LabelAutoRegisterDescription": "Automatically create new users after logging in",
"LabelBackToUser": "Back to User",
"LabelBackupAudioFiles": "Backup Audio Files",
"LabelBackupLocation": "Backup Location",
"LabelBackupsEnableAutomaticBackups": "Enable automatic backups",
"LabelBackupsEnableAutomaticBackupsHelp": "Backups saved in /metadata/backups",
@@ -244,15 +253,18 @@
"LabelBackupsNumberToKeep": "Number of backups to keep",
"LabelBackupsNumberToKeepHelp": "Only 1 backup will be removed at a time so if you already have more backups than this you should manually remove them.",
"LabelBitrate": "Bitrate",
"LabelBonus": "Bonus",
"LabelBooks": "Books",
"LabelButtonText": "Button Text",
"LabelByAuthor": "by {0}",
"LabelChangePassword": "Change Password",
"LabelChannels": "Channels",
"LabelChapterCount": "{0} Chapters",
"LabelChapterTitle": "Chapter Title",
"LabelChapters": "Chapters",
"LabelChaptersFound": "chapters found",
"LabelClickForMoreInfo": "Click for more info",
"LabelClickToUseCurrentValue": "Click to use current value",
"LabelClosePlayer": "Close player",
"LabelCodec": "Codec",
"LabelCollapseSeries": "Collapse Series",
@@ -302,12 +314,25 @@
"LabelEmailSettingsTestAddress": "Test Address",
"LabelEmbeddedCover": "Embedded Cover",
"LabelEnable": "Enable",
"LabelEncodingBackupLocation": "A backup of your original audio files will be stored in:",
"LabelEncodingChaptersNotEmbedded": "Chapters are not embedded in multi-track audiobooks.",
"LabelEncodingClearItemCache": "Make sure to periodically purge items cache.",
"LabelEncodingFinishedM4B": "Finished M4B will be put into your audiobook folder at:",
"LabelEncodingInfoEmbedded": "Metadata will be embedded in the audio tracks inside your audiobook folder.",
"LabelEncodingStartedNavigation": "Once the task is started you can navigate away from this page.",
"LabelEncodingTimeWarning": "Encoding can take up to 30 minutes.",
"LabelEncodingWarningAdvancedSettings": "Warning: Do not update these settings unless you are familiar with ffmpeg encoding options.",
"LabelEncodingWatcherDisabled": "If you have the watcher disabled you will need to re-scan this audiobook afterwards.",
"LabelEnd": "End",
"LabelEndOfChapter": "End of Chapter",
"LabelEpisode": "Episode",
"LabelEpisodeNotLinkedToRssFeed": "Episode not linked to RSS feed",
"LabelEpisodeNumber": "Episode #{0}",
"LabelEpisodeTitle": "Episode Title",
"LabelEpisodeType": "Episode Type",
"LabelEpisodeUrlFromRssFeed": "Episode URL from RSS feed",
"LabelEpisodes": "Episodes",
"LabelEpisodic": "Episodic",
"LabelExample": "Example",
"LabelExpandSeries": "Expand Series",
"LabelExpandSubSeries": "Expand Sub Series",
@@ -335,6 +360,7 @@
"LabelFontScale": "Font scale",
"LabelFontStrikethrough": "Strikethrough",
"LabelFormat": "Format",
"LabelFull": "Full",
"LabelGenre": "Genre",
"LabelGenres": "Genres",
"LabelHardDeleteFile": "Hard delete file",
@@ -390,6 +416,10 @@
"LabelLowestPriority": "Lowest Priority",
"LabelMatchExistingUsersBy": "Match existing users by",
"LabelMatchExistingUsersByDescription": "Used for connecting existing users. Once connected, users will be matched by a unique id from your SSO provider",
"LabelMaxEpisodesToDownload": "Max # of episodes to download. Use 0 for unlimited.",
"LabelMaxEpisodesToDownloadPerCheck": "Max # of new episodes to download per check",
"LabelMaxEpisodesToKeep": "Max # of episodes to keep",
"LabelMaxEpisodesToKeepHelp": "Value of 0 sets no max limit. After a new episode is auto-downloaded this will delete the oldest episode if you have more than X episodes. This will only delete 1 episode per new download.",
"LabelMediaPlayer": "Media Player",
"LabelMediaType": "Media Type",
"LabelMetaTag": "Meta Tag",
@@ -435,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
"LabelOpenRSSFeed": "Open RSS Feed",
"LabelOverwrite": "Overwrite",
"LabelPaginationPageXOfY": "Page {0} of {1}",
"LabelPassword": "Password",
"LabelPath": "Path",
"LabelPermanent": "Permanent",
"LabelPermissionsAccessAllLibraries": "Can Access All Libraries",
"LabelPermissionsAccessAllTags": "Can Access All Tags",
"LabelPermissionsAccessExplicitContent": "Can Access Explicit Content",
"LabelPermissionsCreateEreader": "Can Create Ereader",
"LabelPermissionsDelete": "Can Delete",
"LabelPermissionsDownload": "Can Download",
"LabelPermissionsUpdate": "Can Update",
@@ -464,6 +496,8 @@
"LabelPubDate": "Pub Date",
"LabelPublishYear": "Publish Year",
"LabelPublishedDate": "Published {0}",
"LabelPublishedDecade": "Published Decade",
"LabelPublishedDecades": "Published Decades",
"LabelPublisher": "Publisher",
"LabelPublishers": "Publishers",
"LabelRSSFeedCustomOwnerEmail": "Custom owner Email",
@@ -483,21 +517,28 @@
"LabelRedo": "Redo",
"LabelRegion": "Region",
"LabelReleaseDate": "Release Date",
"LabelRemoveAllMetadataAbs": "Remove all metadata.abs files",
"LabelRemoveAllMetadataJson": "Remove all metadata.json files",
"LabelRemoveCover": "Remove cover",
"LabelRemoveMetadataFile": "Remove metadata files in library item folders",
"LabelRemoveMetadataFileHelp": "Remove all metadata.json and metadata.abs files in your {0} folders.",
"LabelRowsPerPage": "Rows per page",
"LabelSearchTerm": "Search Term",
"LabelSearchTitle": "Search Title",
"LabelSearchTitleOrASIN": "Search Title or ASIN",
"LabelSeason": "Season",
"LabelSeasonNumber": "Season #{0}",
"LabelSelectAll": "Select all",
"LabelSelectAllEpisodes": "Select all episodes",
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
"LabelSelectUsers": "Select users",
"LabelSendEbookToDevice": "Send Ebook to...",
"LabelSequence": "Sequence",
"LabelSerial": "Serial",
"LabelSeries": "Series",
"LabelSeriesName": "Series Name",
"LabelSeriesProgress": "Series Progress",
"LabelServerLogLevel": "Server Log Level",
"LabelServerYearReview": "Server Year in Review ({0})",
"LabelSetEbookAsPrimary": "Set as primary",
"LabelSetEbookAsSupplementary": "Set as supplementary",
@@ -522,6 +563,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "Percent complete is greater than",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Time remaining is less than (seconds)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Mark media item as finished when",
"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",
@@ -586,6 +630,7 @@
"LabelTimeDurationXMinutes": "{0} minutes",
"LabelTimeDurationXSeconds": "{0} seconds",
"LabelTimeInMinutes": "Time in minutes",
"LabelTimeLeft": "{0} left",
"LabelTimeListened": "Time Listened",
"LabelTimeListenedToday": "Time Listened Today",
"LabelTimeRemaining": "{0} remaining",
@@ -593,6 +638,7 @@
"LabelTitle": "Title",
"LabelToolsEmbedMetadata": "Embed Metadata",
"LabelToolsEmbedMetadataDescription": "Embed metadata into audio files including cover image and chapters.",
"LabelToolsM4bEncoder": "M4B Encoder",
"LabelToolsMakeM4b": "Make M4B Audiobook File",
"LabelToolsMakeM4bDescription": "Generate a .M4B audiobook file with embedded metadata, cover image, and chapters.",
"LabelToolsSplitM4b": "Split M4B to MP3's",
@@ -605,6 +651,7 @@
"LabelTracksMultiTrack": "Multi-track",
"LabelTracksNone": "No tracks",
"LabelTracksSingleTrack": "Single-track",
"LabelTrailer": "Trailer",
"LabelType": "Type",
"LabelUnabridged": "Unabridged",
"LabelUndo": "Undo",
@@ -618,8 +665,10 @@
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
"LabelUploaderDropFiles": "Drop files",
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
"LabelUseAdvancedOptions": "Use Advanced Options",
"LabelUseChapterTrack": "Use chapter track",
"LabelUseFullTrack": "Use full track",
"LabelUseZeroForUnlimited": "Use 0 for unlimited",
"LabelUser": "User",
"LabelUsername": "Username",
"LabelValue": "Value",
@@ -666,6 +715,7 @@
"MessageConfirmDeleteMetadataProvider": "Are you sure you want to delete custom metadata provider \"{0}\"?",
"MessageConfirmDeleteNotification": "Are you sure you want to delete this notification?",
"MessageConfirmDeleteSession": "Are you sure you want to delete this session?",
"MessageConfirmEmbedMetadataInAudioFiles": "Are you sure you want to embed metadata in {0} audio files?",
"MessageConfirmForceReScan": "Are you sure you want to force re-scan?",
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?",
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?",
@@ -677,6 +727,7 @@
"MessageConfirmPurgeCache": "Purge cache will delete the entire directory at <code>/metadata/cache</code>. <br /><br />Are you sure you want to remove the cache directory?",
"MessageConfirmPurgeItemsCache": "Purge items cache will delete the entire directory at <code>/metadata/cache/items</code>.<br />Are you sure?",
"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?",
"MessageConfirmQuickMatchEpisodes": "Quick matching episodes will overwrite details if a match is found. Only unmatched episodes will be updated. Are you sure?",
"MessageConfirmReScanLibraryItems": "Are you sure you want to re-scan {0} items?",
"MessageConfirmRemoveAllChapters": "Are you sure you want to remove all chapters?",
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?",
@@ -684,6 +735,7 @@
"MessageConfirmRemoveEpisode": "Are you sure you want to remove episode \"{0}\"?",
"MessageConfirmRemoveEpisodes": "Are you sure you want to remove {0} episodes?",
"MessageConfirmRemoveListeningSessions": "Are you sure you want to remove {0} listening sessions?",
"MessageConfirmRemoveMetadataFiles": "Are you sure you want to remove all metadata.{0} files in your library item folders?",
"MessageConfirmRemoveNarrator": "Are you sure you want to remove narrator \"{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?",
@@ -699,6 +751,7 @@
"MessageDragFilesIntoTrackOrder": "Drag files into correct track order",
"MessageEmbedFailed": "Embed Failed!",
"MessageEmbedFinished": "Embed Finished!",
"MessageEmbedQueue": "Queued for metadata embed ({0} in queue)",
"MessageEpisodesQueuedForDownload": "{0} Episode(s) queued for download",
"MessageEreaderDevices": "To ensure delivery of ebooks, you may need to add the above email address as a valid sender for each device listed below.",
"MessageFeedURLWillBe": "Feed URL will be {0}",
@@ -743,6 +796,7 @@
"MessageNoLogs": "No Logs",
"MessageNoMediaProgress": "No Media Progress",
"MessageNoNotifications": "No Notifications",
"MessageNoPodcastFeed": "Invalid podcast: No Feed",
"MessageNoPodcastsFound": "No podcasts found",
"MessageNoResults": "No Results",
"MessageNoSearchResultsFor": "No search results for \"{0}\"",
@@ -759,6 +813,10 @@
"MessagePlaylistCreateFromCollection": "Create playlist from collection",
"MessagePleaseWait": "Please wait...",
"MessagePodcastHasNoRSSFeedForMatching": "Podcast has no RSS feed url to use for matching",
"MessagePodcastSearchField": "Enter search term or RSS feed URL",
"MessageQuickEmbedInProgress": "Quick embed in progress",
"MessageQuickEmbedQueue": "Queued for quick embed ({0} in queue)",
"MessageQuickMatchAllEpisodes": "Quick Match All Episodes",
"MessageQuickMatchDescription": "Populate empty item details & cover with first match result from '{0}'. Does not overwrite details unless 'Prefer matched metadata' server setting is enabled.",
"MessageRemoveChapter": "Remove chapter",
"MessageRemoveEpisodes": "Remove {0} episode(s)",
@@ -776,6 +834,41 @@
"MessageShareExpiresIn": "Expires in {0}",
"MessageShareURLWillBe": "Share URL will be <strong>{0}</strong>",
"MessageStartPlaybackAtTime": "Start playback for \"{0}\" at {1}?",
"MessageTaskAudioFileNotWritable": "Audio file \"{0}\" is not writable",
"MessageTaskCanceledByUser": "Task canceled by user",
"MessageTaskDownloadingEpisodeDescription": "Downloading episode \"{0}\"",
"MessageTaskEmbeddingMetadata": "Embedding metadata",
"MessageTaskEmbeddingMetadataDescription": "Embedding metadata in audiobook \"{0}\"",
"MessageTaskEncodingM4b": "Encoding M4B",
"MessageTaskEncodingM4bDescription": "Encoding audiobook \"{0}\" into a single m4b file",
"MessageTaskFailed": "Failed",
"MessageTaskFailedToBackupAudioFile": "Failed to backup audio file \"{0}\"",
"MessageTaskFailedToCreateCacheDirectory": "Failed to create cache directory",
"MessageTaskFailedToEmbedMetadataInFile": "Failed to embed metadata in file \"{0}\"",
"MessageTaskFailedToMergeAudioFiles": "Failed to merge audio files",
"MessageTaskFailedToMoveM4bFile": "Failed to move m4b file",
"MessageTaskFailedToWriteMetadataFile": "Failed to write metadata file",
"MessageTaskMatchingBooksInLibrary": "Matching books in library \"{0}\"",
"MessageTaskNoFilesToScan": "No files to scan",
"MessageTaskOpmlImport": "OPML import",
"MessageTaskOpmlImportDescription": "Creating podcasts from {0} RSS feeds",
"MessageTaskOpmlImportFeed": "OPML import feed",
"MessageTaskOpmlImportFeedDescription": "Importing RSS feed \"{0}\"",
"MessageTaskOpmlImportFeedFailed": "Failed to get podcast feed",
"MessageTaskOpmlImportFeedPodcastDescription": "Creating podcast \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Podcast already exists at path",
"MessageTaskOpmlImportFeedPodcastFailed": "Failed to create podcast",
"MessageTaskOpmlImportFinished": "Added {0} podcasts",
"MessageTaskOpmlParseFailed": "Failed to parse OPML file",
"MessageTaskOpmlParseFastFail": "Invalid OPML file <opml> tag not found OR an <outline> tag was not found",
"MessageTaskOpmlParseNoneFound": "No feeds found in OPML file",
"MessageTaskScanItemsAdded": "{0} added",
"MessageTaskScanItemsMissing": "{0} missing",
"MessageTaskScanItemsUpdated": "{0} updated",
"MessageTaskScanNoChangesNeeded": "No changes needed",
"MessageTaskScanningFileChanges": "Scanning file changes in \"{0}\"",
"MessageTaskScanningLibrary": "Scanning \"{0}\" library",
"MessageTaskTargetDirectoryNotWritable": "Target directory is not writable",
"MessageThinking": "Thinking...",
"MessageUploaderItemFailed": "Failed to upload",
"MessageUploaderItemSuccess": "Successfully Uploaded!",
@@ -793,6 +886,10 @@
"NoteUploaderFoldersWithMediaFiles": "Folders with media files will be handled as separate library items.",
"NoteUploaderOnlyAudioFiles": "If uploading only audio files then each audio file will be handled as a separate audiobook.",
"NoteUploaderUnsupportedFiles": "Unsupported files are ignored. When choosing or dropping a folder, other files that are not in an item folder are ignored.",
"NotificationOnBackupCompletedDescription": "Triggered when a backup is completed",
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
"NotificationOnTestDescription": "Event for testing the notification system",
"PlaceholderNewCollection": "New collection name",
"PlaceholderNewFolderPath": "New folder path",
"PlaceholderNewPlaylist": "New playlist name",
@@ -816,14 +913,13 @@
"StatsTopNarrators": "TOP NARRATORS",
"StatsTotalDuration": "With a total duration of…",
"StatsYearInReview": "YEAR IN REVIEW",
"ToastAccountUpdateFailed": "Failed to update account",
"ToastAccountUpdateSuccess": "Account updated",
"ToastAppriseUrlRequired": "Must enter an Apprise URL",
"ToastAsinRequired": "ASIN is required",
"ToastAuthorImageRemoveSuccess": "Author image removed",
"ToastAuthorNotFound": "Author \"{0}\" not found",
"ToastAuthorRemoveSuccess": "Author removed",
"ToastAuthorSearchNotFound": "Author not found",
"ToastAuthorUpdateFailed": "Failed to update author",
"ToastAuthorUpdateMerged": "Author merged",
"ToastAuthorUpdateSuccess": "Author updated",
"ToastAuthorUpdateSuccessNoImageFound": "Author updated (no image found)",
@@ -834,29 +930,29 @@
"ToastBackupDeleteSuccess": "Backup deleted",
"ToastBackupInvalidMaxKeep": "Invalid number of backups to keep",
"ToastBackupInvalidMaxSize": "Invalid maximum backup size",
"ToastBackupPathUpdateFailed": "Failed to update backup path",
"ToastBackupRestoreFailed": "Failed to restore backup",
"ToastBackupUploadFailed": "Failed to upload backup",
"ToastBackupUploadSuccess": "Backup uploaded",
"ToastBatchDeleteFailed": "Batch delete failed",
"ToastBatchDeleteSuccess": "Batch delete success",
"ToastBatchQuickMatchFailed": "Batch Quick Match failed!",
"ToastBatchQuickMatchStarted": "Batch Quick Match of {0} books started!",
"ToastBatchUpdateFailed": "Batch update failed",
"ToastBatchUpdateSuccess": "Batch update success",
"ToastBookmarkCreateFailed": "Failed to create bookmark",
"ToastBookmarkCreateSuccess": "Bookmark added",
"ToastBookmarkRemoveSuccess": "Bookmark removed",
"ToastBookmarkUpdateFailed": "Failed to update bookmark",
"ToastBookmarkUpdateSuccess": "Bookmark updated",
"ToastCachePurgeFailed": "Failed to purge cache",
"ToastCachePurgeSuccess": "Cache purged successfully",
"ToastChaptersHaveErrors": "Chapters have errors",
"ToastChaptersMustHaveTitles": "Chapters must have titles",
"ToastChaptersRemoved": "Chapters removed",
"ToastChaptersUpdated": "Chapters updated",
"ToastCollectionItemsAddFailed": "Item(s) added to collection failed",
"ToastCollectionItemsAddSuccess": "Item(s) added to collection success",
"ToastCollectionItemsRemoveSuccess": "Item(s) removed from collection",
"ToastCollectionRemoveSuccess": "Collection removed",
"ToastCollectionUpdateFailed": "Failed to update collection",
"ToastCollectionUpdateSuccess": "Collection updated",
"ToastCoverUpdateFailed": "Cover update failed",
"ToastDeleteFileFailed": "Failed to delete file",
@@ -865,31 +961,28 @@
"ToastDeviceNameAlreadyExists": "Ereader device with that name already exists",
"ToastDeviceTestEmailFailed": "Failed to send test email",
"ToastDeviceTestEmailSuccess": "Test email sent",
"ToastDeviceUpdateFailed": "Failed to update device",
"ToastEmailSettingsUpdateFailed": "Failed to update email settings",
"ToastEmailSettingsUpdateSuccess": "Email settings updated",
"ToastEncodeCancelFailed": "Failed to cancel encode",
"ToastEncodeCancelSucces": "Encode canceled",
"ToastEpisodeDownloadQueueClearFailed": "Failed to clear queue",
"ToastEpisodeDownloadQueueClearSuccess": "Episode download queue cleared",
"ToastEpisodeUpdateSuccess": "{0} episodes updated",
"ToastErrorCannotShare": "Cannot share natively on this device",
"ToastFailedToLoadData": "Failed to load data",
"ToastFailedToMatch": "Failed to match",
"ToastFailedToShare": "Failed to share",
"ToastFailedToUpdateAccount": "Failed to update account",
"ToastFailedToUpdateUser": "Failed to update user",
"ToastFailedToUpdate": "Failed to update",
"ToastInvalidImageUrl": "Invalid image URL",
"ToastInvalidMaxEpisodesToDownload": "Invalid max episodes to download",
"ToastInvalidUrl": "Invalid URL",
"ToastItemCoverUpdateFailed": "Failed to update item cover",
"ToastItemCoverUpdateSuccess": "Item cover updated",
"ToastItemDeletedFailed": "Failed to delete item",
"ToastItemDeletedSuccess": "Deleted item",
"ToastItemDetailsUpdateFailed": "Failed to update item details",
"ToastItemDetailsUpdateSuccess": "Item details updated",
"ToastItemMarkedAsFinishedFailed": "Failed to mark as Finished",
"ToastItemMarkedAsFinishedSuccess": "Item marked as Finished",
"ToastItemMarkedAsNotFinishedFailed": "Failed to mark as Not Finished",
"ToastItemMarkedAsNotFinishedSuccess": "Item marked as Not Finished",
"ToastItemUpdateFailed": "Failed to update item",
"ToastItemUpdateSuccess": "Item updated",
"ToastLibraryCreateFailed": "Failed to create library",
"ToastLibraryCreateSuccess": "Library \"{0}\" created",
@@ -897,37 +990,42 @@
"ToastLibraryDeleteSuccess": "Library deleted",
"ToastLibraryScanFailedToStart": "Failed to start scan",
"ToastLibraryScanStarted": "Library scan started",
"ToastLibraryUpdateFailed": "Failed to update library",
"ToastLibraryUpdateSuccess": "Library \"{0}\" updated",
"ToastMatchAllAuthorsFailed": "Failed to match all authors",
"ToastMetadataFilesRemovedError": "Error removing metadata.{0} files",
"ToastMetadataFilesRemovedNoneFound": "No metadata.{0} files found in library",
"ToastMetadataFilesRemovedNoneRemoved": "No metadata.{0} files removed",
"ToastMetadataFilesRemovedSuccess": "{0} metadata.{1} files removed",
"ToastMustHaveAtLeastOnePath": "Must have at least one path",
"ToastNameEmailRequired": "Name and email are required",
"ToastNameRequired": "Name is required",
"ToastNewEpisodesFound": "{0} new episodes found",
"ToastNewUserCreatedFailed": "Failed to create account: \"{0}\"",
"ToastNewUserCreatedSuccess": "New account created",
"ToastNewUserLibraryError": "Must select at least one library",
"ToastNewUserPasswordError": "Must have a password, only root user can have an empty password",
"ToastNewUserTagError": "Must select at least one tag",
"ToastNewUserUsernameError": "Enter a username",
"ToastNoNewEpisodesFound": "No new episodes found",
"ToastNoUpdatesNecessary": "No updates necessary",
"ToastNotificationCreateFailed": "Failed to create notification",
"ToastNotificationDeleteFailed": "Failed to delete notification",
"ToastNotificationFailedMaximum": "Max failed attempts must be >= 0",
"ToastNotificationQueueMaximum": "Max notification queue must be >= 0",
"ToastNotificationSettingsUpdateFailed": "Failed to update notification settings",
"ToastNotificationSettingsUpdateSuccess": "Notification settings updated",
"ToastNotificationTestTriggerFailed": "Failed to trigger test notification",
"ToastNotificationTestTriggerSuccess": "Triggered test notification",
"ToastNotificationUpdateFailed": "Failed to update notification",
"ToastNotificationUpdateSuccess": "Notification updated",
"ToastPlaylistCreateFailed": "Failed to create playlist",
"ToastPlaylistCreateSuccess": "Playlist created",
"ToastPlaylistRemoveSuccess": "Playlist removed",
"ToastPlaylistUpdateFailed": "Failed to update playlist",
"ToastPlaylistUpdateSuccess": "Playlist updated",
"ToastPodcastCreateFailed": "Failed to create podcast",
"ToastPodcastCreateSuccess": "Podcast created successfully",
"ToastPodcastGetFeedFailed": "Failed to get podcast feed",
"ToastPodcastNoEpisodesInFeed": "No episodes found in RSS feed",
"ToastPodcastNoRssFeed": "Podcast does not have an RSS feed",
"ToastProgressIsNotBeingSynced": "Progress is not being synced, restart playback",
"ToastProviderCreatedFailed": "Failed to add provider",
"ToastProviderCreatedSuccess": "New provider added",
"ToastProviderNameAndUrlRequired": "Name and Url required",
@@ -950,18 +1048,17 @@
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"",
"ToastSeriesUpdateFailed": "Series update failed",
"ToastSeriesUpdateSuccess": "Series update success",
"ToastServerSettingsUpdateFailed": "Failed to update server settings",
"ToastServerSettingsUpdateSuccess": "Server settings updated",
"ToastSessionCloseFailed": "Failed to close session",
"ToastSessionDeleteFailed": "Failed to delete session",
"ToastSessionDeleteSuccess": "Session deleted",
"ToastSleepTimerDone": "Sleep timer done... zZzzZz",
"ToastSlugMustChange": "Slug contains invalid characters",
"ToastSlugRequired": "Slug is required",
"ToastSocketConnected": "Socket connected",
"ToastSocketDisconnected": "Socket disconnected",
"ToastSocketFailedToConnect": "Socket failed to connect",
"ToastSortingPrefixesEmptyError": "Must have at least 1 sorting prefix",
"ToastSortingPrefixesUpdateFailed": "Failed to update sorting prefixes",
"ToastSortingPrefixesUpdateSuccess": "Sorting prefixes updated ({0} items)",
"ToastTitleRequired": "Title is required",
"ToastUnknownError": "Unknown error",

View File

@@ -0,0 +1 @@
{}

View File

@@ -30,6 +30,7 @@
"ButtonEditChapters": "Editar Capítulo",
"ButtonEditPodcast": "Editar Podcast",
"ButtonEnable": "Permitir",
"ButtonFireAndFail": "Ejecutado y fallido",
"ButtonFireOnTest": "Activar evento de prueba",
"ButtonForceReScan": "Forzar Re-Escaneo",
"ButtonFullPath": "Ruta de Acceso Completa",
@@ -55,6 +56,7 @@
"ButtonOpenManager": "Abrir Editor",
"ButtonPause": "Pausar",
"ButtonPlay": "Reproducir",
"ButtonPlayAll": "Reproducir todo",
"ButtonPlaying": "Reproduciendo",
"ButtonPlaylists": "Listas de reproducción",
"ButtonPrevious": "Anterior",
@@ -64,6 +66,7 @@
"ButtonPurgeItemsCache": "Purgar Elementos de Cache",
"ButtonQueueAddItem": "Agregar a la Fila",
"ButtonQueueRemoveItem": "Remover de la Fila",
"ButtonQuickEmbed": "Inserción rápida",
"ButtonQuickEmbedMetadata": "Agregue metadatos rápidamente",
"ButtonQuickMatch": "Encontrar Rápido",
"ButtonReScan": "Re-Escanear",
@@ -177,6 +180,7 @@
"HeaderRemoveEpisodes": "Remover {0} Episodios",
"HeaderSavedMediaProgress": "Guardar Progreso de Multimedia",
"HeaderSchedule": "Horario",
"HeaderScheduleEpisodeDownloads": "Programar descargas automáticas de episodios",
"HeaderScheduleLibraryScans": "Programar Escaneo Automático de Biblioteca",
"HeaderSession": "Sesión",
"HeaderSetBackupSchedule": "Programar Respaldo",
@@ -223,6 +227,9 @@
"LabelAllUsersIncludingGuests": "Todos los usuarios e invitados",
"LabelAlreadyInYourLibrary": "Ya existe en la Biblioteca",
"LabelAppend": "Adjuntar",
"LabelAudioBitrate": "Tasa de bits del audio (por ejemplo, 128k)",
"LabelAudioChannels": "Canales de audio (1 o 2)",
"LabelAudioCodec": "Códec de audio",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (Nombre Apellido)",
"LabelAuthorLastFirst": "Autor (Apellido, Nombre)",
@@ -235,6 +242,7 @@
"LabelAutoRegister": "Registro automático",
"LabelAutoRegisterDescription": "Crear usuarios automáticamente tras iniciar sesión",
"LabelBackToUser": "Regresar a Usuario",
"LabelBackupAudioFiles": "Copia de seguridad de archivos de audio",
"LabelBackupLocation": "Ubicación del Respaldo",
"LabelBackupsEnableAutomaticBackups": "Habilitar Respaldo Automático",
"LabelBackupsEnableAutomaticBackupsHelp": "Respaldo Guardado en /metadata/backups",
@@ -243,15 +251,18 @@
"LabelBackupsNumberToKeep": "Numero de respaldos para conservar",
"LabelBackupsNumberToKeepHelp": "Solamente 1 respaldo se removerá a la vez. Si tiene mas respaldos guardados, debe removerlos manualmente.",
"LabelBitrate": "Tasa de bits",
"LabelBonus": "Bonus",
"LabelBooks": "Libros",
"LabelButtonText": "Texto del botón",
"LabelByAuthor": "por {0}",
"LabelChangePassword": "Cambiar Contraseña",
"LabelChannels": "Canales",
"LabelChapterCount": "{0} capítulos",
"LabelChapterTitle": "Titulo del Capítulo",
"LabelChapters": "Capítulos",
"LabelChaptersFound": "Capítulo Encontrado",
"LabelClickForMoreInfo": "Click para más información",
"LabelClickToUseCurrentValue": "Haz clic para utilizar el valor actual",
"LabelClosePlayer": "Cerrar reproductor",
"LabelCodec": "Codec",
"LabelCollapseSeries": "Colapsar serie",
@@ -301,12 +312,25 @@
"LabelEmailSettingsTestAddress": "Probar Dirección",
"LabelEmbeddedCover": "Portada Integrada",
"LabelEnable": "Habilitar",
"LabelEncodingBackupLocation": "Se guardará una copia de seguridad de tus archivos de audio originales en:",
"LabelEncodingChaptersNotEmbedded": "Los capítulos no se incrustan en los audiolibros multipista.",
"LabelEncodingClearItemCache": "Asegúrese de purgar periódicamente la caché.",
"LabelEncodingFinishedM4B": "El M4B terminado se colocará en su carpeta de audiolibros en:",
"LabelEncodingInfoEmbedded": "Los metadatos se integrarán en las pistas de audio dentro de la carpeta de audiolibros.",
"LabelEncodingStartedNavigation": "Una vez iniciada la tarea, puedes salir de esta página.",
"LabelEncodingTimeWarning": "La codificación puede tardar hasta 30 minutos.",
"LabelEncodingWarningAdvancedSettings": "Advertencia: No actualice esta configuración a menos que esté familiarizado con las opciones de codificación de ffmpeg.",
"LabelEncodingWatcherDisabled": "Si ha desactivado la supervisión de los archivos, deberá volver a escanear este audiolibro más adelante.",
"LabelEnd": "Fin",
"LabelEndOfChapter": "Fin del capítulo",
"LabelEpisode": "Episodio",
"LabelEpisodeNotLinkedToRssFeed": "Episodio no enlazado al feed RSS",
"LabelEpisodeNumber": "Episodio #{0}",
"LabelEpisodeTitle": "Titulo de Episodio",
"LabelEpisodeType": "Tipo de Episodio",
"LabelEpisodeUrlFromRssFeed": "URL del episodio del feed RSS",
"LabelEpisodes": "Episodios",
"LabelEpisodic": "Episodios",
"LabelExample": "Ejemplo",
"LabelExpandSeries": "Ampliar serie",
"LabelExpandSubSeries": "Expandir la subserie",
@@ -334,6 +358,7 @@
"LabelFontScale": "Tamaño de fuente",
"LabelFontStrikethrough": "Tachado",
"LabelFormat": "Formato",
"LabelFull": "Completo",
"LabelGenre": "Genero",
"LabelGenres": "Géneros",
"LabelHardDeleteFile": "Eliminar Definitivamente",
@@ -389,6 +414,7 @@
"LabelLowestPriority": "Menor prioridad",
"LabelMatchExistingUsersBy": "Emparejar a los usuarios existentes por",
"LabelMatchExistingUsersByDescription": "Se utiliza para conectar usuarios existentes. Una vez conectados, los usuarios serán emparejados por un identificador único de su proveedor de SSO",
"LabelMaxEpisodesToDownload": "Número máximo # de episodios para descargar. Usa 0 para descargar una cantidad ilimitada.",
"LabelMediaPlayer": "Reproductor de Medios",
"LabelMediaType": "Tipo de multimedia",
"LabelMetaTag": "Metaetiqueta",
@@ -459,9 +485,12 @@
"LabelPrimaryEbook": "Ebook principal",
"LabelProgress": "Progreso",
"LabelProvider": "Proveedor",
"LabelProviderAuthorizationValue": "Valor del encabezado de autorización",
"LabelPubDate": "Fecha de publicación",
"LabelPublishYear": "Año de publicación",
"LabelPublishedDate": "Publicado {0}",
"LabelPublishedDecade": "Década de publicación",
"LabelPublishedDecades": "Décadas publicadas",
"LabelPublisher": "Editor",
"LabelPublishers": "Editores",
"LabelRSSFeedCustomOwnerEmail": "Correo electrónico de dueño personalizado",
@@ -496,6 +525,7 @@
"LabelSeries": "Series",
"LabelSeriesName": "Nombre de la Serie",
"LabelSeriesProgress": "Progreso de la Serie",
"LabelServerLogLevel": "Nivel de registro del servidor",
"LabelServerYearReview": "Resumen del año del servidor ({0})",
"LabelSetEbookAsPrimary": "Establecer como primario",
"LabelSetEbookAsSupplementary": "Establecer como suplementario",
@@ -591,6 +621,7 @@
"LabelTitle": "Título",
"LabelToolsEmbedMetadata": "Incrustar Metadatos",
"LabelToolsEmbedMetadataDescription": "Incrusta metadatos en los archivos de audio, incluyendo la portada y capítulos.",
"LabelToolsM4bEncoder": "Codificador M4B",
"LabelToolsMakeM4b": "Hacer Archivo de Audiolibro M4B",
"LabelToolsMakeM4bDescription": "Generar archivo de audiolibro .M4B con metadatos, imágenes de portada y capítulos incorporados.",
"LabelToolsSplitM4b": "Dividir M4B en Archivos MP3",
@@ -616,6 +647,7 @@
"LabelUploaderDragAndDrop": "Arrastre y suelte archivos o carpetas",
"LabelUploaderDropFiles": "Suelte los Archivos",
"LabelUploaderItemFetchMetadataHelp": "Buscar título, autor y series automáticamente",
"LabelUseAdvancedOptions": "Usar opciones avanzadas",
"LabelUseChapterTrack": "Usar pista por capitulo",
"LabelUseFullTrack": "Usar pista completa",
"LabelUser": "Usuario",
@@ -664,6 +696,7 @@
"MessageConfirmDeleteMetadataProvider": "¿Estás seguro de que deseas eliminar el proveedor de metadatos personalizado \"{0}\"?",
"MessageConfirmDeleteNotification": "¿Estás seguro de que deseas eliminar esta notificación?",
"MessageConfirmDeleteSession": "¿Está seguro de que desea eliminar esta sesión?",
"MessageConfirmEmbedMetadataInAudioFiles": "¿Está seguro de que desea incrustar metadatos en {0} archivos de audio?",
"MessageConfirmForceReScan": "¿Está seguro de que desea forzar un re-escaneo?",
"MessageConfirmMarkAllEpisodesFinished": "¿Está seguro de que desea marcar todos los episodios como terminados?",
"MessageConfirmMarkAllEpisodesNotFinished": "¿Está seguro de que desea marcar todos los episodios como no terminados?",
@@ -697,6 +730,7 @@
"MessageDragFilesIntoTrackOrder": "Arrastra los archivos al orden correcto de las pistas",
"MessageEmbedFailed": "¡Error al insertar!",
"MessageEmbedFinished": "Incrustación Terminada!",
"MessageEmbedQueue": "En cola para incrustar metadatos ({0} en cola)",
"MessageEpisodesQueuedForDownload": "{0} Episodio(s) en cola para descargar",
"MessageEreaderDevices": "Para garantizar la entrega de libros electrónicos, es posible que tenga que agregar la dirección de correo electrónico anterior como remitente válido para cada dispositivo enumerado a continuación.",
"MessageFeedURLWillBe": "URL de la fuente será {0}",
@@ -741,6 +775,7 @@
"MessageNoLogs": "No hay logs",
"MessageNoMediaProgress": "Multimedia sin Progreso",
"MessageNoNotifications": "Ninguna Notificación",
"MessageNoPodcastFeed": "Podcast no válido: Sin feed",
"MessageNoPodcastsFound": "Ningún podcast encontrado",
"MessageNoResults": "Sin Resultados",
"MessageNoSearchResultsFor": "No hay resultados para la búsqueda \"{0}\"",
@@ -757,6 +792,9 @@
"MessagePlaylistCreateFromCollection": "Crear una lista de reproducción a partir de una colección",
"MessagePleaseWait": "Por favor, espera...",
"MessagePodcastHasNoRSSFeedForMatching": "El podcast no tiene una URL de fuente RSS que pueda usar",
"MessagePodcastSearchField": "Introduzca el término de búsqueda o la URL de la fuente RSS",
"MessageQuickEmbedInProgress": "Integración rápida en proceso",
"MessageQuickEmbedQueue": "En cola para inserción rápida ({0} en cola)",
"MessageQuickMatchDescription": "Rellenar detalles de elementos vacíos y portada con los primeros resultados de '{0}'. No sobrescribe los detalles a menos que la opción \"Preferir Metadatos Encontrados\" del servidor esté habilitada.",
"MessageRemoveChapter": "Remover capítulos",
"MessageRemoveEpisodes": "Remover {0} episodio(s)",
@@ -774,6 +812,41 @@
"MessageShareExpiresIn": "Caduduca en {0}",
"MessageShareURLWillBe": "La URL para compartir será <strong> {0} </strong>",
"MessageStartPlaybackAtTime": "Iniciar reproducción para \"{0}\" en {1}?",
"MessageTaskAudioFileNotWritable": "El archivo de audio \"{0}\" no se puede grabar",
"MessageTaskCanceledByUser": "Tarea cancelada por el usuario",
"MessageTaskDownloadingEpisodeDescription": "Descargando el episodio \"{0}\"",
"MessageTaskEmbeddingMetadata": "Inserción de metadatos",
"MessageTaskEmbeddingMetadataDescription": "Inserción de metadatos en el audiolibro \"{0}\"",
"MessageTaskEncodingM4b": "Codificación M4B",
"MessageTaskEncodingM4bDescription": "Codificación del audiolibro \"{0}\" en un único archivo m4b",
"MessageTaskFailed": "Fallida",
"MessageTaskFailedToBackupAudioFile": "Error en la copia de seguridad del archivo de audio \"{0}\"",
"MessageTaskFailedToCreateCacheDirectory": "Error al crear el directorio de la caché",
"MessageTaskFailedToEmbedMetadataInFile": "Error al incrustar metadatos en el archivo \"{0}\"",
"MessageTaskFailedToMergeAudioFiles": "Error al fusionar archivos de audio",
"MessageTaskFailedToMoveM4bFile": "Error al mover el archivo m4b",
"MessageTaskFailedToWriteMetadataFile": "Error al escribir el archivo de metadatos",
"MessageTaskMatchingBooksInLibrary": "Libros coincidentes en la biblioteca \"{0}\"",
"MessageTaskNoFilesToScan": "Sin archivos para escanear",
"MessageTaskOpmlImport": "Importar OPML",
"MessageTaskOpmlImportDescription": "Creando podcasts a partir de {0} fuentes RSS",
"MessageTaskOpmlImportFeed": "Feed de importación OPML",
"MessageTaskOpmlImportFeedDescription": "Importando el feed RSS \"{0}\"",
"MessageTaskOpmlImportFeedFailed": "No se puede obtener el podcast",
"MessageTaskOpmlImportFeedPodcastDescription": "Creando podcast \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Podcast ya existe en la ruta",
"MessageTaskOpmlImportFeedPodcastFailed": "Error al crear podcast",
"MessageTaskOpmlImportFinished": "Añadido {0} podcasts",
"MessageTaskOpmlParseFailed": "No se pudo analizar el archivo OPML",
"MessageTaskOpmlParseFastFail": "No se encontró la etiqueta <opml> del archivo OPML no válido O no se encontró la etiqueta <outline>",
"MessageTaskOpmlParseNoneFound": "No se encontraron fuentes en el archivo OPML",
"MessageTaskScanItemsAdded": "{0} añadido",
"MessageTaskScanItemsMissing": "Falta {0}",
"MessageTaskScanItemsUpdated": "{0} actualizado",
"MessageTaskScanNoChangesNeeded": "No se necesitan cambios",
"MessageTaskScanningFileChanges": "Escaneando cambios en el archivo en \"{0}\"",
"MessageTaskScanningLibrary": "Escaneando la biblioteca \"{0}\"",
"MessageTaskTargetDirectoryNotWritable": "El directorio de destino no se puede escribir",
"MessageThinking": "Pensando...",
"MessageUploaderItemFailed": "Error al Subir",
"MessageUploaderItemSuccess": "¡Éxito al Subir!",
@@ -791,6 +864,10 @@
"NoteUploaderFoldersWithMediaFiles": "Las carpetas con archivos multimedia se manejarán como elementos separados en la biblioteca.",
"NoteUploaderOnlyAudioFiles": "Si sube solamente archivos de audio, cada archivo se manejará como un audiolibro por separado.",
"NoteUploaderUnsupportedFiles": "Se ignorarán los archivos no soportados. Al elegir o arrastrar una carpeta, los archivos que no estén dentro de una subcarpeta serán ignorados.",
"NotificationOnBackupCompletedDescription": "Se activa cuando se completa una copia de seguridad",
"NotificationOnBackupFailedDescription": "Se activa cuando falla una copia de seguridad",
"NotificationOnEpisodeDownloadedDescription": "Se activa cuando se descarga automáticamente un episodio de un podcast",
"NotificationOnTestDescription": "Evento para probar el sistema de notificaciones",
"PlaceholderNewCollection": "Nuevo nombre de la colección",
"PlaceholderNewFolderPath": "Nueva ruta de carpeta",
"PlaceholderNewPlaylist": "Nuevo nombre de la lista de reproducción",
@@ -814,14 +891,12 @@
"StatsTopNarrators": "NARRADORES DESTACADOS",
"StatsTotalDuration": "Con una duración total de…",
"StatsYearInReview": "RESEÑA DEL AÑO",
"ToastAccountUpdateFailed": "Error al actualizar cuenta",
"ToastAccountUpdateSuccess": "Cuenta actualizada",
"ToastAppriseUrlRequired": "Debes ingresar una URL de Apprise",
"ToastAuthorImageRemoveSuccess": "Se eliminó la imagen del autor",
"ToastAuthorNotFound": "No se encontró el autor \"{0}\"",
"ToastAuthorRemoveSuccess": "Autor eliminado",
"ToastAuthorSearchNotFound": "No se encontró al autor",
"ToastAuthorUpdateFailed": "Error al actualizar el autor",
"ToastAuthorUpdateMerged": "Autor combinado",
"ToastAuthorUpdateSuccess": "Autor actualizado",
"ToastAuthorUpdateSuccessNoImageFound": "Autor actualizado (Imagen no encontrada)",
@@ -832,7 +907,6 @@
"ToastBackupDeleteSuccess": "Respaldo eliminado",
"ToastBackupInvalidMaxKeep": "Número no válido de copias de seguridad a conservar",
"ToastBackupInvalidMaxSize": "Tamaño máximo de copia de seguridad no válido",
"ToastBackupPathUpdateFailed": "Error al actualizar la ruta de la copia de seguridad",
"ToastBackupRestoreFailed": "Error al restaurar el respaldo",
"ToastBackupUploadFailed": "Error al subir el respaldo",
"ToastBackupUploadSuccess": "Respaldo cargado",
@@ -843,7 +917,6 @@
"ToastBookmarkCreateFailed": "Error al crear marcador",
"ToastBookmarkCreateSuccess": "Marcador Agregado",
"ToastBookmarkRemoveSuccess": "Marcador eliminado",
"ToastBookmarkUpdateFailed": "Error al actualizar el marcador",
"ToastBookmarkUpdateSuccess": "Marcador actualizado",
"ToastCachePurgeFailed": "Error al purgar el caché",
"ToastCachePurgeSuccess": "Caché purgado de manera exitosa",
@@ -854,7 +927,6 @@
"ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente",
"ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección",
"ToastCollectionRemoveSuccess": "Colección removida",
"ToastCollectionUpdateFailed": "Error al actualizar la colección",
"ToastCollectionUpdateSuccess": "Colección actualizada",
"ToastCoverUpdateFailed": "Error al actualizar la cubierta",
"ToastDeleteFileFailed": "Error el eliminar archivo",
@@ -863,8 +935,6 @@
"ToastDeviceNameAlreadyExists": "Un libro electrónico ya existe con ese nombre",
"ToastDeviceTestEmailFailed": "Error al enviar correo de prueba",
"ToastDeviceTestEmailSuccess": "Correo electrónico de prueba enviado",
"ToastDeviceUpdateFailed": "Error al actualizar el dispositivo",
"ToastEmailSettingsUpdateFailed": "Error al actualizar la configuración del correo electrónico",
"ToastEmailSettingsUpdateSuccess": "Configuración del correo electrónico actualizada",
"ToastEncodeCancelFailed": "No se pudo cancelar la codificación",
"ToastEncodeCancelSucces": "Codificación cancelada",
@@ -873,21 +943,17 @@
"ToastErrorCannotShare": "No se puede compartir de forma nativa en este dispositivo",
"ToastFailedToLoadData": "Error al cargar data",
"ToastFailedToShare": "Error al compartir",
"ToastFailedToUpdateAccount": "Error al actualizar la cuenta",
"ToastFailedToUpdateUser": "Error al actualizar el usuario",
"ToastFailedToUpdate": "Error al actualizar",
"ToastInvalidImageUrl": "URL de la imagen no válida",
"ToastInvalidUrl": "URL no válida",
"ToastItemCoverUpdateFailed": "Error al actualizar la portada del elemento",
"ToastItemCoverUpdateSuccess": "Portada del elemento actualizada",
"ToastItemDeletedFailed": "Error al eliminar el elemento",
"ToastItemDeletedSuccess": "Elemento borrado",
"ToastItemDetailsUpdateFailed": "Error al actualizar los detalles del elemento",
"ToastItemDetailsUpdateSuccess": "Detalles del Elemento Actualizados",
"ToastItemMarkedAsFinishedFailed": "Error al marcar como terminado",
"ToastItemMarkedAsFinishedSuccess": "Elemento marcado como terminado",
"ToastItemMarkedAsNotFinishedFailed": "No se ha podido marcar como no finalizado",
"ToastItemMarkedAsNotFinishedSuccess": "Elemento marcado como No Terminado",
"ToastItemUpdateFailed": "Error al actualizar el elemento",
"ToastItemUpdateSuccess": "Elemento actualizado",
"ToastLibraryCreateFailed": "Error al crear biblioteca",
"ToastLibraryCreateSuccess": "Biblioteca \"{0}\" creada",
@@ -895,9 +961,9 @@
"ToastLibraryDeleteSuccess": "Biblioteca eliminada",
"ToastLibraryScanFailedToStart": "Error al iniciar el escaneo",
"ToastLibraryScanStarted": "Se inició el escaneo de la biblioteca",
"ToastLibraryUpdateFailed": "Error al actualizar la biblioteca",
"ToastLibraryUpdateSuccess": "Biblioteca \"{0}\" actualizada",
"ToastNameEmailRequired": "Nombre y correo electrónico obligatorios",
"ToastMatchAllAuthorsFailed": "No coincide con todos los autores",
"ToastNameEmailRequired": "Son obligatorios el nombre y el correo electrónico",
"ToastNameRequired": "Nombre obligatorio",
"ToastNewUserCreatedFailed": "Error al crear la cuenta: \"{0}\"",
"ToastNewUserCreatedSuccess": "Nueva cuenta creada",
@@ -910,16 +976,13 @@
"ToastNotificationDeleteFailed": "Error al borrar la notificación",
"ToastNotificationFailedMaximum": "El número máximo de intentos fallidos debe ser ≥ 0",
"ToastNotificationQueueMaximum": "La cola de notificación máxima debe ser ≥ 0",
"ToastNotificationSettingsUpdateFailed": "Error al actualizar los ajustes de la notificación",
"ToastNotificationSettingsUpdateSuccess": "Ajustes de la notificación actualizados",
"ToastNotificationTestTriggerFailed": "No se ha podido activar la notificación de prueba",
"ToastNotificationTestTriggerSuccess": "Notificación de prueba activada",
"ToastNotificationUpdateFailed": "No se ha podido actualizar la notificación",
"ToastNotificationUpdateSuccess": "Notificación actualizada",
"ToastPlaylistCreateFailed": "Error al crear la lista de reproducción",
"ToastPlaylistCreateSuccess": "Lista de reproducción creada",
"ToastPlaylistRemoveSuccess": "Lista de reproducción eliminada",
"ToastPlaylistUpdateFailed": "Error al actualizar la lista de reproducción",
"ToastPlaylistUpdateSuccess": "Lista de reproducción actualizada",
"ToastPodcastCreateFailed": "Error al crear podcast",
"ToastPodcastCreateSuccess": "Podcast creado",
@@ -948,7 +1011,6 @@
"ToastSendEbookToDeviceSuccess": "Ebook enviado al dispositivo \"{0}\"",
"ToastSeriesUpdateFailed": "Error al actualizar la serie",
"ToastSeriesUpdateSuccess": "Serie actualizada",
"ToastServerSettingsUpdateFailed": "Error al actualizar configuración del servidor",
"ToastServerSettingsUpdateSuccess": "Configuración del servidor actualizada",
"ToastSessionCloseFailed": "Error al cerrar la sesión",
"ToastSessionDeleteFailed": "Error al eliminar sesión",
@@ -959,7 +1021,6 @@
"ToastSocketDisconnected": "Socket desconectado",
"ToastSocketFailedToConnect": "Error al conectar al Socket",
"ToastSortingPrefixesEmptyError": "Debe tener por lo menos 1 prefijo para ordenar",
"ToastSortingPrefixesUpdateFailed": "Error al actualizar los prefijos de ordenar",
"ToastSortingPrefixesUpdateSuccess": "Prefijos de ordenar actualizaron ({0} items)",
"ToastTitleRequired": "Título obligatorio",
"ToastUnknownError": "Error desconocido",

View File

@@ -9,6 +9,7 @@
"ButtonApply": "Rakenda",
"ButtonApplyChapters": "Rakenda peatükid",
"ButtonAuthors": "Autorid",
"ButtonBack": "Tagasi",
"ButtonBrowseForFolder": "Sirvi kausta",
"ButtonCancel": "Tühista",
"ButtonCancelEncode": "Tühista kodeerimine",
@@ -18,6 +19,7 @@
"ButtonChooseFiles": "Vali failid",
"ButtonClearFilter": "Tühista filter",
"ButtonCloseFeed": "Sulge voog",
"ButtonCloseSession": "Sulge avatud sessioon",
"ButtonCollections": "Kogud",
"ButtonConfigureScanner": "Konfigureeri skanner",
"ButtonCreate": "Loo",
@@ -27,6 +29,7 @@
"ButtonEdit": "Muuda",
"ButtonEditChapters": "Muuda peatükke",
"ButtonEditPodcast": "Muuda podcasti",
"ButtonEnable": "Aktiveeri",
"ButtonForceReScan": "Sunnitud uuestiskaneerimine",
"ButtonFullPath": "Täielik asukoht",
"ButtonHide": "Peida",
@@ -43,13 +46,18 @@
"ButtonMatchAllAuthors": "Sobita kõik autorid",
"ButtonMatchBooks": "Sobita raamatud",
"ButtonNevermind": "Pole tähtis",
"ButtonNext": "Järgmine",
"ButtonNextChapter": "Järgmine peatükk",
"ButtonNextItemInQueue": "Järgmine kirje järjekorras",
"ButtonOk": "Ok",
"ButtonOpenFeed": "Ava voog",
"ButtonOpenManager": "Ava haldur",
"ButtonPause": "Peata",
"ButtonPlay": "Mängi",
"ButtonPlayAll": "Mängi kõik",
"ButtonPlaying": "Mängib",
"ButtonPlaylists": "Esitusloendid",
"ButtonPrevious": "Eelmine",
"ButtonPreviousChapter": "Eelmine peatükk",
"ButtonPurgeAllCache": "Tühjenda kogu vahemälu",
"ButtonPurgeItemsCache": "Tühjenda esemete vahemälu",
@@ -58,6 +66,9 @@
"ButtonQuickMatch": "Kiire sobitamine",
"ButtonReScan": "Uuestiskaneeri",
"ButtonRead": "Loe",
"ButtonReadLess": "Loe vähem",
"ButtonReadMore": "Loe rohkem",
"ButtonRefresh": "Värskenda",
"ButtonRemove": "Eemalda",
"ButtonRemoveAll": "Eemalda kõik",
"ButtonRemoveAllLibraryItems": "Eemalda kõik raamatukogu esemed",
@@ -211,7 +222,7 @@
"LabelBackupLocation": "Varukoopia asukoht",
"LabelBackupsEnableAutomaticBackups": "Luba automaatsed varukoopiad",
"LabelBackupsEnableAutomaticBackupsHelp": "Varukoopiad salvestatakse /metadata/backups kausta",
"LabelBackupsMaxBackupSize": "Maksimaalne varukoopia suurus (GB-des)",
"LabelBackupsMaxBackupSize": "Maksimaalne varukoopia suurus (GB-des) (0 lõpmatu suuruse jaoks)",
"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.",
@@ -449,7 +460,7 @@
"LabelSettingsHomePageBookshelfView": "Avaleht kasutage raamatukoguvaadet",
"LabelSettingsLibraryBookshelfView": "Raamatukogu kasutamiseks kasutage raamatukoguvaadet",
"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\"",
"LabelSettingsParseSubtitlesHelp": "Eraldage subtiitrid heliraamatu kaustade nimedest.<br>Subtiitrid peavad olema eraldatud kasutades \" - \".<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",
@@ -682,10 +693,8 @@
"PlaceholderNewPlaylist": "Uue esitusloendi nimi",
"PlaceholderSearch": "Otsi...",
"PlaceholderSearchEpisode": "Otsi episoodi...",
"ToastAccountUpdateFailed": "Konto värskendamine ebaõnnestus",
"ToastAccountUpdateSuccess": "Konto on värskendatud",
"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)",
@@ -701,17 +710,13 @@
"ToastBookmarkCreateFailed": "Järjehoidja loomine ebaõnnestus",
"ToastBookmarkCreateSuccess": "Järjehoidja lisatud",
"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",
"ToastCollectionItemsRemoveSuccess": "Üksus(ed) eemaldatud kogumist",
"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",
"ToastItemMarkedAsFinishedFailed": "Märgistamine kui lõpetatud ebaõnnestus",
"ToastItemMarkedAsFinishedSuccess": "Üksus märgitud kui lõpetatud",
@@ -723,12 +728,10 @@
"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",
"ToastPlaylistRemoveSuccess": "Esitusloend eemaldatud",
"ToastPlaylistUpdateFailed": "Esitusloendi värskendamine ebaõnnestus",
"ToastPlaylistUpdateSuccess": "Esitusloend värskendatud",
"ToastPodcastCreateFailed": "Podcasti loomine ebaõnnestus",
"ToastPodcastCreateSuccess": "Podcast loodud edukalt",

View File

@@ -19,6 +19,7 @@
"ButtonChooseFiles": "Valitse tiedostot",
"ButtonClearFilter": "Poista suodatus",
"ButtonCloseFeed": "Sulje syöte",
"ButtonCloseSession": "Sulje Avoin Sessio",
"ButtonCollections": "Kokoelmat",
"ButtonConfigureScanner": "Skannerin asetukset",
"ButtonCreate": "Luo",
@@ -28,6 +29,9 @@
"ButtonEdit": "Muokkaa",
"ButtonEditChapters": "Muokkaa lukuja",
"ButtonEditPodcast": "Muokkaa podcastia",
"ButtonEnable": "Aktivoi",
"ButtonFireAndFail": "Laukaise ja epäonnistu",
"ButtonFireOnTest": "Laukaise onTest tapahtuma",
"ButtonForceReScan": "Pakota uudelleenskannaus",
"ButtonFullPath": "Koko polku",
"ButtonHide": "Piilota",
@@ -46,10 +50,13 @@
"ButtonNevermind": "Ei sittenkään",
"ButtonNext": "Seuraava",
"ButtonNextChapter": "Seuraava luku",
"ButtonNextItemInQueue": "Seuraava jonossa",
"ButtonOk": "Ok",
"ButtonOpenFeed": "Avaa syöte",
"ButtonOpenManager": "Avaa hallinta",
"ButtonPause": "Pysäytä",
"ButtonPlay": "Toista",
"ButtonPlayAll": "Toista kaikki",
"ButtonPlaying": "Toistetaan",
"ButtonPlaylists": "Soittolistat",
"ButtonPrevious": "Edellinen",
@@ -90,6 +97,7 @@
"ButtonStats": "Tilastot",
"ButtonSubmit": "Lähetä",
"ButtonTest": "Testi",
"ButtonUnlinkOpenId": "Poista OpenID linkitys",
"ButtonUpload": "Lähetä palvelimelle",
"ButtonUploadBackup": "Lähetä varmuuskopio",
"ButtonUploadCover": "Lähetä kansikuva",
@@ -102,6 +110,7 @@
"ErrorUploadFetchMetadataNoResults": "Metadatan haku epäonnistui, yritä päivittää Teoksen nimi ja/tai Tekijä",
"ErrorUploadLacksTitle": "Pitää sisältää Nimi",
"HeaderAccount": "Tili",
"HeaderAddCustomMetadataProvider": "Lisää mukautettu metadata tarjoaja",
"HeaderAdvanced": "Edistynyt",
"HeaderAppriseNotificationSettings": "Apprise-ilmoitusasetukset",
"HeaderAudioTracks": "Ääniraidat",
@@ -126,38 +135,60 @@
"HeaderEreaderDevices": "E-lukijalaitteet",
"HeaderEreaderSettings": "E-lukijan asetukset",
"HeaderFiles": "Tiedostot",
"HeaderFindChapters": "Etsi kappaleet",
"HeaderIgnoredFiles": "Ohitetut tiedostot",
"HeaderLastListeningSession": "Edellinen kuuntelukerta",
"HeaderLatestEpisodes": "Viimeisimmät jaksot",
"HeaderLibraries": "Kirjastot",
"HeaderLibraryFiles": "Kirjaston tiedostot",
"HeaderLibraryStats": "Kirjaston tilastot",
"HeaderListeningSessions": "Kuuntelukerrat",
"HeaderListeningStats": "Kuuntelutilastot",
"HeaderLogin": "Kirjaudu",
"HeaderLogs": "Lokit",
"HeaderManageGenres": "Hallitse lajityyppejä",
"HeaderManageTags": "Hallitse tageja",
"HeaderMetadataToEmbed": "Sisällytettävä metadata",
"HeaderNewAccount": "Uusi tili",
"HeaderNewLibrary": "Uusi kirjasto",
"HeaderNotificationCreate": "Luo ilmoitus",
"HeaderNotificationUpdate": "Päivitä ilmoitus",
"HeaderNotifications": "Ilmoitukset",
"HeaderOpenRSSFeed": "Avaa RSS-syöte",
"HeaderOtherFiles": "Muut tiedostot",
"HeaderPermissions": "Käyttöoikeudet",
"HeaderPlayerQueue": "Soittimen jono",
"HeaderPlayerSettings": "Soittimen asetukset",
"HeaderPlaylist": "Soittolista",
"HeaderPlaylistItems": "Soittolistan kohteet",
"HeaderPodcastsToAdd": "Lisättävät podcastit",
"HeaderPreviewCover": "Esikatsele kansikuvaa",
"HeaderRSSFeedGeneral": "RSS yksityiskohdat",
"HeaderRSSFeedIsOpen": "RSS syöte on avoinna",
"HeaderRSSFeeds": "RSS syötteet",
"HeaderRemoveEpisode": "Poista jakso",
"HeaderRemoveEpisodes": "Poista {0} jaksoa",
"HeaderSchedule": "Ajoita",
"HeaderScheduleLibraryScans": "Ajoita automaattiset kirjastoskannaukset",
"HeaderSession": "Istunto",
"HeaderSetBackupSchedule": "Aseta varmuuskopiointiaikataulu",
"HeaderSettings": "Asetukset",
"HeaderSettingsExperimental": "Kokeelliset ominaisuudet",
"HeaderSettingsGeneral": "Yleiset",
"HeaderSleepTimer": "Uniajastin",
"HeaderStatsMinutesListeningChart": "Kuunteluminuutit (viim. 7 pv)",
"HeaderStatsRecentSessions": "Viimeaikaiset istunnot",
"HeaderStatsTop5Genres": "Top 5 lajityypit",
"HeaderTableOfContents": "Sisällysluettelo",
"HeaderTools": "Työkalut",
"HeaderUpdateAccount": "Päivitä tili",
"HeaderUpdateAuthor": "Päivitä kirjailija",
"HeaderUpdateLibrary": "Päivitä kirjasto",
"HeaderUsers": "Käyttäjät",
"HeaderYourStats": "Tilastosi",
"LabelAbridged": "Lyhennetty",
"LabelAccountType": "Tilin tyyppi",
"LabelAccountTypeAdmin": "Järjestelmänvalvoja",
"LabelAccountTypeGuest": "Vieras",
"LabelAccountTypeUser": "Käyttäjä",
"LabelActivity": "Toiminta",
@@ -166,22 +197,29 @@
"LabelAddToPlaylist": "Lisää soittolistaan",
"LabelAddToPlaylistBatch": "Lisää {0} kohdetta soittolistaan",
"LabelAddedAt": "Lisätty listalle",
"LabelAddedDate": "Lisätty {0}",
"LabelAdminUsersOnly": "Vain järjestelmänvalvojat",
"LabelAll": "Kaikki",
"LabelAllUsers": "Kaikki käyttäjät",
"LabelAllUsersExcludingGuests": "Kaikki käyttäjät vieraita lukuun ottamatta",
"LabelAllUsersIncludingGuests": "Kaikki käyttäjät mukaan lukien vieraat",
"LabelAlreadyInYourLibrary": "Jo kirjastossasi",
"LabelAuthor": "Tekijä",
"LabelAuthorFirstLast": "Tekijä (Etunimi Sukunimi)",
"LabelAuthorLastFirst": "Tekijä (Sukunimi, Etunimi)",
"LabelAuthors": "Tekijät",
"LabelAutoDownloadEpisodes": "Lataa jaksot automaattisesti",
"LabelBackToUser": "Takaisin käyttäjään",
"LabelBackupLocation": "Varmuuskopiointipaikka",
"LabelBackupsEnableAutomaticBackups": "Ota automaattinen varmuuskopiointi käyttöön",
"LabelBackupsEnableAutomaticBackupsHelp": "Varmuuskopiot tallennettu kansioon /metadata/backups",
"LabelBackupsMaxBackupSize": "Varmuuskopion enimmäiskoko (Gt) (0 rajaton)",
"LabelBackupsNumberToKeep": "Säilytettävien varmuuskopioiden määrä",
"LabelBitrate": "Bittinopeus",
"LabelBooks": "Kirjat",
"LabelButtonText": "Painikkeen teksti",
"LabelChangePassword": "Vaihda salasana",
"LabelChannels": "Kanavat",
"LabelChapters": "Luvut",
"LabelClickForMoreInfo": "Napsauta saadaksesi lisätietoja",
"LabelClosePlayer": "Sulje soitin",
@@ -196,79 +234,205 @@
"LabelContinueSeries": "Jatka sarjoja",
"LabelCover": "Kansikuva",
"LabelCoverImageURL": "Kansikuvan URL-osoite",
"LabelCreatedAt": "Luotu",
"LabelCurrent": "Nykyinen",
"LabelDays": "Päivää",
"LabelDescription": "Kuvaus",
"LabelDevice": "Laite",
"LabelDeviceInfo": "Laitteen tiedot",
"LabelDiscover": "Löydä",
"LabelDownload": "Lataa",
"LabelDownloadNEpisodes": "Lataa {0} jaksoa",
"LabelDuration": "Kesto",
"LabelDurationComparisonLonger": "({0} pidempi)",
"LabelDurationComparisonShorter": "({0} lyhyempi)",
"LabelEbook": "E-kirja",
"LabelEbooks": "E-kirjat",
"LabelEdit": "Muokkaa",
"LabelEmail": "Sähköposti",
"LabelEmailSettingsTestAddress": "Testiosoite",
"LabelEmbeddedCover": "Upotettu kansikuva",
"LabelEnable": "Ota käyttöön",
"LabelEnd": "Loppu",
"LabelEndOfChapter": "Luvun loppu",
"LabelEpisode": "Jakso",
"LabelEpisodes": "Jaksot",
"LabelExample": "Esimerkki",
"LabelFeedURL": "Syötteen URL",
"LabelFile": "Tiedosto",
"LabelFileBirthtime": "Tiedoston syntymäaika",
"LabelFileBornDate": "Syntynyt {0}",
"LabelFileModified": "Muutettu tiedosto",
"LabelFileModifiedDate": "Muokattu {0}",
"LabelFilename": "Tiedostonimi",
"LabelFindEpisodes": "Etsi jaksoja",
"LabelFinished": "Valmis",
"LabelFolder": "Kansio",
"LabelFolders": "Kansiot",
"LabelGenre": "Lajityyppi",
"LabelGenres": "Lajityypit",
"LabelHost": "Isäntä",
"LabelHours": "Tunnit",
"LabelInProgress": "Kesken",
"LabelIncomplete": "Keskeneräinen",
"LabelIntervalEvery12Hours": "12 tunnin välein",
"LabelIntervalEvery15Minutes": "15 minuutin välein",
"LabelIntervalEvery2Hours": "2 tunnin välein",
"LabelIntervalEvery30Minutes": "30 minuutin välein",
"LabelIntervalEvery6Hours": "6 tunnin välein",
"LabelIntervalEveryDay": "Joka päivä",
"LabelIntervalEveryHour": "Joka tunti",
"LabelItem": "Kohde",
"LabelLanguage": "Kieli",
"LabelLanguageDefaultServer": "Palvelimen oletuskieli",
"LabelLanguages": "Kielet",
"LabelLastBookAdded": "Viimeisin lisätty kirja",
"LabelLibrary": "Kirjasto",
"LabelLineSpacing": "Riviväli",
"LabelListenAgain": "Kuuntele uudelleen",
"LabelMediaType": "Mediatyyppi",
"LabelMinute": "Minuutti",
"LabelMinutes": "Minuutit",
"LabelMore": "Lisää",
"LabelMoreInfo": "Lisätietoja",
"LabelName": "Nimi",
"LabelNarrator": "Lukija",
"LabelNarrators": "Lukijat",
"LabelNew": "Uusi",
"LabelNewPassword": "Uusi salasana",
"LabelNewestAuthors": "Uusimmat kirjailijat",
"LabelNewestEpisodes": "Uusimmat jaksot",
"LabelNotStarted": "Ei aloitettu",
"LabelPassword": "Salasana",
"LabelPath": "Polku",
"LabelPermanent": "Pysyvä",
"LabelPermissionsAccessAllLibraries": "Käyttöoikeudet kaikkiin kirjastoihin",
"LabelPermissionsDelete": "Voi poistaa",
"LabelPermissionsDownload": "Voi ladata",
"LabelPermissionsUpdate": "Voi päivittää",
"LabelPermissionsUpload": "Voi lähettää",
"LabelPlaylists": "Soittolistat",
"LabelPodcast": "Podcast",
"LabelPodcasts": "Podcastit",
"LabelPort": "Portti",
"LabelPublishYear": "Julkaisuvuosi",
"LabelPublisher": "Julkaisija",
"LabelPublishers": "Julkaisijat",
"LabelRSSFeedPreventIndexing": "Estä indeksointi",
"LabelRandomly": "Satunnaisesti",
"LabelRead": "Lue",
"LabelReadAgain": "Lue uudelleen",
"LabelRecentSeries": "Viimeisimmät sarjat",
"LabelRecentlyAdded": "Viimeeksi lisätyt",
"LabelRecommended": "Suositeltu",
"LabelRegion": "Alue",
"LabelRemoveCover": "Poista kansikuva",
"LabelSeason": "Kausi",
"LabelSelectAll": "Valitse kaikki",
"LabelSelectUsers": "Valitse käyttäjät",
"LabelSeries": "Sarja",
"LabelSeriesName": "Sarjan nimi",
"LabelSetEbookAsPrimary": "Aseta ensisijaiseksi",
"LabelSetEbookAsSupplementary": "Aseta täydentäväksi",
"LabelSettingsAudiobooksOnly": "Vain äänikirjat",
"LabelSettingsChromecastSupport": "Chromecast-tuki",
"LabelSettingsExperimentalFeatures": "Kokeelliset ominaisuudet",
"LabelSettingsFindCovers": "Etsi kansikuvia",
"LabelShare": "Jaa",
"LabelShowAll": "Näytä kaikki",
"LabelShowSeconds": "Näytä sekunnit",
"LabelSize": "Koko",
"LabelSleepTimer": "Uniajastin",
"LabelStart": "Aloita",
"LabelStartTime": "Aloitusaika",
"LabelStatsAudioTracks": "Ääniraidat",
"LabelStatsBestDay": "Paras päivä",
"LabelStatsDailyAverage": "Päivittäinen keskiarvo",
"LabelStatsDays": "Päivää",
"LabelStatsDaysListened": "Päivää kuunneltu",
"LabelStatsHours": "Tunnit",
"LabelStatsInARow": "peräjälkeen",
"LabelStatsMinutes": "minuuttia",
"LabelStatsMinutesListening": "Minuuttia kuunneltu",
"LabelStatsWeekListening": "Viikon aikana kuunneltu",
"LabelTag": "Tägi",
"LabelTags": "Tägit",
"LabelTheme": "Teema",
"LabelThemeDark": "Tumma",
"LabelThemeLight": "Kirkas",
"LabelTimeRemaining": "{0} jäljellä",
"LabelTitle": "Nimi",
"LabelTotalDuration": "Kokonaiskesto",
"LabelTracks": "Raidat",
"LabelType": "Tyyppi",
"LabelUnknown": "Tuntematon",
"LabelUpdateCover": "Päivitä kansikuva",
"LabelUser": "Käyttäjä",
"LabelUsername": "Käyttäjätunnus",
"LabelValue": "Arvo",
"LabelVersion": "Versio",
"LabelYourBookmarks": "Kirjanmerkkisi",
"LabelYourProgress": "Edistymisesi",
"MessageDownloadingEpisode": "Ladataan jaksoa",
"MessageEpisodesQueuedForDownload": "{0} jaksoa on latausjonossa",
"MessageFeedURLWillBe": "Syötteen URL tulee olemaan {0}",
"MessageFetching": "Haetaan...",
"MessageLoading": "Ladataan...",
"MessageMarkAsFinished": "Merkitse valmiiksi",
"MessageNoBookmarks": "Ei kirjanmerkkejä",
"MessageNoChapters": "Ei kappaleita",
"MessageNoCoversFound": "Kansikuvia ei löydetty",
"MessageNoGenres": "Ei lajityyppejä",
"MessageNoItems": "Ei kohteita",
"MessageNoItemsFound": "Kohteita ei löytynyt",
"MessageNoListeningSessions": "Ei kuunteluistuntoja",
"MessageNoPodcastsFound": "Podcasteja ei löytynyt",
"MessageNoUpdatesWereNecessary": "Päivityksiä ei tarvittu",
"MessageNoUserPlaylists": "Sinulla ei ole soittolistoja",
"MessageOr": "tai",
"MessageReportBugsAndContribute": "Ilmoita virheistä, toivo ominaisuuksia ja osallistu",
"MessageTaskFailed": "Epäonnistunut",
"StatsSessions": "istunnot",
"ToastAccountUpdateSuccess": "Tili päivitetty",
"ToastBookmarkCreateFailed": "Kirjanmerkin luominen epäonnistui",
"ToastBookmarkUpdateFailed": "Kirjanmerkin päivittäminen epäonnistui",
"ToastCoverUpdateFailed": "Kansikuvan päivitys epäonnistui",
"ToastItemCoverUpdateSuccess": "Kohteen kansikuva päivitetty",
"ToastItemMarkedAsFinishedFailed": "Valmiiksi merkitseminen epäonnistui",
"ToastItemMarkedAsNotFinishedFailed": "Valmiiksi merkitsemisen poisto epäonnistui",
"ToastItemUpdateSuccess": "Kohde päivitetty",
"ToastLibraryCreateFailed": "Kirjaston luominen epäonnistui",
"ToastLibraryCreateSuccess": "Kirjasto \"{0}\" luotu",
"ToastLibraryDeleteFailed": "Kirjaston poistaminen epäonnistui",
"ToastLibraryDeleteSuccess": "Kirjasto poistettu",
"ToastLibraryUpdateSuccess": "Kirjasto \"{0}\" päivitetty",
"ToastNewUserCreatedFailed": "Tilin \"{0}\" luominen epäonnistui",
"ToastNewUserCreatedSuccess": "Uusi tili luotu",
"ToastPlaylistCreateFailed": "Soittolistan luominen epäonnistui",
"ToastPlaylistCreateSuccess": "Soittolista luotu",
"ToastPlaylistRemoveSuccess": "Soittolista poistettu",
"ToastPlaylistUpdateSuccess": "Soittolista päivitetty",
"ToastPodcastCreateFailed": "Podcastin luominen epäonnistui",
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui"
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui",
"ToastRSSFeedCloseFailed": "RSS syötteen sulkeminen epäonnistui",
"ToastRSSFeedCloseSuccess": "RSS syöte suljettu",
"ToastRemoveFailed": "Poistaminen epäonnistui",
"ToastRemoveItemFromCollectionFailed": "Kohteen poistaminen kokoelmasta epäonnistui",
"ToastRemoveItemFromCollectionSuccess": "Kohde poistettu kokoelmasta",
"ToastRenameFailed": "Uudelleennimeäminen epäonnistui",
"ToastSelectAtLeastOneUser": "Valitse ainakin yksi käyttäjä",
"ToastServerSettingsUpdateSuccess": "Palvelimen asetukset päivitetty",
"ToastSessionCloseFailed": "Istunnon sulkeminen epäonnistui",
"ToastSessionDeleteFailed": "Istunnon poistaminen epäonnistui",
"ToastSessionDeleteSuccess": "Istunto poistettu",
"ToastSocketConnected": "Yhteys saatu",
"ToastSocketDisconnected": "Yhteys katkaistu",
"ToastSocketFailedToConnect": "Yhteyden muodostus epäonnistui",
"ToastTitleRequired": "Otsikko on pakollinen",
"ToastUnknownError": "Tuntematon virhe",
"ToastUserDeleteFailed": "Käyttäjän poisto epäonnistui",
"ToastUserDeleteSuccess": "Käyttäjä poistettu",
"ToastUserPasswordChangeSuccess": "Salasana vaihdettu onnistuneesti",
"ToastUserPasswordMismatch": "Salasanat eivät täsmää",
"ToastUserPasswordMustChange": "Uusi salasana ei voi olla sama kuin vanha salasana",
"ToastUserRootRequireName": "Pääkäyttäjän nimi on pakollinen"
}

View File

@@ -9,7 +9,7 @@
"ButtonApply": "Appliquer",
"ButtonApplyChapters": "Appliquer aux chapitres",
"ButtonAuthors": "Auteurs",
"ButtonBack": "Reculer",
"ButtonBack": "Retour",
"ButtonBrowseForFolder": "Naviguer vers le répertoire",
"ButtonCancel": "Annuler",
"ButtonCancelEncode": "Annuler lencodage",
@@ -51,11 +51,12 @@
"ButtonNext": "Suivant",
"ButtonNextChapter": "Chapitre suivant",
"ButtonNextItemInQueue": "Élément suivant dans la file dattente",
"ButtonOk": "Ok",
"ButtonOk": "Daccord",
"ButtonOpenFeed": "Ouvrir le flux",
"ButtonOpenManager": "Ouvrir le gestionnaire",
"ButtonPause": "Pause",
"ButtonPlay": "Lire",
"ButtonPlayAll": "Lire tout",
"ButtonPlaying": "En lecture",
"ButtonPlaylists": "Listes de lecture",
"ButtonPrevious": "Précédent",
@@ -65,6 +66,7 @@
"ButtonPurgeItemsCache": "Purger le cache des éléments",
"ButtonQueueAddItem": "Ajouter à la liste de lecture",
"ButtonQueueRemoveItem": "Supprimer de la liste de lecture",
"ButtonQuickEmbed": "Intégration rapide",
"ButtonQuickEmbedMetadata": "Ajouter rapidement des métadonnées",
"ButtonQuickMatch": "Recherche rapide",
"ButtonReScan": "Nouvelle analyse",
@@ -98,6 +100,7 @@
"ButtonStats": "Statistiques",
"ButtonSubmit": "Soumettre",
"ButtonTest": "Test",
"ButtonUnlinkOpenId": "Dissocier OpenID",
"ButtonUpload": "Téléverser",
"ButtonUploadBackup": "Téléverser une sauvegarde",
"ButtonUploadCover": "Téléverser une couverture",
@@ -211,11 +214,11 @@
"LabelAccountTypeUser": "Utilisateur",
"LabelActivity": "Activité",
"LabelAddToCollection": "Ajouter à la collection",
"LabelAddToCollectionBatch": "Ajout de {0} livres à la lollection",
"LabelAddToCollectionBatch": "Ajout de {0} livres à la collection",
"LabelAddToPlaylist": "Ajouter à la liste de lecture",
"LabelAddToPlaylistBatch": "{0} éléments ajoutés à la liste de lecture",
"LabelAddedAt": "Date dajout",
"LabelAddedDate": "{0} ajoutés",
"LabelAddedDate": "Ajouté le {0}",
"LabelAdminUsersOnly": "Administrateurs uniquement",
"LabelAll": "Tout",
"LabelAllUsers": "Tous les utilisateurs",
@@ -223,6 +226,9 @@
"LabelAllUsersIncludingGuests": "Tous les utilisateurs, y compris les invités",
"LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
"LabelAppend": "Ajouter",
"LabelAudioBitrate": "Débit audio (par exemple 128k)",
"LabelAudioChannels": "Canaux audio (1 ou 2)",
"LabelAudioCodec": "Codec audio",
"LabelAuthor": "Auteur",
"LabelAuthorFirstLast": "Auteur (Prénom Nom)",
"LabelAuthorLastFirst": "Auteur (Nom, Prénom)",
@@ -235,6 +241,7 @@
"LabelAutoRegister": "Enregistrement automatique",
"LabelAutoRegisterDescription": "Créer automatiquement de nouveaux utilisateurs après la connexion",
"LabelBackToUser": "Retour à lutilisateur",
"LabelBackupAudioFiles": "Sauvegarder les fichiers audio",
"LabelBackupLocation": "Emplacement de la sauvegarde",
"LabelBackupsEnableAutomaticBackups": "Activer les sauvegardes automatiques",
"LabelBackupsEnableAutomaticBackupsHelp": "Sauvegardes enregistrées dans /metadata/backups",
@@ -242,12 +249,14 @@
"LabelBackupsMaxBackupSizeHelp": "Afin de prévenir les mauvaises configuration, la sauvegarde échouera si elle excède la taille limite.",
"LabelBackupsNumberToKeep": "Nombre de sauvegardes à conserver",
"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": "Débit binaire",
"LabelBonus": "Bonus",
"LabelBooks": "Livres",
"LabelButtonText": "Texte du bouton",
"LabelByAuthor": "par {0}",
"LabelChangePassword": "Modifier le mot de passe",
"LabelChannels": "Canaux",
"LabelChapterCount": "{0} Chapitres",
"LabelChapterTitle": "Titre du chapitre",
"LabelChapters": "Chapitres",
"LabelChaptersFound": "chapitres trouvés",
@@ -301,6 +310,15 @@
"LabelEmailSettingsTestAddress": "Adresse de test",
"LabelEmbeddedCover": "Couverture du livre intégrée",
"LabelEnable": "Activer",
"LabelEncodingBackupLocation": "Une sauvegarde de vos fichiers audio originaux sera stockée dans :",
"LabelEncodingChaptersNotEmbedded": "Les chapitres ne sont pas intégrés dans les livres audio multipistes.",
"LabelEncodingClearItemCache": "Assurez-vous de purger périodiquement le cache des éléments.",
"LabelEncodingFinishedM4B": "Le fichier M4B terminé sera placé dans votre dossier de livre audio à l'adresse suivante :",
"LabelEncodingInfoEmbedded": "Les métadonnées seront intégrées dans les pistes audio de votre dossier de livre audio.",
"LabelEncodingStartedNavigation": "Une fois la tâche démarrée, vous pouvez quitter cette page.",
"LabelEncodingTimeWarning": "Lencodage peut prendre jusquà 30 minutes.",
"LabelEncodingWarningAdvancedSettings": "Avertissement : ne mettez pas à jour ces paramètres à moins que vous ne soyez familier avec les options d'encodage « ffmpeg ».",
"LabelEncodingWatcherDisabled": "Si l'observateur est désactivé, vous devrez ensuite réanalyser ce livre audio.",
"LabelEnd": "Fin",
"LabelEndOfChapter": "Fin du chapitre",
"LabelEpisode": "Épisode",
@@ -318,7 +336,7 @@
"LabelFetchingMetadata": "Récupération des métadonnées",
"LabelFile": "Fichier",
"LabelFileBirthtime": "Création du fichier",
"LabelFileBornDate": "Créé {0}",
"LabelFileBornDate": "Créé le {0}",
"LabelFileModified": "Modification du fichier",
"LabelFileModifiedDate": "Modifié le {0}",
"LabelFilename": "Nom de fichier",
@@ -462,7 +480,9 @@
"LabelProviderAuthorizationValue": "Valeur de len-tête dautorisation",
"LabelPubDate": "Date de publication",
"LabelPublishYear": "Année de publication",
"LabelPublishedDate": "{0} publiés",
"LabelPublishedDate": "Publié en {0}",
"LabelPublishedDecade": "Décennie de publication",
"LabelPublishedDecades": "Décennies de publication",
"LabelPublisher": "Éditeur",
"LabelPublishers": "Éditeurs",
"LabelRSSFeedCustomOwnerEmail": "Courriel personnalisée du propriétaire",
@@ -490,13 +510,14 @@
"LabelSeason": "Saison",
"LabelSelectAll": "Tout sélectionner",
"LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
"LabelSelectEpisodesShowing": "Sélectionner {0} episode(s) en cours",
"LabelSelectEpisodesShowing": "Sélectionner {0} épisode(s) en cours",
"LabelSelectUsers": "Sélectionner les utilisateurs",
"LabelSendEbookToDevice": "Envoyer le livre numérique à…",
"LabelSequence": "Séquence",
"LabelSeries": "Séries",
"LabelSeriesName": "Nom de la série",
"LabelSeriesProgress": "Progression de séries",
"LabelServerLogLevel": "Niveau de journalisation du serveur",
"LabelServerYearReview": "Bilan de lannée du serveur ({0})",
"LabelSetEbookAsPrimary": "Définir comme principale",
"LabelSetEbookAsSupplementary": "Définir comme supplémentaire",
@@ -592,6 +613,7 @@
"LabelTitle": "Titre",
"LabelToolsEmbedMetadata": "Métadonnées intégrées",
"LabelToolsEmbedMetadataDescription": "Intègre les métadonnées au fichier audio avec la couverture et les chapitres.",
"LabelToolsM4bEncoder": "Encodeur M4B",
"LabelToolsMakeM4b": "Créer un fichier livre audio M4B",
"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",
@@ -617,6 +639,7 @@
"LabelUploaderDragAndDrop": "Glisser et déposer des fichiers ou dossiers",
"LabelUploaderDropFiles": "Déposer des fichiers",
"LabelUploaderItemFetchMetadataHelp": "Récupérer automatiquement le titre, lauteur et la série",
"LabelUseAdvancedOptions": "Utiliser les options avancées",
"LabelUseChapterTrack": "Utiliser la piste du chapitre",
"LabelUseFullTrack": "Utiliser la piste complète",
"LabelUser": "Utilisateur",
@@ -655,49 +678,51 @@
"MessageChapterErrorStartLtPrev": "Horodatage invalide car il doit débuter au moins après le précédent chapitre",
"MessageChapterStartIsAfter": "Le premier chapitre est situé au début de votre livre audio",
"MessageCheckingCron": "Vérification du cron…",
"MessageConfirmCloseFeed": "Êtes-vous sûr de vouloir fermer ce flux ?",
"MessageConfirmDeleteBackup": "Êtes-vous sûr de vouloir supprimer la sauvegarde de « {0} » ?",
"MessageConfirmDeleteDevice": "Êtes-vous sûr de vouloir supprimer la liseuse « {0} » ?",
"MessageConfirmCloseFeed": "Êtes-vous sûr·e de vouloir fermer ce flux?",
"MessageConfirmDeleteBackup": "Êtes-vous sûr·e de vouloir supprimer la sauvegarde de « {0} »?",
"MessageConfirmDeleteDevice": "Êtes-vous sûr·e de vouloir supprimer la liseuse « {0} » ?",
"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·e de vouloir supprimer définitivement la bibliothèque « {0} »?",
"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": "Cette opération supprimera {0} éléments de la base de données et de votre système de fichiers. Êtes-vous sûr ?",
"MessageConfirmDeleteMetadataProvider": "Êtes-vous sûr de vouloir supprimer le fournisseur de métadonnées personnalisées « {0} » ?",
"MessageConfirmDeleteNotification": "Êtes-vous sûr de vouloir supprimer cette notification?",
"MessageConfirmDeleteSession": "Êtes-vous sûr de vouloir supprimer cette session ?",
"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 ?",
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr de vouloir marquer tous les épisodes comme non terminés ?",
"MessageConfirmMarkItemFinished": "Êtes-vous sûr de vouloir marquer \"{0}\" comme terminé?",
"MessageConfirmMarkItemNotFinished": "Êtes-vous sûr de vouloir marquer \"{0}\" comme non terminé?",
"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 non terminés?",
"MessageConfirmDeleteMetadataProvider": "Êtes-vous sûr·e de vouloir supprimer le fournisseur de métadonnées personnalisées « {0} » ?",
"MessageConfirmDeleteNotification": "Êtes-vous sûr·e de vouloir supprimer cette notification?",
"MessageConfirmDeleteSession": "Êtes-vous sûr·e de vouloir supprimer cette session?",
"MessageConfirmEmbedMetadataInAudioFiles": "Souhaitez-vous vraiment intégrer des métadonnées dans {0} fichiers audio?",
"MessageConfirmForceReScan": "Êtes-vous sûr·e de vouloir lancer une analyse forcée?",
"MessageConfirmMarkAllEpisodesFinished": "Êtes-vous sûr·e de marquer tous les épisodes comme terminés?",
"MessageConfirmMarkAllEpisodesNotFinished": "Êtes-vous sûr·e de vouloir marquer tous les épisodes comme non terminés?",
"MessageConfirmMarkItemFinished": "Êtes-vous sûr·e de vouloir marquer {0} comme terminé?",
"MessageConfirmMarkItemNotFinished": "Êtes-vous sûr·e de vouloir marquer {0} comme non terminé?",
"MessageConfirmMarkSeriesFinished": "Êtes-vous sûr·e de vouloir marquer tous les livres de cette série comme terminées?",
"MessageConfirmMarkSeriesNotFinished": "Êtes-vous sûr·e de vouloir marquer tous les livres de cette série comme non terminés?",
"MessageConfirmNotificationTestTrigger": "Déclencher cette notification avec des données de test?",
"MessageConfirmPurgeCache": "La purge du cache supprimera lintégralité du répertoire à <code>/metadata/cache</code>.<br /><br />Êtes-vous sûr de vouloir supprimer le répertoire de cache?",
"MessageConfirmPurgeCache": "La purge du cache supprimera lintégralité du répertoire à <code>/metadata/cache</code>.<br /><br />Êtes-vous sûr·e de vouloir supprimer le répertoire de cache?",
"MessageConfirmPurgeItemsCache": "Purger le cache des éléments supprimera l'ensemble du répertoire <code>/metadata/cache/items</code>.<br />Êtes-vous sûr?",
"MessageConfirmQuickEmbed": "Attention ! L'intégration rapide ne permet pas de sauvegarder vos fichiers audio. Assurez-vous davoir effectuer une sauvegarde de vos fichiers audio.<br><br>Souhaitez-vous continuer ?",
"MessageConfirmReScanLibraryItems": "Êtes-vous sûr de vouloir re-analyser {0} éléments ?",
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr de vouloir supprimer tous les chapitres ?",
"MessageConfirmRemoveAuthor": "Êtes-vous sûr de vouloir supprimer lauteur « {0} » ?",
"MessageConfirmRemoveCollection": "Êtes-vous sûr de vouloir supprimer la collection « {0} » ?",
"MessageConfirmRemoveEpisode": "Êtes-vous sûr de vouloir supprimer lépisode « {0} » ?",
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr de vouloir supprimer {0} épisodes ?",
"MessageConfirmRemoveListeningSessions": "Êtes-vous sûr de vouloir supprimer {0} sessions découte?",
"MessageConfirmRemoveNarrator": "Êtes-vous sûr de vouloir supprimer le narrateur « {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 éléments ?",
"MessageConfirmReScanLibraryItems": "Êtes-vous sûr·e de vouloir réanalyser {0} éléments?",
"MessageConfirmRemoveAllChapters": "Êtes-vous sûr·e de vouloir supprimer tous les chapitres?",
"MessageConfirmRemoveAuthor": "Êtes-vous sûr·e de vouloir supprimer lauteur « {0} »?",
"MessageConfirmRemoveCollection": "Êtes-vous sûr·e de vouloir supprimer la collection « {0} »?",
"MessageConfirmRemoveEpisode": "Êtes-vous sûr·e de vouloir supprimer lépisode « {0} »?",
"MessageConfirmRemoveEpisodes": "Êtes-vous sûr·e de vouloir supprimer {0} épisodes?",
"MessageConfirmRemoveListeningSessions": "Êtes-vous sûr·e de vouloir supprimer {0} sessions découte?",
"MessageConfirmRemoveNarrator": "Êtes-vous sûr·e de vouloir supprimer le narrateur « {0} »?",
"MessageConfirmRemovePlaylist": "Êtes-vous sûr·e de vouloir supprimer la liste de lecture « {0} »?",
"MessageConfirmRenameGenre": "Êtes-vous sûr·e de vouloir renommer le genre « {0} » en « {1} » pour tous les éléments?",
"MessageConfirmRenameGenreMergeNote": "Information : ce genre existe déjà et sera fusionné.",
"MessageConfirmRenameGenreWarning": "Attention ! Un genre similaire avec une casse différente existe déjà « {0} ».",
"MessageConfirmRenameTag": "Êtes-vous sûr de vouloir renommer létiquette « {0} » en « {1} » pour tous les éléments ?",
"MessageConfirmRenameTag": "Êtes-vous sûr·e de vouloir renommer létiquette « {0} » en « {1} » pour tous les éléments?",
"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} ».",
"MessageConfirmResetProgress": "Êtes-vous sûr de vouloir réinitialiser votre progression?",
"MessageConfirmSendEbookToDevice": "Êtes-vous sûr de vouloir envoyer {0} livre numérique « {1} » à l'appareil « {2} » ?",
"MessageConfirmUnlinkOpenId": "Êtes-vous sûr de vouloir dissocier cet utilisateur dOpenID?",
"MessageConfirmResetProgress": "Êtes-vous sûr·e de vouloir réinitialiser votre progression?",
"MessageConfirmSendEbookToDevice": "Êtes-vous sûr·e de vouloir envoyer {0} livre numérique « {1} » à l'appareil « {2} » ?",
"MessageConfirmUnlinkOpenId": "Êtes-vous sûr·e de vouloir dissocier cet utilisateur dOpenID?",
"MessageDownloadingEpisode": "Téléchargement de lépisode",
"MessageDragFilesIntoTrackOrder": "Faites glisser les fichiers dans lordre correct des pistes",
"MessageEmbedFailed": "Échec de lintégration!",
"MessageEmbedFinished": "Intégration terminée !",
"MessageEmbedQueue": "En file d'attente pour l'intégration des métadonnées ({0} dans la file d'attente)",
"MessageEpisodesQueuedForDownload": "{0} épisode(s) mis en file pour téléchargement",
"MessageEreaderDevices": "Pour garantir lenvoi des livres électroniques, vous devrez peut-être ajouter le courriel ci-dessus comme expéditeur valide pour chaque appareil répertorié ci-dessous.",
"MessageFeedURLWillBe": "LURL du flux sera {0}",
@@ -742,6 +767,7 @@
"MessageNoLogs": "Aucun journaux",
"MessageNoMediaProgress": "Aucun média en cours",
"MessageNoNotifications": "Aucune notification",
"MessageNoPodcastFeed": "Podcast invalide : pas de flux",
"MessageNoPodcastsFound": "Aucun podcast trouvé",
"MessageNoResults": "Aucun résultat",
"MessageNoSearchResultsFor": "Aucun résultat pour la recherche « {0} »",
@@ -758,14 +784,17 @@
"MessagePlaylistCreateFromCollection": "Créer une liste de lecture depuis la collection",
"MessagePleaseWait": "Merci de patienter…",
"MessagePodcastHasNoRSSFeedForMatching": "Le Podcast na pas dURL de flux RSS à utiliser pour la correspondance",
"MessagePodcastSearchField": "Saisissez le terme de recherche ou l'URL du flux RSS",
"MessageQuickEmbedInProgress": "Intégration rapide en cours",
"MessageQuickEmbedQueue": "En file d'attente pour une intégration rapide ({0} dans la file d'attente)",
"MessageQuickMatchDescription": "Renseigne les détails manquants ainsi que la couverture avec la première correspondance de « {0} ». Nécrase pas les données présentes à moins que le paramètre « Préférer les Métadonnées par correspondance » soit activé.",
"MessageRemoveChapter": "Supprimer le chapitre",
"MessageRemoveEpisodes": "Suppression de {0} épisode(s)",
"MessageRemoveFromPlayerQueue": "Supprimer de la liste découte",
"MessageRemoveUserWarning": "Êtes-vous sûr de vouloir supprimer définitivement lutilisateur « {0} » ?",
"MessageRemoveUserWarning": "Êtes-vous sûr·e de vouloir supprimer définitivement lutilisateur « {0} »?",
"MessageReportBugsAndContribute": "Signalez des anomalies, demandez des fonctionnalités et contribuez sur",
"MessageResetChaptersConfirm": "Êtes-vous sûr de vouloir réinitialiser les chapitres et annuler les changements effectués ?",
"MessageRestoreBackupConfirm": "Êtes-vous sûr de vouloir restaurer la sauvegarde créée le",
"MessageResetChaptersConfirm": "Êtes-vous sûr·e de vouloir réinitialiser les chapitres et annuler les changements effectués?",
"MessageRestoreBackupConfirm": "Êtes-vous sûr·e 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.",
"MessageSearchResultsFor": "Résultats de recherche pour",
"MessageSelected": "{0} sélectionnés",
@@ -775,6 +804,41 @@
"MessageShareExpiresIn": "Expire dans {0}",
"MessageShareURLWillBe": "Ladresse de partage sera <strong>{0}</strong>",
"MessageStartPlaybackAtTime": "Démarrer la lecture pour « {0} » à {1} ?",
"MessageTaskAudioFileNotWritable": "Le fichier audio « {0} » nest pas accessible en écriture",
"MessageTaskCanceledByUser": "Tâche annulée par lutilisateur",
"MessageTaskDownloadingEpisodeDescription": "Téléchargement de l'épisode « {0} »",
"MessageTaskEmbeddingMetadata": "Intégration de métadonnées",
"MessageTaskEmbeddingMetadataDescription": "Intégration de métadonnées dans le livre audio « {0} »",
"MessageTaskEncodingM4b": "Encodage M4B",
"MessageTaskEncodingM4bDescription": "Encodage du livre audio « {0} » dans un seul fichier M4B",
"MessageTaskFailed": "Échec",
"MessageTaskFailedToBackupAudioFile": "Échec de la sauvegarde du fichier audio « {0} »",
"MessageTaskFailedToCreateCacheDirectory": "Échec de la création du répertoire de cache",
"MessageTaskFailedToEmbedMetadataInFile": "Échec de l'intégration des métadonnées dans le fichier « {0} »",
"MessageTaskFailedToMergeAudioFiles": "Échec de la fusion des fichiers audio",
"MessageTaskFailedToMoveM4bFile": "Échec du déplacement du fichier M4B",
"MessageTaskFailedToWriteMetadataFile": "Échec de lécriture du fichier de métadonnées",
"MessageTaskMatchingBooksInLibrary": "Livres correspondants dans la bibliothèque « {0} »",
"MessageTaskNoFilesToScan": "Aucun fichier à analyser",
"MessageTaskOpmlImport": "Importation OPML",
"MessageTaskOpmlImportDescription": "Création de podcasts à partir de {0} flux RSS",
"MessageTaskOpmlImportFeed": "Flux dimportation OPML",
"MessageTaskOpmlImportFeedDescription": "Importation du flux RSS « {0} »",
"MessageTaskOpmlImportFeedFailed": "Échec de lobtention du flux de podcast",
"MessageTaskOpmlImportFeedPodcastDescription": "Création du podcast « {0} »",
"MessageTaskOpmlImportFeedPodcastExists": "Le podcast existe déjà à cet emplacement",
"MessageTaskOpmlImportFeedPodcastFailed": "Échec de la création du podcast",
"MessageTaskOpmlImportFinished": "Ajout de {0} podcasts",
"MessageTaskOpmlParseFailed": "Échec de l'analyse du fichier OPML",
"MessageTaskOpmlParseFastFail": "Balise <opml> de fichier OPML non valide introuvable OU une balise <outline> na pas été trouvée",
"MessageTaskOpmlParseNoneFound": "Aucun flux trouvé dans le fichier OPML",
"MessageTaskScanItemsAdded": "{0} ajouté",
"MessageTaskScanItemsMissing": "{0} manquant",
"MessageTaskScanItemsUpdated": "{0} mis à jour",
"MessageTaskScanNoChangesNeeded": "Aucun changement nécessaire",
"MessageTaskScanningFileChanges": "Analyse des modifications du fichier dans « {0} »",
"MessageTaskScanningLibrary": "Analyse de la bibliothèque « {0} »",
"MessageTaskTargetDirectoryNotWritable": "Le répertoire cible nest pas accessible en écriture",
"MessageThinking": "Je cherche…",
"MessageUploaderItemFailed": "Échec du téléversement",
"MessageUploaderItemSuccess": "Téléversement effectué !",
@@ -792,6 +856,10 @@
"NoteUploaderFoldersWithMediaFiles": "Les dossiers contenant des fichiers multimédias seront traités comme des éléments distincts de la bibliothèque.",
"NoteUploaderOnlyAudioFiles": "Si vous téléversez uniquement des fichiers audio, chaque fichier audio sera traité comme un livre audio distinct.",
"NoteUploaderUnsupportedFiles": "Les fichiers non pris en charge sont ignorés. Lorsque vous choisissez ou déposez un dossier, les autres fichiers qui ne sont pas dans un dossier délément sont ignorés.",
"NotificationOnBackupCompletedDescription": "Déclenché lorsquune sauvegarde est terminée",
"NotificationOnBackupFailedDescription": "Déclenché lorsqu'une sauvegarde échoue",
"NotificationOnEpisodeDownloadedDescription": "Déclenché lorsquun épisode de podcast est téléchargé automatiquement",
"NotificationOnTestDescription": "Événement pour tester le système de notification",
"PlaceholderNewCollection": "Nom de la nouvelle collection",
"PlaceholderNewFolderPath": "Nouveau chemin de dossier",
"PlaceholderNewPlaylist": "Nouveau nom de liste de lecture",
@@ -815,14 +883,12 @@
"StatsTopNarrators": "TOP NARRATEURS",
"StatsTotalDuration": "Pour une durée totale de…",
"StatsYearInReview": "BILAN DE LANNÉE",
"ToastAccountUpdateFailed": "Échec de la mise à jour du compte",
"ToastAccountUpdateSuccess": "Compte mis à jour",
"ToastAppriseUrlRequired": "Vous devez entrer une URL Apprise",
"ToastAuthorImageRemoveSuccess": "Image de lauteur supprimée",
"ToastAuthorNotFound": "Auteur \"{0}\" non trouvé",
"ToastAuthorRemoveSuccess": "Auteur supprimé",
"ToastAuthorSearchNotFound": "Auteur non trouvé",
"ToastAuthorUpdateFailed": "Échec de la mise à jour de lauteur",
"ToastAuthorUpdateMerged": "Auteur fusionné",
"ToastAuthorUpdateSuccess": "Auteur mis à jour",
"ToastAuthorUpdateSuccessNoImageFound": "Auteur mis à jour (aucune image trouvée)",
@@ -833,7 +899,6 @@
"ToastBackupDeleteSuccess": "Sauvegarde supprimée",
"ToastBackupInvalidMaxKeep": "Nombre de sauvegardes à conserver invalide",
"ToastBackupInvalidMaxSize": "Taille maximale de sauvegarde invalide",
"ToastBackupPathUpdateFailed": "Échec de la mise à jour du chemin de sauvegarde",
"ToastBackupRestoreFailed": "Échec de la restauration de sauvegarde",
"ToastBackupUploadFailed": "Échec du téléversement de sauvegarde",
"ToastBackupUploadSuccess": "Sauvegarde téléversée",
@@ -844,7 +909,6 @@
"ToastBookmarkCreateFailed": "Échec de la création de signet",
"ToastBookmarkCreateSuccess": "Signet ajouté",
"ToastBookmarkRemoveSuccess": "Signet supprimé",
"ToastBookmarkUpdateFailed": "Échec de la mise à jour de signet",
"ToastBookmarkUpdateSuccess": "Signet mis à jour",
"ToastCachePurgeFailed": "Échec de la purge du cache",
"ToastCachePurgeSuccess": "Cache purgé avec succès",
@@ -855,7 +919,6 @@
"ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi",
"ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection",
"ToastCollectionRemoveSuccess": "Collection supprimée",
"ToastCollectionUpdateFailed": "Échec de la mise à jour de la collection",
"ToastCollectionUpdateSuccess": "Collection mise à jour",
"ToastCoverUpdateFailed": "Échec de la mise à jour de la couverture",
"ToastDeleteFileFailed": "Échec de la suppression du fichier",
@@ -864,8 +927,6 @@
"ToastDeviceNameAlreadyExists": "Un appareil de lecture avec ce nom existe déjà",
"ToastDeviceTestEmailFailed": "Échec de lenvoi du courriel de test",
"ToastDeviceTestEmailSuccess": "Courriel de test envoyé",
"ToastDeviceUpdateFailed": "Échec de la mise à jour",
"ToastEmailSettingsUpdateFailed": "Échec de la mise à jour des paramètres de messagerie",
"ToastEmailSettingsUpdateSuccess": "Paramètres de messagerie mis à jour",
"ToastEncodeCancelFailed": "Échec de lannulation de lencodage",
"ToastEncodeCancelSucces": "Encodage annulé",
@@ -874,21 +935,17 @@
"ToastErrorCannotShare": "Impossible de partager nativement sur cet appareil",
"ToastFailedToLoadData": "Échec du chargement des données",
"ToastFailedToShare": "Échec du partage",
"ToastFailedToUpdateAccount": "Échec de la mise à jour du compte",
"ToastFailedToUpdateUser": "La mise a jour de l'utilisateur à échouée",
"ToastFailedToUpdate": "Échec de la mise à jour",
"ToastInvalidImageUrl": "URL de l'image invalide",
"ToastInvalidUrl": "URL invalide",
"ToastItemCoverUpdateFailed": "Échec de la mise à jour de la couverture de lélément",
"ToastItemCoverUpdateSuccess": "Couverture mise à jour",
"ToastItemDeletedFailed": "La suppression de l'élément à échouée",
"ToastItemDeletedSuccess": "Élément supprimé",
"ToastItemDetailsUpdateFailed": "Échec de la mise à jour des détails de lélément",
"ToastItemDetailsUpdateSuccess": "Détails de lélément mis à jour",
"ToastItemMarkedAsFinishedFailed": "Échec de lannotation terminée",
"ToastItemMarkedAsFinishedSuccess": "Article marqué comme terminé",
"ToastItemMarkedAsNotFinishedFailed": "Échec de lannotation non-terminée",
"ToastItemMarkedAsNotFinishedSuccess": "Article marqué comme non-terminé",
"ToastItemUpdateFailed": "La mise a jour de lélément à échoué",
"ToastItemUpdateSuccess": "Élément mis a jour",
"ToastLibraryCreateFailed": "Échec de la création de bibliothèque",
"ToastLibraryCreateSuccess": "Bibliothèque « {0} » créée",
@@ -896,31 +953,28 @@
"ToastLibraryDeleteSuccess": "Bibliothèque supprimée",
"ToastLibraryScanFailedToStart": "Échec du démarrage de lanalyse",
"ToastLibraryScanStarted": "Analyse de la bibliothèque démarrée",
"ToastLibraryUpdateFailed": "Échec de la mise à jour de la bibliothèque",
"ToastLibraryUpdateSuccess": "Bibliothèque « {0} » mise à jour",
"ToastMatchAllAuthorsFailed": "Tous les auteurs et autrices nont pas pu être classés",
"ToastNameEmailRequired": "Le nom et le courriel sont requis",
"ToastNameRequired": "Le nom est requis",
"ToastNewUserCreatedFailed": "La création du compte à échouée: « {0} »",
"ToastNewUserCreatedSuccess": "Nouveau compte créé",
"ToastNewUserLibraryError": "Au moins une bibliothèque est requise",
"ToastNewUserPasswordError": "Un mot de passe est requis, seul lutilisateur root peut avoir un mot de passe vide",
"ToastNewUserTagError": "Au moins un tag est requis",
"ToastNewUserTagError": "Au moins une étiquette est requise",
"ToastNewUserUsernameError": "Entrez un nom dutilisateur",
"ToastNoUpdatesNecessary": "Aucune mise à jour nécessaire",
"ToastNotificationCreateFailed": "La création de la notification à échouée",
"ToastNotificationDeleteFailed": "La suppression de la notification à échouée",
"ToastNotificationFailedMaximum": "Le nombre maximum de tentatives échouées doit être >= 0",
"ToastNotificationQueueMaximum": "Le nombre de notification maximum doit être >= 0",
"ToastNotificationSettingsUpdateFailed": "La mise a jour des paramètres de notification a échouée",
"ToastNotificationSettingsUpdateSuccess": "Paramètres de notification mis à jour",
"ToastNotificationTestTriggerFailed": "L'envoi de la notification de test à échoué",
"ToastNotificationTestTriggerSuccess": "Notification de test déclenchée",
"ToastNotificationUpdateFailed": "Échec de la mise à jour de la notification",
"ToastNotificationUpdateSuccess": "Notification mise à jour",
"ToastPlaylistCreateFailed": "Échec de la création de la liste de lecture",
"ToastPlaylistCreateSuccess": "Liste de lecture créée",
"ToastPlaylistRemoveSuccess": "Liste de lecture supprimée",
"ToastPlaylistUpdateFailed": "Échec de la mise à jour de la liste de lecture",
"ToastPlaylistUpdateSuccess": "Liste de lecture mise à jour",
"ToastPodcastCreateFailed": "Échec de la création du podcast",
"ToastPodcastCreateSuccess": "Podcast créé avec succès",
@@ -949,7 +1003,6 @@
"ToastSendEbookToDeviceSuccess": "Livre numérique envoyé à lappareil : {0}",
"ToastSeriesUpdateFailed": "Échec de la mise à jour de la série",
"ToastSeriesUpdateSuccess": "Mise à jour de la série réussie",
"ToastServerSettingsUpdateFailed": "Échec de la mise à jour des paramètres du serveur",
"ToastServerSettingsUpdateSuccess": "Mise à jour des paramètres du serveur",
"ToastSessionCloseFailed": "Échec de la fermeture de la session",
"ToastSessionDeleteFailed": "Échec de la suppression de session",
@@ -960,7 +1013,6 @@
"ToastSocketDisconnected": "WebSocket déconnecté",
"ToastSocketFailedToConnect": "Échec de la connexion WebSocket",
"ToastSortingPrefixesEmptyError": "Doit avoir au moins 1 préfixe de tri",
"ToastSortingPrefixesUpdateFailed": "Échec de la mise à jour des préfixes de tri",
"ToastSortingPrefixesUpdateSuccess": "Mise à jour des préfixes de tri ({0} élément)",
"ToastTitleRequired": "Le titre est requis",
"ToastUnknownError": "Erreur inconnue",

View File

@@ -701,10 +701,8 @@
"PlaceholderNewPlaylist": "שם רשימת השמעה חדשה",
"PlaceholderSearch": "חיפוש..",
"PlaceholderSearchEpisode": "חיפוש פרק..",
"ToastAccountUpdateFailed": "עדכון חשבון נכשל",
"ToastAccountUpdateSuccess": "חשבון עודכן בהצלחה",
"ToastAuthorImageRemoveSuccess": "תמונת המחבר הוסרה בהצלחה",
"ToastAuthorUpdateFailed": "עדכון המחבר נכשל",
"ToastAuthorUpdateMerged": "המחבר מוזג",
"ToastAuthorUpdateSuccess": "המחבר עודכן בהצלחה",
"ToastAuthorUpdateSuccessNoImageFound": "המחבר עודכן (תמונה לא נמצאה)",
@@ -720,17 +718,13 @@
"ToastBookmarkCreateFailed": "יצירת סימניה נכשלה",
"ToastBookmarkCreateSuccess": "הסימניה נוספה בהצלחה",
"ToastBookmarkRemoveSuccess": "הסימניה הוסרה בהצלחה",
"ToastBookmarkUpdateFailed": "עדכון הסימניה נכשל",
"ToastBookmarkUpdateSuccess": "הסימניה עודכנה בהצלחה",
"ToastChaptersHaveErrors": "פרקים מכילים שגיאות",
"ToastChaptersMustHaveTitles": "פרקים חייבים לכלול כותרות",
"ToastCollectionItemsRemoveSuccess": "הפריט(ים) הוסרו מהאוסף בהצלחה",
"ToastCollectionRemoveSuccess": "האוסף הוסר בהצלחה",
"ToastCollectionUpdateFailed": "עדכון האוסף נכשל",
"ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה",
"ToastItemCoverUpdateFailed": "עדכון כריכת הפריט נכשל",
"ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה",
"ToastItemDetailsUpdateFailed": "עדכון פרטי הפריט נכשל",
"ToastItemDetailsUpdateSuccess": "פרטי הפריט עודכנו בהצלחה",
"ToastItemMarkedAsFinishedFailed": "סימון כפריט כהושלם נכשל",
"ToastItemMarkedAsFinishedSuccess": "הפריט סומן כהושלם בהצלחה",
@@ -742,12 +736,10 @@
"ToastLibraryDeleteSuccess": "הספרייה נמחקה בהצלחה",
"ToastLibraryScanFailedToStart": "הפעלת הסריקה נכשלה",
"ToastLibraryScanStarted": "הסריקה של הספרייה החלה",
"ToastLibraryUpdateFailed": "עדכון הספרייה נכשל",
"ToastLibraryUpdateSuccess": "הספרייה \"{0}\" עודכנה בהצלחה",
"ToastPlaylistCreateFailed": "יצירת רשימת השמעה נכשלה",
"ToastPlaylistCreateSuccess": "רשימת השמעה נוצרה בהצלחה",
"ToastPlaylistRemoveSuccess": "רשימת השמעה הוסרה בהצלחה",
"ToastPlaylistUpdateFailed": "עדכון רשימת השמעה נכשל",
"ToastPlaylistUpdateSuccess": "רשימת השמעה עודכנה בהצלחה",
"ToastPodcastCreateFailed": "יצירת הפודקאסט נכשלה",
"ToastPodcastCreateSuccess": "הפודקאסט נוצר בהצלחה",
@@ -759,7 +751,6 @@
"ToastSendEbookToDeviceSuccess": "הספר נשלח אל המכשיר \"{0}\"",
"ToastSeriesUpdateFailed": "עדכון הסדרה נכשל",
"ToastSeriesUpdateSuccess": "הסדרה עודכנה בהצלחה",
"ToastServerSettingsUpdateFailed": "כשל בעדכון הגדרות שרת",
"ToastServerSettingsUpdateSuccess": "הגדרות שרת עודכנו בהצלחה",
"ToastSessionDeleteFailed": "מחיקת הפעולה נכשלה",
"ToastSessionDeleteSuccess": "הפעולה נמחקה בהצלחה",

View File

@@ -56,6 +56,7 @@
"ButtonOpenManager": "Otvori Upravitelja",
"ButtonPause": "Pauziraj",
"ButtonPlay": "Reproduciraj",
"ButtonPlayAll": "Reproduciraj sve",
"ButtonPlaying": "Izvodi se",
"ButtonPlaylists": "Popisi za izvođenje",
"ButtonPrevious": "Prethodno",
@@ -65,6 +66,7 @@
"ButtonPurgeItemsCache": "Isprazni predmemoriju stavki",
"ButtonQueueAddItem": "Dodaj u red",
"ButtonQueueRemoveItem": "Ukloni iz reda",
"ButtonQuickEmbed": "Brzo ugrađivanje",
"ButtonQuickEmbedMetadata": "Brzo ugrađivanje meta-podataka",
"ButtonQuickMatch": "Brzo prepoznavanje",
"ButtonReScan": "Ponovno skeniraj",
@@ -80,7 +82,7 @@
"ButtonRemoveSeriesFromContinueSeries": "Ukloni seriju iz Nastavi seriju",
"ButtonReset": "Poništi",
"ButtonResetToDefault": "Vrati na početne postavke",
"ButtonRestore": "Povrati",
"ButtonRestore": "Vraćanje",
"ButtonSave": "Spremi",
"ButtonSaveAndClose": "Spremi i zatvori",
"ButtonSaveTracklist": "Spremi popis zvučnih zapisa",
@@ -96,8 +98,9 @@
"ButtonStartM4BEncode": "Pokreni M4B kodiranje",
"ButtonStartMetadataEmbed": "Pokreni ugradnju meta-podataka",
"ButtonStats": "Statistika",
"ButtonSubmit": "Podnesi",
"ButtonSubmit": "Pošalji",
"ButtonTest": "Test",
"ButtonUnlinkOpenId": "Prekini vezu s OpenID-jem",
"ButtonUpload": "Učitaj",
"ButtonUploadBackup": "Učitaj sigurnosnu kopiju",
"ButtonUploadCover": "Učitaj naslovnicu",
@@ -160,6 +163,7 @@
"HeaderNotificationUpdate": "Ažuriraj obavijest",
"HeaderNotifications": "Obavijesti",
"HeaderOpenIDConnectAuthentication": "Prijava na OpenID Connect",
"HeaderOpenListeningSessions": "Otvorene sesije slušanja",
"HeaderOpenRSSFeed": "Otvori RSS izvor",
"HeaderOtherFiles": "Druge datoteke",
"HeaderPasswordAuthentication": "Provjera autentičnosti zaporkom",
@@ -177,6 +181,7 @@
"HeaderRemoveEpisodes": "Ukloni {0} nastavaka",
"HeaderSavedMediaProgress": "Spremljen napredak medija",
"HeaderSchedule": "Zakazivanje",
"HeaderScheduleEpisodeDownloads": "Zakazivanje automatskog preuzimanja nastavaka",
"HeaderScheduleLibraryScans": "Zakaži automatsko skeniranje knjižnice",
"HeaderSession": "Sesija",
"HeaderSetBackupSchedule": "Zakazivanje sigurnosne pohrane",
@@ -222,7 +227,11 @@
"LabelAllUsersExcludingGuests": "Svi korisnici osim gostiju",
"LabelAllUsersIncludingGuests": "Svi korisnici uključujući i goste",
"LabelAlreadyInYourLibrary": "Već u vašoj knjižnici",
"LabelApiToken": "API Token",
"LabelAppend": "Pridodaj",
"LabelAudioBitrate": "Kvaliteta zvučnog zapisa (npr. 128k)",
"LabelAudioChannels": "Broj zvučnih kanala (1 ili 2)",
"LabelAudioCodec": "Zvučni kodek",
"LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (Ime Prezime)",
"LabelAuthorLastFirst": "Autor (Prezime, Ime)",
@@ -235,37 +244,41 @@
"LabelAutoRegister": "Automatska registracija",
"LabelAutoRegisterDescription": "Automatski izradi nove korisnike nakon prijave",
"LabelBackToUser": "Povratak na korisnika",
"LabelBackupAudioFiles": "Sigurnosno kopiranje zvučnih datoteka",
"LabelBackupLocation": "Lokacija sigurnosnih kopija",
"LabelBackupsEnableAutomaticBackups": "Uključi automatsku izradu sigurnosnih kopija",
"LabelBackupsEnableAutomaticBackups": "Omogući automatsku izradu sigurnosnih kopija",
"LabelBackupsEnableAutomaticBackupsHelp": "Sigurnosne kopije spremaju se u /metadata/backups",
"LabelBackupsMaxBackupSize": "Maksimalna veličina sigurnosne kopije (u GB) (0 za neograničeno)",
"LabelBackupsMaxBackupSizeHelp": "U svrhu sprečavanja izrade krive konfiguracije, sigurnosne kopije neće se izraditi ako su veće od zadane veličine.",
"LabelBackupsNumberToKeep": "Broj sigurnosnih kopija za čuvanje",
"LabelBackupsNumberToKeepHelp": "Moguće je izbrisati samo jednu po jednu sigurnosnu kopiju, ako ih već imate više trebat ćete ih ručno ukloniti.",
"LabelBitrate": "Protok",
"LabelBooks": "knjiga/e",
"LabelBonus": "Bonus",
"LabelBooks": "Knjige",
"LabelButtonText": "Tekst gumba",
"LabelByAuthor": "po {0}",
"LabelChangePassword": "Promijeni lozinku",
"LabelByAuthor": "autor: {0}",
"LabelChangePassword": "Promijeni zaporku",
"LabelChannels": "Kanali",
"LabelChapterCount": "{0} Poglavlje/a",
"LabelChapterTitle": "Naslov poglavlja",
"LabelChapters": "Poglavlja",
"LabelChaptersFound": "poglavlja pronađeno",
"LabelClickForMoreInfo": "Kliknite za više informacija",
"LabelClickToUseCurrentValue": "Kliknite za trenutnu vrijednost",
"LabelClosePlayer": "Zatvori reproduktor",
"LabelCodec": "Kodek",
"LabelCollapseSeries": "Serijale prikaži sažeto",
"LabelCollapseSubSeries": "Podserijale prikaži sažeto",
"LabelCollection": "Zbirka",
"LabelCollections": "Zbirka/i",
"LabelCollections": "Zbirke",
"LabelComplete": "Dovršeno",
"LabelConfirmPassword": "Potvrdi lozinku",
"LabelConfirmPassword": "Potvrda zaporke",
"LabelContinueListening": "Nastavi slušati",
"LabelContinueReading": "Nastavi čitati",
"LabelContinueSeries": "Nastavi serijal",
"LabelCover": "Naslovnica",
"LabelCoverImageURL": "URL naslovnice",
"LabelCreatedAt": "Stvoreno",
"LabelCreatedAt": "Izrađen",
"LabelCronExpression": "Cron izraz",
"LabelCurrent": "Trenutan",
"LabelCurrently": "Trenutno:",
@@ -301,12 +314,25 @@
"LabelEmailSettingsTestAddress": "Probna adresa",
"LabelEmbeddedCover": "Ugrađena naslovnica",
"LabelEnable": "Omogući",
"LabelEncodingBackupLocation": "Sigurnosna kopija vaših izvornih zvučnih datoteka čuvat će se u mapi:",
"LabelEncodingChaptersNotEmbedded": "Poglavlja se ne ugrađuju u zvučne knjige koje se sastoje od više zvučnih zapisa.",
"LabelEncodingClearItemCache": "Svakako redovito praznite predmemoriju stavki.",
"LabelEncodingFinishedM4B": "Stvorene M4B datoteke spremit će se u vašu mapu sa zvučnim knjigama:",
"LabelEncodingInfoEmbedded": "Meta-podatci će se ugraditi u zvučne zapise u vašoj mapi sa zvučnim knjigama.",
"LabelEncodingStartedNavigation": "Nakon pokretanja zadatka možete napustiti ovu stranicu.",
"LabelEncodingTimeWarning": "Kodiranje može potrajati do 30 minuta.",
"LabelEncodingWarningAdvancedSettings": "Pažnja: Ne mijenjajte ove postavke ako niste temeljito upoznati s opcijama kodiranja u ffmpegu.",
"LabelEncodingWatcherDisabled": "Ako vam je onemogućeno praćenje mape, ovu ćete zvučnu knjigu poslije morati ponovno skenirati.",
"LabelEnd": "Kraj",
"LabelEndOfChapter": "Kraj poglavlja",
"LabelEpisode": "Nastavak",
"LabelEpisodeNotLinkedToRssFeed": "Nastavak nije povezan s RSS izvorom",
"LabelEpisodeNumber": "{0}. nastavak",
"LabelEpisodeTitle": "Naslov nastavka",
"LabelEpisodeType": "Vrsta nastavka",
"LabelEpisodeUrlFromRssFeed": "URL nastavka iz RSS izvora",
"LabelEpisodes": "Nastavci",
"LabelEpisodic": "U nastavcima",
"LabelExample": "Primjer",
"LabelExpandSeries": "Serijal prikaži prošireno",
"LabelExpandSubSeries": "Podserijal prikaži prošireno",
@@ -334,6 +360,7 @@
"LabelFontScale": "Veličina slova",
"LabelFontStrikethrough": "Precrtano",
"LabelFormat": "Format",
"LabelFull": "Cijeli",
"LabelGenre": "Žanr",
"LabelGenres": "Žanrovi",
"LabelHardDeleteFile": "Obriši datoteku zauvijek",
@@ -367,7 +394,7 @@
"LabelLanguages": "Jezici",
"LabelLastBookAdded": "Zadnja dodana knjiga",
"LabelLastBookUpdated": "Zadnja ažurirana knjiga",
"LabelLastSeen": "Zadnje pogledano",
"LabelLastSeen": "Zadnji puta viđen",
"LabelLastTime": "Zadnji puta",
"LabelLastUpdate": "Zadnje ažuriranje",
"LabelLayout": "Prikaz",
@@ -389,6 +416,10 @@
"LabelLowestPriority": "Najniži prioritet",
"LabelMatchExistingUsersBy": "Prepoznaj postojeće korisnike pomoću",
"LabelMatchExistingUsersByDescription": "Rabi se za povezivanje postojećih korisnika. Nakon što se spoje, korisnike se prepoznaje temeljem jedinstvene oznake vašeg pružatelja SSO usluga",
"LabelMaxEpisodesToDownload": "Najveći broj nastavaka za preuzimanje. 0 za neograničeno.",
"LabelMaxEpisodesToDownloadPerCheck": "Najviše novih nastavaka za preuzimanje po provjeri",
"LabelMaxEpisodesToKeep": "Najviše nastavaka za čuvanje",
"LabelMaxEpisodesToKeepHelp": "Ako je vrijednost 0, nema ograničenja broja. Nakon automatskog preuzimanja novog nastavka ova funkcija briše najstariji nastavak ako ih ima više od zadanog broja. Ovo briše samo jedan nastavak po novom preuzetom nastavku.",
"LabelMediaPlayer": "Reproduktor medijskih sadržaja",
"LabelMediaType": "Vrsta medija",
"LabelMetaTag": "Meta oznaka",
@@ -408,10 +439,10 @@
"LabelNarrator": "Pripovjedač",
"LabelNarrators": "Pripovjedači",
"LabelNew": "Novo",
"LabelNewPassword": "Nova lozinka",
"LabelNewPassword": "Nova zaporka",
"LabelNewestAuthors": "Najnoviji autori",
"LabelNewestEpisodes": "Najnoviji nastavci",
"LabelNextBackupDate": "Sljedeće izrada sigurnosne kopije",
"LabelNewestEpisodes": "Najnovije epizode",
"LabelNextBackupDate": "Sljedeća izrada sigurnosne kopije",
"LabelNextScheduledRun": "Sljedeće zakazano izvođenje",
"LabelNoCustomMetadataProviders": "Nema prilagođenih pružatelja meta-podataka",
"LabelNoEpisodesSelected": "Nema odabranih nastavaka",
@@ -434,12 +465,14 @@
"LabelOpenIDGroupClaimDescription": "Naziv OpenID zahtjeva koji sadrži popis korisnikovih grupa. Često se naziva <code>groups</code>. <b>Ako se konfigurira</b>, aplikacija će automatski dodijeliti uloge temeljem korisnikovih članstava u grupama, pod uvjetom da se iste zovu 'admin', 'user' ili 'guest' u zahtjevu (ne razlikuju se velika i mala slova). Zahtjev treba sadržavati popis i ako je korisnik član više grupa, aplikacija će dodijeliti ulogu koja odgovara najvišoj razini pristupa. Ukoliko se niti jedna grupa ne podudara, pristup će biti onemogućen.",
"LabelOpenRSSFeed": "Otvori RSS Feed",
"LabelOverwrite": "Prepiši",
"LabelPaginationPageXOfY": "Stranica {0} od {1}",
"LabelPassword": "Zaporka",
"LabelPath": "Putanja",
"LabelPermanent": "Trajno",
"LabelPermissionsAccessAllLibraries": "Ima pristup svim knjižnicama",
"LabelPermissionsAccessAllTags": "Ima pristup svim oznakama",
"LabelPermissionsAccessExplicitContent": "Ima pristup eksplicitnom sadržzaju",
"LabelPermissionsAccessExplicitContent": "Ima pristup eksplicitnom sadržaju",
"LabelPermissionsCreateEreader": "Može stvoriti e-čitač",
"LabelPermissionsDelete": "Smije brisati",
"LabelPermissionsDownload": "Smije preuzimati",
"LabelPermissionsUpdate": "Smije ažurirati",
@@ -461,8 +494,10 @@
"LabelProvider": "Dobavljač",
"LabelProviderAuthorizationValue": "Vrijednost autorizacijskog zaglavlja",
"LabelPubDate": "Datum izdavanja",
"LabelPublishYear": "Godina izdavanja",
"LabelPublishYear": "Godina objavljivanja",
"LabelPublishedDate": "Objavljeno {0}",
"LabelPublishedDecade": "Desetljeće izdanja",
"LabelPublishedDecades": "Desetljeća izdanja",
"LabelPublisher": "Izdavač",
"LabelPublishers": "Izdavači",
"LabelRSSFeedCustomOwnerEmail": "Prilagođena adresa e-pošte vlasnika",
@@ -482,21 +517,28 @@
"LabelRedo": "Ponovi",
"LabelRegion": "Regija",
"LabelReleaseDate": "Datum izlaska",
"LabelRemoveAllMetadataAbs": "Ukloni sve datoteke metadata.abs",
"LabelRemoveAllMetadataJson": "Ukloni sve datoteke metadata.json",
"LabelRemoveCover": "Ukloni naslovnicu",
"LabelRemoveMetadataFile": "Ukloni datoteke s meta-podatcima iz mapa knjižničkih stavki",
"LabelRemoveMetadataFileHelp": "Uklanjanje svih datoteka metadata.json i metadata.abs u vaših {0} mapa.",
"LabelRowsPerPage": "Redaka po stranici",
"LabelSearchTerm": "Traži pojam",
"LabelSearchTitle": "Traži naslov",
"LabelSearchTitleOrASIN": "Traži naslov ili ASIN",
"LabelSeason": "Sezona",
"LabelSeasonNumber": "{0}. sezona",
"LabelSelectAll": "Označi sve",
"LabelSelectAllEpisodes": "Označi sve nastavke",
"LabelSelectEpisodesShowing": "Prikazujem {0} odabranih nastavaka",
"LabelSelectUsers": "Označi korisnike",
"LabelSendEbookToDevice": "Pošalji e-knjigu",
"LabelSequence": "Slijed",
"LabelSeries": "Serijal/a",
"LabelSerial": "Serijal",
"LabelSeries": "Serijal",
"LabelSeriesName": "Ime serijala",
"LabelSeriesProgress": "Napredak u serijalu",
"LabelServerLogLevel": "Razina zapisa poslužitelja",
"LabelServerYearReview": "Godišnji pregled poslužitelja ({0})",
"LabelSetEbookAsPrimary": "Postavi kao primarno",
"LabelSetEbookAsSupplementary": "Postavi kao dopunsko",
@@ -521,6 +563,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Serijali koji se sastoje od samo jedne knjige neće se prikazivati na stranici serijala i na policama početne stranice.",
"LabelSettingsHomePageBookshelfView": "Prikaži početnu stranicu kao policu s knjigama",
"LabelSettingsLibraryBookshelfView": "Prikaži knjižnicu kao policu s knjigama",
"LabelSettingsLibraryMarkAsFinishedPercentComplete": "Postotak dovršenosti veći od",
"LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Preostalo vrijeme je manje od (sekundi)",
"LabelSettingsLibraryMarkAsFinishedWhen": "Označi medij dovršenim kada",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Preskoči ranije knjige u funkciji Nastavi serijal",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "Na polici početne stranice Nastavi serijal prikazuje se prva nezapočeta knjiga serijala koji imaju barem jednu dovršenu knjigu i nijednu započetu knjigu. Ako uključite ovu opciju, serijal će vam se nastaviti od zadnje dovršene knjige umjesto od prve nezapočete knjige.",
"LabelSettingsParseSubtitles": "Raščlani podnaslove",
@@ -585,6 +630,7 @@
"LabelTimeDurationXMinutes": "{0} minuta",
"LabelTimeDurationXSeconds": "{0} sekundi",
"LabelTimeInMinutes": "Vrijeme u minutama",
"LabelTimeLeft": "{0} preostalo",
"LabelTimeListened": "Vremena odslušano",
"LabelTimeListenedToday": "Vremena odslušano danas",
"LabelTimeRemaining": "{0} preostalo",
@@ -592,6 +638,7 @@
"LabelTitle": "Naslov",
"LabelToolsEmbedMetadata": "Ugradi meta-podatke",
"LabelToolsEmbedMetadataDescription": "Ugradi meta-podatke u zvučne datoteke zajedno s naslovnicom i poglavljima.",
"LabelToolsM4bEncoder": "M4B kodiranje",
"LabelToolsMakeM4b": "Stvori M4B datoteku audioknjige",
"LabelToolsMakeM4bDescription": "Izrađuje zvučnu knjigu u .M4B formatu s ugrađenim meta-podatcima, naslovnicom i poglavljima.",
"LabelToolsSplitM4b": "Podijeli M4B datoteke u MP3 datoteke",
@@ -600,10 +647,11 @@
"LabelTotalTimeListened": "Sveukupno vrijeme slušanja",
"LabelTrackFromFilename": "Naslov iz imena datoteke",
"LabelTrackFromMetadata": "Naslov iz meta-podataka",
"LabelTracks": "Naslovi",
"LabelTracks": "Zvučni zapisi",
"LabelTracksMultiTrack": "Više zvučnih zapisa",
"LabelTracksNone": "Nema zapisa",
"LabelTracksSingleTrack": "Jedan zvučni zapis",
"LabelTrailer": "Najava",
"LabelType": "Vrsta",
"LabelUnabridged": "Neskraćeno",
"LabelUndo": "Vrati",
@@ -617,8 +665,10 @@
"LabelUploaderDragAndDrop": "Pritisni i prevuci datoteke ili mape",
"LabelUploaderDropFiles": "Ispusti datoteke",
"LabelUploaderItemFetchMetadataHelp": "Automatski dohvati naslov, autora i serijal",
"LabelUseAdvancedOptions": "Koristi se naprednim opcijama",
"LabelUseChapterTrack": "Koristi zvučni zapis poglavlja",
"LabelUseFullTrack": "Koristi cijeli zvučni zapis",
"LabelUseZeroForUnlimited": "0 za neograničeno",
"LabelUser": "Korisnik",
"LabelUsername": "Korisničko ime",
"LabelValue": "Vrijednost",
@@ -639,7 +689,7 @@
"LabelYourProgress": "Vaš napredak",
"MessageAddToPlayerQueue": "Dodaj u redoslijed izvođenja",
"MessageAppriseDescription": "Da biste se koristili ovom značajkom, treba vam instanca <a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">Apprise API-ja</a> ili API koji može rukovati istom vrstom zahtjeva.<br />The Adresa Apprise API-ja treba biti puna URL putanja za slanje obavijesti, npr. ako vam se API instanca poslužuje na adresi <code>http://192.168.1.1:8337</code> trebate upisati <code>http://192.168.1.1:8337/notify</code>.",
"MessageBackupsDescription": "Backups uključuju korisnike, korisnikov napredak, detalje stavki iz biblioteke, postavke server i slike iz <code>/metadata/items</code> & <code>/metadata/authors</code>. Backups ne uključuju nijedne datoteke koje su u folderima biblioteke.",
"MessageBackupsDescription": "Sigurnosne kopije sadrže korisnike, korisnikov napredak medija, pojedinosti knjižničke građe, postavke poslužitelja i slike koje se spremaju u <code>/metadata/items</code> & <code>/metadata/authors</code>. Sigurnosne kopije ne sadrže niti jednu datoteku iz mapa knjižnice.",
"MessageBackupsLocationEditNote": "Napomena: Uređivanje lokacije za sigurnosne kopije ne premješta ili mijenja postojeće sigurnosne kopije",
"MessageBackupsLocationNoEditNote": "Napomena: Lokacija za sigurnosne kopije zadana je kroz varijablu okoline i ovdje se ne može izmijeniti.",
"MessageBackupsLocationPathEmpty": "Putanja do lokacije za sigurnosne kopije ne može ostati prazna",
@@ -653,7 +703,7 @@
"MessageChapterErrorFirstNotZero": "Prvo poglavlje mora započeti u 0",
"MessageChapterErrorStartGteDuration": "Netočno vrijeme početka, mora biti manje od trajanja zvučne knjige",
"MessageChapterErrorStartLtPrev": "Netočno vrijeme početka, mora biti veće ili jednako vremenu početka prethodnog poglavlja",
"MessageChapterStartIsAfter": "Početak poglavlja je nakon kraja zvučne knjige.",
"MessageChapterStartIsAfter": "Početak poglavlja je nakon kraja zvučne knjige",
"MessageCheckingCron": "Provjeravam cron...",
"MessageConfirmCloseFeed": "Sigurno želite zatvoriti ovaj izvor?",
"MessageConfirmDeleteBackup": "Jeste li sigurni da želite obrisati backup za {0}?",
@@ -665,6 +715,7 @@
"MessageConfirmDeleteMetadataProvider": "Sigurno želite izbrisati prilagođenog pružatelja meta-podataka \"{0}\"?",
"MessageConfirmDeleteNotification": "Sigurno želite izbrisati ovu obavijest?",
"MessageConfirmDeleteSession": "Sigurno želite obrisati ovu sesiju?",
"MessageConfirmEmbedMetadataInAudioFiles": "Sigurno želite ugraditi meta-podatke u {0} zvučnih datoteka?",
"MessageConfirmForceReScan": "Sigurno želite ponovno pokrenuti skeniranje?",
"MessageConfirmMarkAllEpisodesFinished": "Sigurno želite označiti sve nastavke dovršenima?",
"MessageConfirmMarkAllEpisodesNotFinished": "Sigurno želite označiti sve nastavke nedovršenima?",
@@ -676,6 +727,7 @@
"MessageConfirmPurgeCache": "Brisanje predmemorije izbrisat će cijelu mapu <code>/metadata/cache</code>. <br /><br />Sigurno želite izbrisati mapu predmemorije?",
"MessageConfirmPurgeItemsCache": "Brisanje predmemorije stavki izbrisat će cijelu mapu <code>/metadata/cache/items</code>.<br />Jeste li sigurni?",
"MessageConfirmQuickEmbed": "Pažnja! Funkcija brzog ugrađivanja ne stvara sigurnosne kopije vaših zvučnih datoteka. Provjerite imate li sigurnosnu kopiju. <br><br>Želite li nastaviti?",
"MessageConfirmQuickMatchEpisodes": "Brzo prepoznavanje nastavaka prepisat će pojedinosti ukoliko se pronađe podudaranje. Neprepoznati nastavci će se ažurirati. Jeste li sigurni?",
"MessageConfirmReScanLibraryItems": "Sigurno želite ponovno skenirati {0} stavki?",
"MessageConfirmRemoveAllChapters": "Sigurno želite ukloniti sva poglavlja?",
"MessageConfirmRemoveAuthor": "Sigurno želite ukloniti autora \"{0}\"?",
@@ -683,6 +735,7 @@
"MessageConfirmRemoveEpisode": "Sigurno želite ukloniti nastavak \"{0}\"?",
"MessageConfirmRemoveEpisodes": "Sigurno želite ukloniti {0} nastavaka?",
"MessageConfirmRemoveListeningSessions": "Sigurno želite ukloniti {0} sesija slušanja?",
"MessageConfirmRemoveMetadataFiles": "Sigurno želite ukloniti sve datoteke metadata.{0} u mapama vaših knjižničkih stavki?",
"MessageConfirmRemoveNarrator": "Sigurno želite ukloniti pripovjedača \"{0}\"?",
"MessageConfirmRemovePlaylist": "Sigurno želite ukloniti vaš popis za izvođenje \"{0}\"?",
"MessageConfirmRenameGenre": "Sigurno želite preimenovati žanr \"{0}\" u \"{1}\" za sve stavke?",
@@ -695,9 +748,10 @@
"MessageConfirmSendEbookToDevice": "Sigurno želite poslati {0} e-knjiga/u \"{1}\" na uređaj \"{2}\"?",
"MessageConfirmUnlinkOpenId": "Sigurno želite odspojiti ovog korisnika s OpenID-ja?",
"MessageDownloadingEpisode": "Preuzimam nastavak",
"MessageDragFilesIntoTrackOrder": "Ispravi redoslijed zapisa prevlačenje datoteka",
"MessageDragFilesIntoTrackOrder": "Prevlačenjem datoteka složite pravilan redoslijed",
"MessageEmbedFailed": "Ugrađivanje nije uspjelo!",
"MessageEmbedFinished": "Ugrađivanje je dovršeno!",
"MessageEmbedQueue": "Ugrađivanje meta-podataka dodano u red obrade ({0} u redu)",
"MessageEpisodesQueuedForDownload": "{0} nastavak(a) u redu za preuzimanje",
"MessageEreaderDevices": "Da biste osigurali isporuku e-knjiga, možda ćete morati gornju adresu e-pošte dodati kao dopuštenog pošiljatelja za svaki od donjih uređaja.",
"MessageFeedURLWillBe": "URL izvora bit će {0}",
@@ -742,6 +796,7 @@
"MessageNoLogs": "Nema zapisnika",
"MessageNoMediaProgress": "Nema podataka o započetim medijima",
"MessageNoNotifications": "Nema obavijesti",
"MessageNoPodcastFeed": "Neispravan podcast: Nema izvora",
"MessageNoPodcastsFound": "Nije pronađen niti jedan podcast",
"MessageNoResults": "Nema rezultata",
"MessageNoSearchResultsFor": "Nema rezultata pretrage za \"{0}\"",
@@ -758,6 +813,10 @@
"MessagePlaylistCreateFromCollection": "Stvori popis za izvođenje od zbirke",
"MessagePleaseWait": "Molimo pričekajte...",
"MessagePodcastHasNoRSSFeedForMatching": "Podcast nema adresu RSS izvora za prepoznavanje",
"MessagePodcastSearchField": "Unesite upit za pretragu ili URL RSS izvora",
"MessageQuickEmbedInProgress": "Brzo ugrađivanje u tijeku",
"MessageQuickEmbedQueue": "Dodano u red za brzo ugrađivanje ({0} u redu izvođenja)",
"MessageQuickMatchAllEpisodes": "Brzo prepoznavanje svih nastavaka",
"MessageQuickMatchDescription": "Popuni pojedinosti i naslovnice koji nedostaju prvim pronađenim rezultatom za '{0}'. Ne prepisuje podatke osim ako ne uključite mogućnost 'Daj prednost meta-podatcima prepoznatih stavki'.",
"MessageRemoveChapter": "Ukloni poglavlje",
"MessageRemoveEpisodes": "Ukloni {0} nastavaka",
@@ -775,6 +834,41 @@
"MessageShareExpiresIn": "Istječe za {0}",
"MessageShareURLWillBe": "URL za dijeljenje bit će <strong>{0}</strong>",
"MessageStartPlaybackAtTime": "Pokreni reprodukciju za \"{0}\" na {1}?",
"MessageTaskAudioFileNotWritable": "U zvučnu datoteku \"{0}\" nije moguće pisati",
"MessageTaskCanceledByUser": "Korisnik je otkazao izvršavanje zadatka",
"MessageTaskDownloadingEpisodeDescription": "Preuzimanje nastavka \"{0}\"",
"MessageTaskEmbeddingMetadata": "Ugrađivanje meta-podataka",
"MessageTaskEmbeddingMetadataDescription": "Ugrađivanje meta-podataka u zvučnu knjigu \"{0}\"",
"MessageTaskEncodingM4b": "Kodiranje M4B datoteke",
"MessageTaskEncodingM4bDescription": "Kodiranje zvučne knjige \"{0}\" u jedinstvenu m4b datoteku",
"MessageTaskFailed": "Nije uspjelo",
"MessageTaskFailedToBackupAudioFile": "Izrada sigurnosne kopije zvučne datoteke \"{0}\" nije uspjela",
"MessageTaskFailedToCreateCacheDirectory": "Izrada mape predmemorije nije uspjela",
"MessageTaskFailedToEmbedMetadataInFile": "Ugradnja meta-podataka u datoteku \"{0}\" nije uspjela",
"MessageTaskFailedToMergeAudioFiles": "Spajanje zvučnih datoteka nije uspjelo",
"MessageTaskFailedToMoveM4bFile": "Premještanje m4b datoteke nije uspjelo",
"MessageTaskFailedToWriteMetadataFile": "Pisanje datoteke s meta-podatcima nije uspjelo",
"MessageTaskMatchingBooksInLibrary": "Prepoznavanje knjiga u knjižnici \"{0}\"",
"MessageTaskNoFilesToScan": "Nema datoteka za skeniranje",
"MessageTaskOpmlImport": "Uvoz OPML-a",
"MessageTaskOpmlImportDescription": "Stvaram podcaste od {0} RSS izvora",
"MessageTaskOpmlImportFeed": "Uvoz OPML izvora",
"MessageTaskOpmlImportFeedDescription": "Uvoz RSS izvora \"{0}\"",
"MessageTaskOpmlImportFeedFailed": "Izvor podcasta nije dohvaćen",
"MessageTaskOpmlImportFeedPodcastDescription": "Stvaranje podcasta \"{0}\"",
"MessageTaskOpmlImportFeedPodcastExists": "Podcast već postoji u putanji",
"MessageTaskOpmlImportFeedPodcastFailed": "Stvaranje podcasta nije uspjelo",
"MessageTaskOpmlImportFinished": "Dodano {0} podcasta",
"MessageTaskOpmlParseFailed": "Raščlanjivanje OPML datoteke nije uspjelo",
"MessageTaskOpmlParseFastFail": "Neispravna OPML datoteka, oznaka <opml> nije pronađena ILI oznaka <outline> nije pronađena",
"MessageTaskOpmlParseNoneFound": "U OPML datoteci nisu pronađeni izvori",
"MessageTaskScanItemsAdded": "{0} dodan(o)",
"MessageTaskScanItemsMissing": "{0} nedostaje",
"MessageTaskScanItemsUpdated": "{0} ažurirano",
"MessageTaskScanNoChangesNeeded": "Nisu potrebne izmjene",
"MessageTaskScanningFileChanges": "Skeniranje izmijenjenih datoteka u \"{0}\"",
"MessageTaskScanningLibrary": "Skeniranje knjižnice \"{0}\"",
"MessageTaskTargetDirectoryNotWritable": "U odredišnu mapu nije moguće pisati",
"MessageThinking": "Razmišljam...",
"MessageUploaderItemFailed": "Učitavanje nije uspjelo",
"MessageUploaderItemSuccess": "Uspješno učitano!",
@@ -792,6 +886,10 @@
"NoteUploaderFoldersWithMediaFiles": "Mape s medijskim datotekama smatrat će se zasebnim stavkama knjižnice.",
"NoteUploaderOnlyAudioFiles": "Ako učitavate samo zvučne datoteke svaka će se zvučna datoteka uvesti kao zasebna zvučna knjiga.",
"NoteUploaderUnsupportedFiles": "Nepodržane vrste datoteka zanemaruju se. Kada odabirete datoteke ili ispuštate mapu, sve datoteke koje nisu u mapi stavke zanemarit će se.",
"NotificationOnBackupCompletedDescription": "Pokreće se po završetku sigurnosnog kopiranja",
"NotificationOnBackupFailedDescription": "Pokreće se kada sigurnosno kopiranje ne uspije",
"NotificationOnEpisodeDownloadedDescription": "Pokreće se kada se nastavak podcasta automatski preuzme",
"NotificationOnTestDescription": "Događaj za testiranje sustava obavijesti",
"PlaceholderNewCollection": "Ime nove zbirke",
"PlaceholderNewFolderPath": "Nova putanja mape",
"PlaceholderNewPlaylist": "Naziv novog popisa za izvođenje",
@@ -815,14 +913,13 @@
"StatsTopNarrators": "NAJPOPULARNIJI PRIPOVJEDAČI",
"StatsTotalDuration": "S ukupnim trajanjem od…",
"StatsYearInReview": "PREGLED GODINE",
"ToastAccountUpdateFailed": "Ažuriranje računa nije uspjelo",
"ToastAccountUpdateSuccess": "Račun ažuriran",
"ToastAppriseUrlRequired": "Obavezno upisati Apprise URL",
"ToastAsinRequired": "ASIN je obvezan",
"ToastAuthorImageRemoveSuccess": "Slika autora uklonjena",
"ToastAuthorNotFound": "Autor \"{0}\" nije pronađen",
"ToastAuthorRemoveSuccess": "Autor uklonjen",
"ToastAuthorSearchNotFound": "Autor nije pronađen",
"ToastAuthorUpdateFailed": "Ažuriranje autora nije uspjelo",
"ToastAuthorUpdateMerged": "Autor pripojen",
"ToastAuthorUpdateSuccess": "Autor ažuriran",
"ToastAuthorUpdateSuccessNoImageFound": "Autor ažuriran (slika nije pronađena)",
@@ -833,29 +930,29 @@
"ToastBackupDeleteSuccess": "Sigurnosna kopija izbrisana",
"ToastBackupInvalidMaxKeep": "Neispravan broj sigurnosnih kopija za čuvanje",
"ToastBackupInvalidMaxSize": "Neispravna najveća veličina sigurnosne kopije",
"ToastBackupPathUpdateFailed": "Ažuriranje putanje za sigurnosne kopije nije uspjelo",
"ToastBackupRestoreFailed": "Vraćanje sigurnosne kopije nije uspjelo",
"ToastBackupUploadFailed": "Učitavanje sigurnosne kopije nije uspjelo",
"ToastBackupUploadSuccess": "Sigurnosna kopija učitana",
"ToastBatchDeleteFailed": "Grupno brisanje nije uspjelo",
"ToastBatchDeleteSuccess": "Grupno brisanje je uspješno dovršeno",
"ToastBatchQuickMatchFailed": "Grupno brzo prepoznavanje nije uspjelo!",
"ToastBatchQuickMatchStarted": "Započelo je brzo prepoznavanje {0} knjiga!",
"ToastBatchUpdateFailed": "Skupno ažuriranje nije uspjelo",
"ToastBatchUpdateSuccess": "Skupno ažuriranje uspješno dovršeno",
"ToastBookmarkCreateFailed": "Izrada knjižne oznake nije uspjela",
"ToastBookmarkCreateSuccess": "Knjižna oznaka dodana",
"ToastBookmarkRemoveSuccess": "Knjižna oznaka uklonjena",
"ToastBookmarkUpdateFailed": "Ažuriranje knjižne oznake nije uspjelo",
"ToastBookmarkUpdateSuccess": "Knjižna oznaka ažurirana",
"ToastCachePurgeFailed": "Čišćenje predmemorije nije uspjelo",
"ToastCachePurgeSuccess": "Predmemorija uspješno očišćena",
"ToastChaptersHaveErrors": "Poglavlja imaju pogreške",
"ToastChaptersMustHaveTitles": "Poglavlja moraju imati naslove",
"ToastChaptersRemoved": "Poglavlja uklonjena",
"ToastChaptersUpdated": "Poglavlja su ažurirana",
"ToastCollectionItemsAddFailed": "Neuspješno dodavanje stavki u zbirku",
"ToastCollectionItemsAddSuccess": "Uspješno dodavanje stavki u zbirku",
"ToastCollectionItemsRemoveSuccess": "Stavke izbrisane iz zbirke",
"ToastCollectionRemoveSuccess": "Zbirka izbrisana",
"ToastCollectionUpdateFailed": "Ažuriranje zbirke nije uspjelo",
"ToastCollectionUpdateSuccess": "Zbirka ažurirana",
"ToastCoverUpdateFailed": "Ažuriranje naslovnice nije uspjelo",
"ToastDeleteFileFailed": "Brisanje datoteke nije uspjelo",
@@ -864,31 +961,28 @@
"ToastDeviceNameAlreadyExists": "E-čitač s tim nazivom već postoji",
"ToastDeviceTestEmailFailed": "Slanje probne poruke e-pošte nije uspjelo",
"ToastDeviceTestEmailSuccess": "Probna poruka e-pošte poslana",
"ToastDeviceUpdateFailed": "Ažuriranje uređaja nije uspjelo",
"ToastEmailSettingsUpdateFailed": "Ažuriranje postavki e-pošte nije uspjelo",
"ToastEmailSettingsUpdateSuccess": "Postavke e-pošte ažurirane",
"ToastEncodeCancelFailed": "Kodiranje nije uspješno otkazano",
"ToastEncodeCancelSucces": "Kodiranje otkazano",
"ToastEpisodeDownloadQueueClearFailed": "Redoslijed izvođenja nije uspješno očišćen",
"ToastEpisodeDownloadQueueClearSuccess": "Redoslijed preuzimanja nastavaka očišćen",
"ToastEpisodeUpdateSuccess": "{0} nastavak/a ažurirano",
"ToastErrorCannotShare": "Dijeljenje na ovaj uređaj nije moguće",
"ToastFailedToLoadData": "Učitavanje podataka nije uspjelo",
"ToastFailedToMatch": "Nije prepoznato",
"ToastFailedToShare": "Dijeljenje nije uspjelo",
"ToastFailedToUpdateAccount": "Ažuriranje računa nije uspjelo",
"ToastFailedToUpdateUser": "Ažuriranje korisnika nije uspjelo",
"ToastFailedToUpdate": "Ažuriranje nije uspjelo",
"ToastInvalidImageUrl": "Neispravan URL slike",
"ToastInvalidMaxEpisodesToDownload": "Neispravan unos maksimalnog broja nastavaka",
"ToastInvalidUrl": "Neispravan URL",
"ToastItemCoverUpdateFailed": "Ažuriranje naslovnice stavke nije uspjelo",
"ToastItemCoverUpdateSuccess": "Naslovnica stavke ažurirana",
"ToastItemDeletedFailed": "Brisanje stavke nije uspjelo",
"ToastItemDeletedSuccess": "Stavka je izbrisana",
"ToastItemDetailsUpdateFailed": "Ažuriranje podataka stavke nije uspjelo",
"ToastItemDetailsUpdateSuccess": "Pojedinosti stavke su ažurirane",
"ToastItemMarkedAsFinishedFailed": "Označavanje kao Dovršeno nije uspjelo",
"ToastItemMarkedAsFinishedSuccess": "Stavka označena kao dovršena",
"ToastItemMarkedAsNotFinishedFailed": "Označavanje kao Nije dovršeno nije uspjelo",
"ToastItemMarkedAsNotFinishedSuccess": "Stavka označena kao nedovršena",
"ToastItemUpdateFailed": "Ažuriranje stavke nije uspjelo",
"ToastItemUpdateSuccess": "Stavka ažurirana",
"ToastLibraryCreateFailed": "Stvaranje knjižnice nije uspjelo",
"ToastLibraryCreateSuccess": "Knjižnica \"{0}\" stvorena",
@@ -896,37 +990,42 @@
"ToastLibraryDeleteSuccess": "Knjižnica izbrisana",
"ToastLibraryScanFailedToStart": "Skeniranje nije uspjelo",
"ToastLibraryScanStarted": "Skeniranje knjižnice započelo",
"ToastLibraryUpdateFailed": "Ažuriranje knjižnice nije uspjelo",
"ToastLibraryUpdateSuccess": "Knjižnica \"{0}\" ažurirana",
"ToastMatchAllAuthorsFailed": "Nisu prepoznati svi autori",
"ToastMetadataFilesRemovedError": "Pogreška kod uklanjanja datoteka metadata.{0}",
"ToastMetadataFilesRemovedNoneFound": "U knjižnici nisu pronađene datoteke metadata.{0}",
"ToastMetadataFilesRemovedNoneRemoved": "Datoteke metadata.{0} nisu uklonjenje",
"ToastMetadataFilesRemovedSuccess": "uklonjeno {0} datoteka metadata.{1}",
"ToastMustHaveAtLeastOnePath": "Mora postojati barem jedna putanja",
"ToastNameEmailRequired": "Ime i adresa e-pošte su obavezni",
"ToastNameRequired": "Ime je obavezno",
"ToastNewEpisodesFound": "pronađeno {0} novih nastavaka",
"ToastNewUserCreatedFailed": "Račun \"{0}\" nije uspješno izrađen",
"ToastNewUserCreatedSuccess": "Novi račun izrađen",
"ToastNewUserLibraryError": "Treba odabrati barem jednu knjižnicu",
"ToastNewUserPasswordError": "Mora imati zaporku, samo korisnik root može imati praznu zaporku",
"ToastNewUserTagError": "Potrebno je odabrati najmanje jednu oznaku",
"ToastNewUserUsernameError": "Upišite korisničko ime",
"ToastNoNewEpisodesFound": "Nisu pronađeni novi nastavci",
"ToastNoUpdatesNecessary": "Ažuriranja nisu potrebna",
"ToastNotificationCreateFailed": "Stvaranje obavijesti nije uspjelo",
"ToastNotificationDeleteFailed": "Brisanje obavijesti nije uspjelo",
"ToastNotificationFailedMaximum": "Najveći broj neuspješnih pokušaja mora biti >= 0",
"ToastNotificationQueueMaximum": "Najveći broj obavijesti u redu mora biti >= 0",
"ToastNotificationSettingsUpdateFailed": "Ažuriranje postavki obavijesti nije uspjelo",
"ToastNotificationSettingsUpdateSuccess": "Postavke obavijesti ažurirane",
"ToastNotificationTestTriggerFailed": "Okidanje probne obavijesti nije uspjelo",
"ToastNotificationTestTriggerSuccess": "Okinuta je probna obavijest",
"ToastNotificationUpdateFailed": "Ažuriranje obavijesti nije uspjelo",
"ToastNotificationUpdateSuccess": "Obavijest ažurirana",
"ToastPlaylistCreateFailed": "Popis za izvođenje nije izrađen",
"ToastPlaylistCreateSuccess": "Popis za izvođenje izrađen",
"ToastPlaylistRemoveSuccess": "Popis za izvođenje uklonjen",
"ToastPlaylistUpdateFailed": "Ažuriranje popisa za izvođenje nije uspjelo",
"ToastPlaylistUpdateSuccess": "Popis za izvođenje ažuriran",
"ToastPodcastCreateFailed": "Podcast nije izrađen",
"ToastPodcastCreateSuccess": "Podcast uspješno izrađen",
"ToastPodcastGetFeedFailed": "Dohvat izvora podcasta nije uspio",
"ToastPodcastNoEpisodesInFeed": "U RSS izvoru nisu pronađeni nastavci",
"ToastPodcastNoRssFeed": "Podcast nema RSS izvor",
"ToastProgressIsNotBeingSynced": "Napredak se ne sinkronizira, ponovno pokrenite reprodukciju",
"ToastProviderCreatedFailed": "Dodavanje pružatelja nije uspjelo",
"ToastProviderCreatedSuccess": "Novi pružatelj dodan",
"ToastProviderNameAndUrlRequired": "Ime i URL su obavezni",
@@ -949,18 +1048,17 @@
"ToastSendEbookToDeviceSuccess": "E-knjiga poslana uređaju \"{0}\"",
"ToastSeriesUpdateFailed": "Ažuriranje serijala nije uspjelo",
"ToastSeriesUpdateSuccess": "Serijal uspješno ažuriran",
"ToastServerSettingsUpdateFailed": "Ažuriranje postavki poslužitelja nije uspjelo",
"ToastServerSettingsUpdateSuccess": "Postavke poslužitelja ažurirane",
"ToastSessionCloseFailed": "Zatvaranje sesije nije uspjelo",
"ToastSessionDeleteFailed": "Neuspješno brisanje serije",
"ToastSessionDeleteSuccess": "Sesija obrisana",
"ToastSleepTimerDone": "Timer za spavanje istječe... zZzzZz",
"ToastSlugMustChange": "Slug sadrži nedozvoljene znakove",
"ToastSlugRequired": "Slug je obavezan",
"ToastSocketConnected": "Socket priključen",
"ToastSocketDisconnected": "Veza sa socketom je prekinuta",
"ToastSocketFailedToConnect": "Priključivanje na socket nije uspjelo",
"ToastSortingPrefixesEmptyError": "Mora imati najmanje jedan prefiks za sortiranje",
"ToastSortingPrefixesUpdateFailed": "Ažuriranje prefiksa za sortiranje nije uspjelo",
"ToastSortingPrefixesUpdateSuccess": "Prefiksi za sortiranje ažurirani ({0} stavki)",
"ToastTitleRequired": "Naslov je obavezan",
"ToastUnknownError": "Nepoznata pogreška",

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