Compare commits

...

446 Commits

Author SHA1 Message Date
jeffvli 960427fce8 Remove deb 2023-12-13 20:33:44 -08:00
jeffvli 118a9f3257 Separate regular and arm64 build 2023-12-13 20:32:03 -08:00
jeffvli 971bfe3823 Bump to v0.5.3 2023-12-13 19:40:51 -08:00
jeffvli 7765f14110 Add rpm and deb build targets 2023-12-13 19:39:06 -08:00
jeffvli 36b465504f Add arm64 linux build 2023-12-13 19:38:36 -08:00
jeffvli 7b639b45f7 Add new translations 2023-12-13 18:19:24 -08:00
Hosted Weblate 85d9162b12 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (519 of 519 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate 36670b330f Translated using Weblate (Swedish)
Currently translated at 52.6% (273 of 519 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mattias <mattiasghodsian@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sv/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate 9c380a8241 Translated using Weblate (French)
Currently translated at 99.4% (516 of 519 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <KosmoMoustache@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate c26820ee82 Translated using Weblate (Dutch)
Currently translated at 35.8% (186 of 519 strings)

Translated using Weblate (Dutch)

Currently translated at 32.3% (168 of 519 strings)

Translated using Weblate (Dutch)

Currently translated at 15.2% (79 of 519 strings)

Added translation using Weblate (Dutch)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Idris Saklou <idrissaklou@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate dccd6afc3d Translated using Weblate (Italian)
Currently translated at 99.0% (514 of 519 strings)

Translated using Weblate (Italian)

Currently translated at 96.5% (501 of 519 strings)

Co-authored-by: Aurora <arci@anche.no>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: NicKoehler <grillinicola@proton.me>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/it/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate c6a520b0d7 Translated using Weblate (Polish)
Currently translated at 99.8% (518 of 519 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mistify <fabianszafranski@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate 1f4f3a5497 Translated using Weblate (Czech)
Currently translated at 100.0% (519 of 519 strings)

Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate 58d04b3126 Translated using Weblate (German)
Currently translated at 88.8% (461 of 519 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Maik <maikguenes2003@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Hosted Weblate fcac4a5547 Translated using Weblate (Portuguese (Brazil))
Currently translated at 28.3% (147 of 519 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rafael Vieira <rafaelvieiras@pm.me>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pt_BR/
Translation: feishin/Translation
2023-12-13 09:28:56 +01:00
Kendall Garner c05b474827 fix navi null date (#408) 2023-12-13 00:28:53 -08:00
mcneb10 a8814d3e8a Fix 'undefined' in window title when song has no artist name (#402) 2023-12-05 19:05:08 -08:00
Kendall Garner 3f9cdab450 convert value to number on set (#390) 2023-12-04 20:20:19 -08:00
jeffvli 1d2e9484d8 Bump node-abi version 2023-11-18 01:49:00 -08:00
jeffvli f5ec294e0c Add new languages 2023-11-18 01:32:59 -08:00
jeffvli 0beef2a0b7 Bump electron builder version 2023-11-18 01:30:19 -08:00
Hosted Weblate 86209b6272 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (518 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kaiyang Wu <self@origincode.me>
Co-authored-by: kare-Udon <laoliu735@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate b32afc0e49 Translated using Weblate (Serbian)
Currently translated at 100.0% (518 of 518 strings)

Added translation using Weblate (Serbian)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ilija <zojka2g@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sr/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate 58c4ab4a67 Translated using Weblate (Swedish)
Currently translated at 43.4% (225 of 518 strings)

Added translation using Weblate (Swedish)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mattias <mattiasghodsian@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sv/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate bcbd169507 Translated using Weblate (French)
Currently translated at 94.9% (492 of 518 strings)

Translated using Weblate (French)

Currently translated at 94.2% (488 of 518 strings)

Translated using Weblate (French)

Currently translated at 94.2% (488 of 518 strings)

Translated using Weblate (French)

Currently translated at 90.3% (468 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <KosmoMoustache@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate 55a4e74118 Translated using Weblate (Spanish)
Currently translated at 100.0% (518 of 518 strings)

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate e4c449d6de Translated using Weblate (Italian)
Currently translated at 96.1% (498 of 518 strings)

Translated using Weblate (Italian)

Currently translated at 96.1% (498 of 518 strings)

Co-authored-by: Aurora <arci@anche.no>
Co-authored-by: CraftWorks <thelonegamer87@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/it/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate aa5004c866 Translated using Weblate (Polish)
Currently translated at 98.6% (511 of 518 strings)

Translated using Weblate (Polish)

Currently translated at 98.6% (511 of 518 strings)

Co-authored-by: 7Adrian <7adrian.mail@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mistify <fabianszafranski@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate a1aa5f323c Translated using Weblate (Czech)
Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (518 of 518 strings)

Added translation using Weblate (Czech)

Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate 92f91be650 Translated using Weblate (Japanese)
Currently translated at 100.0% (518 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: aorinngoDo <aorinngo@email.cz>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate 419462b22b Translated using Weblate (Russian)
Currently translated at 72.2% (374 of 518 strings)

Translated using Weblate (Russian)

Currently translated at 72.0% (373 of 518 strings)

Translated using Weblate (Russian)

Currently translated at 72.0% (373 of 518 strings)

Co-authored-by: Arseniy <_senyaa@tutanota.com>
Co-authored-by: Gitized <s.v.lazarev.89@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate 22e31a7b09 Translated using Weblate (German)
Currently translated at 87.0% (451 of 518 strings)

Translated using Weblate (German)

Currently translated at 87.0% (451 of 518 strings)

Translated using Weblate (German)

Currently translated at 62.7% (325 of 518 strings)

Translated using Weblate (German)

Currently translated at 62.7% (325 of 518 strings)

Translated using Weblate (German)

Currently translated at 59.6% (309 of 518 strings)

Translated using Weblate (German)

Currently translated at 59.6% (309 of 518 strings)

Translated using Weblate (German)

Currently translated at 56.9% (295 of 518 strings)

Translated using Weblate (German)

Currently translated at 56.9% (295 of 518 strings)

Translated using Weblate (German)

Currently translated at 55.4% (287 of 518 strings)

Translated using Weblate (German)

Currently translated at 55.4% (287 of 518 strings)

Translated using Weblate (German)

Currently translated at 53.8% (279 of 518 strings)

Translated using Weblate (German)

Currently translated at 53.8% (279 of 518 strings)

Translated using Weblate (German)

Currently translated at 1.9% (10 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kobayashi <kobayashi90@protonmail.ch>
Co-authored-by: Rudi Mentaire <stoertebecker@byom.de>
Co-authored-by: ThetaDev <t.testboy@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
Hosted Weblate dce0284e0b Translated using Weblate (Portuguese (Brazil))
Currently translated at 23.5% (122 of 518 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 22.5% (117 of 518 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 13.3% (69 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rafael Vieira <rafaelvieiras@pm.me>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pt_BR/
Translation: feishin/Translation
2023-11-18 10:18:08 +01:00
jeffvli 2afe7e8920 Bump to v0.5.2 2023-11-18 01:17:57 -08:00
jeffvli 9ca364dd0e Fix title case transformer 2023-11-17 02:03:01 -08:00
jeffvli ccd8d2b6b0 Add network error catch 2023-11-16 23:35:26 -08:00
jeffvli fdfbad68e2 Fix app single instance lock (#385) 2023-11-16 23:29:19 -08:00
jeffvli 48a529dd51 Bump electron to v27.1.0 (#383) 2023-11-16 10:17:39 -08:00
jeffvli bc40f93b59 Fix translation titlecase for accented characters (#357) 2023-11-12 03:46:12 -08:00
doggo cf544bea61 Fixed incorrect docker command argument (#365) 2023-11-12 03:40:38 -08:00
Samuli Piipponen f24cf5a928 Fix Discord status with no Artists (#359) 2023-11-12 03:40:28 -08:00
Kendall Garner 11af31c539 [bugfix]: correct text for albumDetail (#376) 2023-11-12 03:40:17 -08:00
jeffvli adc09e6bbf Bump to v0.5.1 2023-11-02 11:41:05 -07:00
jeffvli ba20dc0972 Add languages to config
- Spanish
- German
- French
- Italian
- Japanese
- Russian
- Portugese
- Polish
- Chinese (simplified)
2023-11-02 11:37:24 -07:00
Hosted Weblate 999b6afd0e Added translation using Weblate (Norwegian Bokmål)
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2023-11-02 19:33:32 +01:00
Hosted Weblate bf0cd0e76f Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (517 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 95.5% (495 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 92.8% (481 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 91.5% (474 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 89.7% (465 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kaiyang Wu <self@origincode.me>
Co-authored-by: kare-Udon <laoliu735@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translation: feishin/Translation
2023-11-02 19:33:32 +01:00
Hosted Weblate f98950fe19 Added translation using Weblate (Chinese (Traditional))
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kaiyang Wu <self@origincode.me>
2023-11-02 19:33:32 +01:00
Hosted Weblate 4e58feedd0 Translated using Weblate (English)
Currently translated at 100.0% (518 of 518 strings)

Co-authored-by: 7Adrian <7adrian.mail@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/en/
Translation: feishin/Translation
2023-11-02 19:33:32 +01:00
Hosted Weblate 50aa3960e6 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Chinese (Simplified))

Currently translated at 89.5% (464 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 56.7% (294 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 51.9% (269 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 51.1% (265 of 518 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 52.0% (265 of 509 strings)

Co-authored-by: Darwin <daerwendeshu@outlook.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: kare-Udon <laoliu735@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 5a821bf5ce Translated using Weblate (French)
Currently translated at 77.2% (393 of 509 strings)

Translated using Weblate (French)

Currently translated at 77.0% (392 of 509 strings)

Translated using Weblate (French)

Currently translated at 68.7% (350 of 509 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <KosmoMoustache@users.noreply.hosted.weblate.org>
Co-authored-by: KosmoMoustache <hosted.weblate.org@kosmo.ovh>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 123c9795dc Translated using Weblate (Spanish)
Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (Spanish)

Currently translated at 99.8% (517 of 518 strings)

Translated using Weblate (Spanish)

Currently translated at 99.2% (514 of 518 strings)

Translated using Weblate (Spanish)

Currently translated at 88.6% (459 of 518 strings)

Translated using Weblate (Spanish)

Currently translated at 88.4% (458 of 518 strings)

Translated using Weblate (Spanish)

Currently translated at 88.4% (458 of 518 strings)

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff <jeffvictorli@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 6ac0b8734a Translated using Weblate (Italian)
Currently translated at 94.2% (488 of 518 strings)

Translated using Weblate (Italian)

Currently translated at 5.5% (29 of 518 strings)

Translated using Weblate (Italian)

Currently translated at 5.0% (26 of 518 strings)

Translated using Weblate (Italian)

Currently translated at 4.2% (22 of 518 strings)

Added translation using Weblate (Italian)

Co-authored-by: Aurora <arci@anche.no>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/it/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 8f45d01d00 Translated using Weblate (Polish)
Currently translated at 98.8% (512 of 518 strings)

Translated using Weblate (Polish)

Currently translated at 83.2% (431 of 518 strings)

Translated using Weblate (Polish)

Currently translated at 83.2% (431 of 518 strings)

Translated using Weblate (Polish)

Currently translated at 76.2% (395 of 518 strings)

Translated using Weblate (Polish)

Currently translated at 74.6% (380 of 509 strings)

Translated using Weblate (Polish)

Currently translated at 46.5% (237 of 509 strings)

Co-authored-by: 7Adrian <7adrian.mail@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff <jeffvictorli@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 809f2683a2 Translated using Weblate (Japanese)
Currently translated at 98.6% (511 of 518 strings)

Translated using Weblate (Japanese)

Currently translated at 97.6% (506 of 518 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff <jeffvictorli@gmail.com>
Co-authored-by: aorinngoDo <aorinngo@email.cz>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 8e0ef96596 Translated using Weblate (Russian)
Currently translated at 36.9% (188 of 509 strings)

Translated using Weblate (Russian)

Currently translated at 18.0% (92 of 509 strings)

Added translation using Weblate (Russian)

Co-authored-by: Arseniy <_senyaa@tutanota.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate f74c2bc4e2 Translated using Weblate (English)
Currently translated at 100.0% (518 of 518 strings)

Translated using Weblate (English)

Currently translated at 100.0% (509 of 509 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff <jeffvictorli@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/en/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 8e8380854e Translated using Weblate (German)
Currently translated at 1.3% (7 of 518 strings)

Added translation using Weblate (German)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kobayashi <kobayashi90@protonmail.ch>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
Hosted Weblate 0a48add9f4 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Portuguese (Brazil))

Currently translated at 3.0% (16 of 518 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 2.5% (13 of 518 strings)

Added translation using Weblate (Portuguese (Brazil))

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rafael Vieira <rafaelvieiras@pm.me>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pt_BR/
Translation: feishin/Translation
2023-11-02 03:58:29 +01:00
jeffvli b05d532941 Add missing translation keys to sidebar 2023-11-01 05:40:42 -07:00
jeffvli d517afdfd3 Set fullscreen player text shadow to css var 2023-11-01 04:00:58 -07:00
jeffvli 63e441429e Set most played to display songs on Jellyfin (#324) 2023-11-01 03:57:30 -07:00
jeffvli c46fa75266 Add imageSize parameter to song list hook 2023-11-01 03:51:03 -07:00
jeffvli fd851714ae Add missing translation keys to list filters 2023-11-01 03:32:28 -07:00
jeffvli 364f53e124 Increase sidebar image scale from 300px -> 450px 2023-10-31 22:03:48 -07:00
jeffvli 9756ed2d27 Fix translation key 2023-10-31 22:03:48 -07:00
Kendall Garner 6bd836fad0 [bugfix]: Remove setting path in attempt to bypass bug (#344)
* Remove setting path in attempt to bypass bug
2023-10-31 22:02:34 -07:00
Hosted Weblate 327fa2b02f Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Chinese (Simplified))

Currently translated at 51.7% (263 of 508 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 51.7% (263 of 508 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 51.7% (263 of 508 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 15.7% (80 of 508 strings)

Added translation using Weblate (Chinese (Simplified))

Co-authored-by: Darwin <daerwendeshu@outlook.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: ken <kenxu2022@gmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translation: feishin/Translation
2023-11-01 00:45:28 +01:00
Hosted Weblate 88922766db Translated using Weblate (French)
Currently translated at 67.9% (345 of 508 strings)

Translated using Weblate (French)

Currently translated at 66.9% (340 of 508 strings)

Translated using Weblate (French)

Currently translated at 50.0% (254 of 508 strings)

Translated using Weblate (French)

Currently translated at 49.6% (252 of 508 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (French)

Currently translated at 49.2% (250 of 508 strings)

Added translation using Weblate (French)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <hosted.weblate.org@kosmo.ovh>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translation: feishin/Translation
2023-11-01 00:45:28 +01:00
Hosted Weblate f5b185f5f0 Translated using Weblate (Spanish)
Currently translated at 82.0% (417 of 508 strings)

Added translation using Weblate (Spanish)

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translation: feishin/Translation
2023-11-01 00:45:28 +01:00
Hosted Weblate ce08a70d89 Translated using Weblate (Polish)
Currently translated at 16.5% (84 of 508 strings)

Added translation using Weblate (Polish)

Co-authored-by: FaboThePlayer <fabianszafranski@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mistify <fabianszafranski@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translation: feishin/Translation
2023-11-01 00:45:28 +01:00
Hosted Weblate b635eb20cf Translated using Weblate (Japanese)
Currently translated at 99.8% (507 of 508 strings)

Added translation using Weblate (Japanese)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: aorinngoDo <aorinngo@email.cz>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/
Translation: feishin/Translation
2023-11-01 00:45:28 +01:00
Hosted Weblate 9b0fad6742 Translated using Weblate (English)
Currently translated at 100.0% (508 of 508 strings)

Translated using Weblate (English)

Currently translated at 100.0% (508 of 508 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff <jeffvictorli@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/en/
Translation: feishin/Translation
2023-11-01 00:45:28 +01:00
jeffvli 401912a70c Fix translation keys on full screen player 2023-10-31 04:17:15 -07:00
jeffvli cd3ec158d3 Fix opacity on fullscreen player queue 2023-10-31 04:16:46 -07:00
jeffvli d51ca37e1b Bump to v0.5.0 2023-10-31 03:10:51 -07:00
jeffvli 9d8dcc7ade Add weblate notice 2023-10-31 00:24:29 -07:00
Jeff 8430b1ec95 Add localization support (#333)
* Add updated i18n config and en locale
2023-10-30 19:22:45 -07:00
Martin Pauli 11863fd4c1 Fix clear rating hotkey (#338) 2023-10-30 18:50:48 -07:00
Nicholas Malcolm cf9c7e2640 Build docker container for arm (#336)
* Build all supported container platforms

* Remove less popular platforms
2023-10-30 18:50:21 -07:00
Kendall Garner 9d780e0342 [bugfix]: prevent default (#334)
* [bugfix]: prevent default on rating
2023-10-28 21:10:52 -07:00
Kendall Garner 4ec981df83 [bugfix/feature]: Improve ratings (#332)
* [bugfix/feature]: Improve ratings

Fix: add preventDefault/stopPropagation to prevent scrolling to top in queue
Feat: instead of double click for clear, click on same value
2023-10-28 20:00:01 -07:00
jeffvli e5564c2ac2 Add additional dependencies to linux build (#320) 2023-10-28 16:51:07 -07:00
Martin Pauli 7a580c2c65 Add favorite hotkey options (#326)
* Add favorite hotkey options

* Update wording

---------

Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
2023-10-27 18:22:16 -07:00
jeffvli ac84088c69 Set owner field edit to Navidrome only (#327) 2023-10-26 16:42:21 -07:00
jeffvli 3c2e4d40ec Update play button for dynamic theme 2023-10-23 15:45:47 -07:00
jeffvli fdff79496a Set pause status on last track end (#291) 2023-10-23 09:02:48 -07:00
jeffvli ccfadda729 Add play count to jellyfin album sort (#324) 2023-10-23 08:37:04 -07:00
jeffvli f21b8d6bbd Update base button styles
- Use brightness filter for hover/focus styles
- Re-add default active style
2023-10-23 08:24:23 -07:00
jeffvli 244c00c4c6 Add discord rich presence (#72) 2023-10-23 06:58:39 -07:00
Kendall Garner 2664a80851 Support changing playback rate (#275)
* initial idea for playback rate

* Add transparency to dropdown

* Move playback speed component to right controls

* Set mpv speed on startup

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-10-22 17:47:44 -07:00
Kendall Garner 742b13d65e [Feature] Support changing accent/primary color (#282)
* [Feature] Support changing accent/primary color

- adds color picker to settings with five swatches (blue default, yellow green and red imported from sonixd, purple new)
- changing color will change the appropriate css variable

* Remove hover styles that use an alternate primary

---------

Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-10-22 17:46:28 -07:00
jeffvli 8dcd49d574 Fix add to playlist from artist page (#296) 2023-10-22 16:18:55 -07:00
jeffvli 02c8cbcad6 Revert jellyfin getPlaylistList implementation (#272) 2023-10-22 16:00:41 -07:00
jeffvli 86fb52f6d4 Fix current song row when queue is empty 2023-10-22 15:57:15 -07:00
Kendall Garner 452ef783f2 [bugfix/feat]: always fetch artist image for Navidrome (#317)
* [bugfix/feat]: always fetch artist image for Navidrome

* Add error fallback to library header image

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-10-22 15:46:48 -07:00
Kendall Garner 74cab01013 [feature]: Support using system fonts (#304)
* [feature]: Support using system fonts

Uses the **experimental** queryLocalFonts API, when prompted, to get the fonts and do CSS.
Resolves #270 and #288 (by proxy)

Caveats/notes:
- This is experimental, and is only supported by Chrome/Chromium/Edgeium (see https://caniuse.com/?search=querylocalfonts)
- As far as I can tell, the only way to dynamically change the font (shown in https://wicg.github.io/local-font-access/#example-style-with-local-fonts) was by DOM manipulation; css variables did not seem to work
- This shows **all** fonts, including their variants (bold/italic/etc); given that the style names could be localized, not sure of a way to parse this (on my system, for instance, I had 859 different combinations)
- I made fonts a separate top-level setting because it was easier to manipulate without causing as many rerenders; feel free to put that back

* add permission chec

* add electron magic to support custom font

* restrict content types
2023-10-22 15:25:17 -07:00
Kendall Garner e6ed9229c2 [bugfix]: fix queue offset when removing tracks (#301)
* [bugfix]: fix queue offset when removing tracks

* Fix song index numbers when removing songs

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-10-22 15:21:31 -07:00
jeffvli 3a144ab821 Fix query editor not scrolling (#322) 2023-10-22 15:09:48 -07:00
jeffvli 913e89b01b Set column defs on play queue to use correct row index 2023-10-19 04:33:09 -07:00
jeffvli 768a88de8f Fix row refresh on status change for current song 2023-10-19 03:37:17 -07:00
jeffvli 8e2a107d4a Fix className clash on current song 2023-10-19 03:36:16 -07:00
jeffvli e77efcf836 Add artist name to window title 2023-10-18 20:49:50 -07:00
jeffvli 818f155993 Hide playing icon if player is paused 2023-10-18 19:55:57 -07:00
jeffvli b28fe4cbc9 Convert play icon from base64 to svg 2023-10-18 19:51:55 -07:00
Kendall Garner 8a53fab751 add more emphasis to current song (#283)
* add more emphasis to current song

* add css indicator (rivolumelineup)

* don't use absolute position, support album track number

* Respect order of set-queue function (fix race condition)

* Fix table row actions button on album detail and play queue

* Fix album detail table customizations

* Bump to v0.4.1

* Fix opacity mask for unsynced lyrics container

* Separate sidebar icons to new component

- Fixes react render issue

* Add app focus hook

* Remove css play image

* Add player status as cell refresh condition for queue

* Add current song images

* Add current song styles for all song tables

* Revert row index cell width

* Remove animated svg on browser

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
2023-10-18 18:32:11 -07:00
Kendall Garner 9964f95d5d [Remote] Full PWA support, misc bugfixes (#280)
- Fix setting remote port properly
- Add web worker support (so it can be installed as an "app")
- build fixes/removing stray console.log
2023-10-18 10:49:09 -07:00
Kendall Garner fe298d3232 Reset Carousel position on data refresh (#303)
* Reset Carousel position on data refresh

* add refresh for all carousels
2023-10-18 10:47:55 -07:00
Kendall Garner 03e582f301 [feature]: support running feishin on custom path (#307)
* [feature]: support running feishin on custom path

* add details in readme
2023-10-17 22:10:53 -07:00
Kendall Garner d7b3d5c0bd [bugfix]: do not duplicate tracks when adding to multiple playlists (#300) 2023-10-17 16:11:14 -07:00
Kendall Garner 5fdf4c06f9 properly implement Jellyfin getSongDetail (#298) 2023-10-17 16:05:44 -07:00
TacoCake c7aa5d09c9 In the fullscreen player use dynamic resolution for the main image (#290)
* In the fullscreen player use dynamic resolution for the main image

* Use ceil instead of round

* Add types and lint

---------

Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
2023-10-17 06:47:50 -07:00
Kendall Garner f4f73289c9 [bugfix]: remove ignore CORS/SSL switches from web version (#305) 2023-10-17 06:21:36 -07:00
Lily Rose ac7ec133db Replace hardcoded Jellyfin authentication DeviceId to include hostname and username (#312) 2023-10-17 06:21:15 -07:00
Kendall Garner 1a948ab86b fix artist discography year filter (#299) 2023-10-17 06:05:36 -07:00
Kendall Garner f6667a39a0 fix toggle replay (#292) 2023-10-17 05:49:29 -07:00
jeffvli cbeb4ab7d8 Separate sidebar icons to new component
- Fixes react render issue
2023-10-17 05:46:42 -07:00
jeffvli 3675146f1f Fix opacity mask for unsynced lyrics container 2023-10-07 19:58:04 -07:00
jeffvli 946f4ff306 Bump to v0.4.1 2023-10-07 19:06:30 -07:00
jeffvli 277669c413 Fix album detail table customizations 2023-10-07 18:11:02 -07:00
jeffvli 49b6478b72 Fix table row actions button on album detail and play queue 2023-10-07 17:32:59 -07:00
jeffvli ca39409cc3 Respect order of set-queue function (fix race condition) 2023-10-07 16:46:23 -07:00
jeffvli cca6fa21db Adjust scrobble duration to check in ms 2023-10-05 22:11:48 -07:00
jeffvli 5e1059870c Fix second song on startup not playing 2023-10-05 21:54:11 -07:00
Kendall Garner 6bac172bbe fix scrobble durations (#269)
* fix scrobble durations

* Fix scrobble condition on last song in queue, normalize ms

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-10-05 21:45:47 -07:00
Kendall Garner 118a9f73d1 fix unsynced lyrics (#279) 2023-10-04 22:02:42 -07:00
jeffvli c464be8cea Fix quit functionality (#184) 2023-09-27 02:37:03 -07:00
jeffvli 3bbe696f4c Update react-router and add useTransition support 2023-09-25 16:13:27 -07:00
jeffvli f7cacd2b73 Remove page fade in transition 2023-09-25 16:12:51 -07:00
jeffvli 62794623a3 Fix tracks list refresh on search 2023-09-25 15:57:48 -07:00
Kendall Garner 9e3e038d42 [Remote] Actually fix auth (#260)
* fix favicon, basic auth

* actual fix......
2023-09-24 17:31:33 -07:00
jeffvli b375238baf Bump to v0.4.0 2023-09-24 17:23:39 -07:00
Kendall Garner 02b06a07be fix favicon, basic auth (#259) 2023-09-24 17:02:25 -07:00
Kendall Garner d7f21b3c6b special socket for dev; defer to default otherwise (#258)
* special socket for dev; defer to default otherwise

* Add write-all permissions to docker push

* special socket for dev; defer to default otherwise

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-09-24 16:56:45 -07:00
jeffvli 1113ef972f Remove docker push on pr 2023-09-24 16:54:43 -07:00
jeffvli 46a2c29b22 Add write-all permissions to docker push 2023-09-23 22:36:56 -07:00
jeffvli ebcb7bc4d1 Add path param for ND song list api 2023-09-23 15:37:16 -07:00
jeffvli 0b62bee3a6 Add grid view for tracks (#128) 2023-09-23 15:36:57 -07:00
jeffvli d3503af12c Add song count column to albums list 2023-09-23 04:05:15 -07:00
jeffvli 571ea3c653 Add rating hotkeys (#208) 2023-09-23 03:20:04 -07:00
jeffvli f0e518d3c8 Add quit button to menu (#184) 2023-09-22 18:04:15 -07:00
jeffvli 47dc83f360 Make collapsed sidebar navigation configurable 2023-09-22 17:55:03 -07:00
jeffvli 8cbc25a932 Add browser forward/back hotkeys (#155) 2023-09-22 17:52:00 -07:00
jeffvli 0cba405b45 Add navigation buttons to the collapsed sidebar (#203) 2023-09-22 15:33:28 -07:00
jeffvli 45b80ac395 Add discsubtitle for navidrome (#217) 2023-09-22 15:12:23 -07:00
jeffvli 8b0fe69e1c Fix alignment of button leftIcon 2023-09-22 15:11:27 -07:00
jeffvli 25e621372c Parse URLs from note field (#154) 2023-09-22 04:25:16 -07:00
jeffvli 14f4649b93 Move drag container to scrollarea component 2023-09-22 02:40:27 -07:00
jeffvli 1a87adb728 Fix transient props for styled-components v6 2023-09-22 02:34:57 -07:00
jeffvli bb9bf7ba6a Add docker run instructions 2023-09-22 00:31:56 -07:00
jeffvli fb7e7bfa3e Add latest tag 2023-09-21 21:44:03 -07:00
jeffvli a8a14a62c0 Separate auto and manual docker pushes 2023-09-21 21:13:49 -07:00
jeffvli cd836d54db Add docker publish workflow 2023-09-21 20:46:48 -07:00
jeffvli c90c43944d Fix logo path 2023-09-21 20:31:39 -07:00
jeffvli fd7468a4fe Add drag container for web library headers (#206) 2023-09-21 18:46:47 -07:00
jeffvli c4f9868a6b Revert library header line clamp to 2 lines (#215) 2023-09-21 17:52:14 -07:00
jeffvli fbb0907a70 Fix lyrics mask 2023-09-21 17:41:27 -07:00
jeffvli 201ee895f9 Allow css vendor-prefix 2023-09-21 17:41:19 -07:00
jeffvli 51be0153d3 Adjust fullscreen player styles
- Remove opacity on metadata section
- Add text shadow to metadata text
- Add padding under title
- Uppercase artists and album name
2023-09-21 17:35:22 -07:00
jeffvli 29a9a11085 Fix subsonic song duration 2023-09-21 17:35:22 -07:00
Kendall Garner 65f28bb9dc Replaygain support for Web Player (#243)
* replaygain!

* resume context

* don't fire both players

* replaygain for jellyfin

* actually remove console.log

---------

Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-09-21 17:06:13 -07:00
jeffvli fd264daffc Add new app icon (#232) 2023-09-21 11:24:20 -07:00
Alberto Rodríguez 18e35f2ba9 Added docker image build script (#245)
* Added docker image build script

* Changed to alpine docker and expose port 9180

* Use multi-stage build

---------

Co-authored-by: = <=>
Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
2023-09-20 18:01:47 -07:00
Kendall Garner 487e9be8ec Invalidate playlist song list on update (#248) 2023-09-20 16:28:59 -07:00
Benjamin d9049ed066 Prevent MPV from loading user config/scripts (#247) 2023-09-20 16:27:36 -07:00
Kendall Garner 6e62448b88 fix other places of duration display (and other minor fixes) (#249)
* fix other places of duration display

* add back comma

* add max-width for image
2023-09-20 16:07:40 -07:00
jeffvli ec457d5125 Lint files based on updated rules 2023-09-15 20:42:38 -07:00
jeffvli d45b01625b Re-add linting for styled-components
- Update styled-components to v6
- Update stylelint to v15
- Add styled-components css plugin
2023-09-15 20:42:03 -07:00
jeffvli 2defa5cc13 Fix seek slider from duration normalizations 2023-09-15 19:31:34 -07:00
jeffvli 9cc9c3a87f Bump electron to v25.8.1 2023-09-15 16:54:17 -07:00
jeffvli 153d8ce6ce Fix nd/jf duration normalizations 2023-09-15 16:52:14 -07:00
jeffvli 5e33212112 Add dedicated refresh button to list views (#235) 2023-09-15 13:47:39 -07:00
jeffvli 7d6990eb90 Add notice regarding broken MPV version 2023-09-15 12:52:03 -07:00
jeffvli d75ea94161 Fix first launch mpv playback (#210) 2023-09-15 03:08:17 -07:00
Kendall Garner 1badecc20a always call autoNext, even if not used (#241) 2023-09-10 15:08:48 -07:00
Kendall Garner c90a56811d [bugfix]: support final lyric with no newline (#240) 2023-09-10 15:07:21 -07:00
nate contino 4e5e3bc9a1 Adjust quarantine bit warning wording to include all Macs running 12+ (#236) 2023-09-10 15:04:24 -07:00
Kendall Garner c8397bb5ef Add transparency/opacity for queue sidebar (#231)
* add opacity

* add background for song metadata

* Add padding and border radius to opacity elements

* Remove font-weight transition on active lyrics (#233)

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-09-10 15:03:46 -07:00
Alberto Rodríguez 0ae53b023c improved client detection (#229)
Co-authored-by: = <=>
2023-09-10 13:01:32 -07:00
Kendall Garner 1acfa93f1a Improve MPV initialization and restore (#222)
- set mpv settings only after it has successfully started (at least on linux, settings were not taken)
- change timing of restore queue to behave properly
2023-08-24 18:28:50 -07:00
jeffvli b60ba27892 Allow reuathentication for jellyfin (#214) 2023-08-24 18:17:20 -07:00
jeffvli 7ddba8ede7 Fix JF song filter import (#223) 2023-08-24 18:10:58 -07:00
jeffvli a8bd53b757 Adjust jellyfin playlist fetch 2023-08-24 18:04:01 -07:00
jeffvli 877b2e9f3b Fix normalized album duration values (#205) 2023-08-11 21:08:13 -07:00
jeffvli 663893dccb Fix missing related artist images 2023-08-09 21:30:27 -07:00
jeffvli 96ace40fc3 Upgrade electron-builder to latest 2023-08-08 10:01:08 -07:00
jeffvli e9de9f5b65 Fix electron build target 2023-08-08 09:52:58 -07:00
jeffvli c92c94cf1a Bump to v0.3.0 2023-08-08 09:44:14 -07:00
jeffvli 1d664bbbd7 Home screen improvements
- Only show spinner on load rather than fetch
- Add refresh button to explore
- Adjust stale times
2023-08-08 09:26:53 -07:00
jeffvli 7c59722f0a Improve genres for jellyfin
- Support music folder
- Add images
- Fix genre filters on album/track filters
2023-08-08 09:26:53 -07:00
jeffvli 3f813b1a26 Make album detail columns customizable 2023-08-08 09:26:53 -07:00
jeffvli 13d6758500 Center playerbar time values 2023-08-08 09:26:53 -07:00
jeffvli 2b6323c396 Fix min-size header image 2023-08-08 09:26:53 -07:00
jeffvli 8338aaf18d Add related genre albums to album detail 2023-08-08 09:26:48 -07:00
jeffvli 5f5c3bbb11 Fix filter on id pages 2023-08-08 01:30:05 -07:00
jeffvli 2a9d30e43d Fix genre grid route 2023-08-08 01:29:12 -07:00
jeffvli d1e5571163 Add initial fetch for all grid views 2023-08-08 01:26:02 -07:00
jeffvli e542fcb8aa Memoize context values 2023-08-08 00:38:32 -07:00
jeffvli 1111fd00a1 Remove web build from electron build 2023-08-08 00:35:29 -07:00
jeffvli 6ae8046781 Fix search params being overriden on table view 2023-08-08 00:33:47 -07:00
jeffvli 689b40eb91 Add web prod build 2023-08-08 00:23:46 -07:00
jeffvli b3bdff446d Add vercel rewrite 2023-08-08 00:23:38 -07:00
jeffvli 8686a7c592 Various lyrics improvements
- Update wording on provider fetcher
- Invalidate query on reset instead of resetQuery
2023-08-07 21:59:55 -07:00
jeffvli 59a851f8c8 Add new version dialog 2023-08-07 21:44:39 -07:00
jeffvli fedef48411 Add dialog component 2023-08-07 21:42:52 -07:00
jeffvli e3fc99cf82 Additional adjustments on mpv play 2023-08-07 14:48:02 -07:00
jeffvli f09ad1da89 Add dynamic grid sizing 2023-08-07 14:42:47 -07:00
Gelaechter 1ab75f7187 Fix sidebar layout to show full album cover (#193) 2023-08-06 12:03:45 -07:00
jeffvli 3d1f36e85a Replace header with regular image 2023-08-06 12:02:13 -07:00
jeffvli 23e791c829 Attempt additional cleanup on mpv start 2023-08-06 11:41:40 -07:00
jeffvli aaaaee7043 Format themes 2023-08-06 10:48:51 -07:00
jeffvli fca135ce2b Add additional lyrics customizability options (#146) 2023-08-04 19:32:41 -07:00
jeffvli 72b4a60c7b Adjust folder filter on song list 2023-08-04 13:45:10 -07:00
jeffvli ff68de8c09 Fix jellyfin album song order (#192) 2023-08-04 13:26:33 -07:00
jeffvli c8663db4ba Clean up 2023-08-04 13:08:40 -07:00
jeffvli 95780f1969 Add optional chain to jf genres 2023-08-04 13:07:39 -07:00
jeffvli 1327d58b23 Fix clear, clean up 2023-08-04 12:29:55 -07:00
jeffvli f6d239d87c Improve lyrics persistence
- Search overrides existing lyrics query
- Separate reset / clear commands
2023-08-04 11:51:01 -07:00
jeffvli 80fb844d3c Adjust subsonic genre normalization 2023-08-04 10:32:35 -07:00
jeffvli b35d3c3256 Set scrollbar autohide to leave 2023-08-04 03:31:36 -07:00
jeffvli 14453a8524 Change dropdown transitions back to fade 2023-08-04 03:28:42 -07:00
jeffvli d0aba6e16e Support genres in context menu 2023-08-04 02:27:04 -07:00
jeffvli 0b207c78e7 Add genre link to table cells 2023-08-04 01:53:23 -07:00
jeffvli ee83fdba71 Persist lyrics queries in indexeddb 2023-08-04 01:41:45 -07:00
jeffvli adfa748bfb Adjust genre links on detail pages 2023-08-03 19:04:12 -07:00
jeffvli 505974320f Fix custom filter implementation 2023-08-03 19:03:23 -07:00
jeffvli 5896d886d7 Add genre list route and implementation 2023-08-03 16:58:35 -07:00
jeffvli f6d74ce9c3 Add params to genre list query key, adjust cache/staletime 2023-07-31 17:53:21 -07:00
jeffvli f443c466b0 Fix song list filter genres not loading 2023-07-31 17:40:55 -07:00
jeffvli 8029712b55 Add initial genre list support 2023-07-31 17:17:26 -07:00
jeffvli 4d5085f230 Fix linter rules 2023-07-31 17:17:25 -07:00
jeffvli 9f60769b65 Fix missing autosizer types 2023-07-31 17:17:25 -07:00
jeffvli e618ac7590 Re-add limits to list item queries 2023-07-31 17:17:25 -07:00
jeffvli 9f55238b74 Add simpleimg to title cell 2023-07-31 17:17:25 -07:00
Gelaechter 93e00e7afb Fix Jellyfin album duration normalization (#191) 2023-07-29 10:35:30 -07:00
jeffvli 8e83beffcc Adjust title font sizes, fix placeholder sizing 2023-07-24 15:07:56 -07:00
jeffvli 230fa33525 Adjust various elements on album artist page 2023-07-24 14:51:37 -07:00
jeffvli ed070850a4 Remove current album from "more from this artist" 2023-07-24 14:51:37 -07:00
jeffvli 2072f9554e Memoize context menu provider 2023-07-24 14:51:37 -07:00
jeffvli 2aaf3c34c8 Refactor library header 2023-07-24 14:51:33 -07:00
jeffvli b57f601e1b Add css modules extensions 2023-07-24 14:37:42 -07:00
jeffvli 51f8415025 Update lockfile 2023-07-23 05:36:42 -07:00
jeffvli e6bcb4e237 Fix ag-grid styles 2023-07-23 05:31:10 -07:00
Kendall Garner c9dbf9b5be Add remote control (#164)
* draft add remotes

* add favorite, rating

* add basic auth
2023-07-23 05:23:18 -07:00
jeffvli 0a13d047bb Begin support for container queries with css modules 2023-07-23 05:18:08 -07:00
jeffvli 84bec824f2 Add css modules, remove styled components linters and utils 2023-07-23 05:16:29 -07:00
jeffvli 03a4a1da55 Fix color by id if no image found 2023-07-23 03:50:55 -07:00
jeffvli 2c9509b58d Add recommended css module extension 2023-07-23 03:49:45 -07:00
jeffvli 42ea5af2eb Improve css module support
- Add readable identifiers
- Export as camel case
2023-07-23 03:49:32 -07:00
jeffvli ebf0d3b47f Add new rules 2023-07-23 01:51:08 -07:00
jeffvli e44b8592e5 Bump packages 2023-07-23 01:50:20 -07:00
jeffvli f9338aafcd Fix original search params from being replaced 2023-07-22 05:26:15 -07:00
jeffvli 3aec139f58 Use search params for artist-specific lists 2023-07-22 05:05:33 -07:00
jeffvli 8a367b00a3 Remove paginated table display type 2023-07-22 04:42:46 -07:00
jeffvli 46374ef2b5 Fix delete on full playlist view 2023-07-22 01:52:55 -07:00
jeffvli febe1a703c Revert library header height change 2023-07-21 19:11:15 -07:00
jeffvli 853770ea8e Prevent wrong initial color on navigation on the same route 2023-07-21 18:51:37 -07:00
jeffvli 48eaddbeda Reduce z-index on page headers
- Potentially causes tooltips to be hidden
2023-07-21 18:40:52 -07:00
jeffvli 0a26c489b6 Change page animation easing transition 2023-07-21 18:32:47 -07:00
jeffvli bbee3fc655 Update scrollArea for relevant pages 2023-07-21 18:04:05 -07:00
jeffvli a8dfc7bcd6 Use memoized carousel component for pages 2023-07-21 18:03:19 -07:00
jeffvli 74384639de Add memoized version of grid carousel 2023-07-21 18:00:51 -07:00
jeffvli 20524452ae Fix noHeader implementation for scrollArea 2023-07-21 18:00:13 -07:00
jeffvli f274801be6 Remove table header x margins 2023-07-21 17:58:04 -07:00
jeffvli 9d18384b2d Add stickyHeader prop to table component 2023-07-21 17:53:54 -07:00
jeffvli 92d7560362 Fix custom header text positioning to match originals 2023-07-21 17:30:37 -07:00
jeffvli 47d84fae2d Add missing peer package 2023-07-21 05:38:16 -07:00
jeffvli c3d8791455 Refactor scrollarea component for overlayscrollbars 2023-07-21 05:20:40 -07:00
jeffvli 3d6f5a2748 Add overlayscrollbars package 2023-07-21 04:02:27 -07:00
jeffvli 61403510d4 Increase height of detail header 2023-07-21 03:35:59 -07:00
jeffvli e796b031ea Clean up various queries 2023-07-21 00:18:53 -07:00
jeffvli 2d62b9d72d Set full height on disc number cell 2023-07-21 00:18:53 -07:00
jeffvli f5cbcace64 Remove autofit on gridReady
- Potentially causes horizontal scrollbar to flash on render
2023-07-21 00:18:53 -07:00
jeffvli e7c15ef5f1 Tweak average color algorithm 2023-07-21 00:18:53 -07:00
jeffvli 31eb22f968 Improve header color styles on detail pages 2023-07-21 00:18:44 -07:00
jeffvli 713260bfc9 Add rgb to rgba util 2023-07-20 17:09:10 -07:00
jeffvli ba00538cc3 Brighten sidebar items 2023-07-20 17:08:58 -07:00
jeffvli dd2dd797a1 Add check for undefined list query 2023-07-20 17:00:01 -07:00
jeffvli eec556d34a Additional fix to query key list values 2023-07-20 03:38:50 -07:00
jeffvli 7378fd1f20 Fix broken pagination split logic 2023-07-20 02:08:23 -07:00
jeffvli 6821735f65 Split key pagination in central handler 2023-07-20 01:55:49 -07:00
jeffvli 1cb0a1d72a Add initial data fetch function to grid 2023-07-20 00:41:18 -07:00
jeffvli 287f1dc0e1 Update search list implementation 2023-07-20 00:41:18 -07:00
jeffvli 6dd9333dbb Update album list implementation 2023-07-20 00:41:13 -07:00
jeffvli 55937e71db Allow play button click handler to have args 2023-07-20 00:41:13 -07:00
jeffvli c0e3174d09 Fix artist image placeholders for Navidrome 2023-07-20 00:41:13 -07:00
jeffvli 440cc04fbc Update album artist list implementation 2023-07-20 00:41:13 -07:00
jeffvli 6cd27c3e88 Update song list implementation 2023-07-20 00:41:09 -07:00
jeffvli 85964bfded Update playlist list implementation 2023-07-20 00:41:05 -07:00
jeffvli 8b4a2d1ac0 Simplify list store and table implementation 2023-07-20 00:41:04 -07:00
jeffvli 9bcefb3105 Add generic list context 2023-07-20 00:41:04 -07:00
jeffvli 4029127018 Add fallback to line clamp in case of artist overflow 2023-07-20 00:41:04 -07:00
jeffvli f9ddd3140a Add search to playlist list 2023-07-20 00:41:04 -07:00
jeffvli 651af8539a Add reusable list filter refresh handler 2023-07-20 00:41:00 -07:00
jeffvli 4e4eca14ec Add change action to search clear button (#176) 2023-07-19 01:32:55 -07:00
jeffvli 1ec70bfa78 Add search to playlist api 2023-07-19 01:32:09 -07:00
jeffvli c3f97dfa4c Split pagination from filter in query keys 2023-07-19 01:29:42 -07:00
jeffvli bba27c5ddb Migrate playlist list to use list store 2023-07-19 00:28:46 -07:00
jeffvli 78860db537 Bump react-icons version 2023-07-18 18:40:07 -07:00
jeffvli ece7fecc76 Highlight currently playing song on all song tables (#178) 2023-07-18 18:39:39 -07:00
jeffvli 919016ca5a Add table context 2023-07-18 18:34:51 -07:00
jeffvli b8dfbf9d49 Add Inter font style 2023-07-18 17:38:41 -07:00
jeffvli 179129b7cb Add actions table column 2023-07-18 17:37:32 -07:00
jeffvli 817675ee0e Update playlist headers 2023-07-18 09:54:51 -07:00
jeffvli 57cdb0eb69 Reduce size of sidebar items 2023-07-18 09:51:47 -07:00
jeffvli 8233a56def Fix smart playlist sort order on creation (#163) 2023-07-16 23:59:51 -07:00
jeffvli 0c54b79c09 Clean up 2023-07-16 23:57:31 -07:00
jeffvli 3fb9853eb6 Fix select label positioning 2023-07-16 23:57:01 -07:00
jeffvli 1de89071e8 Fix hidden scrollbar for electron 25
- Newer chromium versions do not support scrollbar overlay css
2023-07-16 23:48:44 -07:00
jeffvli be37dada13 Bump to electron v25 2023-07-16 23:33:38 -07:00
jeffvli c27a9a8ffb Remove base animation of smart playlist query builder display 2023-07-16 23:25:00 -07:00
jeffvli be0792a5c7 Refresh playlist list on delete (#134) 2023-07-16 23:23:07 -07:00
jeffvli 37e4940c2e Set full playlist view as default 2023-07-16 13:40:50 -07:00
jeffvli e965bd2663 Update album artist list views 2023-07-16 13:35:40 -07:00
jeffvli b9caa73405 Fix list response types 2023-07-16 13:35:40 -07:00
jeffvli 0ba8d5bf70 Add extra item types to card row 2023-07-16 13:35:32 -07:00
jeffvli 1fc5e9a0e8 Update song list table view 2023-07-16 11:44:33 -07:00
jeffvli f09227d963 Update album table view 2023-07-16 11:15:47 -07:00
jeffvli 47ecbf0601 Fix column width declarations 2023-07-16 11:06:09 -07:00
jeffvli 481258484c Add reusable virtual table hook 2023-07-15 20:27:35 -07:00
jeffvli 3dcb0dc4ed Allow navigating directly to playlist song view 2023-07-15 15:57:40 -07:00
jeffvli d64040f3f0 Enable delete button in playlist song list 2023-07-15 15:53:36 -07:00
jeffvli 63a77ae68c Add playlist grid views 2023-07-15 11:22:09 -07:00
jeffvli e980e31bd2 Handle playlist card types 2023-07-15 11:22:09 -07:00
jeffvli 3b5dff795f Change default popup transition 2023-07-15 11:22:09 -07:00
jeffvli 8129a3994b Add order toggle button component 2023-07-15 11:22:09 -07:00
jeffvli 734b632c6c Fix item size slider for grid on album list 2023-07-15 11:22:09 -07:00
jeffvli 34f05fa2a5 Add undefined check to error message
- In some cases you can get an irrecoverable UI due to this being undefined
2023-07-15 11:22:09 -07:00
Kyan f74e02eb09 Add fix to hide the "More from this artist" carousel when it contains no albums (#173)
* Add fix to hide carousel with other albums
2023-07-15 11:13:56 -07:00
jeffvli 287fbab29a Set initial count on playlist list (#180)
- Allows for easier infinite scrolling to end of list
2023-07-14 19:13:03 -07:00
jeffvli e9d1e4a597 Set stylelint indentation to 4 2023-07-04 17:19:29 -07:00
jeffvli 70f893e5e9 Create vercel rewrite (#168) 2023-07-01 19:24:06 -07:00
jeffvli 30e52ebb54 Lint all files 2023-07-01 19:14:12 -07:00
jeffvli 22af76b4d6 Update prettier for 4 space tab 2023-07-01 19:06:57 -07:00
jeffvli cb7bf438e9 Add new app logo 2023-07-01 14:11:15 -07:00
zackslash a1b5c21a84 use platform window bar style as default (#150) 2023-07-01 13:50:11 -07:00
zackslash 4c5fa0750b fix label-font-family typo (#157) 2023-07-01 12:45:55 -07:00
Gelaechter 22160ba59f Show first instance when starting a second one (#149) 2023-07-01 12:45:08 -07:00
jeffvli ba8e23e8d4 Add missing sidebar configuration 2023-06-14 00:45:10 -07:00
jeffvli 7fa4641dfe Bump to v0.2.0 2023-06-14 00:13:29 -07:00
jeffvli 4167af098f Various cleanup 2023-06-14 00:12:10 -07:00
jeffvli c5f551e963 Use flex gap instead of line height for lyrics 2023-06-13 18:47:47 -07:00
jeffvli fbd0e5b27b Fix image position when scaling 2023-06-13 18:47:43 -07:00
jeffvli 5877b8cc6f Fix feature carousel dynamic styles 2023-06-13 18:38:55 -07:00
jeffvli 23f4bfde99 Add additional outline button styles 2023-06-13 18:38:07 -07:00
jeffvli 4898fa7dcf Add responsive styling for min-width sidebar 2023-06-13 18:08:42 -07:00
jeffvli a6990fd732 Fix various queue behavior
- Fix add next behavior when shuffle is enabled
- Fix shuffled queue when songs are removed from queue
- Fix queue indices when currently playing song is removed
- Re-shuffle queue after queue is finished when shuffle is enabled
2023-06-13 17:47:40 -07:00
Kendall Garner 2fac9efc1b initial implementation for password saving (#132)
* initial implementation for password saving

* support restoring password in interceptor

* Fix modal overflow and position styles

* warn about 429, better error handling

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
2023-06-13 10:52:51 -07:00
jeffvli a3a84766e4 Fix artist/album artist cells (#117) 2023-06-11 23:27:50 -07:00
jeffvli 0e9a77ffe0 Remove unneeded link condition 2023-06-11 20:40:27 -07:00
jeffvli 8f7e6a5222 Disable link from invalid artist on table cells (#117) 2023-06-11 20:38:32 -07:00
jeffvli 736945d6ef Set explicit window bar width 2023-06-11 20:18:28 -07:00
jeffvli f97e855f51 Support local navidrome album artist image (#116) 2023-06-11 20:02:14 -07:00
Kendall Garner d6e628099c Add LrcLib Fetcher (#136)
* lrclib, do not show search/clear buttons if no fetchers configured
2023-06-11 12:45:50 -07:00
Gelaechter d7ca25525c Add zooming functionality (#140) 2023-06-11 12:45:13 -07:00
Jeff 72099cb1fe Add configuration for player image aspect ratio (#138) 2023-06-10 19:14:49 -07:00
jeffvli eeefe9d9dc Add updated scroll container to unsync lyrics 2023-06-09 17:24:34 -07:00
jeffvli 86c3e54119 Add tooltip to offset input 2023-06-09 17:24:34 -07:00
jeffvli ea0737cf1f Fix overlay scrollbar 2023-06-09 17:24:34 -07:00
jeffvli f4eaacc64a Adjust vertical padding on lyrics display 2023-06-09 17:24:34 -07:00
jeffvli 7f6efbe6dc Hide search / clear actions on web 2023-06-09 17:24:34 -07:00
jeffvli 72811dbedb Fix broken layout on lyrics scroll
- App layout would break when transitioning into the full-screen due to scrollIntoView
- Replace scroll action with scrollTop implementation
2023-06-09 17:24:34 -07:00
jeffvli 493e13ebc0 Fix id return type on netease fetcher 2023-06-09 17:24:34 -07:00
jeffvli 14aeee888f Adjust header, input, and modal styles 2023-06-09 17:24:34 -07:00
jeffvli cbc08d6f03 Improve lyrics match with scored searches 2023-06-09 17:24:34 -07:00
jeffvli 77703b904f Allow override on songs without default found lyrics 2023-06-09 17:24:34 -07:00
jeffvli 762644d23d Fix provider text 2023-06-09 17:24:34 -07:00
jeffvli 75403078d2 Disable action buttons when no song selected 2023-06-09 17:24:34 -07:00
jeffvli 255a131f3b Require only single query for search 2023-06-09 17:24:34 -07:00
jeffvli e56350c1c2 Add remaining lyric actions functionality 2023-06-09 17:24:34 -07:00
jeffvli aaa1b5f63a Fix override on song change 2023-06-09 17:24:34 -07:00
jeffvli 3d409bb6f1 Fix queue container layout 2023-06-09 17:24:34 -07:00
jeffvli 7ab532be07 Improve accessibility of actions bar 2023-06-09 17:24:34 -07:00
jeffvli 946b73d215 Miscellanous fixes 2023-06-09 17:24:34 -07:00
jeffvli 2f0634dc03 Add lyric search selection and override 2023-06-09 17:24:34 -07:00
jeffvli f8ecb3fc53 Update fetchers to support search 2023-06-09 17:24:34 -07:00
jeffvli 01608fa875 Fix ref issue 2023-06-09 17:24:34 -07:00
jeffvli 14a6766072 Add initial lyrics search UI 2023-06-09 17:24:34 -07:00
jeffvli 0fa5b6496f Add lyric search functions and query 2023-06-09 17:24:34 -07:00
jeffvli 43c11ab6e3 Add alternative lyrics format parser
- Many synced lyrics on NetEase are broken due to not being the standard lrc format
2023-06-09 17:24:34 -07:00
jeffvli 41a901f3c4 Catch error on jellyfin query fail 2023-06-09 17:24:34 -07:00
jeffvli 2bdc664619 Fix animation transition on fullscreen player open 2023-06-09 17:24:34 -07:00
Kendall Garner 8835fc640a Include lyric simplification, restore clear button
- merges lyric simplification
- restores metadata/clear
2023-06-09 17:24:34 -07:00
jeffvli f92cd89c46 Add fade in animation for lyrics container 2023-06-09 17:24:34 -07:00
jeffvli a1a113d3c6 Move scroll container to wrap lyrics only 2023-06-09 17:24:34 -07:00
jeffvli 3f78c3f420 Move all lyrics fetching logic into query 2023-06-09 17:24:34 -07:00
jeffvli f10912d930 Set lyrics scrollarea to max height by default 2023-06-09 17:24:34 -07:00
jeffvli 98fa47348c Replace main lyrics listener with promise handler 2023-06-09 17:24:34 -07:00
jeffvli d38c846e80 Update lyric fetcher return types 2023-06-09 17:24:34 -07:00
Kendall Garner 007a099951 Lyrics Improvements
- Make the settings text actually consistent with behavior
- Add metadata (artist/track name) for fetched tracks
- Add ability to remove incorrectly fetched lyric
- Add lyric fetch cache; save the last 10 fetches
- Add ability to change offset in full screen, add more comments
2023-06-09 17:24:34 -07:00
jeffvli 9622cd346c Force play on queue row double click 2023-06-06 19:45:47 -07:00
jeffvli c3c1f4cc5f Refactor mpv initialization/cleanup
- Don't re-initialize the player on re-render
- Fixes the player potentially crashing on hot reload
2023-06-06 10:48:47 -07:00
jeffvli d97fe4c621 Replace node-mpv with fork version 2023-06-06 00:57:05 -07:00
jeffvli 7e5733db34 Add missing key on sidebar 2023-06-06 00:55:49 -07:00
jeffvli d1dde2428f Minor fixes on main component 2023-06-04 16:46:05 -07:00
jeffvli 190dd71b3c Fix styling on unsync lyrics 2023-06-04 16:46:05 -07:00
jeffvli feb61c28d7 Change padding on vertical player 2023-06-04 16:46:05 -07:00
jeffvli f380eccc68 Improve responsive styles for playerbar 2023-06-04 16:46:05 -07:00
jeffvli cf43bf360e Use scale instead of font-size for active lyric 2023-06-04 16:46:05 -07:00
jeffvli 48dfd469ed Additional styling changes 2023-06-04 16:46:05 -07:00
jeffvli 5dd860735d Adjust lyrics styling / animations 2023-06-04 16:46:05 -07:00
jeffvli 7cd2077dcd Refactor layout to grid 2023-06-04 16:46:05 -07:00
jeffvli 7430bba853 Update minimum app dimensions 2023-06-04 16:46:05 -07:00
jeffvli 782c351ca6 Disable query if no song present 2023-06-04 16:46:05 -07:00
Kendall Garner 3aef2a80a7 rename listener function 2023-06-04 16:46:05 -07:00
Kendall Garner 85a10c799a address comments 2023-06-04 16:46:05 -07:00
Kendall Garner 9eef570740 support .txt 2023-06-04 16:46:05 -07:00
Kendall Garner 58f38b2655 add jellyfin, improvements 2023-06-04 16:46:05 -07:00
Kendall Garner 85d2576bdc Improved lyric syncing, fetch
- uses a somewhat more sane way to parse lyrics and teardown timeouts
- adds 'seeked' to setCurrentTime to make detecting seeks in lyric much easier
- adds ability to fetch lyrics from genius/netease (desktop only)
2023-06-04 16:46:05 -07:00
Kendall Garner 23f9bd4e9f initial implementation for lyrics 2023-06-04 16:46:05 -07:00
jeffvli 8eb0029bb8 Add undefined check 2023-06-03 05:46:20 -07:00
jeffvli c8a0df4759 Add configurable sidebar 2023-06-03 05:46:20 -07:00
jeffvli e7bc29a8f1 Remove unneeded hook 2023-06-03 05:46:20 -07:00
jeffvli 5295c69f46 Bump ts-rest 2023-06-03 05:46:15 -07:00
jeffvli f58552be84 Remove unneeded console logs 2023-06-03 00:40:57 -07:00
jeffvli cd57142caf Fix duplicate import 2023-06-03 00:40:13 -07:00
jeffvli 86ad2d0383 Fix invalid spinner props 2023-06-03 00:39:52 -07:00
jeffvli 7d5aa6fd13 Add sidebar customization settings 2023-06-03 00:39:33 -07:00
jeffvli f2ef630921 Fix global button styling on settings page 2023-06-02 22:38:49 -07:00
jeffvli 9250b30249 Downgrade framer-motion from v10 -> v9
- Issues are present in v10 that prevent drag/drop reorder from working properly
2023-06-02 18:35:36 -07:00
jeffvli 2b16cce0aa Move global search to default result 2023-06-02 17:25:15 -07:00
jeffvli 34870556b4 Add auto-focus to search input 2023-06-02 13:13:33 -07:00
jeffvli 7e2d9bd585 Split album list views 2023-06-02 13:09:28 -07:00
jeffvli 691bc8f1ef Add full container spinner/loader 2023-06-02 13:07:30 -07:00
jeffvli 5dbc0c61c5 Clean up from mantine upgrade 2023-06-02 11:48:43 -07:00
jeffvli 0bc1ee3492 Downgrade auto-sizer package
- issue with types with ts v4
2023-06-02 11:48:22 -07:00
jeffvli 7403a46f91 Remove initial animation on page header 2023-06-02 11:38:33 -07:00
jeffvli 8ffb81093d Improve feature carousel component
- Add play button
- Clamp title to 1 line
- Restrict to 1 genre and 1 artist
- Infinite loop pagination
2023-06-02 01:30:08 -07:00
jeffvli d312c3c70a Handle initial render item count for carousel 2023-06-02 01:21:52 -07:00
jeffvli cd66a9dccb Clean up 2023-06-02 01:21:02 -07:00
jeffvli f2690b262f Remove container query requirement for carousel sizing 2023-06-02 01:01:50 -07:00
jeffvli 63c5a83911 Bump packages 2023-06-01 20:19:07 -07:00
jeffvli 17b1acad9d Optimize various pages 2023-06-01 20:08:43 -07:00
jeffvli e7c7eb3ec0 Split up main content route component 2023-05-31 01:13:54 -07:00
jeffvli fa0a21a021 Optimize app outlet 2023-05-31 00:27:16 -07:00
jeffvli 791088deb6 Persist scroll offset on table-view album list 2023-05-30 20:05:52 -07:00
jeffvli 9c1a2a4a8d Fix playlist form
- Invalid initial state and definition for public playlist
2023-05-30 19:35:22 -07:00
jeffvli 6d092d9ebc Add native frame styles per OS 2023-05-27 14:02:10 -07:00
jeffvli 73997cf6c7 Add clarity to conditional restart 2023-05-27 14:02:10 -07:00
jeffvli 1d074dae2e Adjust conditionals in a few places 2023-05-27 14:02:10 -07:00
Gelaechter a878875f83 Add native titlebar & fix app restart for AppImages 2023-05-27 14:02:10 -07:00
Kendall Garner d055ae89e0 media session for windows/mac 2023-05-26 18:20:27 -07:00
Kendall Garner f83639d5f8 round volume, update all clients 2023-05-26 18:20:27 -07:00
Kendall Garner 97ccf3bc6d add media session/mpris 2023-05-26 18:20:27 -07:00
jeffvli 76805a0b19 Fix ND potentially setting undefined undefined credential (#60) 2023-05-24 00:33:35 -07:00
jeffvli 0103a84358 Add clear buttons to search input 2023-05-21 21:01:23 -07:00
jeffvli 611cbc6dd9 Fix search results being capped to window height 2023-05-21 20:51:52 -07:00
463 changed files with 93981 additions and 77504 deletions
+45 -40
View File
@@ -7,53 +7,58 @@ import { dependencies as externals } from '../../release/app/package.json';
import webpackPaths from './webpack.paths';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
const createStyledComponentsTransformer = require('typescript-plugin-styled-components').default;
const styledComponentsTransformer = createStyledComponentsTransformer();
const configuration: webpack.Configuration = {
externals: [...Object.keys(externals || {})],
externals: [...Object.keys(externals || {})],
module: {
rules: [
{
exclude: /node_modules/,
test: /\.[jt]sx?$/,
use: {
loader: 'ts-loader',
options: {
// Remove this line to enable type checking in webpack builds
transpileOnly: true,
},
module: {
rules: [
{
exclude: /node_modules/,
test: /\.[jt]sx?$/,
use: {
loader: 'ts-loader',
options: {
// Remove this line to enable type checking in webpack builds
transpileOnly: true,
getCustomTransformers: () => ({ before: [styledComponentsTransformer] }),
},
},
},
],
},
output: {
// https://github.com/webpack/webpack/issues/1114
library: {
type: 'commonjs2',
},
},
path: webpackPaths.srcPath,
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
],
},
output: {
// https://github.com/webpack/webpack/issues/1114
library: {
type: 'commonjs2',
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
fallback: {
child_process: false,
},
plugins: [new TsconfigPathsPlugin({ baseUrl: webpackPaths.srcPath })],
modules: [webpackPaths.srcPath, 'node_modules'],
},
path: webpackPaths.srcPath,
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
],
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
fallback: {
child_process: false,
},
plugins: [new TsconfigPathsPlugin({ baseUrl: webpackPaths.srcPath })],
modules: [webpackPaths.srcPath, 'node_modules'],
},
stats: 'errors-only',
stats: 'errors-only',
};
export default configuration;
+127
View File
@@ -0,0 +1,127 @@
import 'webpack-dev-server';
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
const { version } = require('../../package.json');
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: ['web'],
entry: {
remote: path.join(webpackPaths.srcRemotePath, 'index.tsx'),
worker: path.join(webpackPaths.srcRemotePath, 'service-worker.ts'),
},
output: {
path: webpackPaths.dllPath,
publicPath: '/',
filename: '[name].js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRemotePath, 'index.ejs'),
favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: true,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
templateParameters: {
version,
prod: false,
},
}),
],
node: {
__dirname: false,
__filename: false,
},
watch: true,
};
export default merge(baseConfig, configuration);
+142
View File
@@ -0,0 +1,142 @@
/**
* Build config for electron renderer process
*/
import path from 'path';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
const { version } = require('../../package.json');
checkNodeEnv('production');
deleteSourceMaps();
const devtoolsConfig =
process.env.DEBUG_PROD === 'true'
? {
devtool: 'source-map',
}
: {};
const configuration: webpack.Configuration = {
...devtoolsConfig,
mode: 'production',
target: ['web'],
entry: {
remote: path.join(webpackPaths.srcRemotePath, 'index.tsx'),
worker: path.join(webpackPaths.srcRemotePath, 'service-worker.ts'),
},
output: {
path: webpackPaths.distRemotePath,
publicPath: './',
filename: '[name].js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?(a|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?(a|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
}),
new CssMinimizerPlugin(),
],
},
plugins: [
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
}),
new MiniCssExtractPlugin({
filename: 'remote.css',
}),
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(webpackPaths.srcRemotePath, 'index.ejs'),
favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: true,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
templateParameters: {
version,
prod: true,
},
}),
],
};
export default merge(baseConfig, configuration);
+13 -1
View File
@@ -67,7 +67,10 @@ const configuration: webpack.Configuration = {
{
loader: 'css-loader',
options: {
modules: true,
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]',
exportLocalsConvention: 'camelCaseOnly',
},
sourceMap: true,
importLoaders: 1,
},
@@ -168,6 +171,14 @@ const configuration: webpack.Configuration = {
.on('close', (code: number) => process.exit(code!))
.on('error', (spawnError) => console.error(spawnError));
console.log('Starting remote.js builder...');
const remoteProcess = spawn('npm', ['run', 'start:remote'], {
shell: true,
stdio: 'inherit',
})
.on('close', (code: number) => process.exit(code!))
.on('error', (spawnError) => console.error(spawnError));
console.log('Starting Main Process...');
spawn('npm', ['run', 'start:main'], {
shell: true,
@@ -175,6 +186,7 @@ const configuration: webpack.Configuration = {
})
.on('close', (code: number) => {
preloadProcess.kill();
remoteProcess.kill();
process.exit(code!);
})
.on('error', (spawnError) => console.error(spawnError));
+4 -1
View File
@@ -54,7 +54,10 @@ const configuration: webpack.Configuration = {
{
loader: 'css-loader',
options: {
modules: true,
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]',
exportLocalsConvention: 'camelCaseOnly',
},
sourceMap: true,
importLoaders: 1,
},
+113 -109
View File
@@ -13,128 +13,132 @@ import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
checkNodeEnv('development');
}
const port = process.env.PORT || 4343;
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
devtool: 'inline-source-map',
mode: 'development',
mode: 'development',
target: ['web', 'electron-renderer'],
target: ['web', 'electron-renderer'],
entry: [
`webpack-dev-server/client?http://localhost:${port}/dist`,
'webpack/hot/only-dev-server',
path.join(webpackPaths.srcRendererPath, 'index.tsx'),
],
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
entry: [
`webpack-dev-server/client?http://localhost:${port}/dist`,
'webpack/hot/only-dev-server',
path.join(webpackPaths.srcRendererPath, 'index.tsx'),
],
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new ReactRefreshWebpackPlugin(),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
],
node: {
__dirname: false,
__filename: false,
},
devServer: {
port,
compress: true,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
publicPath: '/',
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
historyApiFallback: {
verbose: true,
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]',
exportLocalsConvention: 'camelCaseOnly',
},
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
setupMiddlewares(middlewares) {
return middlewares;
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new ReactRefreshWebpackPlugin(),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
],
node: {
__dirname: false,
__filename: false,
},
devServer: {
port,
compress: true,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
publicPath: '/',
},
historyApiFallback: {
verbose: true,
},
setupMiddlewares(middlewares) {
return middlewares;
},
},
},
};
export default merge(baseConfig, configuration);
+135
View File
@@ -0,0 +1,135 @@
/**
* Build config for electron renderer process
*/
import path from 'path';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('production');
deleteSourceMaps();
const devtoolsConfig =
process.env.DEBUG_PROD === 'true'
? {
devtool: 'source-map',
}
: {};
const configuration: webpack.Configuration = {
...devtoolsConfig,
mode: 'production',
target: ['web'],
entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')],
output: {
path: webpackPaths.distWebPath,
publicPath: 'auto',
filename: 'renderer.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?(a|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]',
exportLocalsConvention: 'camelCaseOnly',
},
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?(a|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
}),
new CssMinimizerPlugin(),
],
},
plugins: [
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
}),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
isDevelopment: process.env.NODE_ENV !== 'production',
}),
],
};
export default merge(baseConfig, configuration);
+22 -14
View File
@@ -5,7 +5,9 @@ const rootPath = path.join(__dirname, '../..');
const dllPath = path.join(__dirname, '../dll');
const srcPath = path.join(rootPath, 'src');
const assetsPath = path.join(rootPath, 'assets');
const srcMainPath = path.join(srcPath, 'main');
const srcRemotePath = path.join(srcPath, 'remote');
const srcRendererPath = path.join(srcPath, 'renderer');
const releasePath = path.join(rootPath, 'release');
@@ -16,23 +18,29 @@ const srcNodeModulesPath = path.join(srcPath, 'node_modules');
const distPath = path.join(appPath, 'dist');
const distMainPath = path.join(distPath, 'main');
const distRemotePath = path.join(distPath, 'remote');
const distRendererPath = path.join(distPath, 'renderer');
const distWebPath = path.join(distPath, 'web');
const buildPath = path.join(releasePath, 'build');
export default {
rootPath,
dllPath,
srcPath,
srcMainPath,
srcRendererPath,
releasePath,
appPath,
appPackagePath,
appNodeModulesPath,
srcNodeModulesPath,
distPath,
distMainPath,
distRendererPath,
buildPath,
assetsPath,
rootPath,
dllPath,
srcPath,
srcMainPath,
srcRemotePath,
srcRendererPath,
releasePath,
appPath,
appPackagePath,
appNodeModulesPath,
srcNodeModulesPath,
distPath,
distMainPath,
distRemotePath,
distRendererPath,
distWebPath,
buildPath,
};
+13 -4
View File
@@ -5,20 +5,29 @@ import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';
const mainPath = path.join(webpackPaths.distMainPath, 'main.js');
const remotePath = path.join(webpackPaths.distMainPath, 'remote.js');
const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js');
if (!fs.existsSync(mainPath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The main process is not built yet. Build it by running "npm run build:main"'
)
'The main process is not built yet. Build it by running "npm run build:main"',
),
);
}
if (!fs.existsSync(remotePath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The remote process is not built yet. Build it by running "npm run build:remote"',
),
);
}
if (!fs.existsSync(rendererPath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The renderer process is not built yet. Build it by running "npm run build:renderer"'
)
'The renderer process is not built yet. Build it by running "npm run build:renderer"',
),
);
}
+1
View File
@@ -4,5 +4,6 @@ import webpackPaths from '../configs/webpack.paths';
export default function deleteSourceMaps() {
rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map'));
rimraf.sync(path.join(webpackPaths.distRemotePath, '*.js.map'));
rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map'));
}
+92 -85
View File
@@ -1,90 +1,97 @@
module.exports = {
extends: ['erb', 'plugin:typescript-sort-keys/recommended'],
ignorePatterns: ['.erb/*', 'server'],
parser: '@typescript-eslint/parser',
parserOptions: {
createDefaultProgram: true,
ecmaVersion: 12,
extends: ['erb', 'plugin:typescript-sort-keys/recommended'],
ignorePatterns: ['.erb/*', 'server'],
parser: '@typescript-eslint/parser',
project: './tsconfig.json',
sourceType: 'module',
tsconfigRootDir: './',
},
plugins: ['@typescript-eslint', 'import', 'sort-keys-fix'],
rules: {
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-shadow': ['off'],
'default-case': 'off',
'import/extensions': 'off',
'import/no-absolute-path': 'off',
// A temporary hack related to IDE not resolving correct package.json
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'error',
'import/order': [
'error',
{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
groups: ['builtin', 'external', 'internal', ['parent', 'sibling']],
'newlines-between': 'never',
pathGroups: [
{
group: 'external',
pattern: 'react',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
'import/prefer-default-export': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/interactive-supports-focus': 'off',
'jsx-a11y/media-has-caption': 'off',
'no-await-in-loop': 'off',
'no-console': 'off',
'no-nested-ternary': 'off',
'no-restricted-syntax': 'off',
'no-underscore-dangle': 'off',
'prefer-destructuring': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
ignoreCase: false,
noSortAlphabetically: false,
reservedFirst: true,
shorthandFirst: true,
shorthandLast: false,
},
],
'react/no-array-index-key': 'off',
'react/react-in-jsx-scope': 'off',
'react/require-default-props': 'off',
'sort-keys-fix/sort-keys-fix': 'warn',
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
// See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
typescript: {
alwaysTryTypes: true,
parserOptions: {
createDefaultProgram: true,
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
project: './tsconfig.json',
},
webpack: {
config: require.resolve('./.erb/configs/webpack.config.eslint.ts'),
},
sourceType: 'module',
tsconfigRootDir: './',
},
plugins: ['@typescript-eslint', 'import', 'sort-keys-fix'],
rules: {
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-shadow': ['off'],
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/no-use-before-define': ['error'],
'default-case': 'off',
'import/extensions': 'off',
'import/no-absolute-path': 'off',
// A temporary hack related to IDE not resolving correct package.json
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'error',
'import/order': [
'error',
{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
groups: ['builtin', 'external', 'internal', ['parent', 'sibling']],
'newlines-between': 'never',
pathGroups: [
{
group: 'external',
pattern: 'react',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
'import/prefer-default-export': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/interactive-supports-focus': 'off',
'jsx-a11y/media-has-caption': 'off',
'no-await-in-loop': 'off',
'no-console': 'off',
'no-nested-ternary': 'off',
'no-restricted-syntax': 'off',
'no-shadow': 'off',
'no-underscore-dangle': 'off',
'no-unused-vars': 'off',
'no-use-before-define': 'off',
'prefer-destructuring': 'off',
'react/function-component-definition': 'off',
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'react/jsx-no-useless-fragment': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
ignoreCase: false,
noSortAlphabetically: false,
reservedFirst: true,
shorthandFirst: true,
shorthandLast: false,
},
],
'react/no-array-index-key': 'off',
'react/react-in-jsx-scope': 'off',
'react/require-default-props': 'off',
'sort-keys-fix/sort-keys-fix': 'warn',
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
// See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
webpack: {
config: require.resolve('./.erb/configs/webpack.config.eslint.ts'),
},
},
},
},
};
+4 -4
View File
@@ -39,7 +39,7 @@ labels: 'bug'
<!--- Include as many relevant details about the environment you experienced the bug in -->
- Application version (e.g. v0.1.0) :
- Operating System and version (e.g. Windows 10) :
- Server and version (e.g. Navidrome v0.48.0) :
- Node version (if developing locally) :
- Application version (e.g. v0.1.0) :
- Operating System and version (e.g. Windows 10) :
- Server and version (e.g. Navidrome v0.48.0) :
- Node version (if developing locally) :
+5 -5
View File
@@ -1,6 +1,6 @@
requiredHeaders:
- Prerequisites
- Expected Behavior
- Current Behavior
- Possible Solution
- Your Environment
- Prerequisites
- Expected Behavior
- Current Behavior
- Possible Solution
- Your Environment
+5 -5
View File
@@ -4,14 +4,14 @@ daysUntilStale: 60
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- discussion
- security
- discussion
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+54
View File
@@ -0,0 +1,54 @@
# Referenced from: https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#introduction
name: Publish Docker to GHCR
permissions: write-all
on:
push:
tags:
- 'v*.*.*'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: |
linux/amd64
linux/arm/v7
linux/arm64/v8
+46
View File
@@ -0,0 +1,46 @@
# Referenced from: https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#introduction
name: Publish Docker to GHCR (Manual)
on: workflow_dispatch
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: |
linux/amd64
linux/arm/v7
linux/arm64/v8
+43 -29
View File
@@ -3,37 +3,51 @@ name: Publish Linux (Manual)
on: workflow_dispatch
jobs:
publish:
runs-on: ${{ matrix.os }}
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
strategy:
matrix:
os: [ubuntu-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
steps:
- name: Checkout git repo
uses: actions/checkout@v1
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --linux
on_retry_command: npm cache clean --force
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --linux
on_retry_command: npm cache clean --force
- name: Publish releases (arm64)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --linux
on_retry_command: npm cache clean --force
+29 -29
View File
@@ -3,37 +3,37 @@ name: Publish Windows and macOS (Manual)
on: workflow_dispatch
jobs:
publish:
runs-on: ${{ matrix.os }}
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest]
strategy:
matrix:
os: [macos-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
steps:
- name: Checkout git repo
uses: actions/checkout@v1
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --win --mac
on_retry_command: npm cache clean --force
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --win --mac
on_retry_command: npm cache clean --force
+51 -51
View File
@@ -1,54 +1,54 @@
name: Comment on pull request
on:
workflow_run:
workflows: ['Publish (PR)']
types: [completed]
workflow_run:
workflows: ['Publish (PR)']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
script: |
async function upsertComment(owner, repo, issue_number, purpose, body) {
const {data: comments} = await github.rest.issues.listComments(
{owner, repo, issue_number});
const marker = `<!-- bot: ${purpose} -->`;
body = marker + "\n" + body;
const existing = comments.filter((c) => c.body.includes(marker));
if (existing.length > 0) {
const last = existing[existing.length - 1];
core.info(`Updating comment ${last.id}`);
await github.rest.issues.updateComment({
owner, repo,
body,
comment_id: last.id,
});
} else {
core.info(`Creating a comment in issue / PR #${issue_number}`);
await github.rest.issues.createComment({issue_number, body, owner, repo});
}
}
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
if (!pull_requests.length) {
return core.error("This workflow doesn't match any pull requests!");
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
core.info("Review thread message body:", body);
for (const pr of pull_requests) {
await upsertComment(owner, repo, pr.number,
"nightly-link", body);
}
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
script: |
async function upsertComment(owner, repo, issue_number, purpose, body) {
const {data: comments} = await github.rest.issues.listComments(
{owner, repo, issue_number});
const marker = `<!-- bot: ${purpose} -->`;
body = marker + "\n" + body;
const existing = comments.filter((c) => c.body.includes(marker));
if (existing.length > 0) {
const last = existing[existing.length - 1];
core.info(`Updating comment ${last.id}`);
await github.rest.issues.updateComment({
owner, repo,
body,
comment_id: last.id,
});
} else {
core.info(`Creating a comment in issue / PR #${issue_number}`);
await github.rest.issues.createComment({issue_number, body, owner, repo});
}
}
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
if (!pull_requests.length) {
return core.error("This workflow doesn't match any pull requests!");
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
core.info("Review thread message body:", body);
for (const pr of pull_requests) {
await upsertComment(owner, repo, pr.number,
"nightly-link", body);
}
+47 -47
View File
@@ -1,60 +1,60 @@
name: Publish (PR)
on:
pull_request:
branches:
- development
pull_request:
branches:
- development
jobs:
publish:
runs-on: ${{ matrix.os }}
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest]
strategy:
matrix:
os: [macos-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v3
steps:
- name: Checkout git repo
uses: actions/checkout@v3
- name: Install Node and NPM
uses: actions/setup-node@v3
with:
node-version: 16
cache: npm
- name: Install Node and NPM
uses: actions/setup-node@v3
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Build releases
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm run package:pr
on_retry_command: npm cache clean --force
- name: Build releases
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm run package:pr
on_retry_command: npm cache clean --force
- uses: actions/upload-artifact@v3
with:
name: windows-binaries
path: |
release/build/*.exe
- uses: actions/upload-artifact@v3
with:
name: windows-binaries
path: |
release/build/*.exe
- uses: actions/upload-artifact@v3
with:
name: linux-binaries
path: |
release/build/*.AppImage
release/build/*.deb
release/build/*.rpm
- uses: actions/upload-artifact@v3
with:
name: linux-binaries
path: |
release/build/*.AppImage
release/build/*.deb
release/build/*.rpm
- uses: actions/upload-artifact@v3
with:
name: macos-binaries
path: |
release/build/*.dmg
- uses: actions/upload-artifact@v3
with:
name: macos-binaries
path: |
release/build/*.dmg
+24 -24
View File
@@ -3,32 +3,32 @@ name: Test
on: [push, pull_request]
jobs:
release:
runs-on: ${{ matrix.os }}
release:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
steps:
- name: Check out Git repository
uses: actions/checkout@v1
steps:
- name: Check out Git repository
uses: actions/checkout@v1
- name: Install Node.js and NPM
uses: actions/setup-node@v2
with:
node-version: 16
cache: npm
- name: Install Node.js and NPM
uses: actions/setup-node@v2
with:
node-version: 16
cache: npm
- name: npm install
run: |
npm install --legacy-peer-deps
- name: npm install
run: |
npm install --legacy-peer-deps
- name: npm test
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm run package
npm run lint
npm exec tsc
npm test
- name: npm test
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm run lint
npm run package
npm exec tsc
npm test
+20 -18
View File
@@ -1,20 +1,22 @@
{
"printWidth": 100,
"semi": true,
"singleQuote": true,
"overrides": [
{
"files": ["**/*.css", "**/*.scss", "**/*.html"],
"options": {
"singleQuote": false
}
}
],
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "lf",
"singleAttributePerLine": true
"printWidth": 100,
"semi": true,
"singleQuote": true,
"tabWidth": 4,
"useTabs": false,
"overrides": [
{
"files": ["**/*.css", "**/*.scss", "**/*.html"],
"options": {
"singleQuote": true
}
}
],
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "lf",
"singleAttributePerLine": true
}
+14 -28
View File
@@ -1,31 +1,17 @@
{
"processors": ["stylelint-processor-styled-components"],
"customSyntax": "postcss-scss",
"extends": [
"stylelint-config-standard-scss",
"stylelint-config-styled-components",
"stylelint-config-rational-order"
],
"rules": {
"color-function-notation": ["legacy"],
"declaration-empty-line-before": null,
"order/properties-order": [],
"plugin/rational-order": [
true,
{
"border-in-box-model": false,
"empty-line-between-groups": false
}
"customSyntax": "postcss-styled-syntax",
"extends": [
"stylelint-config-standard",
"stylelint-config-styled-components",
"stylelint-config-recess-order"
],
"string-quotes": "single",
"declaration-block-no-redundant-longhand-properties": null,
"selector-class-pattern": null,
"selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
"selector-type-no-unknown": [
true,
{ "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }
],
"value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }],
"declaration-colon-newline-after": null
}
"rules": {
"declaration-empty-line-before": null,
"declaration-block-no-redundant-longhand-properties": null,
"selector-class-pattern": null,
"selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
"selector-type-no-unknown": [true, { "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }],
"declaration-colon-newline-after": null,
"property-no-vendor-prefix": null
}
}
+8 -6
View File
@@ -1,8 +1,10 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode"
]
"recommendations": [
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode",
"clinyong.vscode-css-modules",
"Huuums.vscode-fast-folder-structure"
]
}
+26 -28
View File
@@ -1,30 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Electron: Main",
"type": "node",
"request": "launch",
"protocol": "inspector",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run start:main --inspect=5858 --remote-debugging-port=9223"
],
"preLaunchTask": "Start Webpack Dev"
},
{
"name": "Electron: Renderer",
"type": "chrome",
"request": "attach",
"port": 9223,
"webRoot": "${workspaceFolder}",
"timeout": 15000
}
],
"compounds": [
{
"name": "Electron: All",
"configurations": ["Electron: Main", "Electron: Renderer"]
}
]
"version": "0.2.0",
"configurations": [
{
"name": "Electron: Main",
"type": "node",
"request": "launch",
"protocol": "inspector",
"runtimeExecutable": "npm",
"runtimeArgs": ["run start:main --inspect=5858 --remote-debugging-port=9223"],
"preLaunchTask": "Start Webpack Dev"
},
{
"name": "Electron: Renderer",
"type": "chrome",
"request": "attach",
"port": 9223,
"webRoot": "${workspaceFolder}",
"timeout": 15000
}
],
"compounds": [
{
"name": "Electron: All",
"configurations": ["Electron: Main", "Electron: Renderer"]
}
]
}
+70 -41
View File
@@ -1,43 +1,72 @@
{
"files.associations": {
".eslintrc": "jsonc",
".prettierrc": "jsonc",
".eslintignore": "ignore"
},
"eslint.validate": ["typescript"],
"eslint.workingDirectories": [
{ "directory": "./", "changeProcessCWD": true },
{ "directory": "./server", "changeProcessCWD": true }
],
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": false
},
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.format.enable": false,
"search.exclude": {
".git": true,
".eslintcache": true,
".erb/dll": true,
"release/{build,app/dist}": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"package-lock.json": true,
"*.{css,sass,scss}.d.ts": true
},
"i18n-ally.localesPaths": ["src/i18n", "src/i18n/locales"],
"typescript.tsdk": "node_modules\\typescript\\lib",
"typescript.preferences.importModuleSpecifier": "non-relative",
"stylelint.validate": ["css", "less", "postcss", "typescript", "typescriptreact", "scss"],
"typescript.updateImportsOnFileMove.enabled": "always",
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true
"files.associations": {
".eslintrc": "jsonc",
".prettierrc": "jsonc",
".eslintignore": "ignore"
},
"eslint.validate": ["typescript"],
"eslint.workingDirectories": [
{ "directory": "./", "changeProcessCWD": true },
{ "directory": "./server", "changeProcessCWD": true }
],
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true,
"source.organizeImports": false,
"source.formatDocument": true
},
"css.validate": true,
"less.validate": false,
"scss.validate": true,
"scss.lint.unknownAtRules": "warning",
"scss.lint.unknownProperties": "warning",
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.format.enable": false,
"search.exclude": {
".git": true,
".eslintcache": true,
".erb/dll": true,
"release/{build,app/dist}": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"package-lock.json": true,
"*.{css,sass,scss}.d.ts": true
},
"i18n-ally.localesPaths": ["src/i18n", "src/i18n/locales"],
"typescript.tsdk": "node_modules\\typescript\\lib",
"typescript.preferences.importModuleSpecifier": "non-relative",
"stylelint.validate": ["css", "scss", "typescript", "typescriptreact"],
"typescript.updateImportsOnFileMove.enabled": "always",
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true,
"folderTemplates.structures": [
{
"name": "TypeScript Feature Component With CSS Modules",
"omitParentDirectory": true,
"structure": [
{
"fileName": "<FTName | kebabcase>.tsx",
"template": "Functional Component with CSS Modules"
},
{
"fileName": "<FTName | kebabcase>.module.scss"
}
]
}
],
"folderTemplates.fileTemplates": {
"Functional Component with CSS Modules": [
"import styles from './<FTName | kebabcase>.module.scss';",
"",
"interface <FTName | pascalcase>Props {}",
"",
"export const <FTName | pascalcase> = ({}: <FTName | pascalcase>Props) => {",
" return <div></div>;",
"};"
]
}
}
+22 -22
View File
@@ -1,25 +1,25 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"label": "Start Webpack Dev",
"script": "start:renderer",
"options": {
"cwd": "${workspaceFolder}"
},
"isBackground": true,
"problemMatcher": {
"owner": "custom",
"pattern": {
"regexp": "____________"
},
"background": {
"activeOnStart": true,
"beginsPattern": "Compiling\\.\\.\\.$",
"endsPattern": "(Compiled successfully|Failed to compile)\\.$"
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"label": "Start Webpack Dev",
"script": "start:renderer",
"options": {
"cwd": "${workspaceFolder}"
},
"isBackground": true,
"problemMatcher": {
"owner": "custom",
"pattern": {
"regexp": "____________"
},
"background": {
"activeOnStart": true,
"beginsPattern": "Compiling\\.\\.\\.$",
"endsPattern": "(Compiled successfully|Failed to compile)\\.$"
}
}
}
}
}
]
]
}
+18
View File
@@ -0,0 +1,18 @@
# --- Builder stage
FROM node:18-alpine as builder
WORKDIR /app
COPY . /app
# Scripts include electron-specific dependencies, which we don't need
RUN npm install --legacy-peer-deps --ignore-scripts
RUN npm run build:web
# --- Production stage
FROM nginx:alpine-slim
COPY --chown=nginx:nginx --from=builder /app/release/app/dist/web /usr/share/nginx/html
COPY ng.conf.template /etc/nginx/templates/default.conf.template
ENV PUBLIC_PATH="/"
EXPOSE 9180
CMD ["nginx", "-g", "daemon off;"]
+52 -13
View File
@@ -1,3 +1,5 @@
<img src="assets/icons/icon.png" alt="logo" title="feishin" align="right" height="60px" />
# Feishin
<p align="center">
@@ -29,32 +31,65 @@ Rewrite of [Sonixd](https://github.com/jeffvli/sonixd).
## Features
- [x] MPV player backend
- [x] Web player backend
- [x] Modern UI
- [x] Scrobble playback to your server
- [x] Smart playlist editor (Navidrome)
- [ ] [Request a feature](https://github.com/jeffvli/feishin/issues) or [view taskboard](https://github.com/users/jeffvli/projects/5/views/1)
- [x] MPV player backend
- [x] Web player backend
- [x] Modern UI
- [x] Scrobble playback to your server
- [x] Smart playlist editor (Navidrome)
- [x] Synchronized and unsynchronized lyrics support
- [ ] [Request a feature](https://github.com/jeffvli/feishin/issues) or [view taskboard](https://github.com/users/jeffvli/projects/5/views/1)
## Screenshots
<a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_home.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_home.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_artist_detail.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_artist_detail.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_detail.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_detail.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_smart_playlist.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_smart_playlist.png" width="49.5%"/></a>
<a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_full_screen_player.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_full_screen_player.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_artist_detail.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_artist_detail.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_detail.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_album_detail.png" width="49.5%"/></a> <a href="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_smart_playlist.png"><img src="https://raw.githubusercontent.com/jeffvli/feishin/development/media/preview_smart_playlist.png" width="49.5%"/></a>
## Getting Started
Download the [latest desktop client](https://github.com/jeffvli/feishin/releases).
### Desktop (recommended)
Download the [latest desktop client](https://github.com/jeffvli/feishin/releases). The desktop client is the recommended way to use Feishin. It supports both the MPV and web player backends, as well as includes built-in fetching for lyrics.
If you're using a device running macOS 12 (Monterey) or higher, [check here](https://github.com/jeffvli/feishin/issues/104#issuecomment-1553914730) for instructions on how to remove the app from quarantine.
### Web and Docker
Visit [https://feishin.vercel.app](https://feishin.vercel.app) to use the hosted web version of Feishin. The web client only supports the web player backend.
Feishin is also available as a Docker image. The images are hosted via `ghcr.io` and are available to view [here](https://github.com/jeffvli/feishin/pkgs/container/feishin). You can run the container using the following commands:
```bash
# Run the latest version
docker run --name feishin -p 9180:9180 ghcr.io/jeffvli/feishin:latest
# Build the image locally
docker build -t feishin .
docker run --name feishin -p 9180:9180 feishin
```
### Configuration
1. Upon startup you will be greeted with a prompt to select the path to your MPV binary. If you do not have MPV installed, you can download it [here](https://mpv.io/installation/) or install it using any package manager supported by your OS. After inputting the path, restart the app.
2. After restarting the app, you will be prompted to select a server. Click the `Open menu` button and select `Manage servers`. Click the `Add server` button in the popup and fill out all applicable details. You will need to enter the full URL to your server, including the protocol and port if applicable (e.g. `https://navidrome.my-server.com` or `http://192.168.0.1:4533`).
- **Navidrome** - For the best experience, select "Save password" when creating the server and configure the `SessionTimeout` setting in your Navidrome config to a larger value (e.g. 72h).
3. _Optional_ - If you want to host Feishin on a subpath (not `/`), then pass in the following environment variable: `PUBLIC_PATH=PATH`. For example, to host on `/feishin`, pass in `PUBLIC_PATH=/feishin`.
## FAQ
### MPV is either not working or is rapidly switching between pause/play states
First thing to do is check that your MPV binary path is correct. Navigate to the settings page and re-set the path and restart the app. If your issue still isn't resolved, try reinstalling MPV. Known working versions include `v0.35.x` and `v0.36.x`. `v0.34.x` is a known broken version.
### What music servers does Feishin support?
Feishin supports any music server that implements a [Navidrome](https://www.navidrome.org/) or [Jellyfin](https://jellyfin.org/) API. **Subsonic API is not currently supported**. This will likely be added in [later when the new Subsonic API is decided on](https://support.symfonium.app/t/subsonic-servers-participation/1233).
- [Navidrome](https://github.com/navidrome/navidrome)
- [Jellyfin](https://github.com/jellyfin/jellyfin)
- ~~[Gonic](https://github.com/sentriz/gonic)~~
- ~~[Astiga](https://asti.ga/)~~
- ~~[Supysonic](https://github.com/spl0k/supysonic)~~
- [Navidrome](https://github.com/navidrome/navidrome)
- [Jellyfin](https://github.com/jellyfin/jellyfin)
- [Funkwhale](https://funkwhale.audio/) - TBD
- Subsonic-compatible servers - TBD
## Development
@@ -62,6 +97,10 @@ Built and tested using Node `v16.15.0`.
This project is built off of [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate) v4.6.0.
## Translation
This project uses [Weblate](https://hosted.weblate.org/projects/feishin/) for translations. If you would like to contribute, please visit the link and submit a translation.
## License
[GNU General Public License v3.0 ©](https://github.com/jeffvli/feishin/blob/dev/LICENSE)
+12 -12
View File
@@ -1,31 +1,31 @@
type Styles = Record<string, string>;
declare module '*.svg' {
const content: string;
export default content;
const content: string;
export default content;
}
declare module '*.png' {
const content: string;
export default content;
const content: string;
export default content;
}
declare module '*.jpg' {
const content: string;
export default content;
const content: string;
export default content;
}
declare module '*.scss' {
const content: Styles;
export default content;
const content: Styles;
export default content;
}
declare module '*.sass' {
const content: Styles;
export default content;
const content: Styles;
export default content;
}
declare module '*.css' {
const content: Styles;
export default content;
const content: Styles;
export default content;
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-1
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

+19
View File
@@ -0,0 +1,19 @@
server {
listen 9180;
sendfile on;
default_type application/octet-stream;
gzip on;
gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\.";
gzip_min_length 256;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 9;
location ${PUBLIC_PATH} {
alias /usr/share/nginx/html/;
try_files $uri $uri/ /index.html =404;
}
}
+37073 -41289
View File
File diff suppressed because it is too large Load Diff
+355 -306
View File
@@ -1,319 +1,368 @@
{
"name": "feishin",
"productName": "Feishin",
"description": "Feishin music server",
"version": "0.1.1",
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
"lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
"lint:styles": "npx stylelint **/*.tsx",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never",
"package:pr": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never --win --mac --linux",
"package:dev": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never --dir",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
"start:main": "cross-env NODE_ENV=development electron -r ts-node/register/transpile-only ./src/main/main.ts",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"start:web": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.web.ts",
"test": "jest",
"prepare": "husky install",
"i18next": "i18next -c src/renderer/i18n/i18next-parser.config.js",
"prod:buildserver": "pwsh -c \"./scripts/server-build.ps1\"",
"prod:publishserver": "pwsh -c \"./scripts/server-publish.ps1\""
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"cross-env NODE_ENV=development eslint --cache"
],
"*.json,.{eslintrc,prettierrc}": [
"prettier --ignore-path .eslintignore --parser json --write"
],
"*.{css,scss}": [
"prettier --ignore-path .eslintignore --single-quote --write"
],
"*.{html,md,yml}": [
"prettier --ignore-path .eslintignore --single-quote --write"
]
},
"build": {
"name": "feishin",
"productName": "Feishin",
"appId": "org.jeffvli.feishin",
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"asar": true,
"asarUnpack": "**\\*.{node,dll}",
"files": [
"dist",
"node_modules",
"package.json"
],
"afterSign": ".erb/scripts/notarize.js",
"electronVersion": "22.3.1",
"mac": {
"target": {
"target": "default",
"arch": [
"arm64",
"x64"
"description": "Feishin music server",
"version": "0.5.3",
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\" \"npm run build:remote\"",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
"build:remote": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.remote.prod.ts",
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"build:web": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.web.prod.ts",
"build:docker": "npm run build:web && docker build -t jeffvli/feishin .",
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
"lint": "concurrently \"npm run lint:code\" \"npm run lint:styles\"",
"lint:code": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx --fix",
"lint:styles": "npx stylelint **/*.tsx --fix",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never",
"package:pr": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never --win --mac --linux",
"package:dev": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never --dir",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
"start:main": "cross-env NODE_ENV=development electron -r ts-node/register/transpile-only ./src/main/main.ts",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:remote": "cross-env NODE_ENV=developemnt TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.remote.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"start:web": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.web.ts",
"test": "jest",
"prepare": "husky install",
"i18next": "i18next -c src/i18n/i18next-parser.config.js",
"prod:buildserver": "pwsh -c \"./scripts/server-build.ps1\"",
"prod:publishserver": "pwsh -c \"./scripts/server-publish.ps1\""
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"cross-env NODE_ENV=development eslint --cache"
],
"*.json,.{eslintrc,prettierrc}": [
"prettier --ignore-path .eslintignore --parser json --write"
],
"*.{css,scss}": [
"prettier --ignore-path .eslintignore --single-quote --write"
],
"*.{html,md,yml}": [
"prettier --ignore-path .eslintignore --single-quote --write"
]
},
"type": "distribution",
"hardenedRuntime": true,
"entitlements": "assets/entitlements.mac.plist",
"entitlementsInherit": "assets/entitlements.mac.plist",
"gatekeeperAssess": false
},
"dmg": {
"contents": [
{
"x": 130,
"y": 220
"build": {
"productName": "Feishin",
"appId": "org.jeffvli.feishin",
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"asar": true,
"asarUnpack": "**\\*.{node,dll}",
"files": [
"dist",
"node_modules",
"package.json"
],
"afterSign": ".erb/scripts/notarize.js",
"electronVersion": "27.1.0",
"mac": {
"target": {
"target": "default",
"arch": [
"arm64",
"x64"
]
},
"icon": "assets/icons/icon.icns",
"type": "distribution",
"hardenedRuntime": true,
"entitlements": "assets/entitlements.mac.plist",
"entitlementsInherit": "assets/entitlements.mac.plist",
"gatekeeperAssess": false
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
"nsis",
"zip"
],
"icon": "assets/icons/icon.ico"
},
"deb": {
"depends": [
"libgssapi_krb5.so.2",
"libavahi-common.so.3",
"libavahi-client.so.3",
"libkrb5.so.3",
"libkrb5support.so.0",
"libkeyutils.so.1",
"libcups.so.2"
]
},
"rpm": {
"depends": [
"libgssapi_krb5.so.2",
"libavahi-common.so.3",
"libavahi-client.so.3",
"libkrb5.so.3",
"libkrb5support.so.0",
"libkeyutils.so.1",
"libcups.so.2"
]
},
"freebsd": {
"depends": [
"libgssapi_krb5.so.2",
"libavahi-common.so.3",
"libavahi-client.so.3",
"libkrb5.so.3",
"libkrb5support.so.0",
"libkeyutils.so.1",
"libcups.so.2"
]
},
"linux": {
"target": [
"AppImage",
"rpm",
"tar.xz"
],
"icon": "assets/icons/icon.png",
"category": "Development"
},
"directories": {
"app": "release/app",
"buildResources": "assets",
"output": "release/build"
},
"extraResources": [
"./assets/**"
],
"publish": {
"provider": "github",
"owner": "jeffvli",
"repo": "feishin"
}
]
},
"win": {
"target": [
"nsis",
"zip"
]
"repository": {
"type": "git",
"url": "git+https://github.com/jeffvli/feishin.git"
},
"linux": {
"target": [
"AppImage",
"tar.xz"
],
"icon": "assets/icons/placeholder.png",
"category": "Development"
"author": {
"name": "jeffvli",
"url": "https://github.com/jeffvli/"
},
"directories": {
"app": "release/app",
"buildResources": "assets",
"output": "release/build"
"contributors": [],
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/jeffvli/feishin/issues"
},
"extraResources": [
"./assets/**"
"keywords": [
"subsonic",
"navidrome",
"airsonic",
"jellyfin",
"react",
"electron"
],
"publish": {
"provider": "github",
"owner": "jeffvli",
"repo": "feishin"
"homepage": "https://github.com/jeffvli/feishin",
"jest": {
"testURL": "http://localhost/",
"testEnvironment": "jsdom",
"transform": {
"\\.(ts|tsx|js|jsx)$": "ts-jest"
},
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"js",
"jsx",
"ts",
"tsx",
"json"
],
"moduleDirectories": [
"node_modules",
"release/app/node_modules"
],
"testPathIgnorePatterns": [
"release/app/dist"
],
"setupFiles": [
"./.erb/scripts/check-build-exists.ts"
]
},
"devDependencies": {
"@electron/rebuild": "^3.2.10",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
"@stylelint/postcss-css-in-js": "^0.38.0",
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.0.0",
"@types/electron-localshortcut": "^3.1.0",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.188",
"@types/md5": "^2.3.2",
"@types/node": "^17.0.23",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"@types/react-test-renderer": "^17.0.1",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
"@types/react-window-infinite-loader": "^1.0.6",
"@types/styled-components": "^5.1.26",
"@types/terser-webpack-plugin": "^5.0.4",
"@types/webpack-bundle-analyzer": "^4.4.1",
"@types/webpack-env": "^1.16.3",
"@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.47.0",
"browserslist-config-erb": "^0.0.3",
"chalk": "^4.1.2",
"concurrently": "^7.1.0",
"core-js": "^3.21.1",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1",
"detect-port": "^1.3.0",
"electron": "^27.1.0",
"electron-builder": "^24.9.0",
"electron-devtools-installer": "^3.2.0",
"electron-notarize": "^1.2.1",
"electronmon": "^2.0.2",
"eslint": "^8.30.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-erb": "^4.0.3",
"eslint-import-resolver-typescript": "^2.7.1",
"eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-compat": "^4.0.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.1.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-sort-keys-fix": "^1.1.2",
"eslint-plugin-typescript-sort-keys": "^2.1.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4",
"i18next-parser": "^6.6.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1",
"lint-staged": "^12.3.7",
"mini-css-extract-plugin": "^2.6.0",
"postcss-scss": "^4.0.4",
"postcss-styled-syntax": "^0.5.0",
"postcss-syntax": "^0.36.2",
"prettier": "^2.6.2",
"react-refresh": "^0.12.0",
"react-refresh-typescript": "^2.0.4",
"react-test-renderer": "^18.0.0",
"rimraf": "^3.0.2",
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.1",
"stylelint": "^15.10.3",
"stylelint-config-css-modules": "^4.3.0",
"stylelint-config-recess-order": "^4.3.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-config-standard-scss": "^4.0.0",
"stylelint-config-styled-components": "^0.1.1",
"terser-webpack-plugin": "^5.3.1",
"ts-jest": "^27.1.4",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"tsconfig-paths-webpack-plugin": "^4.0.0",
"typescript": "^5.2.2",
"typescript-plugin-styled-components": "^3.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.71.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.0",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@ag-grid-community/client-side-row-model": "^28.2.1",
"@ag-grid-community/core": "^28.2.1",
"@ag-grid-community/infinite-row-model": "^28.2.1",
"@ag-grid-community/react": "^28.2.1",
"@ag-grid-community/styles": "^28.2.1",
"@emotion/react": "^11.10.4",
"@mantine/core": "^6.0.17",
"@mantine/dates": "^6.0.17",
"@mantine/form": "^6.0.17",
"@mantine/hooks": "^6.0.17",
"@mantine/modals": "^6.0.17",
"@mantine/notifications": "^6.0.17",
"@mantine/utils": "^6.0.17",
"@tanstack/react-query": "^4.32.1",
"@tanstack/react-query-devtools": "^4.32.1",
"@tanstack/react-query-persist-client": "^4.32.1",
"@ts-rest/core": "^3.23.0",
"@xhayper/discord-rpc": "^1.0.24",
"axios": "^1.4.0",
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"dayjs": "^1.11.6",
"electron-debug": "^3.2.0",
"electron-localshortcut": "^3.2.1",
"electron-log": "^4.4.6",
"electron-store": "^8.1.0",
"electron-updater": "^4.6.5",
"fast-average-color": "^9.3.0",
"format-duration": "^2.0.0",
"framer-motion": "^10.13.0",
"fuse.js": "^6.6.2",
"history": "^5.3.0",
"i18next": "^21.10.0",
"idb-keyval": "^6.2.1",
"immer": "^9.0.21",
"is-electron": "^2.2.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"memoize-one": "^6.0.0",
"nanoid": "^3.3.3",
"net": "^1.0.2",
"node-mpv": "github:jeffvli/Node-MPV",
"overlayscrollbars": "^2.2.1",
"overlayscrollbars-react": "^0.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-i18next": "^11.18.6",
"react-icons": "^4.10.1",
"react-player": "^2.11.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"react-simple-img": "^3.0.0",
"react-virtualized-auto-sizer": "^1.0.17",
"react-window": "^1.8.9",
"react-window-infinite-loader": "^1.0.9",
"styled-components": "^6.0.8",
"swiper": "^9.3.1",
"zod": "^3.21.4",
"zustand": "^4.3.9"
},
"resolutions": {
"styled-components": "^6"
},
"devEngines": {
"node": ">=14.x",
"npm": ">=7.x"
},
"browserslist": [],
"electronmon": {
"patterns": [
"!server",
"!src/renderer"
]
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/jeffvli/feishin.git"
},
"author": {
"name": "jeffvli",
"url": "https://github.com/jeffvli/"
},
"contributors": [],
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/jeffvli/feishin/issues"
},
"keywords": [
"subsonic",
"navidrome",
"airsonic",
"jellyfin",
"react",
"electron"
],
"homepage": "https://github.com/jeffvli/feishin",
"jest": {
"testURL": "http://localhost/",
"testEnvironment": "jsdom",
"transform": {
"\\.(ts|tsx|js|jsx)$": "ts-jest"
},
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"js",
"jsx",
"ts",
"tsx",
"json"
],
"moduleDirectories": [
"node_modules",
"release/app/node_modules"
],
"testPathIgnorePatterns": [
"release/app/dist"
],
"setupFiles": [
"./.erb/scripts/check-build-exists.ts"
]
},
"devDependencies": {
"@electron/rebuild": "^3.2.10",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
"@stylelint/postcss-css-in-js": "^0.38.0",
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.0.0",
"@types/electron-localshortcut": "^3.1.0",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.188",
"@types/md5": "^2.3.2",
"@types/node": "^17.0.23",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"@types/react-test-renderer": "^17.0.1",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
"@types/react-window-infinite-loader": "^1.0.6",
"@types/styled-components": "^5.1.26",
"@types/terser-webpack-plugin": "^5.0.4",
"@types/webpack-bundle-analyzer": "^4.4.1",
"@types/webpack-env": "^1.16.3",
"@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.47.0",
"browserslist-config-erb": "^0.0.3",
"chalk": "^4.1.2",
"concurrently": "^7.1.0",
"core-js": "^3.21.1",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1",
"detect-port": "^1.3.0",
"electron": "^22.3.1",
"electron-builder": "^24.0.0-alpha.13",
"electron-devtools-installer": "^3.2.0",
"electron-notarize": "^1.2.1",
"electronmon": "^2.0.2",
"eslint": "^8.30.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-erb": "^4.0.3",
"eslint-import-resolver-typescript": "^2.7.1",
"eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-compat": "^4.0.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.1.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-sort-keys-fix": "^1.1.2",
"eslint-plugin-typescript-sort-keys": "^2.1.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4",
"i18next-parser": "^6.3.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1",
"lint-staged": "^12.3.7",
"mini-css-extract-plugin": "^2.6.0",
"postcss-scss": "^4.0.4",
"postcss-syntax": "^0.36.2",
"prettier": "^2.6.2",
"react-refresh": "^0.12.0",
"react-refresh-typescript": "^2.0.4",
"react-test-renderer": "^18.0.0",
"rimraf": "^3.0.2",
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.1",
"stylelint": "^14.9.1",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard-scss": "^4.0.0",
"stylelint-config-styled-components": "^0.1.1",
"stylelint-order": "^5.0.0",
"stylelint-processor-styled-components": "^1.10.0",
"terser-webpack-plugin": "^5.3.1",
"ts-jest": "^27.1.4",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"tsconfig-paths-webpack-plugin": "^4.0.0",
"typescript": "^4.8.4",
"typescript-plugin-styled-components": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.71.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.0",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@ag-grid-community/client-side-row-model": "^28.2.1",
"@ag-grid-community/core": "^28.2.1",
"@ag-grid-community/infinite-row-model": "^28.2.1",
"@ag-grid-community/react": "^28.2.1",
"@ag-grid-community/styles": "^28.2.1",
"@emotion/react": "^11.10.4",
"@mantine/core": "^6.0.10",
"@mantine/dates": "^6.0.10",
"@mantine/form": "^6.0.10",
"@mantine/hooks": "^6.0.10",
"@mantine/modals": "^6.0.10",
"@mantine/notifications": "^6.0.10",
"@mantine/utils": "^6.0.10",
"@tanstack/react-query": "^4.29.5",
"@tanstack/react-query-devtools": "^4.29.6",
"@ts-rest/core": "^3.19.4",
"axios": "^1.4.0",
"cmdk": "^0.2.0",
"dayjs": "^1.11.6",
"electron-debug": "^3.2.0",
"electron-localshortcut": "^3.2.1",
"electron-log": "^4.4.6",
"electron-store": "^8.1.0",
"electron-updater": "^4.6.5",
"fast-average-color": "^9.2.0",
"format-duration": "^2.0.0",
"framer-motion": "^8.1.3",
"history": "^5.3.0",
"i18next": "^21.6.16",
"immer": "^9.0.21",
"is-electron": "^2.2.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"memoize-one": "^6.0.0",
"nanoid": "^3.3.3",
"net": "^1.0.2",
"node-mpv": "^2.0.0-beta.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-i18next": "^11.16.7",
"react-icons": "^4.8.0",
"react-player": "^2.11.0",
"react-router": "^6.5.0",
"react-router-dom": "^6.5.0",
"react-simple-img": "^3.0.0",
"react-virtualized-auto-sizer": "^1.0.15",
"react-window": "^1.8.9",
"react-window-infinite-loader": "^1.0.9",
"styled-components": "^5.3.10",
"swiper": "^9.3.1",
"zod": "^3.21.4",
"zustand": "^4.3.8"
},
"resolutions": {
"styled-components": "^5"
},
"devEngines": {
"node": ">=14.x",
"npm": ">=7.x"
},
"browserslist": [],
"electronmon": {
"patterns": [
"!server",
"!src/renderer"
]
}
}
+2321 -1989
View File
File diff suppressed because it is too large Load Diff
+22 -20
View File
@@ -1,22 +1,24 @@
{
"name": "feishin",
"version": "0.1.1",
"description": "",
"main": "./dist/main/main.js",
"author": {
"name": "jeffvli",
"url": "https://github.com/jeffvli/"
},
"scripts": {
"electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js",
"link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts",
"postinstall": "npm run electron-rebuild && npm run link-modules"
},
"dependencies": {
"mpris-service": "^2.1.2"
},
"devDependencies": {
"electron": "22.3.1"
},
"license": "GPL-3.0"
"name": "feishin",
"version": "0.5.3",
"description": "",
"main": "./dist/main/main.js",
"author": {
"name": "jeffvli",
"url": "https://github.com/jeffvli/"
},
"scripts": {
"electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js",
"link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts",
"postinstall": "npm run electron-rebuild && npm run link-modules"
},
"dependencies": {
"cheerio": "^1.0.0-rc.12",
"mpris-service": "^2.1.2",
"ws": "^8.13.0"
},
"devDependencies": {
"electron": "25.3.0"
},
"license": "GPL-3.0"
}
+3 -3
View File
@@ -3,7 +3,7 @@ import { render } from '@testing-library/react';
import { App } from '../renderer/app';
describe('App', () => {
it('should render', () => {
expect(render(<App />)).toBeTruthy();
});
it('should render', () => {
expect(render(<App />)).toBeTruthy();
});
});
-32
View File
@@ -1,32 +0,0 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const en = require('./locales/en.json');
const resources = {
en: { translation: en },
};
export const Languages = [
{
label: 'English',
value: 'en',
},
];
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
fallbackLng: 'en',
// language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option
interpolation: {
escapeValue: false, // react already safes from xss
},
lng: 'en',
resources,
});
export default i18n;
+159
View File
@@ -0,0 +1,159 @@
import { PostProcessorModule } from 'i18next';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import en from './locales/en.json';
import es from './locales/es.json';
import fr from './locales/fr.json';
import ja from './locales/ja.json';
import pl from './locales/pl.json';
import zhHans from './locales/zh-Hans.json';
import de from './locales/de.json';
import it from './locales/it.json';
import ru from './locales/ru.json';
import ptBr from './locales/pt-BR.json';
import sr from './locales/sr.json';
import sv from './locales/sv.json';
import cs from './locales/cs.json';
import nbNO from './locales/nb-NO.json';
import nl from './locales/nl.json';
const resources = {
en: { translation: en },
es: { translation: es },
de: { translation: de },
it: { translation: it },
ru: { translation: ru },
'pt-BR': { translation: ptBr },
fr: { translation: fr },
ja: { translation: ja },
pl: { translation: pl },
'zh-Hans': { translation: zhHans },
sr: { translation: sr },
sv: { translation: sv },
cs: { translation: cs },
nl: { translation: nl },
'nb-NO': { translation: nbNO },
};
export const languages = [
{
label: 'English',
value: 'en',
},
{
label: 'Čeština',
value: 'cs',
},
{
label: 'Español',
value: 'es',
},
{
label: 'Deutsch',
value: 'de',
},
{
label: 'Français',
value: 'fr',
},
{
label: 'Italiano',
value: 'it',
},
{
label: '日本語',
value: 'ja',
},
{
label: 'Nederlands',
value: 'nl',
},
{
label: 'Norsk (Bokmål)',
value: 'nb-NO',
},
{
label: 'Português (Brasil)',
value: 'pt-BR',
},
{
label: 'Polski',
value: 'pl',
},
{
label: 'Русский',
value: 'ru',
},
{
label: 'Srpski',
value: 'sr',
},
{
label: 'Svenska',
value: 'sv',
},
{
label: '简体中文',
value: 'zh-Hans',
},
];
const lowerCasePostProcessor: PostProcessorModule = {
type: 'postProcessor',
name: 'lowerCase',
process: (value: string) => {
return value.toLocaleLowerCase();
},
};
const upperCasePostProcessor: PostProcessorModule = {
type: 'postProcessor',
name: 'upperCase',
process: (value: string) => {
return value.toLocaleUpperCase();
},
};
const titleCasePostProcessor: PostProcessorModule = {
type: 'postProcessor',
name: 'titleCase',
process: (value: string) => {
return value.replace(/\S\S*/g, (txt) => {
return txt.charAt(0).toLocaleUpperCase() + txt.slice(1).toLowerCase();
});
},
};
const sentenceCasePostProcessor: PostProcessorModule = {
type: 'postProcessor',
name: 'sentenceCase',
process: (value: string) => {
const sentences = value.split('. ');
return sentences
.map((sentence) => {
return (
sentence.charAt(0).toLocaleUpperCase() + sentence.slice(1).toLocaleLowerCase()
);
})
.join('. ');
},
};
i18n.use(lowerCasePostProcessor)
.use(upperCasePostProcessor)
.use(titleCasePostProcessor)
.use(sentenceCasePostProcessor)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
fallbackLng: 'en',
// language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option
interpolation: {
escapeValue: false, // react already safes from xss
},
resources,
});
export default i18n;
+41 -114
View File
@@ -1,117 +1,44 @@
// i18next-parser.config.js
// Reference: https://github.com/i18next/i18next-parser#options
module.exports = {
contextSeparator: '_',
// Key separator used in your translation keys
createOldCatalogs: true,
// Exit with an exit code of 1 when translations are updated (for CI purpose)
customValueTemplate: null,
// Save the \_old files
defaultNamespace: 'translation',
// Default namespace used in your i18next config
defaultValue: '',
// Exit with an exit code of 1 on warnings
failOnUpdate: false,
// Display info about the parsing including some stats
failOnWarnings: false,
// The locale to compare with default values to determine whether a default value has been changed.
// If this is set and a default value differs from a translation in the specified locale, all entries
// for that key across locales are reset to the default value, and existing translations are moved to
// the `_old` file.
i18nextOptions: null,
// Default value to give to empty keys
// You may also specify a function accepting the locale, namespace, and key as arguments
indentation: 2,
// Plural separator used in your translation keys
// If you want to use plain english keys, separators such as `_` might conflict. You might want to set `pluralSeparator` to a different string that does not occur in your keys.
input: [
'../components/**/*.{js,jsx,ts,tsx}',
'../features/**/*.{js,jsx,ts,tsx}',
'../layouts/**/*.{js,jsx,ts,tsx}',
'!../../src/node_modules/**',
'!../../src/**/*.prod.js',
],
// Indentation of the catalog files
keepRemoved: false,
// Keep keys from the catalog that are no longer in code
keySeparator: '.',
// Key separator used in your translation keys
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
// see below for more details
lexers: {
default: ['JavascriptLexer'],
handlebars: ['HandlebarsLexer'],
hbs: ['HandlebarsLexer'],
htm: ['HTMLLexer'],
html: ['HTMLLexer'],
js: ['JavascriptLexer'],
jsx: ['JsxLexer'],
mjs: ['JavascriptLexer'],
// if you're writing jsx inside .js files, change this to JsxLexer
ts: ['JavascriptLexer'],
tsx: ['JsxLexer'],
},
lineEnding: 'auto',
// Control the line ending. See options at https://github.com/ryanve/eol
locales: ['en'],
// An array of the locales in your applications
namespaceSeparator: false,
// Namespace separator used in your translation keys
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
output: 'src/renderer/i18n/locales/$LOCALE.json',
// Supports $LOCALE and $NAMESPACE injection
// Supports JSON (.json) and YAML (.yml) file formats
// Where to write the locale files relative to process.cwd()
pluralSeparator: '_',
// If you wish to customize the value output the value as an object, you can set your own format.
// ${defaultValue} is the default value you set in your translation function.
// Any other custom property will be automatically extracted.
//
// Example:
// {
// message: "${defaultValue}",
// description: "${maxLength}", //
// }
resetDefaultValueLocale: 'en',
// Whether or not to sort the catalog. Can also be a [compareFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#parameters)
skipDefaultValues: false,
// An array of globs that describe where to look for source files
// relative to the location of the configuration file
sort: true,
// Whether to ignore default values
// You may also specify a function accepting the locale and namespace as arguments
useKeysAsDefaultValue: true,
// Whether to use the keys as the default value; ex. "Hello": "Hello", "World": "World"
// This option takes precedence over the `defaultValue` and `skipDefaultValues` options
// You may also specify a function accepting the locale and namespace as arguments
verbose: false,
// If you wish to customize options in internally used i18next instance, you can define an object with any
// configuration property supported by i18next (https://www.i18next.com/overview/configuration-options).
// { compatibilityJSON: 'v3' } can be used to generate v3 compatible plurals.
contextSeparator: '_',
createOldCatalogs: true,
customValueTemplate: null,
defaultNamespace: 'translation',
defaultValue: '',
failOnUpdate: false,
failOnWarnings: false,
i18nextOptions: null,
indentation: 4,
input: [
'../renderer/components/**/*.{js,jsx,ts,tsx}',
'../renderer/features/**/*.{js,jsx,ts,tsx}',
'../renderer/layouts/**/*.{js,jsx,ts,tsx}',
'!../src/node_modules/**',
'!../src/**/*.prod.js',
],
keepRemoved: false,
keySeparator: '.',
lexers: {
default: ['JavascriptLexer'],
handlebars: ['HandlebarsLexer'],
hbs: ['HandlebarsLexer'],
htm: ['HTMLLexer'],
html: ['HTMLLexer'],
js: ['JavascriptLexer'],
jsx: ['JsxLexer'],
mjs: ['JavascriptLexer'],
ts: ['JavascriptLexer'],
tsx: ['JsxLexer'],
},
lineEnding: 'auto',
locales: ['en'],
namespaceSeparator: false,
output: 'src/renderer/i18n/locales/$LOCALE.json',
pluralSeparator: '_',
resetDefaultValueLocale: 'en',
skipDefaultValues: false,
sort: true,
useKeysAsDefaultValue: true,
verbose: false,
};
+632
View File
@@ -0,0 +1,632 @@
{
"player": {
"repeat_all": "opakovat vše",
"stop": "zastavit",
"repeat": "opakovat",
"queue_remove": "odebrat vybrané",
"playRandom": "přehrát náhodné",
"skip": "přeskočit",
"previous": "předchozí",
"toggleFullscreenPlayer": "přepnout celoobrazovkový přehrávač",
"skip_back": "přeskočit dozadu",
"favorite": "oblíbené",
"next": "další",
"shuffle": "náhodně",
"playbackFetchNoResults": "nenalezeny žádné skladby",
"playbackFetchInProgress": "načítání skladeb…",
"addNext": "přidat další",
"playbackSpeed": "rychlost přehrávání",
"playbackFetchCancel": "chvíli to trvá… zavřete oznámení pro zrušení akce",
"play": "přehrát",
"repeat_off": "opakování zakázáno",
"pause": "pozastavit",
"queue_clear": "vymazat frontu",
"muted": "ztlumeno",
"unfavorite": "odebrat z oblíbených",
"queue_moveToTop": "přesunout vybrané dolů",
"queue_moveToBottom": "přesunout vybrané nahoru",
"shuffle_off": "náhodně zakázáno",
"addLast": "přidat poslední",
"mute": "ztlumit",
"skip_forward": "přeskočit dopředu"
},
"setting": {
"crossfadeStyle_description": "vyberte způsob prolnutí u přehrávače zvuku",
"remotePort_description": "nastavení portu pro server vzdáleného ovládání",
"hotkey_skipBackward": "přeskočení zpět",
"replayGainMode_description": "úprava zesílení hlasitosti podle hodnot {{ReplayGain}} uložených v metadatech souborů",
"volumeWheelStep_description": "počet procent, o které má být hlasitost posunuta při přejetí kolečkem myši na posuvníku hlasitosti",
"audioDevice_description": "vyberte zvukové zařízení k přehrávání (pouze webový přehrávač)",
"theme_description": "nastavení motivu použitého v aplikaci",
"hotkey_playbackPause": "pozastavení",
"replayGainFallback": "fallback {{ReplayGain}}",
"sidebarCollapsedNavigation_description": "zobrazit nebo skrýt navigaci ve sbaleném postranním panelu",
"mpvExecutablePath_help": "jedna na řádek",
"hotkey_volumeUp": "zvýšení hlasitosti",
"skipDuration": "doba k přeskočení",
"discordIdleStatus_description": "při povolení bude upraven stav když je přehrávač nečinný",
"showSkipButtons": "zobrazit tlačítka k přeskočení",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "minimální doba pro scrobblování (v procentech)",
"lyricFetch": "načtení textů z internetu",
"scrobble": "scrobblování",
"skipDuration_description": "nastavení doby k přeskočení při použití tlačítek k přeskočení na liště přehrávače",
"enableRemote_description": "povolí vzdálený ovládací server pro umožnění ostatním zařízením ovládat aplikaci",
"fontType_optionSystem": "systémové písmo",
"mpvExecutablePath_description": "nastavení cesty ke spustitelnému souboru mpv",
"replayGainClipping_description": "Zabránění clippingu způsobenému funkcí {{ReplayGain}} automatickým snížením zesílení",
"replayGainPreamp": "před-zesílení {{ReplayGain}} (dB)",
"hotkey_favoriteCurrentSong": "oblíbit $t(common.currentSong)",
"sampleRate": "vzorkovací frekvence",
"crossfadeStyle": "způsob prolnutí",
"sidePlayQueueStyle_optionAttached": "připojené",
"sidebarConfiguration": "nastavení postranního panelu",
"sampleRate_description": "vyberte výstupní vzorkovací frekvenci k použití, když je vybraná vzorkovací frekvence jiná, než ta u aktuálního média",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainClipping": "clipping {{ReplayGain}}",
"hotkey_zoomIn": "přiblížení",
"scrobble_description": "scrobblovat přehrání na váš multimediální server",
"hotkey_browserForward": "vpřed v prohlížeči",
"audioExclusiveMode_description": "zapnout režim výhradního výstupu. V tomto režimu bude obvykle v systému schopný přehrávat zvuk pouze přehrávač mpv",
"discordUpdateInterval": "interval aktualizací {{discord}} rich presence",
"themeLight": "motiv (světlý)",
"fontType_optionBuiltIn": "vestavěné písmo",
"hotkey_playbackPlayPause": "přehrání / pozastavení",
"hotkey_rate1": "hodnocení 1 hvězdou",
"hotkey_skipForward": "přeskočení vpřed",
"disableLibraryUpdateOnStartup": "vypnout kontrolu nových verzí při spuštění",
"discordApplicationId_description": "id aplikace pro {{discord}} rich presence (výchozí je {{defaultId}})",
"sidePlayQueueStyle": "styl postranní fronty přehrávání",
"gaplessAudio": "zvuk bez mezer",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"zoom": "procento přiblížení",
"minimizeToTray_description": "minimalizovat aplikaci do systémové lišty",
"hotkey_playbackPlay": "přehrání",
"hotkey_togglePreviousSongFavorite": "přepnutí oblíbení u $t(common.previousSong)",
"hotkey_volumeDown": "snížení hlasitosti",
"hotkey_unfavoritePreviousSong": "zrušení oblíbení u $t(common.previousSong)",
"audioPlayer_description": "vyberte zvukový přehrávač pro použití k přehrávání",
"globalMediaHotkeys": "globální klávesové zkratky médií",
"hotkey_globalSearch": "globální vyhledávání",
"gaplessAudio_description": "nastavení přehrávače mpv pro přehrávání bez mezer",
"remoteUsername_description": "nastavení uživatelského jména pro server vzdáleného ovládání. pokud je jméno i heslo prázdné, bude autentifikace zakázána",
"disableAutomaticUpdates": "vypnout automatické aktualizace",
"exitToTray_description": "ukončit aplikaci do systémové lišty",
"followLyric_description": "přesouvat texty s aktuální pozicí přehrávání",
"hotkey_favoritePreviousSong": "oblíbit $t(common.previousSong)",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "odsazení textů (ms)",
"discordUpdateInterval_description": "čas v sekundách mezi každou aktualizací (minimálně 15 sekund)",
"fontType_optionCustom": "vlastní písmo",
"themeDark_description": "nastavit použití tmavého motivu v aplikaci",
"audioExclusiveMode": "režim výhradního výstupu",
"remotePassword": "heslo serveru pro vzdálené ovládání",
"lyricFetchProvider": "poskytovatelé textů",
"language_description": "nastavení jazyka aplikace ($t(common.restartRequired))",
"playbackStyle_optionCrossFade": "křížové prolnutí",
"hotkey_rate3": "hodnocení 3 hvězdami",
"font": "písmo",
"mpvExtraParameters": "parametry mpv",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "nastavit použití světlého motivu v aplikaci",
"hotkey_toggleFullScreenPlayer": "přepnutí přehrávače na celou obrazovku",
"hotkey_localSearch": "vyhledávání na stránce",
"hotkey_toggleQueue": "přepnutí fronty",
"zoom_description": "nastavte procento přiblížení aplikace",
"remotePassword_description": "nastavení hesla pro server vzdáleného ovládání. Tyto údaje jsou ve výchozím nastavení přenášeny nezabezpečeným spojením, takže doporučujeme použití unikátního hesla, na kterém vám nezáleží",
"hotkey_rate5": "hodnocení 5 hvězdami",
"hotkey_playbackPrevious": "předchozí skladba",
"showSkipButtons_description": "zobrazit nebo skrýt tlačítka k přeskočení na liště přehrávače",
"crossfadeDuration_description": "nastavte trvání efektu prolnutí",
"language": "jazyk",
"playbackStyle": "způsob přehrávání",
"hotkey_toggleShuffle": "přepnutí náhodného přehrávání",
"theme": "motiv",
"playbackStyle_description": "nastavení způsobu přehrávání pro přehrávač zvuku",
"discordRichPresence_description": "povolit stav přehrávání v {{discord}} rich presence. Klíče obrázků jsou: {{icon}}, {{playing}}, {{paused}} ",
"mpvExecutablePath": "cesta ke spustitelnému souboru mpv",
"audioDevice": "zvukové zařízení",
"hotkey_rate2": "hodnocení 2 hvězdami",
"playButtonBehavior_description": "nastavení výchozího chování tlačítka přehrávání při přidávání skladeb do fronty",
"minimumScrobblePercentage_description": "minimální procento skladby, které musí být přehráno před jejím scrobblováním",
"exitToTray": "ukončit do lišty",
"hotkey_rate4": "hodnocení 4 hvězdami",
"enableRemote": "povolit vzdálený ovládací server",
"showSkipButton_description": "zobrazit nebo skrýt tlačítka k přeskočení na liště přehrávače",
"savePlayQueue": "uložit frontu přehrávání",
"minimumScrobbleSeconds_description": "minimální doba v sekundách, která musí být přehrána před scrobblováním skladby",
"skipPlaylistPage_description": "při navigaci na playlist přejít na stránku seznamu skladeb v playlistu namísto výchozí stránky",
"fontType_description": "vestavěné písmo vybere jedno z písem poskytovaných programem Feishin. systémové písmo vám umožní vybrat si jakékoli písmo poskytované vaším operačním systémem. vlastní vám umožňuje použít vaše vlastní písmo",
"playButtonBehavior": "chování tlačítka přehrávání",
"volumeWheelStep": "krok kolečka hlasitosti",
"sidebarPlaylistList_description": "zobrazit nebo skrýt seznam playlistů v postranním panelu",
"accentColor": "barva",
"sidePlayQueueStyle_description": "nastavení stylu postranní fronty přehrávání",
"accentColor_description": "nastaví barvu aplikace",
"replayGainMode": "režim {{ReplayGain}}",
"playbackStyle_optionNormal": "normální",
"windowBarStyle": "styl záhlaví okna",
"floatingQueueArea": "zobrazit plovoucí oblast přejetí nad frontou",
"replayGainFallback_description": "zesílení v db k použití, když nemá soubor žádné značky {{ReplayGain}}",
"replayGainPreamp_description": "úprava předběžného zesílení použitého na hodnoty {{ReplayGain}}",
"hotkey_toggleRepeat": "přepnutí opakování",
"lyricOffset_description": "odsazení textů o určité množství milisekund",
"sidebarConfiguration_description": "vyberte položky a pořadí, ve kterém budou v postranním panelu",
"fontType": "typ písma",
"remotePort": "port serveru vzdáleného ovládání",
"applicationHotkeys": "aplikační zkratky",
"hotkey_playbackNext": "další skladba",
"useSystemTheme_description": "následovat systémovou předvolbu světlého nebo tmavého motivu",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "načtení textů z různých internetových zdrojů",
"lyricFetchProvider_description": "vyberte poskytovatele textů. pořadí poskytovatelů je pořadí, ve kterém budou načítány",
"globalMediaHotkeys_description": "zapnout nebo vypnout použití vašich systémových zkratek médií pro ovládání přehrávače",
"customFontPath": "vlastní cesta k písmům",
"followLyric": "zobrazit aktuální texty",
"crossfadeDuration": "trvání prolnutí",
"discordIdleStatus": "zobrazit stav nečinnosti v rich presence",
"sidePlayQueueStyle_optionDetached": "odpojené",
"audioPlayer": "zvukový přehrávač",
"hotkey_zoomOut": "oddálení",
"hotkey_unfavoriteCurrentSong": "zrušení oblíbení u $t(common.currentSong)",
"hotkey_rate0": "vymazání hodnocení",
"discordApplicationId": "aplikační id pro {{discord}}",
"applicationHotkeys_description": "nastavení klávesových zkratek aplikace. přepněte pole pro nastavení jako globální zkratku (pouze na počítači)",
"floatingQueueArea_description": "zobrazit ikonu přejetí myší na pravé straně obrazovky pro zobrazení fronty",
"hotkey_volumeMute": "ztlumení",
"hotkey_toggleCurrentSongFavorite": "přepnutí oblíbení u $t(common.currentSong)",
"remoteUsername": "uživatelské jméno serveru vzdáleného ovládání",
"hotkey_browserBack": "zpět v prohlížeči",
"showSkipButton": "zobrazit tlačítka k přeskočení",
"sidebarPlaylistList": "postranní seznam playlistů",
"minimizeToTray": "minimalizovat do lišty",
"skipPlaylistPage": "přeskočit stránku playlistu",
"themeDark": "motiv (tmavý)",
"sidebarCollapsedNavigation": "postranní (sbalená) navigace",
"customFontPath_description": "nastavení cesty k vlastnímu písmu k využití v aplikaci",
"gaplessAudio_optionWeak": "slabý (doporučeno)",
"minimumScrobbleSeconds": "minimální scrobblování (v sekundách)",
"hotkey_playbackStop": "zastavení",
"windowBarStyle_description": "vyberte styl záhlaví okna",
"discordRichPresence": "{{discord}} rich presence",
"font_description": "nastavení písma použitého v aplikaci",
"savePlayQueue_description": "uložit frontu přehrávání, když je aplikace zavřena a obnovit ji při otevření aplikace",
"useSystemTheme": "použít systémový motiv"
},
"action": {
"editPlaylist": "upravit $t(entity.playlist_one)",
"goToPage": "přejít na stránku",
"moveToTop": "přesunout nahoru",
"clearQueue": "vymazat frontu",
"addToFavorites": "přidat do $t(entity.favorite_other)",
"addToPlaylist": "přidat do $t(entity.playlist_one)",
"createPlaylist": "vytvořit $t(entity.playlist_one)",
"removeFromPlaylist": "odebrat z $t(entity.playlist_one)",
"viewPlaylists": "zobrazit $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "odstranit $t(entity.playlist_one)",
"removeFromQueue": "odebrat z fronty",
"deselectAll": "zrušit výběr všeho",
"moveToBottom": "přesunout dolů",
"setRating": "nastavit hodnocení",
"toggleSmartPlaylistEditor": "přepnout editor $t(entity.smartPlaylist)",
"removeFromFavorites": "odebrat z $t(entity.favorite_other)"
},
"common": {
"backward": "zpátky",
"increase": "zvýčit",
"rating": "hodnocení",
"bpm": "bpm",
"refresh": "obnovit",
"unknown": "neznámý",
"areYouSure": "opravdu?",
"edit": "upravit",
"favorite": "oblíbený",
"left": "vlevo",
"save": "uložit",
"right": "vpravo",
"currentSong": "aktuální $t(entity.track_one)",
"collapse": "sbalit",
"trackNumber": "stopa",
"descending": "sestupně",
"add": "přidat",
"gap": "mezera",
"ascending": "vzestupně",
"dismiss": "zavřít",
"year": "rok",
"manage": "správa",
"limit": "limit",
"minimize": "minimalizovat",
"modified": "upraveno",
"duration": "trvání",
"name": "název",
"maximize": "maximalizovat",
"decrease": "snížit",
"ok": "ok",
"description": "popis",
"configure": "nastavit",
"path": "cesta",
"center": "uprostřed",
"no": "ne",
"owner": "majitel",
"enable": "zapnout",
"clear": "vymazat",
"forward": "vpřed",
"delete": "odstranit",
"cancel": "zrušit",
"forceRestartRequired": "restartujte pro použití změn… zavřete oznámení pro restartování",
"setting": "nastavení",
"version": "verze",
"title": "název",
"filter_one": "filtr",
"filter_few": "filtry",
"filter_other": "filtrů",
"filters": "filtry",
"create": "vytvořit",
"bitrate": "datový tok",
"saveAndReplace": "uložit a nahradit",
"action_one": "akce",
"action_few": "akce",
"action_other": "akcí",
"playerMustBePaused": "přehrávač musí být pozastaven",
"confirm": "potvrdit",
"resetToDefault": "resetovat na výchozí",
"home": "domů",
"comingSoon": "již brzy…",
"reset": "resetovat",
"channel_one": "kanál",
"channel_few": "kanály",
"channel_other": "kanálů",
"disable": "vypnout",
"sortOrder": "pořadí",
"none": "žádný",
"menu": "nabídka",
"restartRequired": "vyžadován restart",
"previousSong": "předchozí $t(entity.track_one)",
"noResultsFromQuery": "nebyly nalezeny žádné výsledky",
"quit": "ukončit",
"expand": "rozbalit",
"search": "hledat",
"saveAs": "uložit jako",
"disc": "disk",
"yes": "ano",
"random": "náhodně",
"size": "velikost",
"biography": "biografie",
"note": "poznámka"
},
"table": {
"config": {
"view": {
"card": "karta",
"table": "tabulka",
"poster": "plakát"
},
"general": {
"displayType": "typ zobrazení",
"gap": "$t(common.gap)",
"tableColumns": "sloupce tabulky",
"autoFitColumns": "automaticky přizpůsobit sloupce",
"size": "$t(common.size)"
},
"label": {
"releaseDate": "datum vydání",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (kombinovaný)",
"dateAdded": "datum přidání",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "naposledy přehráno",
"trackNumber": "číslo stopy",
"rowIndex": "index řádku",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "počet přehrání",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "číslo disku",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)"
}
},
"column": {
"comment": "komentář",
"album": "album",
"rating": "hodnocení",
"favorite": "oblíbené",
"playCount": "přehrání",
"albumCount": "$t(entity.album_other)",
"releaseYear": "rok",
"lastPlayed": "naposledy přehráno",
"biography": "biografie",
"releaseDate": "datum vydání",
"bitrate": "datový tok",
"title": "název",
"bpm": "bpm",
"dateAdded": "datum přidání",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "skladba",
"genre": "$t(entity.genre_one)",
"albumArtist": "umělec alba",
"path": "cesta",
"discNumber": "disk",
"channels": "$t(common.channel_other)"
}
},
"error": {
"remotePortWarning": "restartujte server pro použití nového portu",
"systemFontError": "při pokusu o získání systémových písem se vyskytla chyba",
"playbackError": "při pokusu o přehrání médií se vyskytla chyba",
"endpointNotImplementedError": "endpoint {{endpoint}} není u serveru {{serverType}} implementován",
"remotePortError": "při pokusu o nastavení portu vzdáleného serveru se vyskytla chyba",
"serverRequired": "vyžadován server",
"authenticationFailed": "ověření selhalo",
"apiRouteError": "nepodařilo se přesměrovat žádost",
"genericError": "vyskytla se chyba",
"credentialsRequired": "vyžadovány údaje",
"sessionExpiredError": "vaše relace vypršela",
"remoteEnableError": "při pokusu $t(common.enable) vzdálený server se vyskytla chyba",
"localFontAccessDenied": "přístup k místním písmům zakázán",
"serverNotSelectedError": "není vybrán žádný server",
"remoteDisableError": "při pokusu $t(common.disable) vzdálený server se vyskytla chyba",
"mpvRequired": "vyžadován přehrávač MPV",
"audioDeviceFetchError": "při pokusu o přístup ke zvukovým zařízením se vyskytla chyba",
"invalidServer": "neplatný server",
"loginRateError": "příliš mnoho pokusů o přihlášení, zkuste to znovu za pár vteřin"
},
"filter": {
"mostPlayed": "nejvíce přehráváno",
"comment": "komentář",
"playCount": "počet přehrání",
"recentlyUpdated": "nedávno upraveno",
"channels": "$t(common.channel_other)",
"isCompilation": "je kompilace",
"recentlyPlayed": "nedávno přehráno",
"isRated": "je hodnoceno",
"owner": "$t(common.owner)",
"title": "název",
"rating": "hodnocení",
"search": "hledat",
"bitrate": "datový tok",
"genre": "$t(entity.genre_one)",
"recentlyAdded": "nedávno přidáno",
"note": "poznámka",
"name": "název",
"dateAdded": "datum přidání",
"releaseDate": "datum vydání",
"albumCount": "počet $t(entity.album_other)",
"communityRating": "komunitní hodnocení",
"path": "cesta",
"favorited": "oblíbené",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "je nedávno přehráno",
"isFavorited": "je oblíbené",
"bpm": "bpm",
"releaseYear": "rok vydání",
"id": "id",
"disc": "disk",
"biography": "biografie",
"songCount": "počet skladeb",
"artist": "$t(entity.artist_one)",
"duration": "trvání",
"isPublic": "je veřejné",
"random": "náhodně",
"lastPlayed": "naposledy přehráno",
"toYear": "do roku",
"fromYear": "z roku",
"criticRating": "hodnocení kritiků",
"album": "$t(entity.album_one)",
"trackNumber": "skladba"
},
"page": {
"sidebar": {
"nowPlaying": "právě hraje",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "zobrazit shodu textů",
"dynamicBackground": "dynamické pozadí",
"synchronized": "synchronizováno",
"followCurrentLyric": "následovat aktuální text",
"opacity": "neprůhlednost",
"lyricSize": "velikost textů",
"showLyricProvider": "zobrazit poskytovatele textů",
"unsynchronized": "nesynchronizováno",
"lyricAlignment": "zarovnání textů",
"useImageAspectRatio": "použít poměr stran obrázku",
"lyricGap": "mezera textů"
},
"upNext": "další",
"lyrics": "texty",
"related": "související"
},
"appMenu": {
"selectServer": "vybrat server",
"version": "verze {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "správce serverů",
"expandSidebar": "rozbalit postranní panel",
"collapseSidebar": "sbalit postranní panel",
"openBrowserDevtools": "otevřít vývojářské nástroje",
"quit": "$t(common.quit)",
"goBack": "přejít zpět",
"goForward": "přejít vpřed"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "vybráno {{count}}",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"home": {
"mostPlayed": "nejpřehrávanější",
"newlyAdded": "nově přidáno",
"title": "$t(common.home)",
"explore": "procházet z vaší knihovny",
"recentlyPlayed": "nedávno přehráno"
},
"albumDetail": {
"moreFromArtist": "více od tohoto umělce",
"moreFromGeneric": "více od {{item}}"
},
"setting": {
"playbackTab": "přehrávání",
"generalTab": "obecné",
"hotkeysTab": "klávesové zkratky",
"windowTab": "okno"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "příkazy serveru",
"goToPage": "přejít na stránku",
"searchFor": "hledání {{query}}"
},
"title": "příkazy"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"form": {
"deletePlaylist": {
"title": "odstranit $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) úspěšně odstraněn",
"input_confirm": "pro potvrzení zadejte název $t(entity.playlist_one)u"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "vytvořit $t(entity.playlist_one)",
"input_public": "veřejné",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) úspěšně vytvořen",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "přidat server",
"input_username": "uživatelské jméno",
"input_url": "adresa url",
"input_password": "heslo",
"input_legacyAuthentication": "zapnout zastaralé ověřování",
"input_name": "název serveru",
"success": "server úspěšně přidán",
"input_savePassword": "uložit heslo",
"ignoreSsl": "ignorovat SSL $t(common.restartRequired)",
"ignoreCors": "ignorovat CORS $t(common.restartRequired)",
"error_savePassword": "při ukládání hesla se vyskytla chyba"
},
"addToPlaylist": {
"success": "přidáno {{message}} $t(entity.song_other) do {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "přidat do $t(entity.playlist_one)",
"input_skipDuplicates": "přeskočit duplicity",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "upravit server",
"success": "server úspěšně upraven"
},
"queryEditor": {
"input_optionMatchAll": "shoda všeho",
"input_optionMatchAny": "shoda libovolného"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "Hledat texty"
},
"editPlaylist": {
"title": "upravit $t(entity.playlist_one)"
}
},
"entity": {
"genre_one": "žánr",
"genre_few": "žánry",
"genre_other": "žánry",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_few": "{{count}} playlisty",
"playlistWithCount_other": "{{count}} playlistů",
"playlist_one": "playlist",
"playlist_few": "playlisty",
"playlist_other": "playlisty",
"artist_one": "umělec",
"artist_few": "umělci",
"artist_other": "umělci",
"folderWithCount_one": "{{count}} složka",
"folderWithCount_few": "{{count}} složky",
"folderWithCount_other": "{{count}} složek",
"albumArtist_one": "umělec alba",
"albumArtist_few": "umělci alba",
"albumArtist_other": "umělců alba",
"track_one": "skladba",
"track_few": "skladby",
"track_other": "skladby",
"albumArtistCount_one": "{{count}} umělec alba",
"albumArtistCount_few": "{{count}} umělci alba",
"albumArtistCount_other": "{{count}} umělců alba",
"albumWithCount_one": "{{count}} album",
"albumWithCount_few": "{{count}} alba",
"albumWithCount_other": "{{count}} alb",
"favorite_one": "oblíbená",
"favorite_few": "oblíbené",
"favorite_other": "oblíbených",
"artistWithCount_one": "{{count}} umělec",
"artistWithCount_few": "{{count}} umělci",
"artistWithCount_other": "{{count}} umělců",
"folder_one": "složka",
"folder_few": "složky",
"folder_other": "složek",
"smartPlaylist": "chytrý $t(entity.playlist_one)",
"album_one": "album",
"album_few": "alba",
"album_other": "alba",
"genreWithCount_one": "{{count}} žánr",
"genreWithCount_few": "{{count}} žánry",
"genreWithCount_other": "{{count}} žánrů",
"trackWithCount_one": "{{count}} skladba",
"trackWithCount_few": "{{count}} skladby",
"trackWithCount_other": "{{count}} skladeb"
}
}
+556
View File
@@ -0,0 +1,556 @@
{
"action": {
"editPlaylist": "bearbeiten $t(entity.playlist_one)",
"clearQueue": "Warteschlange löschen",
"addToFavorites": "hinzufügen zu $t(entity.favorite_other)",
"addToPlaylist": "hinzufügen zu $t(entity.playlist_one)",
"createPlaylist": "erstelle $t(entity.playlist_one)",
"deletePlaylist": "löschen $t(entity.playlist_one)",
"deselectAll": "Alle abwählen",
"goToPage": "Gehe zur Seite",
"moveToTop": "Nach Oben",
"moveToBottom": "Nach Unten",
"removeFromPlaylist": "Entfernen von $t(entity.playlist_one)",
"viewPlaylists": "Ansicht $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"removeFromQueue": "Von Warteschlange entfernen",
"setRating": "Bewertung festlegen",
"toggleSmartPlaylistEditor": "Editor $t(entity.smartPlaylist) umschalten",
"removeFromFavorites": "Entfernen von $t(entity.favorite_other)"
},
"common": {
"backward": "rückwärts",
"increase": "erhöhen",
"rating": "Wertung",
"bpm": "bpm",
"refresh": "erneuern",
"unknown": "Unbekannt",
"areYouSure": "Bist Du sicher?",
"edit": "Bearbeiten",
"favorite": "Favorit",
"left": "links",
"save": "Speichern",
"right": "rechts",
"currentSong": "momentaner $t(entity.track_one)",
"collapse": "Zusammenklappen",
"trackNumber": "Track",
"descending": "absteigend",
"add": "Hinzufügen",
"gap": "Lücke",
"ascending": "aufsteigend",
"dismiss": "Verwerfen",
"year": "Jahr",
"manage": "Verwalten",
"limit": "Limit",
"minimize": "minimieren",
"modified": "geändert",
"duration": "Laufzeit",
"name": "Name",
"maximize": "maximieren",
"decrease": "verringern",
"ok": "okay",
"description": "Beschreibung",
"configure": "Konfigurieren",
"path": "Pfad",
"center": "Zentrieren",
"no": "Nein",
"owner": "Eigentümer",
"enable": "Aktivieren",
"clear": "Bereinigen",
"forward": "vorwärts",
"delete": "Löschen",
"cancel": "Abbrechen",
"forceRestartRequired": "Neustarten um die Änderungen zu übernehmen... Schließe die Benachrichtigung zum Neustarten",
"setting": "Einstellung",
"version": "Version",
"title": "Titel",
"filter_one": "Filter",
"filter_other": "Filter",
"filters": "Filter",
"create": "Erstellen",
"bitrate": "Bitrate",
"saveAndReplace": "Speichern und Ersetzen",
"action_one": "Aktion",
"action_other": "Aktionen",
"playerMustBePaused": "Player muss pausiert sein",
"confirm": "Bestätigen",
"resetToDefault": "Auf Standard zurücksetzen",
"home": "Home",
"comingSoon": "Kommt bald…",
"reset": "zurücksetzen",
"channel_one": "Kanal",
"channel_other": "Kanäle",
"disable": "Deaktivieren",
"sortOrder": "Reihenfolge",
"none": "keine",
"menu": "Menü",
"restartRequired": "Neustart benötigt",
"previousSong": "vorheriger $t(entity.track_one)",
"noResultsFromQuery": "Die Abfrage brachte keine Ergebnisse",
"quit": "Verlassen",
"expand": "expandieren",
"search": "Suchen",
"saveAs": "Speichern unter",
"disc": "Disk",
"yes": "Ja",
"random": "zufällig",
"size": "Größe",
"biography": "Biografie",
"note": "Hinweis"
},
"error": {
"remotePortWarning": "Starten Sie den Server neu, um den neuen Port anzuwenden",
"systemFontError": "Beim Versuch, Systemschriftarten abzurufen, ist ein Fehler aufgetreten",
"playbackError": "Beim Versuch, das Medium abzuspielen, ist ein Fehler aufgetreten",
"endpointNotImplementedError": "Endgerät {{endpoint}} ist nicht für {{serverType}} implementiert",
"remotePortError": "Beim Versuch, den Remote-Server-Port festzulegen, ist ein Fehler aufgetreten",
"serverRequired": "Server benötigt",
"authenticationFailed": "Authentifizierung fehlgeschlagen",
"apiRouteError": "Anforderung kann nicht weitergeleitet werden",
"genericError": "Ein Fehler ist aufgetreten",
"credentialsRequired": "Anmeldeinformationen erforderlich",
"sessionExpiredError": "Deine Sitzung ist abgelaufen",
"remoteEnableError": "Beim Versuch, den Remote-Server mit $t(common.enable), ist ein Fehler aufgetreten",
"localFontAccessDenied": "Zugriff auf lokale Schriftarten verweigert",
"serverNotSelectedError": "Kein Server ausgewählt",
"remoteDisableError": "Beim Versuch, den Remote-Server mit $t(common.disable), ist ein Fehler aufgetreten",
"mpvRequired": "MPV benötigt",
"audioDeviceFetchError": "Beim Versuch, Audiogeräte abzurufen, ist ein Fehler aufgetreten",
"invalidServer": "Ungültiger Server",
"loginRateError": "Zu viele Anmeldeversuche, bitte versuche es in einigen Sekunden erneut"
},
"filter": {
"mostPlayed": "Meist gespielt",
"comment": "Kommentar",
"playCount": "Anzahl abgespielt",
"recentlyUpdated": "kürzlich aktualisiert",
"isCompilation": "ist Zusammenstellung",
"recentlyPlayed": "kürzlich gespielt",
"isRated": "ist bewertet",
"title": "Titel",
"rating": "Bewertung",
"search": "Suche",
"bitrate": "Bitrate",
"recentlyAdded": "kürzlich hinzugefügt",
"note": "Hinweis",
"name": "Name",
"dateAdded": "Datum hinzugefügt",
"releaseDate": "Veröffentlichungsdatum",
"albumCount": "$t(entity.album_other) Anzahl",
"communityRating": "Community-Wertung",
"path": "Pfad",
"favorited": "favorisiert",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "wurde kürzlich gespielt",
"isFavorited": "wird favorisiert",
"bpm": "bpm",
"releaseYear": "Erscheinungsjahr",
"id": "ID",
"disc": "Disk",
"biography": "Biografie",
"songCount": "Anzahl Lieder",
"duration": "Dauer",
"isPublic": "ist öffentlich",
"random": "zufällig",
"lastPlayed": "Zuletzt gespielt",
"toYear": "bis Jahr",
"fromYear": "ab Jahr",
"criticRating": "Kritikerbewertung",
"album": "$t(entity.album_one)",
"trackNumber": "Track",
"channels": "$t(common.channel_other)",
"owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)",
"artist": "$t(entity.artist_one)"
},
"form": {
"deletePlaylist": {
"title": "Lösche $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) erfolgreich gelöscht",
"input_confirm": "Geben Sie zur Bestätigung den Namen von $t(entity.playlist_one) ein"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "Erstellen $t(entity.playlist_one)",
"input_public": "öffentlich",
"success": "$t(entity.playlist_one) erfolgreich erstellt",
"input_name": "$t(common.name)",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "Server hinzufügen",
"input_username": "Benutzername",
"input_url": "URL",
"input_password": "Passwort",
"input_legacyAuthentication": "Aktivieren der Legacy-Authentifizierung",
"input_name": "Server Name",
"success": "Server erfolgreich hinzugefügt",
"input_savePassword": "Passwort speichern",
"ignoreSsl": "ignoriere ssl $t(common.restartRequired)",
"ignoreCors": "ignoriere cors $t(common.restartRequired)",
"error_savePassword": "Beim Versuch, das Passwort zu speichern, ist ein Fehler aufgetreten"
},
"addToPlaylist": {
"success": "{{message}} $t(entity.song_other) zu {{numOfPlaylists}} $t(entity.playlist_other) hinzugefügt",
"title": "Zu $t(entity.playlist_one) hinzufügen",
"input_skipDuplicates": "Duplikate überspringen",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "Server aktualisieren",
"success": "Server erfolgreich aktualisiert"
},
"queryEditor": {
"input_optionMatchAll": "Treffer Alle",
"input_optionMatchAny": "Treffer Einige"
},
"editPlaylist": {
"title": "Bearbeite $t(entity.playlist_one)"
},
"lyricSearch": {
"title": "Songtext Suche",
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)"
}
},
"entity": {
"genre_one": "Genre",
"genre_other": "Genres",
"playlistWithCount_one": "{{count}} Wiedergabeliste",
"playlistWithCount_other": "{{count}} Wiedergabelisten",
"playlist_one": "Wiedergabeliste",
"playlist_other": "Wiedergabelisten",
"artist_one": "Interpret",
"artist_other": "Interpreten",
"folderWithCount_one": "{{count}} Verzeichnis",
"folderWithCount_other": "{{count}} Verzeichnisse",
"albumArtist_one": "Album Interpret",
"albumArtist_other": "Album Interpreten",
"track_one": "Track",
"track_other": "Tracks",
"albumArtistCount_one": "{{count}} Album Interpret",
"albumArtistCount_other": "{{count}} Album Interpreten",
"albumWithCount_one": "{{count}} Album",
"albumWithCount_other": "{{count}} Alben",
"favorite_one": "Favorit",
"favorite_other": "Favoriten",
"artistWithCount_one": "{{count}} Interpret",
"artistWithCount_other": "{{count}} Interpreten",
"folder_one": "Verzeichnis",
"folder_other": "Verzeichnisse",
"album_one": "Album",
"album_other": "Alben",
"genreWithCount_one": "{{count}} Genre",
"genreWithCount_other": "{{count}} Genres",
"trackWithCount_one": "{{count}} Track",
"trackWithCount_other": "{{count}} Tracks",
"smartPlaylist": "Smart $t(entity.playlist_one)"
},
"table": {
"config": {
"view": {
"table": "Tabelle"
},
"general": {
"tableColumns": "Tabellenspalten"
}
},
"column": {
"releaseYear": "Jahr",
"biography": "Biografie",
"releaseDate": "Veröffentlichungsdatum",
"bitrate": "Bitrate",
"title": "Titel",
"path": "Pfad"
}
},
"page": {
"fullscreenPlayer": {
"config": {
"showLyricMatch": "Textübereinstimmung anzeigen",
"dynamicBackground": "Dynamischer Hintergrund",
"synchronized": "synchronisiert",
"followCurrentLyric": "dem Songtext folgen",
"opacity": "Deckkraft",
"lyricSize": "Songtext Größe",
"showLyricProvider": "Songtext-Anbieter anzeigen",
"unsynchronized": "nicht synchronisiert",
"lyricAlignment": "Songtext Ausrichtung",
"useImageAspectRatio": "Bildseitenverhältnis verwenden",
"lyricGap": "Songtext Lücke"
},
"upNext": "als nächstes",
"lyrics": "Songtexte",
"related": "Ähnliche"
},
"appMenu": {
"selectServer": "Server auswählen",
"version": "Version {{version}}",
"manageServers": "Server verwalten",
"expandSidebar": "Seitenleiste erweitern",
"collapseSidebar": "Seitenleiste einklappen",
"openBrowserDevtools": "Browser Entwicklungswerkzeuge öffnen",
"goBack": "Gehe zurück",
"goForward": "Gehe vorwärts",
"settings": "$t(common.setting_other)",
"quit": "$t(common.quit)"
},
"home": {
"mostPlayed": "Meist gespielt",
"newlyAdded": "Neu hinzugefügte Veröffentlichungen",
"explore": "Entdecken Sie Ihre Bibliothek",
"recentlyPlayed": "Kürzlich gespielt",
"title": "$t(common.home)"
},
"albumDetail": {
"moreFromArtist": "Mehr von diesem $t(entity.genre_one)",
"moreFromGeneric": "Mehr von {{item}}"
},
"globalSearch": {
"commands": {
"serverCommands": "Serverbefehle",
"goToPage": "Gehe zur Seite",
"searchFor": "Suche nach {{query}}"
},
"title": "Befehle"
},
"contextMenu": {
"numberSelected": "{{count}} Ausgewählte",
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"sidebar": {
"nowPlaying": "läuft gerade",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"setting": {
"playbackTab": "Wiedergabe",
"generalTab": "allgemein",
"hotkeysTab": "Kurzbefehle",
"windowTab": "Fenster"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"player": {
"next": "Nächster",
"addNext": "Als Nächstes einfügen",
"play": "Abspielen",
"muted": "Stummgeschaltet",
"addLast": "Ans Ende einzufügen",
"mute": "Stumm",
"playRandom": "Zufällige Wiedergabe",
"previous": "Vorheriger",
"favorite": "Favorit",
"playbackFetchNoResults": "Keine Lieder gefunden",
"playbackFetchInProgress": "Lieder werden geladen…",
"playbackSpeed": "Wiedergabegeschwindigkeit",
"playbackFetchCancel": "Das dauert eine Weile. Schließen Sie die Benachrichtigung, um den Vorgang abzubrechen",
"queue_clear": "Bereinige Warteschlange",
"repeat_all": "Alle wiederholen",
"repeat": "Wiederholen",
"queue_remove": "Ausgewählte entfernen",
"shuffle": "Zufallswiedergabe",
"repeat_off": "Nicht wiederholen",
"queue_moveToTop": "Ausgewählte nach unten verschieben",
"queue_moveToBottom": "Ausgewählte nach oben verschieben",
"shuffle_off": "Zufallswiedergabe deaktiviert",
"stop": "Stopp",
"toggleFullscreenPlayer": "Vollbildmodus",
"skip_back": "Zurückspulen",
"pause": "Pause",
"unfavorite": "Aus Favoriten entfernen",
"skip_forward": "Vorspulen",
"skip": "Überspringen"
},
"setting": {
"audioDevice_description": "Wählen Sie das Audiogerät aus, das für die Wiedergabe verwendet werden soll (nur Webplayer).",
"audioExclusiveMode": "Audio Exklusiver Modus",
"audioDevice": "Audiogerät",
"accentColor": "Akzentfarbe",
"accentColor_description": "Legt die Akzentfarbe für die Anwendung fest",
"applicationHotkeys": "Tastenkombinationen der Anwendung",
"applicationHotkeys_description": "Konfiguriere die Tastenkombinationen der Anwendung. Setze einen Haken, um die Tastenkombination global zu verwenden (nur für die Desktopanwendung)",
"crossfadeStyle_description": "Wählen Sie Art des Überblendungseffekts aus, welcher für den Audioplayer verwendet werden soll",
"discordIdleStatus_description": "Wenn aktiviert wird der Rich Presence Status aktiviert, wenn sich der Player im Leerlauf befindet",
"crossfadeStyle": "Art der Überblendung",
"audioExclusiveMode_description": "Aktivieren Sie den exklusiven Ausgabemodus. In diesem Modus ist das System normalerweise gesperrt und nur MPV ist in der Lage Audio ausgeben",
"disableLibraryUpdateOnStartup": "Beim Start nicht nach neuen Versionen suchen",
"discordApplicationId_description": "Die Application-ID für {{discord}} Rich Presence (Standard: {{defaultId}})",
"audioPlayer_description": "Wählen Sie den Audioplayer aus, der für die Wiedergabe verwendet werden soll",
"disableAutomaticUpdates": "Automatische Updates deaktivieren",
"crossfadeDuration_description": "Legt die Dauer der Überblendung fest",
"customFontPath": "Benutzerdefinierter Pfad für Schriftarten",
"crossfadeDuration": "Dauer der Überblendung",
"discordIdleStatus": "Rich Presence Status im Leerlauf",
"audioPlayer": "Audio-Player",
"discordApplicationId": "{{discord}} Anwendungs ID",
"customFontPath_description": "Legt den Pfad zur benutzerdefinierten Schriftart fest, welche für die Anwendung verwendet werden soll",
"discordRichPresence": "{{discord}} Rich Presence",
"remotePort_description": "Legt den Port des Fernsteuerungsserver fest",
"hotkey_skipBackward": "rückwärts springen",
"replayGainMode_description": "Passen Sie die Lautstärkeverstärkung entsprechend den in den Dateimetadaten gespeicherten {{ReplayGain}}-Werten an",
"volumeWheelStep_description": "die Lautstärke, die beim Scrollen des Mausrads auf dem Lautstärkeregler geändert werden soll",
"theme_description": "Legt das für die Anwendung zu verwendende Thema fest",
"hotkey_playbackPause": "Pause",
"sidebarCollapsedNavigation_description": "Zeigt die Navigation in der minimierten Seitenleiste an oder verbirgt sie",
"mpvExecutablePath_help": "eine pro Zeile",
"hotkey_volumeUp": "Lauter",
"skipDuration": "Sprung Dauer",
"showSkipButtons": "Schaltflächen zum Überspringen anzeigen",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "minimale Scrobble-Dauer (Prozentsatz)",
"lyricFetch": "Songtexte aus dem Internet abrufen",
"scrobble": "Scrobbeln",
"skipDuration_description": "Legt die zu überspringende Dauer fest, wenn die Überspringen-Schaltflächen in der Player-Leiste verwendet werden",
"mpvExecutablePath_description": "Legt den Pfad zur ausführbaren MPV-Datei fest",
"replayGainClipping_description": "Verhindern Sie durch {{ReplayGain}} verursachtes Clipping, indem Sie die Verstärkung automatisch verringern",
"replayGainPreamp": "{{ReplayGain}} Vorverstärker (db)",
"hotkey_favoriteCurrentSong": "Favorit $t(common.currentSong)",
"sampleRate": "Abtastrate",
"sidePlayQueueStyle_optionAttached": "angefügt",
"sidebarConfiguration": "Seitenleistenkonfiguration",
"sampleRate_description": "Wählen Sie die auszugebende Abtastrate aus, wenn sich die ausgewählte Abtastfrequenz von der des aktuellen Mediums unterscheidet",
"replayGainMode_optionNone": "$t(common.none)",
"hotkey_zoomIn": "Hineinzoomen",
"scrobble_description": "Scrobble wird auf Ihrem Medienserver abgespielt",
"hotkey_browserForward": "Browser vor",
"hotkey_playbackPlayPause": "Wiedergabe / Pause",
"hotkey_rate1": "Bewertung 1 Stern",
"hotkey_skipForward": "vorwärts springen",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"minimizeToTray_description": "Minimieren der Anwendung in die Taskleiste",
"hotkey_playbackPlay": "Wiedergabe",
"hotkey_volumeDown": "Leiser",
"hotkey_unfavoritePreviousSong": "$t(common.previousSong) aus Favoriten entfernen",
"globalMediaHotkeys": "Globale Medien Kurzbefehle",
"hotkey_globalSearch": "Globale Suche",
"gaplessAudio_description": "Legt die lückenlose Audioeinstellung für MPV fest",
"remoteUsername_description": "Legt den Benutzernamen für den Fernsteuerungsserver fest. Wenn sowohl Benutzername als auch Passwort leer sind, wird die Authentifizierung deaktiviert",
"hotkey_favoritePreviousSong": "Favorit $t(common.previousSong)",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "Liedtext-Versatz (ms)",
"themeDark_description": "Legt das dunkle Design fest, das für die Anwendung verwendet werden soll",
"remotePassword": "Passwort des Fernsteuerungsservers",
"lyricFetchProvider": "Anbieter, von denen Liedtexte abgerufen werden können",
"language_description": "Legt die Sprache für die Anwendung fest $t(common.restartRequired)",
"playbackStyle_optionCrossFade": "Überblendung",
"hotkey_rate3": "Bewertung 3 Sterne",
"mpvExtraParameters": "mpv Parameter",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "Legt das helle Thema fest, das für die Anwendung verwendet werden soll",
"hotkey_toggleFullScreenPlayer": "Vollbildmodus umschalten",
"hotkey_localSearch": "Suche auf Seite",
"hotkey_toggleQueue": "Warteschlange umschalten",
"remotePassword_description": "Legt das Passwort für den Fernsteuerungsserver fest. Diese Anmeldeinformationen werden standardmäßig unsicher übertragen, daher sollten Sie ein eindeutiges Passwort verwenden, das Ihnen egal ist",
"hotkey_rate5": "Bewertung 5 Sterne",
"hotkey_playbackPrevious": "Vorheriger Track",
"showSkipButtons_description": "Ein- oder Ausblenden der Überspringen-Schaltflächen in der Player-Leiste",
"language": "Sprache",
"playbackStyle": "Wiedergabestil",
"hotkey_toggleShuffle": "Zufallswiedergabe umschalten",
"theme": "Thema",
"playbackStyle_description": "Wählen Sie den Wiedergabestil aus, der für den Audioplayer verwendet werden soll",
"mpvExecutablePath": "Pfad der ausführbaren MPV-Datei",
"hotkey_rate2": "Bewertung 2 Sterne",
"playButtonBehavior_description": "Legt das Standardverhalten der Wiedergabeschaltfläche fest, wenn Songs zur Warteschlange hinzugefügt werden",
"minimumScrobblePercentage_description": "Der Mindestprozentsatz des Songs, der gespielt werden muss, bevor er gescrobbelt wird",
"hotkey_rate4": "Bewertung 4 Sterne",
"showSkipButton_description": "Ein- oder Ausblenden der Überspringen-Schaltflächen in der Player-Leiste",
"savePlayQueue": "Wiedergabe-Warteschlange speichern",
"minimumScrobbleSeconds_description": "die Mindestdauer in Sekunden, die das Lied abspielen muss, bevor es gescrobbelt wird",
"skipPlaylistPage_description": "Gehen Sie beim Navigieren zu einer Wiedergabeliste zur Titelseite der Wiedergabeliste und nicht zur Standardseite",
"fontType_description": "Die integrierte Schriftart wählt eine der von Feishin bereitgestellten Schriftarten aus. Mit der Systemschriftart können Sie jede von Ihrem Betriebssystem bereitgestellte Schriftart auswählen. Benutzerdefiniert erlaubt es eine eigene Schriftart bereitstellen",
"playButtonBehavior": "Verhalten der Wiedergabetaste",
"volumeWheelStep": "Lautstärkeregler Stufe",
"sidebarPlaylistList_description": "Ein- oder Ausblenden der Playlisten-Liste in der Seitenleiste",
"sidePlayQueueStyle_description": "Legt den Stil der Wiedergabewarteliste in der Seitenleiste fest",
"replayGainMode": "{{ReplayGain}} Modus",
"playbackStyle_optionNormal": "Normal",
"windowBarStyle": "Fensterleistenstil",
"replayGainFallback_description": "Verstärkung in db, die angewendet werden soll, wenn die Datei keine {{ReplayGain}}-Tags hat",
"replayGainPreamp_description": "Passen Sie die Vorverstärkerverstärkung an, die auf die {{ReplayGain}}-Werte angewendet wird",
"hotkey_toggleRepeat": "Wiederholung umschalten",
"lyricOffset_description": "Versetzen Sie den Liedtext um die angegebene Anzahl von Millisekunden",
"sidebarConfiguration_description": "Wählen Sie die Elemente und die Reihenfolge aus, in der sie in der Seitenleiste angezeigt werden",
"remotePort": "Port des Fernsteuerungsserver",
"hotkey_playbackNext": "Nächster Track",
"useSystemTheme_description": "der systemdefinierten Hell oder Dunkel Präferenz folgen",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "Songtexte aus verschiedenen Internetquellen abrufen",
"lyricFetchProvider_description": "Wählen Sie die Anbieter aus, von denen Sie Liedtexte abrufen möchten. Die Reihenfolge der Anbieter ist die Reihenfolge, in der sie abgefragt werden",
"globalMediaHotkeys_description": "Aktivieren oder deaktivieren Sie die Verwendung der Medien-Kurzbefehle Ihres Systems zur Steuerung der Wiedergabe",
"hotkey_zoomOut": "Herauszoomen",
"hotkey_unfavoriteCurrentSong": "$t(common.currentSong) aus Favoriten entfernen",
"hotkey_rate0": "Bewertung löschen",
"hotkey_volumeMute": "Lautstärke stumm",
"remoteUsername": "Benutzername des Fernsteuerungsserver",
"hotkey_browserBack": "Browser zurück",
"showSkipButton": "Schaltflächen zum Überspringen anzeigen",
"sidebarPlaylistList": "Seitenleiste Playlisten-Liste",
"minimizeToTray": "Zur Taskleiste minimieren",
"skipPlaylistPage": "Playlisten-Seite überspringen",
"themeDark": "Thema (dunkel)",
"sidebarCollapsedNavigation": "Navigation in der Seitenleiste (komprimiert)",
"gaplessAudio_optionWeak": "schwach (empfohlen)",
"minimumScrobbleSeconds": "minimales Scrobble (Sekunden)",
"hotkey_playbackStop": "Stoppen",
"savePlayQueue_description": "Speichert Wiedergabewarteschlange, wenn die Anwendung geschlossen wird, und stellt sie wieder her, wenn die Anwendung geöffnet wird",
"useSystemTheme": "Systemdesign verwenden",
"enableRemote_description": "Aktiviere den eingebauten Webserver, um die Anwendung von anderen Geräten aus zu steuern",
"fontType_optionSystem": "Systemschriftart",
"discordUpdateInterval": "{{discord}} Rich Presence Aktualisierungsintervall",
"fontType_optionBuiltIn": "Eingebaute Schriftart",
"gaplessAudio": "Unterbrechungsfreie Wiedergabe",
"exitToTray_description": "Die Anwendung beim Schließen in die Taskleiste minimieren",
"followLyric_description": "Der Songtext scrollt automatisch mir der Wiedergabe",
"discordUpdateInterval_description": "Zeit in Sekunden zwischen den Statusupdates (Minimum: 15s)",
"fontType_optionCustom": "Benutzerdefinierte Schriftart",
"font": "Schriftart",
"exitToTray": "In die Taskleiste minimieren",
"enableRemote": "Server für Fernzugriff aktivieren",
"floatingQueueArea": "Beim Darüberfahren schwebende Warteschlange anzeigen",
"fontType": "Schriftartenquelle",
"followLyric": "Songtext synchronisieren",
"floatingQueueArea_description": "Zeige ein Icon auf der rechten Seite, um beim Darüberfahren die Wartschlange anzuzeigen",
"font_description": "Wähle die Schriftart für die Anwendung",
"themeLight": "Thema (hell)",
"sidePlayQueueStyle_optionDetached": "lösgelöst",
"windowBarStyle_description": "Wähle den Stil der Windows-Leiste"
}
}
+617 -7
View File
@@ -1,9 +1,619 @@
{
"player": {
"next": "player.next",
"play": "player.play",
"prev": "player.prev",
"seekBack": "player.seekBack",
"seekForward": "player.seekForward"
}
"action": {
"addToFavorites": "add to $t(entity.favorite_other)",
"addToPlaylist": "add to $t(entity.playlist_one)",
"clearQueue": "clear queue",
"createPlaylist": "create $t(entity.playlist_one)",
"deletePlaylist": "delete $t(entity.playlist_one)",
"deselectAll": "deselect all",
"editPlaylist": "edit $t(entity.playlist_one)",
"goToPage": "go to page",
"moveToBottom": "move to bottom",
"moveToTop": "move to top",
"refresh": "$t(common.refresh)",
"removeFromFavorites": "remove from $t(entity.favorite_other)",
"removeFromPlaylist": "remove from $t(entity.playlist_one)",
"removeFromQueue": "remove from queue",
"setRating": "set rating",
"toggleSmartPlaylistEditor": "toggle $t(entity.smartPlaylist) editor",
"viewPlaylists": "view $t(entity.playlist_other)"
},
"common": {
"action_one": "action",
"action_other": "actions",
"add": "add",
"areYouSure": "are you sure?",
"ascending": "ascending",
"backward": "backward",
"biography": "biography",
"bitrate": "bitrate",
"bpm": "bpm",
"cancel": "cancel",
"center": "center",
"channel_one": "channel",
"channel_other": "channels",
"clear": "clear",
"collapse": "collapse",
"comingSoon": "coming soon…",
"configure": "configure",
"confirm": "confirm",
"create": "create",
"currentSong": "current $t(entity.track_one)",
"decrease": "decrease",
"delete": "delete",
"descending": "descending",
"description": "description",
"disable": "disable",
"disc": "disc",
"dismiss": "dismiss",
"duration": "duration",
"edit": "edit",
"enable": "enable",
"expand": "expand",
"favorite": "favorite",
"filter_one": "filter",
"filter_other": "filters",
"filters": "filters",
"forceRestartRequired": "restart to apply changes… close the notification to restart",
"forward": "forward",
"gap": "gap",
"home": "home",
"increase": "increase",
"left": "left",
"limit": "limit",
"manage": "manage",
"maximize": "maximize",
"menu": "menu",
"minimize": "minimize",
"modified": "modified",
"name": "name",
"no": "no",
"none": "none",
"noResultsFromQuery": "the query returned no results",
"note": "note",
"ok": "ok",
"owner": "owner",
"path": "path",
"playerMustBePaused": "player must be paused",
"previousSong": "previous $t(entity.track_one)",
"quit": "quit",
"random": "random",
"rating": "rating",
"refresh": "refresh",
"reset": "reset",
"resetToDefault": "reset to default",
"restartRequired": "restart required",
"right": "right",
"save": "save",
"saveAndReplace": "save and replace",
"saveAs": "save as",
"search": "search",
"setting": "setting",
"setting_one": "setting",
"setting_other": "settings",
"size": "size",
"sortOrder": "order",
"title": "title",
"trackNumber": "track",
"unknown": "unknown",
"version": "version",
"year": "year",
"yes": "yes"
},
"entity": {
"album_one": "album",
"album_other": "albums",
"albumArtist_one": "album artist",
"albumArtist_other": "album artists",
"albumArtistCount_one": "{{count}} album artist",
"albumArtistCount_other": "{{count}} album artists",
"albumWithCount_one": "{{count}} album",
"albumWithCount_other": "{{count}} albums",
"artist_one": "artist",
"artist_other": "artists",
"artistWithCount_one": "{{count}} artist",
"artistWithCount_other": "{{count}} artists",
"favorite_one": "favorite",
"favorite_other": "favorites",
"folder_one": "folder",
"folder_other": "folders",
"folderWithCount_one": "{{count}} folder",
"folderWithCount_other": "{{count}} folders",
"genre_one": "genre",
"genre_other": "genres",
"genreWithCount_one": "{{count}} genre",
"genreWithCount_other": "{{count}} genres",
"playlist_one": "playlist",
"playlist_other": "playlists",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_other": "{{count}} playlists",
"smartPlaylist": "smart $t(entity.playlist_one)",
"track_one": "track",
"track_other": "tracks",
"trackWithCount_one": "{{count}} track",
"trackWithCount_other": "{{count}} tracks"
},
"error": {
"apiRouteError": "unable to route request",
"audioDeviceFetchError": "an error occurred when trying to get audio devices",
"authenticationFailed": "authentication failed",
"credentialsRequired": "credentials required",
"endpointNotImplementedError": "endpoint {{endpoint}} is not implemented for {{serverType}}",
"genericError": "an error occurred",
"invalidServer": "invalid server",
"localFontAccessDenied": "access denied to local fonts",
"loginRateError": "too many login attempts, please try again in a few seconds",
"mpvRequired": "MPV required",
"networkError": "a network error occurred",
"playbackError": "an error occurred when trying to play the media",
"remoteDisableError": "an error occurred when trying to $t(common.disable) the remote server",
"remoteEnableError": "an error occurred when trying to $t(common.enable) the remote server",
"remotePortError": "an error occurred when trying to set the remote server port",
"remotePortWarning": "restart the server to apply the new port",
"serverNotSelectedError": "no server selected",
"serverRequired": "server required",
"sessionExpiredError": "your session has expired",
"systemFontError": "an error occurred when trying to get system fonts"
},
"filter": {
"album": "$t(entity.album_one)",
"albumArtist": "$t(entity.albumArtist_one)",
"albumCount": "$t(entity.album_other) count",
"artist": "$t(entity.artist_one)",
"biography": "biography",
"bitrate": "bitrate",
"bpm": "bpm",
"channels": "$t(common.channel_other)",
"comment": "comment",
"communityRating": "community rating",
"criticRating": "critic rating",
"dateAdded": "date added",
"disc": "disc",
"duration": "duration",
"favorited": "favorited",
"fromYear": "from year",
"genre": "$t(entity.genre_one)",
"id": "id",
"isCompilation": "is compilation",
"isFavorited": "is favorited",
"isPublic": "is public",
"isRated": "is rated",
"isRecentlyPlayed": "is recently played",
"lastPlayed": "last played",
"mostPlayed": "most played",
"name": "name",
"note": "note",
"owner": "$t(common.owner)",
"path": "path",
"playCount": "play count",
"random": "random",
"rating": "rating",
"recentlyAdded": "recently added",
"recentlyPlayed": "recently played",
"recentlyUpdated": "recently updated",
"releaseDate": "release date",
"releaseYear": "release year",
"search": "search",
"songCount": "song count",
"title": "title",
"toYear": "to year",
"trackNumber": "track"
},
"form": {
"addServer": {
"error_savePassword": "an error occurred when trying to save the password",
"ignoreCors": "ignore cors ($t(common.restartRequired))",
"ignoreSsl": "ignore ssl ($t(common.restartRequired))",
"input_legacyAuthentication": "enable legacy authentication",
"input_name": "server name",
"input_password": "password",
"input_savePassword": "save password",
"input_url": "url",
"input_username": "username",
"success": "server added successfully",
"title": "add server"
},
"addToPlaylist": {
"input_playlists": "$t(entity.playlist_other)",
"input_skipDuplicates": "skip duplicates",
"success": "added {{message}} $t(entity.song_other) to {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "add to $t(entity.playlist_one)"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"input_name": "$t(common.name)",
"input_owner": "$t(common.owner)",
"input_public": "public",
"success": "$t(entity.playlist_one) created successfully",
"title": "create $t(entity.playlist_one)"
},
"deletePlaylist": {
"input_confirm": "type the name of the $t(entity.playlist_one) to confirm",
"success": "$t(entity.playlist_one) deleted successfully",
"title": "delete $t(entity.playlist_one)"
},
"editPlaylist": {
"title": "edit $t(entity.playlist_one)"
},
"lyricSearch": {
"input_artist": "$t(entity.artist_one)",
"input_name": "$t(common.name)",
"title": "lyric search"
},
"queryEditor": {
"input_optionMatchAll": "match all",
"input_optionMatchAny": "match any"
},
"updateServer": {
"success": "server updated successfully",
"title": "update server"
}
},
"page": {
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"albumDetail": {
"moreFromArtist": "more from this $t(entity.artist_one)",
"moreFromGeneric": "more from {{item}}"
},
"albumList": {
"title": "$t(entity.album_other)"
},
"appMenu": {
"collapseSidebar": "collapse sidebar",
"expandSidebar": "expand sidebar",
"goBack": "go back",
"goForward": "go forward",
"manageServers": "manage servers",
"openBrowserDevtools": "open browser devtools",
"quit": "$t(common.quit)",
"selectServer": "select server",
"settings": "$t(common.setting_other)",
"version": "version {{version}}"
},
"contextMenu": {
"addFavorite": "$t(action.addToFavorites)",
"addLast": "$t(player.addLast)",
"addNext": "$t(player.addNext)",
"addToFavorites": "$t(action.addToFavorites)",
"addToPlaylist": "$t(action.addToPlaylist)",
"createPlaylist": "$t(action.createPlaylist)",
"deletePlaylist": "$t(action.deletePlaylist)",
"deselectAll": "$t(action.deselectAll)",
"moveToBottom": "$t(action.moveToBottom)",
"moveToTop": "$t(action.moveToTop)",
"numberSelected": "{{count}} selected",
"play": "$t(player.play)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromQueue": "$t(action.removeFromQueue)",
"setRating": "$t(action.setRating)"
},
"fullscreenPlayer": {
"config": {
"dynamicBackground": "dynamic background",
"followCurrentLyric": "follow current lyric",
"lyricAlignment": "lyric alignment",
"lyricGap": "lyric gap",
"lyricSize": "lyric size",
"opacity": "opacity",
"showLyricMatch": "show lyric match",
"showLyricProvider": "show lyric provider",
"synchronized": "synchronized",
"unsynchronized": "unsynchronized",
"useImageAspectRatio": "use image aspect ratio"
},
"lyrics": "lyrics",
"related": "related",
"upNext": "up next"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"globalSearch": {
"commands": {
"goToPage": "go to page",
"searchFor": "search for {{query}}",
"serverCommands": "server commands"
},
"title": "commands"
},
"home": {
"explore": "explore from your library",
"mostPlayed": "most played",
"newlyAdded": "newly added releases",
"recentlyPlayed": "recently played",
"title": "$t(common.home)"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"setting": {
"generalTab": "general",
"hotkeysTab": "hotkeys",
"playbackTab": "playback",
"windowTab": "window"
},
"sidebar": {
"albumArtists": "$t(entity.albumArtist_other)",
"albums": "$t(entity.album_other)",
"artists": "$t(entity.artist_other)",
"folders": "$t(entity.folder_other)",
"genres": "$t(entity.genre_other)",
"home": "$t(common.home)",
"nowPlaying": "now playing",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"settings": "$t(common.setting_other)",
"tracks": "$t(entity.track_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
}
},
"player": {
"addLast": "add last",
"addNext": "add next",
"favorite": "favorite",
"mute": "mute",
"muted": "muted",
"next": "next",
"play": "play",
"playbackFetchCancel": "this is taking a while… close the notification to cancel",
"playbackFetchInProgress": "loading songs…",
"playbackFetchNoResults": "no songs found",
"playbackSpeed": "playback speed",
"playRandom": "play random",
"previous": "previous",
"queue_clear": "clear queue",
"queue_moveToBottom": "move selected to top",
"queue_moveToTop": "move selected to bottom",
"queue_remove": "remove selected",
"repeat": "repeat",
"repeat_all": "repeat all",
"repeat_off": "repeat disabled",
"repeat_one": "repeat one",
"repeat_other": "",
"shuffle": "shuffle",
"shuffle_off": "shuffle disabled",
"skip": "skip",
"skip_back": "skip backwards",
"skip_forward": "skip forwards",
"stop": "stop",
"toggleFullscreenPlayer": "toggle fullscreen player",
"unfavorite": "unfavorite",
"pause": "pause"
},
"setting": {
"accentColor": "accent color",
"accentColor_description": "sets the accent color for the application",
"applicationHotkeys": "application hotkeys",
"applicationHotkeys_description": "configure application hotkeys. toggle the checkbox to set as a global hotkey (desktop only)",
"audioDevice": "audio device",
"audioDevice_description": "select the audio device to use for playback (web player only)",
"audioExclusiveMode": "audio exclusive mode",
"audioExclusiveMode_description": "enable exclusive output mode. In this mode, the system is usually locked out, and only mpv will be able to output audio",
"audioPlayer": "audio player",
"audioPlayer_description": "select the audio player to use for playback",
"crossfadeDuration": "crossfade duration",
"crossfadeDuration_description": "sets the duration of the crossfade effect",
"crossfadeStyle": "crossfade style",
"crossfadeStyle_description": "select the crossfade style to use for the audio player",
"customFontPath": "custom font path",
"customFontPath_description": "sets the path to the custom font to use for the application",
"disableAutomaticUpdates": "disable automatic updates",
"disableLibraryUpdateOnStartup": "disable checking for new versions on startup",
"discordApplicationId": "{{discord}} application id",
"discordApplicationId_description": "the application id for {{discord}} rich presence (defaults to {{defaultId}})",
"discordIdleStatus": "show rich presence idle status",
"discordIdleStatus_description": "when enabled, update status while player is idle",
"discordRichPresence": "{{discord}} rich presence",
"discordRichPresence_description": "enable playback status in {{discord}} rich presence. Image keys are: {{icon}}, {{playing}}, and {{paused}} ",
"discordUpdateInterval": "{{discord}} rich presence update interval",
"discordUpdateInterval_description": "the time in seconds between each update (minimum 15 seconds)",
"enableRemote": "enable remote control server",
"enableRemote_description": "enables the remote control server to allow other devices to control the application",
"exitToTray": "exit to tray",
"exitToTray_description": "exit the application to the system tray",
"floatingQueueArea": "show floating queue hover area",
"floatingQueueArea_description": "display a hover icon on the right side of the screen to view the play queue",
"followLyric": "follow current lyric",
"followLyric_description": "scroll the lyric to the current playing position",
"font": "font",
"font_description": "sets the font to use for the application",
"fontType": "font type",
"fontType_description": "built-in font selects one of the fonts provided by Feishin. system font allows you to select any font provided by your operating system. custom allows you to provide your own font",
"fontType_optionBuiltIn": "built-in font",
"fontType_optionCustom": "custom font",
"fontType_optionSystem": "system font",
"gaplessAudio": "gapless audio",
"gaplessAudio_description": "sets the gapless audio setting for mpv",
"gaplessAudio_optionWeak": "weak (recommended)",
"globalMediaHotkeys": "global media hotkeys",
"globalMediaHotkeys_description": "enable or disable the usage of your system media hotkeys to control playback",
"hotkey_browserBack": "browser back",
"hotkey_browserForward": "browser forward",
"hotkey_favoriteCurrentSong": "favorite $t(common.currentSong)",
"hotkey_favoritePreviousSong": "favorite $t(common.previousSong)",
"hotkey_globalSearch": "global search",
"hotkey_localSearch": "in-page search",
"hotkey_playbackNext": "next track",
"hotkey_playbackPause": "pause",
"hotkey_playbackPlay": "play",
"hotkey_playbackPlayPause": "play / pause",
"hotkey_playbackPrevious": "previous track",
"hotkey_playbackStop": "stop",
"hotkey_rate0": "rating clear",
"hotkey_rate1": "rating 1 star",
"hotkey_rate2": "rating 2 stars",
"hotkey_rate3": "rating 3 stars",
"hotkey_rate4": "rating 4 stars",
"hotkey_rate5": "rating 5 stars",
"hotkey_skipBackward": "skip backward",
"hotkey_skipForward": "skip forward",
"hotkey_toggleCurrentSongFavorite": "toggle $t(common.currentSong) favorite",
"hotkey_toggleFullScreenPlayer": "toggle full screen player",
"hotkey_togglePreviousSongFavorite": "toggle $t(common.previousSong) favorite",
"hotkey_toggleQueue": "toggle queue",
"hotkey_toggleRepeat": "toggle repeat",
"hotkey_toggleShuffle": "toggle shuffle",
"hotkey_unfavoriteCurrentSong": "unfavorite $t(common.currentSong)",
"hotkey_unfavoritePreviousSong": "unfavorite $t(common.previousSong)",
"hotkey_volumeDown": "volume down",
"hotkey_volumeMute": "volume mute",
"hotkey_volumeUp": "volume up",
"hotkey_zoomIn": "zoom in",
"hotkey_zoomOut": "zoom out",
"language": "language",
"language_description": "sets the language for the application ($t(common.restartRequired))",
"lyricFetch": "fetch lyrics from the internet",
"lyricFetch_description": "fetch lyrics from various internet sources",
"lyricFetchProvider": "providers to fetch lyrics from",
"lyricFetchProvider_description": "select the providers to fetch lyrics from. the order of the providers is the order in which they will be queried",
"lyricOffset": "lyric offset (ms)",
"lyricOffset_description": "offset the lyric by the specified amount of milliseconds",
"minimizeToTray": "minimize to tray",
"minimizeToTray_description": "minimize the application to the system tray",
"minimumScrobblePercentage": "minimum scrobble duration (percentage)",
"minimumScrobblePercentage_description": "the minimum percentage of the song that must be played before it is scrobbled",
"minimumScrobbleSeconds": "minimum scrobble (seconds)",
"minimumScrobbleSeconds_description": "the minimum duration in seconds of the song that must be played before it is scrobbled",
"mpvExecutablePath": "mpv executable path",
"mpvExecutablePath_description": "sets the path to the mpv executable",
"mpvExecutablePath_help": "one per line",
"mpvExtraParameters": "mpv parameters",
"playbackStyle": "playback style",
"playbackStyle_description": "select the playback style to use for the audio player",
"playbackStyle_optionCrossFade": "crossfade",
"playbackStyle_optionNormal": "normal",
"playButtonBehavior": "play button behavior",
"playButtonBehavior_description": "sets the default behavior of the play button when adding songs to the queue",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"playButtonBehavior_optionPlay": "$t(player.play)",
"remotePassword": "remote control server password",
"remotePassword_description": "sets the password for the remote control server. These credentials are by default transferred insecurely, so you should use a unique password that you do not care about",
"remotePort": "remote control server port",
"remotePort_description": "sets the port for the remote control server",
"remoteUsername": "remote control server username",
"remoteUsername_description": "sets the username for the remote control server. if both username and password are empty, authentication will be disabled",
"replayGainClipping": "{{ReplayGain}} clipping",
"replayGainClipping_description": "Prevent clipping caused by {{ReplayGain}} by automatically lowering the gain",
"replayGainFallback": "{{ReplayGain}} fallback",
"replayGainFallback_description": "gain in db to apply if the file has no {{ReplayGain}} tags",
"replayGainMode": "{{ReplayGain}} mode",
"replayGainMode_description": "adjust volume gain according to {{ReplayGain}} values stored in the file metadata",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"replayGainPreamp": "{{ReplayGain}} preamp (dB)",
"replayGainPreamp_description": "adjust the preamp gain applied to the {{ReplayGain}} values",
"sampleRate": "sample rate",
"sampleRate_description": "select the output sample rate to be used if the sample frequency selected is different from that of the current media",
"savePlayQueue": "save play queue",
"savePlayQueue_description": "save the play queue when the application is closed and restore it when the application is opened",
"scrobble": "scrobble",
"scrobble_description": "scrobble plays to your media server",
"showSkipButton": "show skip buttons",
"showSkipButton_description": "show or hide the skip buttons on the player bar",
"showSkipButtons": "show skip buttons",
"showSkipButtons_description": "show or hide the skip buttons on the player bar",
"sidebarCollapsedNavigation": "sidebar (collapsed) navigation",
"sidebarCollapsedNavigation_description": "show or hide the navigation in the collapsed sidebar",
"sidebarConfiguration": "sidebar configuration",
"sidebarConfiguration_description": "select the items and order in which they appear in the sidebar",
"sidebarPlaylistList": "sidebar playlist list",
"sidebarPlaylistList_description": "show or hide the playlist list in the sidebar",
"sidePlayQueueStyle": "side play queue style",
"sidePlayQueueStyle_description": "sets the style of the side play queue",
"sidePlayQueueStyle_optionAttached": "attached",
"sidePlayQueueStyle_optionDetached": "detached",
"skipDuration": "skip duration",
"skipDuration_description": "sets the duration to skip when using the skip buttons on the player bar",
"skipPlaylistPage": "skip playlist page",
"skipPlaylistPage_description": "when navigating to a playlist, go to the playlist song list page instead of the default page",
"theme": "theme",
"theme_description": "sets the theme to use for the application",
"themeDark": "theme (dark)",
"themeDark_description": "sets the dark theme to use for the application",
"themeLight": "theme (light)",
"themeLight_description": "sets the light theme to use for the application",
"useSystemTheme": "use system theme",
"useSystemTheme_description": "follow the system-defined light or dark preference",
"volumeWheelStep": "volume wheel step",
"volumeWheelStep_description": "the amount of volume to change when scrolling the mouse wheel on the volume slider",
"windowBarStyle": "window bar style",
"windowBarStyle_description": "select the style of the window bar",
"zoom": "zoom percentage",
"zoom_description": "sets the zoom percentage for the application"
},
"table": {
"column": {
"album": "album",
"albumArtist": "album artist",
"albumCount": "$t(entity.album_other)",
"artist": "$t(entity.artist_one)",
"biography": "biography",
"bitrate": "bitrate",
"bpm": "bpm",
"channels": "$t(common.channel_other)",
"comment": "comment",
"dateAdded": "date added",
"discNumber": "disc",
"favorite": "favorite",
"genre": "$t(entity.genre_one)",
"lastPlayed": "last played",
"path": "path",
"playCount": "plays",
"rating": "rating",
"releaseDate": "release date",
"releaseYear": "year",
"songCount": "$t(entity.track_other)",
"title": "title",
"trackNumber": "track"
},
"config": {
"general": {
"autoFitColumns": "auto fit columns",
"displayType": "display type",
"gap": "$t(common.gap)",
"size": "$t(common.size)",
"tableColumns": "table columns"
},
"label": {
"actions": "$t(common.action_other)",
"album": "$t(entity.album_one)",
"albumArtist": "$t(entity.albumArtist_one)",
"artist": "$t(entity.artist_one)",
"biography": "$t(common.biography)",
"bitrate": "$t(common.bitrate)",
"bpm": "$t(common.bpm)",
"channels": "$t(common.channel_other)",
"dateAdded": "date added",
"discNumber": "disc number",
"duration": "$t(common.duration)",
"favorite": "$t(common.favorite)",
"genre": "$t(entity.genre_one)",
"lastPlayed": "last played",
"note": "$t(common.note)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"playCount": "play count",
"rating": "$t(common.rating)",
"releaseDate": "release date",
"rowIndex": "row index",
"size": "$t(common.size)",
"title": "$t(common.title)",
"titleCombined": "$t(common.title) (combined)",
"trackNumber": "track number",
"year": "$t(common.year)"
},
"view": {
"card": "card",
"poster": "poster",
"table": "table"
}
}
}
}
+632
View File
@@ -0,0 +1,632 @@
{
"player": {
"repeat_all": "repetir todo",
"stop": "detener",
"repeat": "repetir",
"queue_remove": "eliminar seleccionado",
"playRandom": "reproducción aleatoria",
"skip": "saltar",
"previous": "anterior",
"toggleFullscreenPlayer": "activar el reproductor a pantalla completa",
"skip_back": "saltar hacia atrás",
"favorite": "favorito",
"next": "siguiente",
"shuffle": "mezclar",
"playbackFetchNoResults": "ninguna canción encontrada",
"playbackFetchInProgress": "cargando canciones…",
"addNext": "añadir siguiente",
"playbackSpeed": "velocidad de reproducción",
"playbackFetchCancel": "esto está tomando un tiempo... cierra la notificación para cancelar",
"play": "reproducir",
"repeat_off": "repetir desactivado",
"queue_clear": "limpiar cola",
"muted": "silenciado",
"unfavorite": "no favorito",
"queue_moveToTop": "mover seleccionado al fondo",
"queue_moveToBottom": "mover seleccionado al principio",
"shuffle_off": "mezclar desactivado",
"addLast": "añadir último",
"mute": "silencio",
"skip_forward": "saltar hacia delante",
"pause": "pausa"
},
"setting": {
"crossfadeStyle_description": "selecciona el estilo de crossfade a usar por el reproductor de audio",
"remotePort_description": "establece el puerto para el control remoto del servidor",
"hotkey_skipBackward": "saltar hacia atrás",
"replayGainMode_description": "ajusta el volumen de ganancia acorde a los valores de {{ReplayGain}} almacenados en los metadatos del archivo",
"audioDevice_description": "selecciona el dispositivo de audio para usar en la reproducción (solo reproductor web)",
"theme_description": "establece el tema a usar por la aplicación",
"hotkey_playbackPause": "pausa",
"replayGainFallback": "{{ReplayGain}} alternativa",
"sidebarCollapsedNavigation_description": "mostrar u ocultar la navegación en la barra lateral contraída",
"mpvExecutablePath_help": "uno por línea",
"hotkey_volumeUp": "subir volumen",
"skipDuration": "duración de salto",
"discordIdleStatus_description": "cuando se activa, actualiza el estado mientras el reproductor está inactivo",
"showSkipButtons": "mostrar botones de saltar",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "mínima duración de scrobble (porcentaje)",
"lyricFetch": "busca letras en Internet",
"scrobble": "scrobble",
"skipDuration_description": "establece la duración a saltar cuando se usa los botones de saltar en la barra del reproductor",
"enableRemote_description": "activa el control remoto del servidor para permitir a otros dispositivos controlar la aplicación",
"fontType_optionSystem": "fuente del sistema",
"mpvExecutablePath_description": "establece la ruta del ejecutable mpv",
"replayGainClipping_description": "previene el recorte causado por {{ReplayGain}} bajando automáticamente la ganancia",
"replayGainPreamp": "preamplificador (dB) de {{ReplayGain}}",
"hotkey_favoriteCurrentSong": "$t(common.currentSong) favorita",
"sampleRate": "ratio de muestreo",
"crossfadeStyle": "estilo de crossfade",
"sidePlayQueueStyle_optionAttached": "acoplada",
"sidebarConfiguration": "configuración de la barra lateral",
"sampleRate_description": "selecciona el ratio de muestreo de salida a ser usado si la frecuencia de muestreo seleccionada es diferente de la del medio actual",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainClipping": "recortar {{ReplayGain}}",
"hotkey_zoomIn": "ampliar",
"scrobble_description": "hace scrobble de las reproducciones en tu servidor de medios",
"audioExclusiveMode_description": "activa el modo de audio exclusivo. En este modo, el sistema es normalmente bloqueado, y solo se permitirá mpv en la salida de audio",
"discordUpdateInterval": "intervalo de actualización del estado de actividad de {{discord}}",
"themeLight": "tema (luminoso)",
"fontType_optionBuiltIn": "fuente incorporada",
"hotkey_playbackPlayPause": "play / pausa",
"hotkey_rate1": "calificar con 1 estrella",
"hotkey_skipForward": "saltar hacia delante",
"disableLibraryUpdateOnStartup": "desactiva la comprobación de nuevas versiones al inicio",
"discordApplicationId_description": "el id de aplicación para el estado de actividad de {{discord}} (por defecto es {{defaultId}})",
"sidePlayQueueStyle": "estilo de la cola de reproducción lateral",
"gaplessAudio": "audio sin pausas",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"minimizeToTray_description": "minimiza la aplicación a la bandeja del sistema",
"hotkey_playbackPlay": "reproducir",
"hotkey_togglePreviousSongFavorite": "cambia $t(common.previousSong) a favorito",
"hotkey_volumeDown": "bajar volumen",
"hotkey_unfavoritePreviousSong": "$t(common.previousSong) no favorito",
"audioPlayer_description": "selecciona el reproductor de audio a usar en la reproducción",
"globalMediaHotkeys": "teclas de acceso rápido globales a medios",
"hotkey_globalSearch": "búsqueda global",
"gaplessAudio_description": "establece la configuración de audio sin pausas para mpv",
"remoteUsername_description": "establece el nombre de usuario para el control remoto del servidor. si el usuario y la contraseña están vacíos, la autenticación será deshabilitada",
"disableAutomaticUpdates": "desactiva las actualizaciones automáticas",
"exitToTray_description": "sale de la aplicación a la bandeja del sistema",
"followLyric_description": "desplaza la letra a la posición de reproducción actual",
"hotkey_favoritePreviousSong": "$t(common.previousSong) favorita",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "desfase de letra (ms)",
"discordUpdateInterval_description": "el tiempo en segundos entre cada actualización (mínimo 15 segundos)",
"fontType_optionCustom": "fuente personalizada",
"themeDark_description": "establece el tema oscuro a usar por la aplicación",
"audioExclusiveMode": "modo de audio exclusivo",
"remotePassword": "contraseña del control remoto del servidor",
"lyricFetchProvider": "proveedores para buscar letras",
"language_description": "establece el idioma de la aplicación ($t(common.restartRequired))",
"playbackStyle_optionCrossFade": "crossfade",
"hotkey_rate3": "calificar con 3 estrellas",
"font": "fuente",
"mpvExtraParameters": "parámetros de mpv",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "establece el tema luminoso a usar por la aplicación",
"hotkey_toggleFullScreenPlayer": "cambia el reproductor a pantalla completa",
"hotkey_localSearch": "búsqueda en la página",
"hotkey_toggleQueue": "cambia la cola",
"remotePassword_description": "establece la contraseña para el control remoto del servidor. Esas credenciales son transferidas de forma insegura por defecto, por lo que deberías usar una contraseña única para que no tengas nada de qué preocuparte",
"hotkey_rate5": "calificar con 5 estrellas",
"hotkey_playbackPrevious": "pista anterior",
"showSkipButtons_description": "muestra o esconde los botones de saltar en la barra del reproductor",
"crossfadeDuration_description": "establece la duración del efecto de crossfade",
"language": "idioma",
"playbackStyle": "estilo de reproducción",
"hotkey_toggleShuffle": "alterna aleatorio",
"theme": "tema",
"playbackStyle_description": "selecciona el estilo de reproducción a usar por el reproductor de audio",
"discordRichPresence_description": "activa el estado de reproducción en el estado de actividad de {{discord}}. Las teclas de imagen son: {{icon}}, {{playing}}, y {{paused}} ",
"mpvExecutablePath": "ruta del ejecutable mpv",
"audioDevice": "dispositivo de audio",
"hotkey_rate2": "calificar con 2 estrellas",
"playButtonBehavior_description": "establece el comportamiento por defecto del botón de reproducción cuando se añaden canciones a la cola",
"minimumScrobblePercentage_description": "el porcentaje mínimo de la canción que debe ser reproducido antes de hacer scrobble",
"exitToTray": "salida a bandeja",
"hotkey_rate4": "calificar con 4 estrellas",
"enableRemote": "activar control remoto del servidor",
"showSkipButton_description": "muestra o esconde los botones de saltar en la barra del reproductor",
"savePlayQueue": "guardar cola de reproducción",
"minimumScrobbleSeconds_description": "la duración mínima en segundos de la canción que debe ser reproducida antes de hacer scrobble",
"fontType_description": "fuente incorporada selecciona una de las fuentes proporcionadas por Feishin. fuente del sistema te permite seleccionar cualquier fuente proporcionada por tu sistema operativo. personalizada te permite proporcionar tu propia fuente",
"playButtonBehavior": "comportamiento del botón de reproducción",
"sidebarPlaylistList_description": "muestra o esconde las listas de reproducción en la barra lateral",
"sidePlayQueueStyle_description": "establece el estilo de la cola de reproducción lateral",
"replayGainMode": "modo de {{ReplayGain}}",
"playbackStyle_optionNormal": "normal",
"floatingQueueArea": "mostrar área flotante de cola",
"replayGainFallback_description": "ganancia en db a aplicar si el archivo no tiene etiquetas de {{ReplayGain}}",
"replayGainPreamp_description": "ajusta la ganancia del preamplificador aplicada a los valores de {{ReplayGain}}",
"hotkey_toggleRepeat": "alterna repetir",
"lyricOffset_description": "desfasa la letra por la cantidad de milisegundos especificada",
"sidebarConfiguration_description": "selecciona los elementos y el orden en que aparecerán en la barra lateral",
"fontType": "tipo de fuente",
"remotePort": "puerto del control remoto del servidor",
"applicationHotkeys": "teclas de acceso rápido de la aplicación",
"hotkey_playbackNext": "pista siguiente",
"useSystemTheme_description": "sigue la preferencia luminosa u oscura definida por el sistema",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "busca letras en varias fuentes de Internet",
"lyricFetchProvider_description": "selecciona los proveedores para buscar letras. el orden de los proveedores es el orden en el que se consultarán",
"globalMediaHotkeys_description": "activa o desactiva el uso de las teclas de acceso rápidas del sistema a medios para controlar la reproducción",
"customFontPath": "ruta de fuente personalizada",
"followLyric": "seguir la letra actual",
"crossfadeDuration": "duración del crossfade",
"discordIdleStatus": "mostrar el estado inactivo en el estado de actividad",
"sidePlayQueueStyle_optionDetached": "separada",
"audioPlayer": "reproductor de audio",
"hotkey_zoomOut": "reducir",
"hotkey_unfavoriteCurrentSong": "$t(common.currentSong) no favorito",
"hotkey_rate0": "limpiar calificación",
"discordApplicationId": "id de aplicación {{discord}}",
"applicationHotkeys_description": "configura las teclas de acceso rápido de la aplicación. marca la casilla para establecerlas como teclas de acceso rápido globales (solo escritorio)",
"floatingQueueArea_description": "muestra un icono flotante en el lado derecho de la pantalla para ver la cola de reproducción",
"hotkey_volumeMute": "silenciar volumen",
"hotkey_toggleCurrentSongFavorite": "cambia $t(common.currentSong) a favorito",
"remoteUsername": "nombre de usuario del control remoto del servidor",
"showSkipButton": "mostrar botones de saltar",
"sidebarPlaylistList": "listas de reproducción de la barra lateral",
"minimizeToTray": "minimizar a la bandeja",
"themeDark": "tema (oscuro)",
"sidebarCollapsedNavigation": "navegación de barra lateral (contraída)",
"customFontPath_description": "establece la ruta de la fuente personalizada a usar por la aplicación",
"gaplessAudio_optionWeak": "débil (recomendado)",
"minimumScrobbleSeconds": "mínimo scrobble (segundos)",
"hotkey_playbackStop": "parar",
"discordRichPresence": "estado de actividad de {{discord}}",
"font_description": "establece la fuente a usar por la aplicación",
"savePlayQueue_description": "guarda la cola de reproducción cuando se cierra la aplicación y la restaura cuando se abre",
"useSystemTheme": "usar tema del sistema",
"volumeWheelStep_description": "la cantidad de volumen a cambiar cuando se desplaza la rueda del ratón en el control deslizante del volumen",
"zoom": "porcentaje de zoom",
"zoom_description": "establece el porcentaje de zoom de la aplicación",
"volumeWheelStep": "paso de rueda del volumen",
"windowBarStyle": "estilo de la barra de ventana",
"windowBarStyle_description": "selecciona el estilo de la barra de ventana",
"skipPlaylistPage_description": "cuando se navega a una lista de reproducción, se va a la página de lista de canciones de la lista de reproducción en lugar de a la página por defecto",
"accentColor": "color de realce",
"accentColor_description": "establece el color de realce de la aplicación",
"skipPlaylistPage": "saltar página de lista de reproducción",
"hotkey_browserForward": "avance",
"hotkey_browserBack": "retroceso"
},
"action": {
"editPlaylist": "editar $t(entity.playlist_one)",
"goToPage": "ir a la página",
"moveToTop": "mover al principio",
"clearQueue": "limpiar cola",
"addToFavorites": "añadir a $t(entity.favorite_other)",
"addToPlaylist": "añadir a $t(entity.playlist_one)",
"createPlaylist": "crear $t(entity.playlist_one)",
"removeFromPlaylist": "eliminar de $t(entity.playlist_one)",
"viewPlaylists": "ver $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "eliminar $t(entity.playlist_one)",
"removeFromQueue": "eliminar de la cola",
"deselectAll": "desmarcar todo",
"moveToBottom": "mover al fondo",
"setRating": "establecer calificación",
"toggleSmartPlaylistEditor": "cambiar editor $t(entity.smartPlaylist)",
"removeFromFavorites": "eliminar de $t(entity.favorite_other)"
},
"common": {
"backward": "hacia atrás",
"increase": "aumentar",
"rating": "calificación",
"bpm": "bpm",
"refresh": "actualizar",
"unknown": "desconocido",
"areYouSure": "estás seguro?",
"edit": "editar",
"favorite": "favorito",
"left": "izquierda",
"save": "guardar",
"right": "derecha",
"currentSong": "actual $t(entity.track_one)",
"collapse": "contraer",
"trackNumber": "pista",
"descending": "descendiente",
"add": "añadir",
"ascending": "ascendente",
"dismiss": "descartar",
"year": "año",
"manage": "gestionar",
"limit": "limitar",
"minimize": "minimizar",
"modified": "modificado",
"duration": "duración",
"name": "nombre",
"maximize": "maximizar",
"decrease": "reducir",
"ok": "vale",
"description": "descripción",
"configure": "configurar",
"path": "ruta",
"center": "centrar",
"no": "no",
"owner": "propietario",
"enable": "activar",
"clear": "limpiar",
"forward": "hacia delante",
"delete": "eliminar",
"cancel": "cancelar",
"forceRestartRequired": "reiniciar para aplicar cambios... cerrar la notificación para reiniciar",
"setting": "configuración",
"version": "versión",
"title": "título",
"filters": "filtros",
"create": "crear",
"bitrate": "tasa de bits",
"saveAndReplace": "guardar y reemplazar",
"playerMustBePaused": "el reproductor debe pausarse",
"confirm": "confirmar",
"resetToDefault": "restablecer a valor por defecto",
"home": "inicio",
"comingSoon": "próximamente…",
"reset": "restablecer",
"disable": "desactivar",
"sortOrder": "ordenar",
"none": "ninguno",
"menu": "menú",
"restartRequired": "reinicio requerido",
"previousSong": "anterior $t(entity.track_one)",
"noResultsFromQuery": "la petición no devolvió resultados",
"quit": "salir",
"expand": "ampliar",
"search": "buscar",
"saveAs": "guardar como",
"disc": "disco",
"yes": "sí",
"random": "aleatorio",
"size": "tamaño",
"biography": "biografía",
"note": "nota",
"gap": "desfase",
"filter_one": "filtro",
"filter_many": "filtros",
"filter_other": "filtros",
"action_one": "acción",
"action_many": "acciones",
"action_other": "acciones",
"channel_one": "Canal",
"channel_many": "Canales",
"channel_other": "Canales"
},
"error": {
"remotePortWarning": "reiniciar el servidor para aplicar el nuevo puerto",
"systemFontError": "un error ocurrió cuando se intentó obtener las fuentes del sistema",
"playbackError": "un error ocurrió cuando se intentó reproducir el medio",
"endpointNotImplementedError": "el punto final {{endpoint}} no está implementado para {{serverType}}",
"remotePortError": "un error ocurrió cuando se intentó establecer el puerto del servidor remoto",
"serverRequired": "servidor requerido",
"authenticationFailed": "autenticación fallida",
"apiRouteError": "no se puede encaminar la solicitud",
"genericError": "sucedió un error",
"credentialsRequired": "credenciales requeridas",
"sessionExpiredError": "tu sesión ha expirado",
"remoteEnableError": "un error ocurrió cuando se intentó $t(common.enable) el servidor remoto",
"localFontAccessDenied": "acceso denegado a las fuentes locales",
"serverNotSelectedError": "ningún servidor seleccionado",
"remoteDisableError": "un error ocurrió cuando se intentó $t(common.disable) el servidor remoto",
"mpvRequired": "MPV requerido",
"audioDeviceFetchError": "un error ocurrió cuando se intentó obtener los dispositivos de audio",
"invalidServer": "servidor inválido",
"loginRateError": "demasiados intentos de inicio de sesión, por favor inténtalo en unos segundos"
},
"filter": {
"mostPlayed": "más reproducido",
"isCompilation": "es compilación",
"recentlyPlayed": "recientemente reproducido",
"isRated": "es clasificado",
"title": "título",
"rating": "calificación",
"search": "buscar",
"bitrate": "tasa de bits",
"recentlyAdded": "recientemente añadido",
"note": "nota",
"name": "nombre",
"dateAdded": "fecha añadida",
"releaseDate": "fecha de lanzamiento",
"communityRating": "calificación de la comunidad",
"path": "ruta",
"favorited": "favoritos",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "reproducido recientemente",
"isFavorited": "es favorito",
"bpm": "bpm",
"releaseYear": "año de lanzamiento",
"disc": "disco",
"biography": "biografía",
"artist": "$t(entity.artist_one)",
"duration": "duración",
"random": "aleatorio",
"lastPlayed": "última reproducción",
"toYear": "hasta el año",
"fromYear": "desde el año",
"criticRating": "calificación de la crítica",
"trackNumber": "pista",
"comment": "comentarios",
"playCount": "número de reproducción",
"recentlyUpdated": "actualizado recientemente",
"channels": "$t(common.channel_other)",
"owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)",
"id": "id",
"songCount": "número de canción",
"isPublic": "es público",
"album": "$t(entity.album_one)",
"albumCount": "Contar $t(entity.album_other)"
},
"page": {
"sidebar": {
"nowPlaying": "en reproducción",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"appMenu": {
"selectServer": "seleccionar servidor",
"version": "versión {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "gestionar servidores",
"expandSidebar": "ampliar barra lateral",
"collapseSidebar": "contraer barra lateral",
"openBrowserDevtools": "abrir herramientas de desarrollador del navegador",
"quit": "$t(common.quit)",
"goBack": "retroceder",
"goForward": "avanzar"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} seleccionado",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"home": {
"mostPlayed": "más reproducidos",
"newlyAdded": "nuevos lanzamientos añadidos",
"title": "$t(common.home)",
"explore": "explorar desde tu biblioteca",
"recentlyPlayed": "recientemente reproducidos"
},
"fullscreenPlayer": {
"upNext": "siguiente",
"config": {
"dynamicBackground": "fondo dinámico",
"synchronized": "sincronizado",
"followCurrentLyric": "seguir la letra actual",
"opacity": "opacidad",
"lyricSize": "tamaño de letra",
"showLyricProvider": "mostrar proveedor de letra",
"unsynchronized": "no sincronizado",
"lyricAlignment": "alineación de letra",
"useImageAspectRatio": "usar ratio de aspecto de imagen",
"showLyricMatch": "mostrar coincidencia de letras",
"lyricGap": "desfase de letra"
},
"lyrics": "letras",
"related": "relacionado"
},
"albumDetail": {
"moreFromArtist": "más de este $t(entity.artist_one)",
"moreFromGeneric": "más de {{item}}"
},
"setting": {
"playbackTab": "reproducción",
"generalTab": "general",
"hotkeysTab": "teclas de acceso rápido",
"windowTab": "ventana"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "comandos del servidor",
"goToPage": "ir a la página",
"searchFor": "buscar por {{query}}"
},
"title": "comandos"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"form": {
"deletePlaylist": {
"title": "eliminar $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) eliminado correctamente",
"input_confirm": "escribe el nombre de $t(entity.playlist_one) para confirmar"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "crear $t(entity.playlist_one)",
"input_public": "público",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) creado correctamente",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "añadir servidor",
"input_username": "nombre de usuario",
"input_url": "url",
"input_password": "contraseña",
"input_legacyAuthentication": "permitir autenticación heredada",
"input_name": "nombre del servidor",
"success": "servidor añadido correctamente",
"input_savePassword": "guardar contraseña",
"ignoreSsl": "ignorar ssl ($t(common.restartRequired))",
"ignoreCors": "ignorar cors ($t(common.restartRequired))",
"error_savePassword": "un error ocurrió cuando se intentó guardar la contraseña"
},
"addToPlaylist": {
"success": "añadido {{message}} $t(entity.song_other) a {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "añadir a $t(entity.playlist_one)",
"input_skipDuplicates": "saltar duplicados",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "actualizar servidor",
"success": "servidor actualizado correctamente"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "buscar letras"
},
"editPlaylist": {
"title": "editar $t(entity.playlist_one)"
},
"queryEditor": {
"input_optionMatchAll": "coincidir todos",
"input_optionMatchAny": "coincidir cualquiera"
}
},
"table": {
"column": {
"rating": "calificación",
"comment": "comentarios",
"album": "álbum",
"favorite": "favorito",
"playCount": "reproducciones",
"albumCount": "$t(entity.album_other)",
"releaseYear": "año",
"lastPlayed": "última reproducción",
"biography": "biografía",
"releaseDate": "fecha de lanzamiento",
"bitrate": "tasa de bits",
"title": "título",
"bpm": "bpm",
"dateAdded": "fecha de adición",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "pista",
"genre": "$t(entity.genre_one)",
"albumArtist": "artista de álbum",
"path": "ruta",
"discNumber": "disco",
"channels": "$t(common.channel_other)"
},
"config": {
"label": {
"rating": "$t(common.rating)",
"dateAdded": "fecha de adición",
"bpm": "$t(common.bpm)",
"lastPlayed": "última reproducción",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"biography": "$t(common.biography)",
"channels": "$t(common.channel_other)",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"albumArtist": "$t(entity.albumArtist_one)",
"discNumber": "número de disco",
"releaseDate": "fecha de lanzamiento",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (combinado)",
"size": "$t(common.size)",
"trackNumber": "número de pista",
"rowIndex": "índice de filas",
"note": "$t(common.note)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"playCount": "número de reproducción",
"genre": "$t(entity.genre_one)",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)"
},
"general": {
"gap": "$t(common.gap)",
"tableColumns": "columnas de la tabla",
"autoFitColumns": "ajuste automático de columnas",
"size": "$t(common.size)",
"displayType": "tipo de visualización"
},
"view": {
"card": "tarjeta",
"table": "tabla",
"poster": "cartel"
}
}
},
"entity": {
"smartPlaylist": "$t(entity.playlist_one) inteligente",
"genre_one": "género",
"genre_many": "géneros",
"genre_other": "géneros",
"playlistWithCount_one": "{{count}} lista de reproducción",
"playlistWithCount_many": "{{count}} listas de reproducción",
"playlistWithCount_other": "{{count}} listas de reproducción",
"playlist_one": "lista de reproducción",
"playlist_many": "listas de reproducción",
"playlist_other": "listas de reproducción",
"artist_one": "artista",
"artist_many": "artistas",
"artist_other": "artistas",
"folderWithCount_one": "{{count}} carpeta",
"folderWithCount_many": "{{count}} carpetas",
"folderWithCount_other": "{{count}} carpetas",
"albumArtist_one": "artista de álbum",
"albumArtist_many": "artistas de álbum",
"albumArtist_other": "artistas de álbum",
"track_one": "pista",
"track_many": "pistas",
"track_other": "pistas",
"albumArtistCount_one": "{{count}} artista de álbum",
"albumArtistCount_many": "{{count}} artistas de álbum",
"albumArtistCount_other": "{{count}} artistas de álbum",
"albumWithCount_one": "{{count}} álbum",
"albumWithCount_many": "{{count}} álbumes",
"albumWithCount_other": "{{count}} álbumes",
"favorite_one": "favorito",
"favorite_many": "favoritos",
"favorite_other": "favoritos",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistas",
"artistWithCount_other": "{{count}} artistas",
"folder_one": "carpeta",
"folder_many": "carpetas",
"folder_other": "carpetas",
"album_one": "álbum",
"album_many": "álbumes",
"album_other": "álbumes",
"genreWithCount_one": "{{count}} género",
"genreWithCount_many": "{{count}} géneros",
"genreWithCount_other": "{{count}} géneros",
"trackWithCount_one": "{{count}} pista",
"trackWithCount_many": "{{count}} pistas",
"trackWithCount_other": "{{count}} pistas"
}
}
+632
View File
@@ -0,0 +1,632 @@
{
"player": {
"repeat_all": "répète tout",
"stop": "stop",
"repeat": "répéter",
"queue_remove": "effacer la sélection",
"playRandom": "lecture aléatoire",
"skip": "sauter",
"previous": "précédant",
"toggleFullscreenPlayer": "plein écran",
"skip_back": "reculer",
"favorite": "favori",
"next": "suivant",
"shuffle": "aléatoire",
"playbackFetchNoResults": "aucune chansons trouvées",
"playbackFetchInProgress": "chargement des chansons…",
"addNext": "ajouter ensuite",
"playbackSpeed": "vitesse de lecture",
"playbackFetchCancel": "cela prend du temps… fermez la notification pour annuler",
"play": "lecture",
"repeat_off": "répétition désactivée",
"queue_clear": "effacer la file d'attente",
"muted": "en sourdine",
"queue_moveToTop": "déplacer la sélection vers le bas",
"queue_moveToBottom": "déplacer la sélection vers le haut",
"shuffle_off": "aléatoire désactivée",
"addLast": "ajouter en dernier",
"mute": "muet",
"skip_forward": "avancer",
"pause": "pause",
"unfavorite": "dé-favori"
},
"action": {
"editPlaylist": "éditer $t(entity.playlist_one)",
"goToPage": "aller à la page",
"moveToTop": "déplacer en haut",
"clearQueue": "effacer la liste de lecture",
"addToFavorites": "ajouter aux $t(entity.favorite_other)",
"addToPlaylist": "ajouter à $t(entity.playlist_one)",
"createPlaylist": "créer $t(entity.playlist_one)",
"removeFromPlaylist": "supprimer des $t(entity.playlist_one)",
"viewPlaylists": "voir $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "supprimer de $t(entity.playlist_one)",
"removeFromQueue": "retirer de la file d'attente",
"deselectAll": "désélectionner tout",
"moveToBottom": "déplacer en bas",
"setRating": "noter",
"toggleSmartPlaylistEditor": "basculer l'éditeur de $t(entity.smartPlaylist)",
"removeFromFavorites": "retirer des $t(entity.favorite_other)"
},
"common": {
"backward": "reculer",
"increase": "augmenter",
"rating": "note",
"bpm": "bpm",
"refresh": "rafraichir",
"unknown": "inconnu",
"areYouSure": "êtes vous sûr ?",
"edit": "éditer",
"favorite": "favoris",
"left": "gauche",
"save": "sauvegarder",
"right": "droite",
"currentSong": "$t(entity.track_one) actuelle",
"collapse": "réduire",
"trackNumber": "piste",
"descending": "décroisant",
"add": "ajouter",
"gap": "écart",
"ascending": "croissant",
"dismiss": "rejeter",
"year": "année",
"manage": "gérer",
"limit": "limite",
"minimize": "minimiser",
"modified": "modifier",
"duration": "durée",
"name": "nom",
"maximize": "agrandir",
"decrease": "baisser",
"ok": "ok",
"description": "description",
"configure": "configurer",
"path": "chemin",
"center": "centre",
"no": "non",
"owner": "propriétaire",
"enable": "activer",
"clear": "effacer",
"forward": "avancer",
"delete": "supprimer",
"cancel": "annuler",
"forceRestartRequired": "redémarrer pour appliquer les changements… fermer la notification pour redémarrer",
"setting": "paramètre",
"version": "version",
"title": "titre",
"filter_one": "filtre",
"filter_many": "filtres",
"filter_other": "filtres",
"filters": "filtres",
"create": "créer",
"bitrate": "bitrate",
"saveAndReplace": "sauvegarder et remplacer",
"action_one": "action",
"action_many": "actions",
"action_other": "actions",
"playerMustBePaused": "le lecteur doit être en pause",
"confirm": "confirmer",
"resetToDefault": "réinitialiser par défaut",
"home": "accueil",
"comingSoon": "prochainement…",
"reset": "réinitialiser",
"channel_one": "canal",
"channel_many": "canaux",
"channel_other": "canaux",
"disable": "désactiver",
"sortOrder": "ordre",
"none": "aucun",
"menu": "menu",
"restartRequired": "redémarrage requis",
"previousSong": "précédant $t(entity.track_one)",
"noResultsFromQuery": "la requête n'a retourné aucun résultat",
"quit": "quitter",
"expand": "étendre",
"search": "recherche",
"saveAs": "sauvegarder en tant que",
"disc": "disque",
"yes": "oui",
"random": "aléatoire",
"size": "taille",
"biography": "biographie",
"note": "note"
},
"error": {
"remotePortWarning": "redémarrer le serveur pour appliquer le nouveau port",
"systemFontError": "une erreur sest produite lors de la tentative dobtenir les polices système",
"playbackError": "une erreur s'est produite lors de la tentative de lecture du média",
"endpointNotImplementedError": "endpoint {{endpoint}} n'est pas implémenté pour {{serverType}}",
"remotePortError": "une erreur s'est produite lors de la tentative de définir le port du serveur distant",
"serverRequired": "serveur requis",
"authenticationFailed": "l'authentification à échoué",
"apiRouteError": "incapable dacheminer la demande",
"genericError": "une erreur s'est produite",
"credentialsRequired": "identifiants requis",
"sessionExpiredError": "votre session a expiré",
"remoteEnableError": "une erreur s'est produite lors de la tentative de $t(common.enable) le serveur distant",
"localFontAccessDenied": "accès refusé aux polices locales",
"serverNotSelectedError": "aucun serveur sélectionner",
"remoteDisableError": "une erreur s'est produite lors de la tentative de $t(common.disable) le serveur distant",
"mpvRequired": "MPV requis",
"audioDeviceFetchError": "une erreur sest produite lors de la tentative dobtenir les périphériques audio",
"invalidServer": "serveur invalide",
"loginRateError": "trop de tentative de connexion, merci d'essayer dans quelque secondes"
},
"filter": {
"mostPlayed": "plus joués",
"playCount": "nombre d'écoutes",
"isCompilation": "est une compilation",
"recentlyPlayed": "récemment joué",
"isRated": "est noté",
"title": "titre",
"rating": "note",
"search": "recherche",
"bitrate": "bitrate",
"recentlyAdded": "ajout récent",
"note": "note",
"name": "nom",
"dateAdded": "date d'ajout",
"releaseDate": "date de sortie",
"communityRating": "note de la communauté",
"path": "chemin",
"favorited": "favoris",
"isRecentlyPlayed": "est récemment joué",
"isFavorited": "est favoris",
"bpm": "bpm",
"releaseYear": "année de sortie",
"disc": "disque",
"biography": "biographie",
"songCount": "nombre de chansons",
"duration": "durée",
"random": "aléatoire",
"lastPlayed": "dernière joué",
"toYear": "à l'année",
"fromYear": "depuis l'année",
"criticRating": "note des critiques",
"trackNumber": "piste",
"albumArtist": "$t(entity.albumArtist_one)",
"comment": "commentaire",
"recentlyUpdated": "mis à jour récemment",
"channels": "$t(common.channel_other)",
"owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)",
"albumCount": "$t(entity.album_other) total",
"id": "id",
"artist": "$t(entity.artist_one)",
"isPublic": "est public",
"album": "$t(entity.album_one)"
},
"page": {
"sidebar": {
"nowPlaying": "lecture en cours",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "afficher la correspondance des paroles",
"dynamicBackground": "arrière-plan dynamique",
"synchronized": "synchronisé",
"followCurrentLyric": "suivre les paroles",
"showLyricProvider": "afficher la source des paroles",
"unsynchronized": "désynchronisé",
"lyricAlignment": "alignement des paroles",
"useImageAspectRatio": "utiliser le ratio de l'image",
"opacity": "opacitée",
"lyricSize": "Taille des paroles",
"lyricGap": "espacement des lettres"
},
"upNext": "à suivre",
"lyrics": "paroles",
"related": "similaire"
},
"appMenu": {
"selectServer": "sélectionner le serveur",
"manageServers": "gérer les serveurs",
"expandSidebar": "développer la barre latérale",
"collapseSidebar": "réduire la barre latérale",
"openBrowserDevtools": "ouvrir les outils de développement du navigateur",
"goBack": "retour arrière",
"goForward": "avancer",
"version": "version {{version}}",
"settings": "$t(common.setting_other)",
"quit": "$t(common.quit)"
},
"home": {
"mostPlayed": "plus joués",
"newlyAdded": "versions récemment ajoutés",
"explore": "explorer depuis votre bibliothèque",
"recentlyPlayed": "récemment joué",
"title": "$t(common.home)"
},
"albumDetail": {
"moreFromArtist": "plus de $t(entity.artist_one)",
"moreFromGeneric": "plus de {{item}}"
},
"setting": {
"generalTab": "générale",
"hotkeysTab": "raccourci",
"windowTab": "fenêtre",
"playbackTab": "lecteur"
},
"globalSearch": {
"commands": {
"serverCommands": "commandes du serveur",
"goToPage": "aller à la page",
"searchFor": "recherche pour {{query}}"
},
"title": "commandes"
},
"contextMenu": {
"numberSelected": "{{count}} sélectionné",
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"setting": {
"audioDevice_description": "sélectionnez le périphérique audio à utiliser pour la lecture (lecteur Web uniquement)",
"crossfadeStyle": "style du fondu enchaîné",
"audioExclusiveMode_description": "activer le mode de sortie exclusif. Dans ce mode, le système est généralement verrouillé et seul mpv pourra émettre de l'audio",
"audioPlayer_description": "sélectionnez le lecteur audio à utiliser pour la lecture",
"crossfadeDuration_description": "définit la durée du fondu enchaîné",
"audioDevice": "périphérique audio",
"accentColor": "couleur d'accentuation",
"accentColor_description": "définit la couleur d'accentuation de l'application",
"applicationHotkeys": "raccourcis clavier d'application",
"crossfadeDuration": "durée de fondue enchaînée",
"audioPlayer": "lecteur audio",
"applicationHotkeys_description": "configurer les raccourcis clavier dapplication. activer la case à cocher pour définir comme raccourci clavier global (bureau uniquement)",
"crossfadeStyle_description": "sélectionnez le style du fondu enchaîné à utiliser pour le lecteur audio",
"customFontPath": "chemin de police personnalisé",
"disableAutomaticUpdates": "désactiver les mises à jour automatique",
"customFontPath_description": "définit le chemin de police personnalisé pour l'application",
"remotePort_description": "définit le port du serveur de contrôle à distance",
"hotkey_skipBackward": "reculer",
"hotkey_playbackPause": "pause",
"mpvExecutablePath_help": "line par line",
"hotkey_volumeUp": "monter le volume",
"discordIdleStatus_description": "quand activé, mettre à jour le status pendant que le lecteur est inactif",
"showSkipButtons": "affiche les boutons suivants et précédents",
"minimumScrobblePercentage": "durée minimal du scobble (pourcentage)",
"lyricFetch": "récupère les paroles depuis internet",
"scrobble": "scrobble",
"enableRemote_description": "activer le serveur de contrôle à distance, qui permet à d'autres appareils de contrôler l'application",
"fontType_optionSystem": "police système",
"mpvExecutablePath_description": "définit le chemin vers l'exécutable mpv",
"hotkey_favoriteCurrentSong": "favori $t(common.currentSong)",
"sampleRate": "taux d'échantillonnage",
"sampleRate_description": "sélectionnez le taux d'échantillonnage de sortie utilisé si la fréquence d'échantillonnage sélectionnée est différente de celle du média actuel",
"hotkey_zoomIn": "zoom avant",
"scrobble_description": "scrobble les lectures à votre serveur multimédia",
"hotkey_browserForward": "avancer",
"discordUpdateInterval": "interval de mise à jour de {{discord}} rich presence",
"fontType_optionBuiltIn": "police intégrée",
"hotkey_playbackPlayPause": "lecture / pause",
"hotkey_rate1": "noter 1 étoile",
"hotkey_skipForward": "avancer",
"disableLibraryUpdateOnStartup": "désactive la recherche de mise à jour au démarrage",
"gaplessAudio": "audio sans interruption",
"minimizeToTray_description": "réduit l'application vers la barre des tâches",
"hotkey_playbackPlay": "lecture",
"hotkey_togglePreviousSongFavorite": "basculer $t(common.previousSong) favoris",
"hotkey_volumeDown": "baisser le volume",
"hotkey_unfavoritePreviousSong": "défavorisé $t(common.previousSong)",
"globalMediaHotkeys": "raccourci clavier multimédia global",
"hotkey_globalSearch": "recherche globale",
"gaplessAudio_description": "définit les paramètres d'audio sans interruption pour mpv",
"remoteUsername_description": "définit le nom d'utilisateur du serveur de contrôle à distance. si le nom d'utilisateur et le mot de passe sont vides, l'authentification sera désactivée",
"exitToTray_description": "quitte l'application vers la barre des tâches",
"followLyric_description": "faire défiler les paroles jusqu'à la position de lecture actuelle",
"hotkey_favoritePreviousSong": "favori $t(common.previousSong)",
"lyricOffset": "décalage des paroles (ms)",
"discordUpdateInterval_description": "temps en seconde entre chaque mise à jour (minimum de 15 secondes)",
"fontType_optionCustom": "police personnalisée",
"remotePassword": "mot de passe du serveur de contrôle à distance",
"lyricFetchProvider": "fournisseur depuis lequel récupérer les paroles",
"language_description": "définit la langue de l'application $t(common.restartRequired)",
"playbackStyle_optionCrossFade": "fondu enchaîné",
"hotkey_rate3": "noter 3 étoiles",
"font": "police",
"mpvExtraParameters": "paramètres de mpv",
"hotkey_toggleFullScreenPlayer": "basculer en plein écran",
"hotkey_localSearch": "recherche dans la page",
"hotkey_toggleQueue": "basculer la liste de lecteur",
"remotePassword_description": "définit le mot de passe du serveur de contrôle à distance. Ces identifiants sont par défaut transmises de façon non sécurisées, donc vous devriez utiliser un mot de passe unique dont vous n'avez pas grand-chose à faire",
"hotkey_rate5": "noter 5 étoiles",
"hotkey_playbackPrevious": "piste précédente",
"showSkipButtons_description": "affiche ou cache les boutons suivants et précédents de la barre de lecture",
"language": "language",
"playbackStyle": "style de lecture",
"hotkey_toggleShuffle": "basculer la lecture aléatoire",
"playbackStyle_description": "sélectionnez le style de lecture à utiliser pour le lecteur audio",
"discordRichPresence_description": "active l'état de lecteur dans le status d'activité {{discord}}. Les images clés sont: {{icon}}, {{playing}}, et {{paused}} ",
"mpvExecutablePath": "chemin de l'exécutable mpv",
"hotkey_rate2": "noter 2 étoiles",
"playButtonBehavior_description": "définit le comportement par défaut du bouton play, lors de l'ajout de chanson à la file d'attente",
"minimumScrobblePercentage_description": "le pourcentage minimum de la chanson qui doit être joué avant qu'elle ne soit scrobbleée",
"exitToTray": "quitter vers la barre des tâches",
"hotkey_rate4": "noter 4 étoiles",
"enableRemote": "activer le serveur de contrôle à distance",
"showSkipButton_description": "affiche ou cache les boutons suivants et précédents de la barre de lecture",
"savePlayQueue": "sauvegarder la liste de lecture",
"minimumScrobbleSeconds_description": "la durée minimale en secondes de la chanson qui doit être jouée avant qu'elle ne soit scrobbleée",
"fontType_description": "police intégré vous permet de sélectionner une des polices fourni par Feishin. Police système vous permet de sélectionner une des polices fourni par votre système d'éxploitation. personnalisé vous permet de fournir votre propre police",
"playButtonBehavior": "comportement du bouton play",
"playbackStyle_optionNormal": "normale",
"floatingQueueArea": "afficher le zone de file d'attente flottante",
"hotkey_toggleRepeat": "basculer la répétition",
"lyricOffset_description": "décale les paroles par le nombre de millisecondes spécifiées",
"fontType": "type de police",
"remotePort": "port du serveur de contrôle à distance",
"hotkey_playbackNext": "piste suivante",
"lyricFetch_description": "récupère les paroles depuis divers source d'internet",
"lyricFetchProvider_description": "sélectionnez le fournisseur auprès desquels récupérer les paroles. l'ordre des fournisseurs et l'ordre dans lequel ils seront interrogés",
"globalMediaHotkeys_description": "active ou désactive l'utilisation des raccourcis clavier multimédia système pour contrôler la lecture",
"followLyric": "suivre les paroles actuelles",
"discordIdleStatus": "afficher l'état d'inactivité dans le status de l'activité",
"hotkey_zoomOut": "zoom arrière",
"hotkey_unfavoriteCurrentSong": "retirer des favoris la $t(common.currentSong)",
"hotkey_rate0": "supprimer la note",
"hotkey_volumeMute": "couper le son",
"hotkey_toggleCurrentSongFavorite": "basculer favori de la $t(common.currentSong)",
"remoteUsername": "nom d'utilisateur du serveur de contrôle à distance",
"hotkey_browserBack": "retour arrière",
"showSkipButton": "affiche les boutons suivants et précédents",
"minimizeToTray": "réduire vers la barre des tâches",
"gaplessAudio_optionWeak": "faible (recommandée)",
"minimumScrobbleSeconds": "scrobble minimum (secondes)",
"hotkey_playbackStop": "stop",
"font_description": "définit la police à utiliser pour l'application",
"savePlayQueue_description": "sauvegarde la liste de lecture quand l'application est fermée et la restaure quand l'application est ouverte",
"sidebarCollapsedNavigation_description": "affiche ou cache la navigation dans la barre latérale réduite",
"sidebarConfiguration": "configuration de la barre latérale",
"sidebarConfiguration_description": "sélectionnez les items et l'ordre dans lesquels ils seront affichaient dans la barre latérale",
"sidebarPlaylistList": "liste de playlist de la barre latérale",
"sidebarCollapsedNavigation": "navigation de la barre latéral (réduite)",
"skipDuration": "durée de l'avance rapide",
"sidePlayQueueStyle_optionAttached": "attaché",
"sidePlayQueueStyle": "style de la liste de lecture latérale",
"sidebarPlaylistList_description": "affiche ou cache la liste de playlist de la barre latérale",
"sidePlayQueueStyle_description": "définit le style de la liste de lecture latérale",
"sidePlayQueueStyle_optionDetached": "détaché",
"volumeWheelStep_description": "la valeur de volume à modifier lors du défilement de la molette de la souris sur le curseur de volume",
"theme_description": "définit le thème à utiliser pour l'application",
"skipDuration_description": "définit le durée du saut rapide, lors de l'utilisation des boutons avancer/reculer de la barre de lecture",
"themeLight": "thème (clair)",
"zoom": "pourcentage de zoom",
"themeDark_description": "définit le thème sombre à utiliser pour l'application",
"themeLight_description": "définit le thème clair à utiliser pour l'application",
"zoom_description": "définit le pourcentage de zoom de l'application",
"theme": "thème",
"skipPlaylistPage_description": "lors de la navigation dans une playlist, aller directement vers le liste des morceaux, au lieu de la page par défaut",
"volumeWheelStep": "valeur du pas de volume",
"windowBarStyle": "style de la barre de la fenêtre",
"useSystemTheme_description": "suivre les préférence du système (sombre ou clair)",
"skipPlaylistPage": "sauter la page de playlist",
"themeDark": "thème (sombre)",
"windowBarStyle_description": "sélectionner le style de la barre de la fenêtre",
"useSystemTheme": "utiliser le thème du système",
"discordApplicationId_description": "l'identifiant de l'application pour le statut d'activité {{discord}} (par défaut à {{defaultId}})",
"audioExclusiveMode": "mode de sortie audio exclusif",
"discordApplicationId": "identifiant d'application {{discord}}",
"floatingQueueArea_description": "afficher une icon flottante sur le côté droit de l'écran pour afficher la liste d'attente",
"discordRichPresence": "status d'activité de {{discord}}",
"playButtonBehavior_optionPlay": "$t(player.play)",
"replayGainMode_optionNone": "$t(common.none)",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"replayGainMode_description": "ajuste le gain de volume accordement à la valeur de {{ReplayGain}} sauvegardé dans les métadonnées du fichier",
"replayGainFallback": "{{ReplayGain}} fallback",
"replayGainClipping_description": "Préviens le clipping causé par {{ReplayGain}} en baissant automatiquement le gain",
"replayGainPreamp": "préamplificateur (dB) de {{ReplayGain}}",
"replayGainClipping": "{{ReplayGain}} clipping",
"replayGainMode": "mode de {{ReplayGain}}",
"replayGainFallback_description": "gain en dB à appliquer si le fichier n'a pas de tag {{ReplayGain}}",
"replayGainPreamp_description": "ajuste le gain de préampli appliqué a la valeur de {{ReplayGain}}"
},
"form": {
"deletePlaylist": {
"title": "supprimer de $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) supprimée avec succès",
"input_confirm": "taper le nom de la $t(entity.playlist_one) pour confirmer"
},
"addServer": {
"title": "ajouter un serveur",
"input_username": "nom d'utilisateur",
"input_url": "url",
"input_password": "mot de passe",
"input_legacyAuthentication": "activer l'authtication legacy",
"input_name": "nom du serveur",
"success": "serveur ajouté avec succès",
"input_savePassword": "enregister le mot de passe",
"ignoreSsl": "ignorer ssl $t(common.restartRequired)",
"ignoreCors": "ignorer cors $t(common.restartRequired)",
"error_savePassword": "une erreur sest produite lors de la tentative de sauvegarde du mot de passe"
},
"addToPlaylist": {
"success": "{{message}} $t(entity.song_other) ajouté à {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "ajouter à $t(entity.playlist_one)",
"input_skipDuplicates": "sauter les doublons",
"input_playlists": "$t(entity.playlist_other)"
},
"createPlaylist": {
"title": "créer $t(entity.playlist_one)",
"input_public": "publique",
"success": "$t(entity.playlist_one) créée avec succès",
"input_description": "$t(common.description)",
"input_name": "$t(common.name)",
"input_owner": "$t(common.owner)"
},
"updateServer": {
"title": "mise à jour du serveur",
"success": "serveur mis à jour avec succès"
},
"queryEditor": {
"input_optionMatchAll": "correspondre à tous",
"input_optionMatchAny": "correspondre à n'importe quel"
},
"editPlaylist": {
"title": "modifier $t(entity.playlist_one)"
},
"lyricSearch": {
"title": "rechercher parole",
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)"
}
},
"entity": {
"genre_one": "genre",
"genre_many": "genres",
"genre_other": "genres",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlists",
"playlistWithCount_other": "{{count}} playlists",
"playlist_one": "playlist",
"playlist_many": "playlists",
"playlist_other": "playlists",
"artist_one": "artiste",
"artist_many": "artistes",
"artist_other": "artistes",
"folderWithCount_one": "{{count}} dossier",
"folderWithCount_many": "{{count}} dossiers",
"folderWithCount_other": "{{count}} dossiers",
"albumArtist_one": "artiste de l'album",
"albumArtist_many": "artistes d'albums",
"albumArtist_other": "artistes d'albums",
"track_one": "piste",
"track_many": "pistes",
"track_other": "pistes",
"albumArtistCount_one": "{{count}} artiste de l'album",
"albumArtistCount_many": "{{count}} artistes d'albums",
"albumArtistCount_other": "{{count}} artistes d'albums",
"albumWithCount_one": "{{count}} album",
"albumWithCount_many": "{{count}} albums",
"albumWithCount_other": "{{count}} albums",
"favorite_one": "favori",
"favorite_many": "favoris",
"favorite_other": "favoris",
"artistWithCount_one": "{{count}} artiste",
"artistWithCount_many": "{{count}} artistes",
"artistWithCount_other": "{{count}} artistes",
"folder_one": "dossier",
"folder_many": "dossiers",
"folder_other": "dossiers",
"smartPlaylist": "$t(entity.playlist_one) intelligente",
"album_one": "album",
"album_many": "albums",
"album_other": "albums",
"genreWithCount_one": "{{count}} genre",
"genreWithCount_many": "{{count}} genres",
"genreWithCount_other": "{{count}} genres",
"trackWithCount_one": "{{count}} piste",
"trackWithCount_many": "{{count}} pistes",
"trackWithCount_other": "{{count}} pistes"
},
"table": {
"config": {
"general": {
"displayType": "Type d'affichage",
"tableColumns": "colonnes de la liste",
"autoFitColumns": "colonnes à ajustement automatique",
"gap": "$t(common.gap)",
"size": "$t(common.size)"
},
"view": {
"table": "liste",
"poster": "poster",
"card": "Carte"
},
"label": {
"releaseDate": "date de sortie",
"titleCombined": "$t(common.title) (combiné)",
"dateAdded": "date d'ajout",
"lastPlayed": "dernière écoute",
"trackNumber": "numéro de piste",
"rowIndex": "index de ligne",
"playCount": "nombre de lecture",
"discNumber": "disque n°",
"duration": "$t(common.duration)",
"bpm": "$t(common.bpm)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"biography": "$t(common.biography)",
"channels": "$t(common.channel_other)",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"favorite": "$t(common.favorite)",
"albumArtist": "$t(entity.albumArtist_one)",
"rating": "$t(common.rating)",
"note": "$t(common.note)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"title": "$t(common.title)",
"size": "$t(common.size)",
"genre": "$t(entity.genre_one)",
"year": "$t(common.year)"
}
},
"column": {
"comment": "commentaire",
"album": "album",
"rating": "note",
"favorite": "favori",
"playCount": "lectures",
"releaseYear": "année",
"biography": "biographie",
"releaseDate": "date de sortie",
"bitrate": "bitrate",
"title": "titre",
"bpm": "bpm",
"dateAdded": "date d'ajout",
"trackNumber": "piste",
"albumArtist": "artiste de l'album",
"path": "chemin",
"discNumber": "disque",
"albumCount": "$t(entity.album_other)",
"lastPlayed": "dernière lecture",
"artist": "$t(entity.artist_one)",
"genre": "$t(entity.genre_one)",
"songCount": "$t(entity.track_other)",
"channels": "$t(common.channel_other)"
}
}
}
+627
View File
@@ -0,0 +1,627 @@
{
"action": {
"editPlaylist": "modifica $t(entity.playlist_one)",
"goToPage": "vai alla pagina",
"clearQueue": "cancella la coda",
"addToFavorites": "aggiungi a $t(entity.favorite_other)",
"addToPlaylist": "aggiungi a $t(entity.playlist_one)",
"createPlaylist": "crea $t(entity.playlist_one)",
"removeFromPlaylist": "rimuovi da $t(entity.playlist_one)",
"viewPlaylists": "visualizza $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "elimina $t(entity.playlist_one)",
"removeFromQueue": "rimuovi dalla coda",
"deselectAll": "deseleziona tutto",
"setRating": "vota",
"toggleSmartPlaylistEditor": "attiva/disattiva editor $t(entity.smartPlaylist)",
"removeFromFavorites": "rimuovi da $t(entity.favorite_other)",
"moveToTop": "sposta in cima",
"moveToBottom": "sposta in fondo"
},
"common": {
"backward": "indietro",
"areYouSure": "sei sicurə?",
"add": "aggiungi",
"ascending": "crescente",
"bitrate": "bitrate",
"action_one": "azione",
"action_many": "azioni",
"action_other": "azioni",
"biography": "biografia",
"bpm": "bpm",
"center": "centrale",
"cancel": "annulla",
"channel_one": "canale",
"channel_many": "canali",
"channel_other": "canali",
"collapse": "collassa",
"configure": "configura",
"comingSoon": "a seguire…",
"increase": "incrementa",
"rating": "voto",
"refresh": "ricarica",
"unknown": "sconosciuto",
"edit": "modifica",
"favorite": "preferito",
"left": "sinistra",
"save": "salva",
"right": "destra",
"currentSong": "$t(entity.track_one) corrente",
"trackNumber": "traccia",
"descending": "decrescente",
"gap": "gap",
"dismiss": "dimetti",
"year": "anno",
"manage": "gestisci",
"limit": "limite",
"minimize": "minimizza",
"modified": "modificato",
"duration": "durata",
"name": "nome",
"maximize": "massimizza",
"decrease": "decrementa",
"ok": "ok",
"description": "descrizione",
"path": "percorso",
"no": "no",
"owner": "proprietariə",
"enable": "abilita",
"clear": "svuota",
"forward": "successivo",
"delete": "elimina",
"forceRestartRequired": "riavvia per applicare le modifiche... chiudi la notifica per riavviare",
"setting": "impostazione",
"version": "versione",
"title": "titolo",
"filter_one": "filtro",
"filter_many": "filtri",
"filter_other": "filtri",
"filters": "filtri",
"create": "crea",
"saveAndReplace": "salva e sovrascrivi",
"playerMustBePaused": "il player deve essere messo in pausa",
"confirm": "conferma",
"resetToDefault": "ripristina ai valori di default",
"home": "home",
"reset": "ripristina",
"disable": "disabilita",
"sortOrder": "ordine",
"none": "nessuno",
"menu": "menù",
"restartRequired": "riavvio richiesto",
"previousSong": "$t(entity.track_one) precedente",
"noResultsFromQuery": "la query non ha ritornato risultati",
"quit": "esci",
"expand": "espandi",
"search": "cerca",
"saveAs": "salva come",
"disc": "disco",
"yes": "si",
"random": "casuale",
"size": "dimensione",
"note": "nota"
},
"player": {
"repeat_all": "ripeti coda",
"stop": "ferma",
"repeat": "ripeti traccia",
"queue_remove": "rimuovi selezionati",
"playRandom": "riproduci casuale",
"skip": "salta",
"previous": "precedente",
"toggleFullscreenPlayer": "attiva/disattiva player a schermo intero",
"skip_back": "salta indietro",
"favorite": "preferito",
"next": "successivo",
"shuffle": "mescola",
"playbackFetchNoResults": "nessuna canzone trovata",
"playbackFetchInProgress": "caricamento canzoni…",
"addNext": "aggiungi successivo",
"playbackSpeed": "velocità di riproduzione",
"playbackFetchCancel": "ci sta mettendo un po'... chiudi la notifica per annullare",
"play": "riproduci",
"repeat_off": "non ripetere",
"pause": "pausa",
"queue_clear": "cancella coda",
"muted": "silenziato",
"unfavorite": "togli dai preferiti",
"queue_moveToTop": "sposta selezionati in fondo",
"queue_moveToBottom": "sposta selezionati in cima",
"shuffle_off": "non mescolare",
"addLast": "aggiungi in coda",
"mute": "silenzia",
"skip_forward": "salta avanti"
},
"setting": {
"crossfadeStyle_description": "seleziona lo stile dissolvenza da usare per il player audio",
"remotePort_description": "imposta la porta del server di controllo remoto",
"hotkey_skipBackward": "salta a precedente",
"volumeWheelStep_description": "la quantità di volume da cambiare quando si scorre la rotellina del mouse sullo slider del volume",
"audioDevice_description": "seleziona il device audioda usare per la riproduzione (solo web player)",
"theme_description": "imposta il tema da usare per l'applicazione",
"hotkey_playbackPause": "pausa",
"mpvExecutablePath_help": "uno per linea",
"hotkey_volumeUp": "alza volume",
"skipDuration": "salta durata",
"discordIdleStatus_description": "quando è attivo, aggiorna lo stato mentre il player è inattivo",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "durata minima scrobble (percentuale)",
"lyricFetch": "ottieni testi da internet",
"scrobble": "scrobble",
"skipDuration_description": "imposta la durata da saltare quando vengono usati i pulsanti di salto nella barra del player",
"enableRemote_description": "abilita il controllo remoto del server per permettere ad altri dispositivi di controllare l'applicazione",
"fontType_optionSystem": "font di sistema",
"mpvExecutablePath_description": "imposta il percorso dell'eseguibile di mpv",
"hotkey_favoriteCurrentSong": "$t(common.currentSong) preferita",
"crossfadeStyle": "stile dissolvenza",
"sidebarConfiguration": "configurazione barra laterale",
"replayGainMode_optionNone": "$t(common.none)",
"hotkey_zoomIn": "ingrandisci layout",
"scrobble_description": "invia lo scrobble delle riproduzioni al tuo media server",
"audioExclusiveMode_description": "abilità modalità output esclusiva. In questa modalità il sistema è di solito chiuso fuori, e solo mpv potrà riprodurre audio",
"discordUpdateInterval": "intervallo aggiornamento stato attività {{discord}}",
"themeLight": "tema (chiaro)",
"fontType_optionBuiltIn": "font built-in",
"hotkey_playbackPlayPause": "riproduci / pausa",
"hotkey_rate1": "voto 1 stella",
"hotkey_skipForward": "salta a successivo",
"disableLibraryUpdateOnStartup": "disabilita il controllo di nuove versioni all'avvio",
"discordApplicationId_description": "l'application id per lo stato attività di {{discord}} ({{defaultId}} è il valore predefinito)",
"gaplessAudio": "audio gapless",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"zoom": "percentuale zoom",
"minimizeToTray_description": "riduce a icona nella barra di sistema",
"hotkey_playbackPlay": "riproduci",
"hotkey_volumeDown": "abbassa volume",
"audioPlayer_description": "seleziona il player audio da usare per la riproduzione",
"globalMediaHotkeys": "tasti media globali",
"hotkey_globalSearch": "ricerca globale",
"gaplessAudio_description": "imposta l'audio gapless per mpv",
"remoteUsername_description": "imposta l'username del server di controllo remoto. Se username e password sono vuoti, l'autenticazione sarà disattivata",
"disableAutomaticUpdates": "disabilita aggiornamenti automatici",
"exitToTray_description": "riduce a icona nella barra di sistema all'uscita",
"followLyric_description": "scorre il testo alla posizione di riproduzione corrente",
"hotkey_favoritePreviousSong": "$t(common.previousSong) preferita",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "offset testi (ms)",
"discordUpdateInterval_description": "il tempo in secondi tra ogni aggiornamento (minimo 15 secondi)",
"fontType_optionCustom": "font personalizzato",
"themeDark_description": "imposta il tema scuro da usare per l'applicazione",
"audioExclusiveMode": "modalità audio esclusiva",
"remotePassword": "password server controllo remoto",
"lyricFetchProvider": "providers da dove ottenere testi",
"language_description": "imposta la lingua dell'applicazione ($t(common.restartRequired))",
"playbackStyle_optionCrossFade": "dissolvenza",
"hotkey_rate3": "voto 3 stelle",
"font": "font",
"mpvExtraParameters": "parametri mpv",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "imposta il tema chiaro da usare per l'applicazione",
"hotkey_toggleFullScreenPlayer": "attiva/disattiva player a schermo intero",
"hotkey_localSearch": "ricerca in-pagina",
"hotkey_toggleQueue": "attiva/disattiva coda",
"zoom_description": "imposta la percentuale zoom per l'applicazione",
"remotePassword_description": "imposta la password del server di controllo remoto. Queste credenziali sono di default trasferite in modo non sicuro, quindi dovresti usare una password unica di cui non ti importa",
"hotkey_rate5": "voto 5 stelle",
"hotkey_playbackPrevious": "traccia precedente",
"crossfadeDuration_description": "imposta la durata dell'effetto di dissolvenza",
"language": "lingua",
"playbackStyle": "stile riproduzione",
"hotkey_toggleShuffle": "attiva/disattiva mescolamento",
"theme": "tema",
"playbackStyle_description": "selezione lo stile di riproduzione da usare per il player audio",
"discordRichPresence_description": "abilita lo status del playback nello stato attività di {{discord}}. Le chiavi immagine sono: {{icon}}, {{playing}} e {{paused}} ",
"mpvExecutablePath": "percorso eseguibile mpv",
"audioDevice": "device audio",
"hotkey_rate2": "voto 2 stelle",
"playButtonBehavior_description": "imposta il comportamente di default del pulsante di riproduzione quando viene aggiunta una canzone alla coda",
"minimumScrobblePercentage_description": "la minima percentuale di una canzone che deve essere riprodutta prima di eseguire lo scrobble",
"exitToTray": "riduci a icona all'uscita",
"hotkey_rate4": "voto 4 stelle",
"enableRemote": "abilita controllo remoto server",
"savePlayQueue": "salva coda di riproduzione",
"minimumScrobbleSeconds_description": "la minima durata in secondi di una canzone che deve essere riprodutta prima di eseguire lo scrobble",
"fontType_description": "Font built-in seleziona uno dei font forniti da Feishin. Font di sistema ti permette di selezionare ogni font fornito dal tuo sistema operativo. Custom ti permette di fornire il tuo font",
"playButtonBehavior": "comportamento pulsante riproduzione",
"volumeWheelStep": "step rotellina volume",
"sidebarPlaylistList_description": "mostra o nascondi la lista delle playlist nella barra laterale",
"accentColor": "colore d'accento",
"accentColor_description": "imposta colore d'accento per l'applicazione",
"playbackStyle_optionNormal": "normale",
"windowBarStyle": "stile barra della finestra",
"floatingQueueArea": "mostra l'area di passaggio della coda fluttante",
"hotkey_toggleRepeat": "attiva/disattiva ripeti",
"lyricOffset_description": "aumenta/dimuisce l'offset del testo di una specifica quantità di millisecondi",
"sidebarConfiguration_description": "seleziona gli elementi e l'ordine in cui appaiono nella barra laterale",
"fontType": "tipo font",
"remotePort": "porta del server di controllo remoto",
"applicationHotkeys": "tasti a scelta rapida applicazione",
"hotkey_playbackNext": "traccia successiva",
"useSystemTheme_description": "segui le preferenze del tema definite dal sistema",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "ottieni testi da varie sorgenti internet",
"lyricFetchProvider_description": "seleziona i provider da dove prendere i testi. l'ordine dei provider è l'ordine in cui vengono fatte le richieste",
"globalMediaHotkeys_description": "attiva/disattiva l'uso dei tasti media globali per controllare la riproduzione",
"customFontPath": "percorso font personalizzato",
"followLyric": "segui testo corrente",
"crossfadeDuration": "durata dissolvenza",
"discordIdleStatus": "visualizza lo stato attività in stato inattivo",
"audioPlayer": "player audio",
"hotkey_zoomOut": "rimpicciolisci layout",
"hotkey_rate0": "rimuovi voto",
"discordApplicationId": "application id {{discord}}",
"applicationHotkeys_description": "configura tasti a scelta rapida dell'applicazione. attiva/disattiva la casella per impostare un tasto a scelta rapida globale (solo desktop)",
"floatingQueueArea_description": "visualizza l'icona di passaggio sul lato destro dello schermo per mostrare la coda di riproduzione",
"hotkey_volumeMute": "silenzia volume",
"remoteUsername": "username server di controllo remoto",
"sidebarPlaylistList": "lista playlist nella barra laterale",
"minimizeToTray": "riduci a icona",
"themeDark": "tema (scuro)",
"customFontPath_description": "imposta il percorso al font personalizzato da usare per l'applicazione",
"gaplessAudio_optionWeak": "debole (raccomandato)",
"minimumScrobbleSeconds": "durata minima scrobble (secondi)",
"hotkey_playbackStop": "ferma",
"windowBarStyle_description": "seleziona lo stile della barra della finestra",
"discordRichPresence": "stato attività {{discord}}",
"font_description": "imposta il font da usare per l'applicazione",
"savePlayQueue_description": "salva la coda di riproduzione quando l'applicazione viene chiusa e ripristina quando l'applicazione viene riaperta",
"useSystemTheme": "usa il tema di sistema",
"replayGainMode_description": "aggiusta il volume secondo i valori {{ReplayGain}} salvati nei metadati del file",
"showSkipButtons": "mostra pulsanti per saltare",
"sampleRate": "frequenza di campionamento",
"sampleRate_description": "seleziona la frequenza di campionamento di output da usare se la frequenza di campionamento selezionata è diversa da quella della del media attuale",
"hotkey_togglePreviousSongFavorite": "imposta/rimuovi $t(common.previousSong) favorito",
"hotkey_unfavoritePreviousSong": "rimuovi $t(common.previousSong) dai preferiti",
"showSkipButton_description": "mostra o nascondi i pulsanti per saltare nella barra del player",
"hotkey_unfavoriteCurrentSong": "rimuovi $t(common.currentSong) dai preferiti",
"hotkey_toggleCurrentSongFavorite": "imposta/rimuovi $t(common.currentSong) favorito",
"showSkipButton": "mostra pulsanti per saltare",
"hotkey_browserForward": "Vai avanti di una pagina",
"hotkey_browserBack": "Torna indietro di una pagina",
"sidebarCollapsedNavigation_description": "mostra o nascondi la navigazione nella barra laterale collassata",
"replayGainClipping_description": "Previeni il clipping causato da {{ReplayGain}} abbassando automaticamente il gain",
"replayGainPreamp": "preamplificazione {{ReplayGain}} (dB)",
"sidePlayQueueStyle": "stile della coda di riproduzione laterale",
"showSkipButtons_description": "mostra o nascondi i pulsanti per saltare dalla barra di riproduzione",
"skipPlaylistPage_description": "quando si naviga in una playlist, si va alla pagina dell'elenco dei brani della playlist invece che alla pagina predefinita",
"sidePlayQueueStyle_description": "imposta lo stile della coda di riproduzione laterale",
"replayGainMode": "modalità {{ReplayGain}}",
"replayGainFallback_description": "gain in db da applicare se il file non possiede tag {{ReplayGain}}",
"replayGainPreamp_description": "aggiusta la preamplificazione del gain applicato sui valori {{ReplayGain}}",
"skipPlaylistPage": "Salta la pagina playlist",
"sidebarCollapsedNavigation": "navigazione con barra laterale (collassata)"
},
"error": {
"remotePortWarning": "riavvia il server per applicare la nuova porta",
"systemFontError": "si è verificato un errore nell'otternere i font di sistema",
"playbackError": "si è verificato un errore nel provare a riprodurre il media",
"endpointNotImplementedError": "l'endpoint {{endpoint}} non è implementato per {{serverType}}",
"remotePortError": "si è verificato un errore nel provare a impostare la porta del server remoto",
"serverRequired": "server richiesto",
"authenticationFailed": "autenticazione fallita",
"apiRouteError": "impossibile indirizzare la richiesta",
"genericError": "si è verificato un errore",
"credentialsRequired": "credenziali richieste",
"sessionExpiredError": "la tua sessione è scaduta",
"remoteEnableError": "si è verificato un errore nel $t(common.enable) il server remoto",
"localFontAccessDenied": "accesso non consentito ai font locali",
"serverNotSelectedError": "nessun server selezionato",
"remoteDisableError": "si è verificato un errore nel $t(common.disable) il server remoto",
"mpvRequired": "MPV richiesto",
"audioDeviceFetchError": "si è verificato un errore nel provare ad ottenre i device audio",
"invalidServer": "server non valido",
"loginRateError": "troppi tentativi di accesso, per favore riprova tra qualche secondo"
},
"filter": {
"mostPlayed": "più riprodotti",
"comment": "commento",
"playCount": "numero di riproduzioni",
"recentlyUpdated": "aggiornati recentemente",
"channels": "$t(common.channel_other)",
"isCompilation": "è una compilation",
"recentlyPlayed": "riprodotti recentemente",
"isRated": "è valutato",
"owner": "$t(common.owner)",
"title": "titolo",
"rating": "voto",
"search": "cerca",
"bitrate": "bitrate",
"genre": "$t(entity.genre_one)",
"recentlyAdded": "aggiunti recentemente",
"note": "nota",
"name": "nome",
"dateAdded": "data aggiunta",
"releaseDate": "data di rilascio",
"albumCount": "numero $t(entity.album_other)",
"communityRating": "voto della community",
"path": "percorso",
"favorited": "preferito",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "è stato recentemente riprodotto",
"isFavorited": "è preferito",
"bpm": "bpm",
"releaseYear": "anno di rilascio",
"id": "id",
"disc": "disco",
"biography": "biografia",
"songCount": "conteggio canzoni",
"artist": "$t(entity.artist_one)",
"duration": "durata",
"isPublic": "è pubblico",
"random": "casuale",
"lastPlayed": "ultima riproduzione",
"toYear": "fino all'anno",
"fromYear": "dall'anno",
"criticRating": "voto della critica",
"album": "$t(entity.album_one)",
"trackNumber": "traccia"
},
"page": {
"sidebar": {
"nowPlaying": "in riproduzione",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "mostra corrispondenza testi",
"dynamicBackground": "background dinamico",
"synchronized": "sincronizzato",
"followCurrentLyric": "segui testo corrente",
"opacity": "opacità",
"lyricSize": "dimensione testo",
"showLyricProvider": "mostra provider testi",
"unsynchronized": "non sinncronizzato",
"lyricAlignment": "allineamento testo",
"useImageAspectRatio": "usa le proporzioni dell'immagine",
"lyricGap": "gap testo"
},
"upNext": "successivamente",
"lyrics": "testi",
"related": "correlati"
},
"appMenu": {
"selectServer": "seleziona server",
"version": "versione {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "gestisci server",
"expandSidebar": "espandi barra laterale",
"collapseSidebar": "collassa barra laterale",
"openBrowserDevtools": "apri devtools browser",
"quit": "$t(common.quit)",
"goBack": "torna indietro",
"goForward": "vai avanti"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} selezionati",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"home": {
"mostPlayed": "più riprodotti",
"newlyAdded": "nuovi rilasci aggiunti",
"title": "$t(common.home)",
"explore": "esplora dalla tua libreria",
"recentlyPlayed": "riprodotti recentemente"
},
"albumDetail": {
"moreFromArtist": "di più da questo $t(entity.artist_one)",
"moreFromGeneric": "di più da {{item}}"
},
"setting": {
"playbackTab": "riproduzione",
"generalTab": "generale",
"hotkeysTab": "tasti a scelta rapida",
"windowTab": "finestra"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "comandi server",
"goToPage": "vai alla pagina",
"searchFor": "cerca per {{query}}"
},
"title": "comandi"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"form": {
"deletePlaylist": {
"title": "elimina $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) eliminata correttamente",
"input_confirm": "digita il nome della $t(entity.playlist_one) per confermare"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "crea $t(entity.playlist_one)",
"input_public": "publico",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) creata con successo",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "aggiungi server",
"input_username": "nome utente",
"input_url": "url",
"input_password": "password",
"input_legacyAuthentication": "abilita autenticazione legacy",
"input_name": "nome server",
"success": "server aggiunto con successo",
"input_savePassword": "salva password",
"ignoreSsl": "ignora ssl ($t(common.restartRequired))",
"ignoreCors": "ignora cors ($t(common.restartRequired))",
"error_savePassword": "si è verificato un errore quando si è provato a salvare la password"
},
"addToPlaylist": {
"success": "aggiunto {{message}} $t(entity.song_other) a {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "aggiungi a $t(entity.playlist_one)",
"input_skipDuplicates": "salta duplicati",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "aggiorna server",
"success": "server aggiornato con successo"
},
"queryEditor": {
"input_optionMatchAll": "soddisfa tutti",
"input_optionMatchAny": "soddisfa qualsiasi"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "cerca testi"
},
"editPlaylist": {
"title": "modifica $t(entity.playlist_one)"
}
},
"table": {
"config": {
"general": {
"displayType": "mostra tipo",
"gap": "$t(common.gap)",
"tableColumns": "tabella colonne",
"autoFitColumns": "adatta colonne automaticamente",
"size": "$t(common.size)"
},
"view": {
"table": "tabella",
"card": "Scheda"
},
"label": {
"releaseDate": "data rilascio",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (combinati)",
"dateAdded": "data aggiunta",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "ultima riproduzione",
"trackNumber": "numero traccia",
"rowIndex": "indice riga",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "numero riproduzioni",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "numero disco",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)"
}
},
"column": {
"comment": "commento",
"album": "album",
"rating": "voto",
"favorite": "preferito",
"playCount": "riproduzioni",
"albumCount": "$t(entity.album_other)",
"releaseYear": "anno",
"lastPlayed": "ultima riproduzione",
"biography": "biografia",
"releaseDate": "data di rilascio",
"bitrate": "bitrate",
"title": "titolo",
"bpm": "bpm",
"dateAdded": "data aggiunta",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "traccia",
"genre": "$t(entity.genre_one)",
"albumArtist": "artista album",
"path": "percorso",
"discNumber": "disco",
"channels": "$t(common.channel_other)"
}
},
"entity": {
"genre_one": "genere",
"genre_many": "generi",
"genre_other": "generi",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlist",
"playlistWithCount_other": "{{count}} playlist",
"playlist_one": "playlist",
"playlist_many": "playlist",
"playlist_other": "playlist",
"artist_one": "artista",
"artist_many": "artisti",
"artist_other": "artisti",
"folderWithCount_one": "{{count}} cartella",
"folderWithCount_many": "{{count}} cartelle",
"folderWithCount_other": "{{count}} cartelle",
"albumArtist_one": "artista album",
"albumArtist_many": "artisti album",
"albumArtist_other": "artisti album",
"track_one": "traccia",
"track_many": "tracce",
"track_other": "tracce",
"albumArtistCount_one": "{{count}} artista album",
"albumArtistCount_many": "{{count}} artisti album",
"albumArtistCount_other": "{{count}} artisti album",
"albumWithCount_one": "{{count}} album",
"albumWithCount_many": "{{count}} album",
"albumWithCount_other": "{{count}} album",
"favorite_one": "preferito",
"favorite_many": "preferiti",
"favorite_other": "preferiti",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artisti",
"artistWithCount_other": "{{count}} artisti",
"folder_one": "cartella",
"folder_many": "cartelle",
"folder_other": "cartelle",
"smartPlaylist": "$t(entity.playlist_one) smart",
"album_one": "album",
"album_many": "album",
"album_other": "album",
"genreWithCount_one": "{{count}} genere",
"genreWithCount_many": "{{count}} generi",
"genreWithCount_other": "{{count}} generi",
"trackWithCount_one": "{{count}} traccia",
"trackWithCount_many": "{{count}} tracce",
"trackWithCount_other": "{{count}} tracce"
}
}
+596
View File
@@ -0,0 +1,596 @@
{
"player": {
"repeat_all": "全曲リピート",
"stop": "停止",
"repeat": "リピート",
"queue_remove": "選択項目を削除",
"playRandom": "ランダム再生",
"skip": "スキップ",
"previous": "前へ",
"toggleFullscreenPlayer": "フルスクリーンプレーヤーの切り替え",
"skip_back": "前へスキップ",
"favorite": "お気に入り",
"next": "次へ",
"shuffle": "シャッフル",
"playbackFetchNoResults": "曲が見つかりません",
"playbackFetchInProgress": "曲を読み込み中…",
"addNext": "次へ追加",
"playbackSpeed": "再生速度",
"playbackFetchCancel": "処理に時間がかかります… 通知を閉じるとキャンセルします",
"play": "再生",
"repeat_off": "リピート無効",
"queue_clear": "キューをクリア",
"muted": "ミュート中",
"unfavorite": "お気に入り解除",
"queue_moveToTop": "選択項目を先末尾に移動",
"queue_moveToBottom": "選択項目を先頭に移動",
"shuffle_off": "シャッフル無効",
"addLast": "最後へ追加",
"mute": "ミュート",
"skip_forward": "次へスキップ",
"pause": "一時停止"
},
"setting": {
"crossfadeStyle_description": "オーディオプレーヤーが使用するクロスフェードのスタイルを選択します",
"remotePort_description": "リモートコントロール サーバーのポートを設定します",
"hotkey_skipBackward": "前にスキップ",
"replayGainMode_description": "ファイルのメタデータに保存されている {{ReplayGain}} 値に従って音量ゲインを調整します",
"volumeWheelStep_description": "音量スライダーでマウスホイールをスクロールしたときに変化する音量を設定します",
"audioDevice_description": "再生に使用するオーディオデバイスを選択します (Webプレーヤーのみ)",
"theme_description": "アプリケーションに使用するテーマを設定します",
"hotkey_playbackPause": "一時停止",
"replayGainFallback": "{{ReplayGain}} フォールバック",
"sidebarCollapsedNavigation_description": "折りたたみサイドバーのナビゲーションを表示/非表示にします",
"mpvExecutablePath_help": "1行ごとに1アイテム",
"hotkey_volumeUp": "音量を上げる",
"skipDuration": "スキップの長さ",
"discordIdleStatus_description": "プレイヤーがアイドル状態でもステータスを更新します",
"showSkipButtons": "スキップボタンを表示",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "最小 Scrobble 時間 (%)",
"lyricFetch": "インターネットから歌詞を取得",
"scrobble": "Scrobble",
"skipDuration_description": "プレーヤーバーのスキップボタンでスキップする時間を設定します",
"enableRemote_description": "リモートコントロール サーバーを有効化し、他のデバイスからアプリケーションを制御できるようにします",
"fontType_optionSystem": "システムフォント",
"mpvExecutablePath_description": "mpvの実行ファイルが存在するパスを設定します",
"replayGainClipping_description": "自動的にゲインを下げて {{ReplayGain}} によるクリッピングを防ぎます",
"replayGainPreamp": "{{ReplayGain}} プリアンプ (dB)",
"hotkey_favoriteCurrentSong": "$t(common.currentSong) をお気に入り",
"sampleRate": "サンプルレート",
"crossfadeStyle": "クロスフェードスタイル",
"sidePlayQueueStyle_optionAttached": "結合",
"sidebarConfiguration": "サイドバー設定",
"sampleRate_description": "設定とメディアのサンプル周波数が異なる場合に使用する出力サンプルレートを選択します",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainClipping": "{{ReplayGain}} クリッピング",
"hotkey_zoomIn": "拡大",
"scrobble_description": "再生した音楽をメディアサーバーから Scrobble します",
"hotkey_browserForward": "ブラウザ 進む",
"audioExclusiveMode_description": "専用の排他出力モードを有効にします。 このモードでは、システムの他の出力がロックされ、mpvだけがオーディオを出力できるようになります",
"discordUpdateInterval": "{{discord}} Rich Presenceアップデート間隔",
"themeLight": "テーマ (ライト)",
"fontType_optionBuiltIn": "組み込みフォント",
"hotkey_playbackPlayPause": "再生 / 一時停止",
"hotkey_rate1": "1つ星で評価",
"hotkey_skipForward": "次へスキップ",
"disableLibraryUpdateOnStartup": "起動時の新バージョンチェックを無効にします",
"discordApplicationId_description": "{{discord}} にRich Presenceステータスを表示するためのアプリケーションID (デフォルトは {{defaultId}} です)",
"sidePlayQueueStyle": "サイド再生キュースタイル",
"gaplessAudio": "ギャップレス再生",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"zoom": "ズーム率",
"minimizeToTray_description": "最小化ボタンが押された際、システムトレイに格納します",
"hotkey_playbackPlay": "再生",
"hotkey_togglePreviousSongFavorite": "$t(common.previousSong) をお気に入り登録/解除",
"hotkey_volumeDown": "音量を下げる",
"hotkey_unfavoritePreviousSong": "$t(common.previousSong) をお気に入り解除",
"audioPlayer_description": "再生に使用するオーディオプレーヤーを選択します",
"globalMediaHotkeys": "グローバルメディアホットキー",
"hotkey_globalSearch": "グローバル検索",
"gaplessAudio_description": "mpv用にギャップレス再生を設定します",
"remoteUsername_description": "リモートコントロール サーバーのユーザ名を設定します。 ユーザー名とパスワードの両方が空の場合、認証は無効になります",
"disableAutomaticUpdates": "自動更新を無効化",
"exitToTray_description": "アプリケーション終了ボタンが押された際、システムトレイに格納します",
"followLyric_description": "現在の再生位置に歌詞をスクロールします",
"hotkey_favoritePreviousSong": "$t(common.previousSong) をお気に入り",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "歌詞オフセット (ミリ秒)",
"discordUpdateInterval_description": "更新間隔 (秒単位, 最小15秒)",
"fontType_optionCustom": "カスタムフォント",
"themeDark_description": "アプリケーションに使用するダークテーマを設定します",
"audioExclusiveMode": "オーディオ排他モード",
"remotePassword": "リモートコントロール サーバー パスワード",
"lyricFetchProvider": "歌詞取得先",
"language_description": "アプリケーションの言語を設定します ($t(common.restartRequired))",
"playbackStyle_optionCrossFade": "クロスフェード",
"hotkey_rate3": "3つ星で評価",
"font": "フォント",
"mpvExtraParameters": "mpv パラメーター",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "アプリケーションに使用するライトテーマを設定します",
"hotkey_toggleFullScreenPlayer": "フルスクリーンプレーヤーの切り替え",
"hotkey_localSearch": "ページ内検索",
"hotkey_toggleQueue": "キューの切り替え",
"zoom_description": "アプリケーションのズーム率を設定します",
"remotePassword_description": "リモートコントロール サーバーのパスワードを設定します。 ログイン情報はデフォルトでセキュアな通信がされないため、個人情報と関係ないランダムなパスワードを利用してください",
"hotkey_rate5": "5つ星で評価",
"hotkey_playbackPrevious": "前のトラック",
"showSkipButtons_description": "プレーヤーバーのスキップボタンを表示/非表示にします",
"crossfadeDuration_description": "クロスフェード効果の時間を設定します",
"language": "言語",
"playbackStyle": "再生スタイル",
"hotkey_toggleShuffle": "シャッフルの切り替え",
"theme": "テーマ",
"playbackStyle_description": "オーディオプレーヤーに使用する再生スタイルを選択します",
"discordRichPresence_description": "{{discord}} のRich Presenceに再生ステータスを表示するようにします。画像キー: {{icon}}, {{playing}}, {{paused}} ",
"mpvExecutablePath": "mpv 実行ファイルパス",
"audioDevice": "オーディオデバイス",
"hotkey_rate2": "2つ星で評価",
"playButtonBehavior_description": "キューに曲を追加するときの再生ボタンのデフォルトの動作を設定します",
"minimumScrobblePercentage_description": "Scrobbleされるために必要な最短の再生時間(%)",
"exitToTray": "終了時にシステムトレイに格納",
"hotkey_rate4": "4つ星で評価",
"enableRemote": "リモートコントロール サーバーを有効化",
"showSkipButton_description": "プレーヤーバーのスキップボタンを表示/非表示にします",
"savePlayQueue": "再生キューを保存",
"minimumScrobbleSeconds_description": "Scrobbleされるために必要な最短の再生時間(秒)",
"skipPlaylistPage_description": "プレイリストに移動するときに、デフォルトページではなくプレイリストの曲リストページに移動します",
"fontType_description": "組み込みフォントの場合、Feishin が提供するフォントから1つを選択します。 システムフォントの場合、OSにインストール済みの任意のフォントを選択できます。 カスタムフォントの場合、フォントファイルを自身で選択できます",
"playButtonBehavior": "再生ボタンの動作",
"volumeWheelStep": "音量ホイールステップ",
"sidebarPlaylistList_description": "サイドバーでプレイリストのリストを表示/非表示にします",
"accentColor": "アクセントカラー",
"sidePlayQueueStyle_description": "サイド再生キューのスタイルを設定します",
"accentColor_description": "アプリケーションが利用するアクセントカラーを設定します",
"replayGainMode": "{{ReplayGain}} モード",
"playbackStyle_optionNormal": "通常",
"windowBarStyle": "ウィンドウバースタイル",
"floatingQueueArea": "フローティング再生キューエリアの表示",
"replayGainFallback_description": "ファイルに{{ReplayGain}}タグがない場合に適用するゲイン (dB単位)",
"replayGainPreamp_description": "{{ReplayGain}}の値に適用されるプリアンプゲインを調整します",
"hotkey_toggleRepeat": "リピートの切り替え",
"lyricOffset_description": "歌詞のオフセットをミリ秒単位で指定します",
"sidebarConfiguration_description": "サイドバーに表示されるアイテムと並び順を選択します",
"fontType": "フォントタイプ",
"remotePort": "リモートコントロール サーバー ポート",
"applicationHotkeys": "アプリケーションホットキー",
"hotkey_playbackNext": "次のトラック",
"useSystemTheme_description": "システム設定のライト/ダークテーマに従います",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "様々なインターネットソースから歌詞を取得します",
"lyricFetchProvider_description": "歌詞を取得するサービスを選択します。サービスの並び順に検索されます",
"globalMediaHotkeys_description": "システムのメディアホットキーでの再生コントロールを有効/無効化します",
"customFontPath": "カスタムフォントパス",
"followLyric": "歌詞を再生位置に追従",
"crossfadeDuration": "クロスフェードの長さ",
"discordIdleStatus": "アイドル状態でRich Presenceステータスを表示",
"sidePlayQueueStyle_optionDetached": "分離",
"audioPlayer": "オーディオプレーヤー",
"hotkey_zoomOut": "縮小",
"hotkey_unfavoriteCurrentSong": "$t(common.currentSong) をお気に入り解除",
"hotkey_rate0": "評価をクリア",
"discordApplicationId": "{{discord}} アプリケーション ID",
"applicationHotkeys_description": "アプリケーションのホットキーを設定します。 チェックボックスを切り替えて、グローバルホットキー(デスクトップのみ)として設定できます",
"floatingQueueArea_description": "画面右側に、再生キューをフローティング表示するためのホバーアイコンが表示されます",
"hotkey_volumeMute": "音量をミュート",
"hotkey_toggleCurrentSongFavorite": "$t(common.currentSong) をお気に入り登録/解除",
"remoteUsername": "リモートコントロール サーバー ユーザー名",
"hotkey_browserBack": "ブラウザ 戻る",
"showSkipButton": "スキップボタンを表示",
"sidebarPlaylistList": "サイドバー プレイリスト リスト",
"minimizeToTray": "最小化時にシステムトレイに格納",
"skipPlaylistPage": "プレイリストページをスキップ",
"themeDark": "テーマ (ダーク)",
"sidebarCollapsedNavigation": "サイドバー (折りたたみ) ナビゲーション",
"customFontPath_description": "アプリケーションが使用するカスタムフォントへのパスを設定します",
"gaplessAudio_optionWeak": "弱 (推奨)",
"minimumScrobbleSeconds": "最小 Scrobble 時間 (秒)",
"hotkey_playbackStop": "停止",
"windowBarStyle_description": "ウィンドウバーのスタイルを選択します",
"discordRichPresence": "{{discord}} Rich Presence ステータス表示",
"font_description": "アプリケーションに使用するフォントを設定します",
"savePlayQueue_description": "アプリケーション終了時に再生キューを保存し、アプリケーション開始時に復元します",
"useSystemTheme": "システムテーマを使用"
},
"action": {
"editPlaylist": "$t(entity.playlist_one) を編集",
"goToPage": "ページへ移動",
"moveToTop": "先頭に移動",
"clearQueue": "キューをクリア",
"addToFavorites": "$t(entity.favorite_other) に追加",
"addToPlaylist": "$t(entity.playlist_one) に追加",
"createPlaylist": "$t(entity.playlist_one) を作成",
"removeFromPlaylist": "$t(entity.playlist_one) から削除",
"viewPlaylists": "$t(entity.playlist_other) を表示",
"refresh": "$t(common.refresh)",
"deletePlaylist": "$t(entity.playlist_one) を削除",
"removeFromQueue": "キューから削除",
"deselectAll": "すべて選択解除",
"moveToBottom": "末尾に移動",
"setRating": "評価",
"toggleSmartPlaylistEditor": "$t(entity.smartPlaylist) エディタの切り替え",
"removeFromFavorites": "$t(entity.favorite_other) から削除"
},
"common": {
"backward": "戻る",
"increase": "増加",
"rating": "評価",
"bpm": "BPM",
"refresh": "再読み込み",
"unknown": "不明",
"areYouSure": "実行しますか?",
"edit": "編集",
"favorite": "お気に入り",
"left": "左側",
"save": "保存",
"right": "右側",
"currentSong": "現在の $t(entity.track_one)",
"collapse": "折りたたみ",
"trackNumber": "トラック",
"descending": "降順",
"add": "追加",
"gap": "ギャップ",
"ascending": "昇順",
"dismiss": "無視",
"year": "年",
"manage": "管理",
"limit": "制限",
"minimize": "最小化",
"modified": "変更済み",
"duration": "長さ",
"name": "名前",
"maximize": "最大化",
"decrease": "減少",
"ok": "OK",
"description": "説明",
"configure": "設定",
"path": "パス",
"center": "中央",
"no": "いいえ",
"owner": "所有者",
"enable": "有効",
"clear": "クリア",
"forward": "進む",
"delete": "削除",
"cancel": "キャンセル",
"forceRestartRequired": "変更を適用するために再起動が必要です… 通知を閉じると再起動します",
"setting": "設定",
"version": "バージョン",
"title": "タイトル",
"filter_other": "フィルタ",
"filters": "フィルタ",
"create": "作成",
"bitrate": "ビットレート",
"saveAndReplace": "保存して変更",
"action_other": "アクション",
"playerMustBePaused": "プレイヤーを一時停止する必要があります",
"confirm": "確認",
"resetToDefault": "デフォルトにリセット",
"home": "ホーム",
"comingSoon": "近日利用可能になる予定です…",
"reset": "リセット",
"channel_other": "チャンネル",
"disable": "無効",
"sortOrder": "順序",
"none": "なし",
"menu": "メニュー",
"restartRequired": "再起動が必要です",
"previousSong": "前の $t(entity.track_one)",
"noResultsFromQuery": "条件にマッチするものがありません",
"quit": "終了",
"expand": "展開",
"search": "検索",
"saveAs": "名前を付けて保存",
"disc": "ディスク",
"yes": "はい",
"random": "ランダム",
"size": "サイズ",
"biography": "バイオグラフィー",
"note": "ノート"
},
"table": {
"config": {
"view": {
"card": "カード",
"table": "テーブル",
"poster": "ポスター"
},
"general": {
"displayType": "表示タイプ",
"gap": "$t(common.gap)",
"tableColumns": "テーブル カラム",
"autoFitColumns": "カラム長を自動調整",
"size": "$t(common.size)"
},
"label": {
"releaseDate": "リリース日時",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (結合)",
"dateAdded": "追加された日時",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "最後に再生",
"trackNumber": "トラック番号",
"rowIndex": "行インデックス",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "再生回数",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "ディスク番号",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)"
}
},
"column": {
"comment": "コメント",
"album": "アルバム",
"rating": "評価",
"favorite": "お気に入り",
"playCount": "再生回数",
"albumCount": "$t(entity.album_other)",
"releaseYear": "年",
"lastPlayed": "最後に再生",
"biography": "バイオグラフィー",
"releaseDate": "リリース日時",
"bitrate": "ビットレート",
"title": "タイトル",
"bpm": "BPM",
"dateAdded": "追加された日時",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "トラック",
"genre": "$t(entity.genre_one)",
"albumArtist": "アルバムアーティスト",
"path": "パス",
"discNumber": "ディスク",
"channels": "$t(common.channel_other)"
}
},
"error": {
"remotePortWarning": "新たなポート設定を適用するためサーバーを再起動してください",
"systemFontError": "システムフォントを取得する際にエラーが発生しました",
"playbackError": "メディアの再生開始時にエラーが発生しました",
"remotePortError": "リモートサーバーのポート設定時にエラーが発生しました",
"serverRequired": "サーバーが必要です",
"authenticationFailed": "認証に失敗しました",
"apiRouteError": "リクエストをルーティングできません",
"genericError": "エラーが発生しました",
"credentialsRequired": "ログイン情報が必要です",
"sessionExpiredError": "セッションの有効期限が切れました",
"remoteEnableError": "リモートサーバーを $t(common.enable) にする際にエラーが発生しました",
"localFontAccessDenied": "ローカルフォントへのアクセスが拒否されました",
"serverNotSelectedError": "サーバーが選択されていません",
"remoteDisableError": "リモートサーバーを $t(common.disable) にする際にエラーが発生しました",
"mpvRequired": "MPVが必要です",
"audioDeviceFetchError": "オーディオデバイスの取得時にエラーが発生しました",
"invalidServer": "無効なサーバー",
"loginRateError": "ログイン試行回数が多すぎます、数秒後に再試行してください",
"endpointNotImplementedError": "{{serverType}} にはエンドポイント {{endpoint}} が実装されていません"
},
"filter": {
"mostPlayed": "最も多く再生",
"playCount": "再生回数",
"isCompilation": "コンピレーションアルバム",
"recentlyPlayed": "最近の再生",
"isRated": "評価済み",
"title": "タイトル",
"rating": "評価",
"search": "検索",
"bitrate": "ビットレート",
"recentlyAdded": "最近の追加",
"note": "ノート",
"name": "名前",
"dateAdded": "追加された日時",
"releaseDate": "リリース日時",
"communityRating": "コミュニティの評価",
"path": "パス",
"favorited": "お気に入り",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "最近再生済み",
"isFavorited": "お気に入り済み",
"bpm": "BPM",
"releaseYear": "リリース年",
"disc": "ディスク",
"biography": "バイオグラフィー",
"songCount": "曲数",
"artist": "$t(entity.artist_one)",
"duration": "長さ",
"random": "ランダム",
"lastPlayed": "最後に再生",
"toYear": "年まで",
"fromYear": "年から",
"criticRating": "批評家の評価",
"trackNumber": "トラック",
"comment": "コメント",
"recentlyUpdated": "新規更新",
"isPublic": "共有済み",
"channels": "$t(common.channel_other)",
"owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)",
"albumCount": "$t(entity.album_other) 個",
"id": "id",
"album": "$t(entity.album_one)"
},
"page": {
"sidebar": {
"nowPlaying": "再生中",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "歌詞のマッチを表示",
"dynamicBackground": "ダイナミック背景",
"synchronized": "同期",
"followCurrentLyric": "歌詞を再生位置に追従",
"opacity": "非透過率",
"lyricSize": "歌詞のサイズ",
"showLyricProvider": "歌詞の提供元を表示",
"unsynchronized": "非同期",
"lyricAlignment": "歌詞の位置",
"useImageAspectRatio": "画像のアスペクト比を使用する",
"lyricGap": "歌詞の間隔"
},
"upNext": "次へ",
"lyrics": "歌詞",
"related": "関連"
},
"appMenu": {
"selectServer": "サーバを選択",
"version": "バージョン {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "サーバーの管理",
"expandSidebar": "サイドバーを展開",
"collapseSidebar": "サイドバーを折りたたむ",
"openBrowserDevtools": "ブラウザの開発者ツールを開く",
"quit": "$t(common.quit)",
"goBack": "戻る",
"goForward": "進む"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} 個 選択",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"home": {
"mostPlayed": "最も多く再生",
"newlyAdded": "新規追加リリース",
"title": "$t(common.home)",
"explore": "ライブラリから検索",
"recentlyPlayed": "最近の再生"
},
"albumDetail": {
"moreFromArtist": "$t(entity.artist_one) の他の項目",
"moreFromGeneric": "{{item}} の他の項目"
},
"setting": {
"playbackTab": "再生",
"generalTab": "一般",
"hotkeysTab": "ホットキー",
"windowTab": "ウィンドウ"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "サーバーコマンド",
"goToPage": "ページへ移動",
"searchFor": "{{query}} を検索"
},
"title": "コマンド"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"form": {
"deletePlaylist": {
"title": "$t(entity.playlist_one) を削除",
"success": "$t(entity.playlist_one) が削除されました",
"input_confirm": "確認のため $t(entity.playlist_one) の名前を入力してください"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "$t(entity.playlist_one) を作成",
"input_public": "公開",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) を作成しました",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "サーバーを追加",
"input_username": "ユーザー名",
"input_url": "URL",
"input_password": "パスワード",
"input_legacyAuthentication": "レガシー認証を有効化",
"input_name": "サーバー名",
"success": "サーバーが追加されました",
"input_savePassword": "パスワードを保存",
"ignoreSsl": "SSLを無視 ($t(common.restartRequired))",
"ignoreCors": "CORSを無視 ($t(common.restartRequired))",
"error_savePassword": "パスワードを保存する際にエラーが発生しました"
},
"addToPlaylist": {
"success": "{{message}} $t(entity.song_other) を {{numOfPlaylists}} $t(entity.playlist_other) に追加しました",
"title": "$t(entity.playlist_one) に追加",
"input_skipDuplicates": "重複をスキップ",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "サーバーをアップデート",
"success": "サーバーがアップデートされました"
},
"queryEditor": {
"input_optionMatchAll": "すべて一致",
"input_optionMatchAny": "一部一致"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "歌詞検索"
},
"editPlaylist": {
"title": "$t(entity.playlist_one) を編集"
}
},
"entity": {
"genre_other": "ジャンル",
"playlistWithCount_other": "{{count}} プレイリスト",
"playlist_other": "プレイリスト",
"artist_other": "アーティスト",
"folderWithCount_other": "{{count}} フォルダ",
"albumArtist_other": "アルバムアーティスト",
"track_other": "トラック",
"albumArtistCount_other": "{{count}} アルバムアーティスト",
"albumWithCount_other": "{{count}} アルバム",
"favorite_other": "お気に入り",
"artistWithCount_other": "{{count}} アーティスト",
"folder_other": "フォルダ",
"smartPlaylist": "スマート $t(entity.playlist_one)",
"album_other": "アルバム",
"genreWithCount_other": "{{count}} ジャンル",
"trackWithCount_other": "{{count}} トラック"
}
}
+1
View File
@@ -0,0 +1 @@
{}
+232
View File
@@ -0,0 +1,232 @@
{
"action": {
"editPlaylist": "pas $t(entity.playlist_one) aan",
"goToPage": "ga naar pagina",
"moveToTop": "verplaats naar top",
"addToFavorites": "toevoegen aan $t(entity.favorite_other)",
"addToPlaylist": "toevoegen aan $t(entity.playlist_one)",
"createPlaylist": "maak $t(entity.playlist_one)",
"removeFromPlaylist": "verwijder van $t(entity.playlist_one)",
"viewPlaylists": "bekijk $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "verwijder $t(entity.playlist_one)",
"removeFromQueue": "verwijder van lijst",
"deselectAll": "deselecteer alles",
"moveToBottom": "verplaats naar bodem",
"setRating": "selecteer rating",
"toggleSmartPlaylistEditor": "editor $t(entity.smartPlaylist) schakelen",
"removeFromFavorites": "verwijder van $t(entity.favorite_other)",
"clearQueue": "lijst leegmaken"
},
"common": {
"backward": "achteruit",
"increase": "verhogen",
"rating": "rating",
"bpm": "bpm",
"areYouSure": "weet je het zeker?",
"edit": "aanpassen",
"favorite": "favoriet",
"left": "links",
"currentSong": "huidig $t(entity.track_one)",
"collapse": "samenvouwen",
"descending": "aflopend",
"add": "toevoegen",
"gap": "gat",
"ascending": "oplopend",
"dismiss": "negeren",
"manage": "beheren",
"limit": "limiet",
"minimize": "minimaliseren",
"modified": "aangepast",
"duration": "duur",
"name": "naam",
"maximize": "maximaliseren",
"decrease": "verminder",
"ok": "ok",
"description": "beschrijving",
"configure": "configureren",
"path": "pad",
"center": "centreren",
"no": "nee",
"owner": "eigenaar",
"enable": "activeren",
"clear": "opschonen",
"forward": "vooruit",
"delete": "verwijder",
"cancel": "annuleer",
"forceRestartRequired": "herstart om aanpassingen toe te passen... wanneer de notificatie gesloten wordt zal de applicatie herstarten",
"filter_one": "filter",
"filter_other": "filters",
"filters": "filters",
"create": "aanmaken",
"bitrate": "bitrate",
"action_one": "actie",
"action_other": "acties",
"playerMustBePaused": "player moet gepauzeerd zijn",
"confirm": "bevestig",
"home": "home",
"comingSoon": "komt binnenkort…",
"channel_one": "kanaal",
"channel_other": "kanalen",
"disable": "deactiveren",
"none": "geen",
"menu": "menu",
"previousSong": "vorige $t(entity.track_one)",
"noResultsFromQuery": "de zoekopdracht leverde geen resultaten op",
"quit": "sluiten",
"expand": "vergroten",
"disc": "disk",
"random": "willekeurig",
"biography": "biografie",
"note": "Opmerking",
"refresh": "verversen",
"unknown": "onbekend",
"save": "opslaan",
"right": "rechts",
"trackNumber": "track",
"year": "jaar",
"version": "versie",
"title": "titel",
"saveAndReplace": "opslaan en vervangen",
"resetToDefault": "herstellen naar standaard",
"reset": "terugzetten",
"sortOrder": "volgorde",
"restartRequired": "herstart is nodig",
"search": "zoeken",
"saveAs": "opslaan als",
"yes": "ja",
"size": "grootte"
},
"filter": {
"rating": "rating",
"communityRating": "community rating",
"criticRating": "criticus rating",
"mostPlayed": "meest gespeeld",
"comment": "commentaar",
"playCount": "aantal keer afgespeeld",
"recentlyUpdated": "recentelijk geüpdate",
"channels": "$t(common.channel_other)",
"isCompilation": "is compilatie",
"recentlyPlayed": "recentelijk afgespeeld",
"isRated": "is rated",
"owner": "$t(common.owner)",
"bitrate": "bitrate",
"genre": "$t(entity.genre_one)",
"recentlyAdded": "recentelijk toegevoegd",
"note": "notitie",
"name": "naam",
"dateAdded": "datum toegevoegd",
"albumCount": "$t(entity.album_other) totaal",
"path": "pad",
"favorited": "favoriet",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "is recentelijk afgespeeld",
"isFavorited": "is favoriet",
"bpm": "bpm",
"id": "id",
"disc": "disk",
"biography": "biografie",
"artist": "$t(entity.artist_one)",
"duration": "duratie",
"isPublic": "is publiek",
"random": "willekeurig",
"lastPlayed": "laatst gespeeld",
"fromYear": "van jaar",
"album": "$t(entity.album_one)",
"title": "titel",
"search": "zoeken",
"releaseDate": "releasedatum",
"releaseYear": "release jaar",
"songCount": "aantal nummers",
"toYear": "tot jaar",
"trackNumber": "track"
},
"page": {
"contextMenu": {
"setRating": "$t(action.setRating)"
}
},
"error": {
"remotePortWarning": "herstart de server om de nieuwe poort in te stellen",
"systemFontError": "er is iets fout gegaan tijdens het verkrijgen van systeem fonts",
"playbackError": "er is iets fout gegaan bij het afspelen van de media",
"endpointNotImplementedError": "endpoint {{endpoint}} is niet geïmplementeerd voor {{serverType}}",
"remotePortError": "er is iets fout gegaan tijdens het selecteren van de remote server",
"serverRequired": "server vereist",
"authenticationFailed": "authenticatie mislukt",
"apiRouteError": "verzoek kan niet doorgestuurd worden",
"genericError": "er is iets fout gegaan",
"credentialsRequired": "inloggegevens vereist",
"sessionExpiredError": "jouw sessie is verlopen",
"remoteEnableError": "er is iets fout gegaan tijdens het $t(common.enable) van de remote server",
"localFontAccessDenied": "toegang geweigerd tot lokale fonts",
"serverNotSelectedError": "geen server geselecteerd",
"remoteDisableError": "er is iets fout gegaan tijdens het $t(common.disable) van de remote server",
"mpvRequired": "MPV vereist",
"audioDeviceFetchError": "er is iets mis gegaan met het ophalen van de audioapparaten",
"invalidServer": "ongeldige server",
"loginRateError": "te veel login pogingen, probeer het opnieuw in een paar seconde"
},
"entity": {
"genre_one": "genre",
"genre_other": "genres",
"playlistWithCount_one": "{{count}} afspeellijst",
"playlistWithCount_other": "{{count}} afspeellijsten",
"playlist_one": "afspeellijst",
"playlist_other": "afspeellijsten",
"artist_one": "artiest",
"artist_other": "artiesten",
"folderWithCount_one": "{{count}} folder",
"folderWithCount_other": "{{count}} folders",
"albumArtist_one": "album artiest",
"albumArtist_other": "album artiesten",
"track_one": "track",
"track_other": "tracks",
"albumArtistCount_one": "{{count}} album artiest",
"albumArtistCount_other": "{{count}} album artiesten",
"albumWithCount_one": "{{count}} album",
"albumWithCount_other": "{{count}} albums",
"favorite_one": "favoriet",
"favorite_other": "favorieten",
"artistWithCount_one": "{{count}} artiest",
"artistWithCount_other": "{{count}} artiesten",
"folder_one": "folder",
"folder_other": "folders",
"smartPlaylist": "smart $t(entity.playlist_one)",
"album_one": "album",
"album_other": "albums",
"genreWithCount_one": "{{count}} genre",
"genreWithCount_other": "{{count}} genres",
"trackWithCount_one": "{{count}} track",
"trackWithCount_other": "{{count}} tracks"
},
"table": {
"column": {
"rating": "rating"
},
"config": {
"label": {
"rating": "$t(common.rating)"
}
}
},
"setting": {
"hotkey_rate5": "rating 5 sterren",
"hotkey_rate4": "rating 4 sterren"
},
"form": {
"addServer": {
"title": "server toevoegen",
"input_username": "gebruikersnaam",
"input_url": "url",
"input_password": "wachtwoord",
"input_legacyAuthentication": "activeer legacy authenticatie",
"input_name": "server naam",
"success": "server met succes toegevoegd",
"input_savePassword": "wachtwoord opslaan",
"ignoreSsl": "negeer ssl $t(common.restartRequired)",
"ignoreCors": "negeer cors $t(common.restartRequired)",
"error_savePassword": "er is iets mis gegaan met het opslaan van het wachtwoord"
}
}
}
+632
View File
@@ -0,0 +1,632 @@
{
"action": {
"editPlaylist": "edytuj $t(entity.playlist_one)",
"goToPage": "idź do strony",
"clearQueue": "wyczyść kolejkę",
"addToFavorites": "dodaj do $t(entity.favorite_other)",
"removeFromPlaylist": "usuń z $t(entity.playlist_one)",
"viewPlaylists": "zobacz $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"removeFromQueue": "usuń z kolejki",
"deselectAll": "odznacz wszystko",
"toggleSmartPlaylistEditor": "przełącz edytor $t(entity.smartPlaylist)",
"removeFromFavorites": "usuń z $t(entity.favorite_other)",
"moveToTop": "przesuń na górę",
"addToPlaylist": "dodaj do $t(entity.playlist_one)",
"createPlaylist": "utwórz $t(entity.playlist_one)",
"deletePlaylist": "usuń $t(entity.playlist_one)",
"moveToBottom": "przesuń na dół",
"setRating": "oceń"
},
"common": {
"increase": "zwiększ",
"rating": "ocena",
"bpm": "bpm",
"refresh": "odśwież",
"unknown": "nieznany",
"areYouSure": "czy jesteś pewien?",
"edit": "edytuj",
"favorite": "ulubiony",
"save": "zapisz",
"right": "prawo",
"trackNumber": "utwór",
"descending": "malejąco",
"add": "dodaj",
"ascending": "rosnąco",
"dismiss": "anuluj",
"year": "rok",
"limit": "limit",
"minimize": "zminimalizuj",
"modified": "zmodyfikowany",
"duration": "długość",
"name": "nazwa",
"maximize": "zmaksymalizuj",
"ok": "ok",
"description": "opis",
"configure": "konfiguruj",
"no": "nie",
"owner": "właściciel",
"enable": "włącz",
"clear": "wyczyść",
"forward": "do przodu",
"delete": "usuń",
"cancel": "cofnij",
"forceRestartRequired": "zrestartuj aby zastosować zmiany... zamknij powiadomienie aby zrestartować",
"setting": "ustawienia",
"version": "wersja",
"title": "tytuł",
"filter_one": "filtr",
"filter_few": "filtry",
"filter_many": "filtrów",
"filters": "filtry",
"create": "stwórz",
"bitrate": "bitrate",
"saveAndReplace": "zapisz i zamień",
"action_one": "akcja",
"action_few": "akcje",
"action_many": "akcji",
"playerMustBePaused": "odtwarzacz musi być zapauzowany",
"confirm": "potwierdź",
"resetToDefault": "przywróć do domyślnych",
"home": "główna",
"comingSoon": "już wkrótce…",
"reset": "zresetuj",
"channel_one": "kanał",
"channel_few": "kanałów",
"channel_many": "kanałów",
"disable": "wyłącz",
"sortOrder": "kolejność",
"none": "żaden",
"menu": "menu",
"restartRequired": "wymagany restart",
"previousSong": "poprzedni $t(entity.track_one)",
"noResultsFromQuery": "kolejka zwróciła brak wyników",
"quit": "wyjdź",
"expand": "rozszerz",
"search": "szukaj",
"saveAs": "zapisz jako",
"disc": "płyta",
"yes": "tak",
"random": "losowy",
"size": "wielkość",
"biography": "biografia",
"backward": "wstecz",
"left": "lewo",
"currentSong": "obecnie $t(entity.track_one)",
"collapse": "zwiń",
"gap": "luka",
"manage": "zarządzaj",
"decrease": "obniż",
"path": "ścieżka",
"center": "środkowy",
"note": "notatka"
},
"entity": {
"genre_one": "gatunek",
"genre_few": "gatunków",
"genre_many": "gatunków",
"artist_one": "artysta",
"artist_few": "artystów",
"artist_many": "artystów",
"albumArtist_one": "artysta albumu",
"albumArtist_few": "artysta albumów",
"albumArtist_many": "artysta albumów",
"albumWithCount_one": "{{count}} album",
"albumWithCount_few": "{{count}} albumów",
"albumWithCount_many": "{{count}} albumów",
"favorite_one": "ulubiony",
"favorite_few": "ulubione",
"favorite_many": "ulubione",
"artistWithCount_one": "{{count}} artysta",
"artistWithCount_few": "{{count}} artystów",
"artistWithCount_many": "{{count}} artystów",
"folder_one": "katalog",
"folder_few": "katalogi",
"folder_many": "katalogów",
"album_one": "album",
"album_few": "albumów",
"album_many": "albumów",
"playlistWithCount_one": "{{count}} lista odtwarzania",
"playlistWithCount_few": "{{count}} listy odtwarzania",
"playlistWithCount_many": "{{count}} list odtwarzania",
"playlist_one": "lista odtwarzania",
"playlist_few": "listy odtwarzania",
"playlist_many": "list odtwarzania",
"folderWithCount_one": "{{count}} katalog",
"folderWithCount_few": "{{count}} katalogi",
"folderWithCount_many": "{{count}} katalogów",
"track_one": "utwór",
"track_few": "utwory",
"track_many": "utworów",
"albumArtistCount_one": "{{count}} wykonawca albumu",
"albumArtistCount_few": "{{count}} wykonawców albumu",
"albumArtistCount_many": "{{count}} wykonawców albumu",
"smartPlaylist": "inteligentna $t(entity.playlist_one)",
"genreWithCount_one": "{{count}} gatunek",
"genreWithCount_few": "{{count}} gatunki",
"genreWithCount_many": "{{count}} gatunków",
"trackWithCount_one": "{{count}} utwór",
"trackWithCount_few": "{{count}} utwory",
"trackWithCount_many": "{{count}} utworów"
},
"error": {
"remotePortWarning": "uruchom ponownie serwer aby używać nowego portu",
"systemFontError": "wystąpił błąd podczas próby pobrania czcionek systemowych",
"playbackError": "wystąpił błąd podczas próby odtwarzania mediów",
"endpointNotImplementedError": "punkt końcowy {{endpoint}} nie został zaimplementowany dla {{serverType}}",
"remotePortError": "wystąpił problem podczas ustawiania portu dla zdalnego serwera",
"serverRequired": "wymagany serwer",
"authenticationFailed": "uwierzytelnianie nie powiodło się",
"apiRouteError": "nie można wykonać żądania",
"genericError": "wystąpił błąd",
"credentialsRequired": "wymagane poświadczenia",
"sessionExpiredError": "twoja sesja wygasła",
"remoteEnableError": "wystąpił błąd podczas próby $t(common.enable) zdalnego serwera",
"localFontAccessDenied": "dostęp do lokalnych czcionek odrzucony",
"serverNotSelectedError": "nie zaznaczono serwera",
"remoteDisableError": "wystąpił błąd podczas próby $t(common.disable) zdalnego serwera",
"mpvRequired": "wymagane MPV",
"audioDeviceFetchError": "wystąpił błąd podczas próby znalezienia urządzeń dźwiękowych",
"invalidServer": "nieprawidłowy serwer",
"loginRateError": "zbyt dużo prób logowania, poczekaj chwilę i spróbuj ponownie"
},
"filter": {
"mostPlayed": "najczęściej odtwarzane",
"playCount": "liczba odtworzeń",
"isCompilation": "jest kompilacją",
"recentlyPlayed": "ostatnio odtwarzane",
"isRated": "jest ocenione",
"title": "tytuł",
"rating": "ocena",
"search": "wyszukaj",
"bitrate": "bitrate",
"recentlyAdded": "ostatnio dodane",
"note": "notatka",
"name": "nazwa",
"dateAdded": "dodano datę",
"releaseDate": "data premiery",
"communityRating": "ocena społeczności",
"path": "ścieżka",
"favorited": "ulubione",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "było niedawno odtwarzane",
"isFavorited": "jest ulubione",
"bpm": "bpm",
"releaseYear": "rok wydania",
"disc": "płyta",
"biography": "biografia",
"songCount": "liczba utworów",
"artist": "$t(entity.artist_one)",
"duration": "długość",
"random": "losowy",
"lastPlayed": "ostatnio odtwarzane",
"toYear": "do roku",
"fromYear": "od roku",
"criticRating": "ocena krytyków",
"trackNumber": "utwór",
"comment": "komentarz",
"recentlyUpdated": "ostatnio aktualizowane",
"channels": "$t(common.channel_other)",
"owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)",
"albumCount": "liczba $t(entity.album_other)",
"id": "id",
"isPublic": "jest publiczny",
"album": "$t(entity.album_one)"
},
"form": {
"deletePlaylist": {
"title": "usuń $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) usunięto pomyślnie",
"input_confirm": "wpisz nazwę $t(entity.playlist_one) aby potwierdzić"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "utwórz $t(entity.playlist_one)",
"input_public": "publiczny",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) utworzono pomyślnie",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "dodaj serwer",
"input_username": "nazwa użytkownika",
"input_url": "adres",
"input_password": "hasło",
"input_legacyAuthentication": "umożliw starsze uwierzytelnianie",
"input_name": "nazwa serwera",
"success": "serwer dodany pomyślnie",
"input_savePassword": "zapisz hasło",
"ignoreSsl": "zignoruj ssl ($t(common.restartRequired))",
"ignoreCors": "zignoruj cors ($t(common.restartRequired))",
"error_savePassword": "wystąpił błąd podczas próby zapisania hasła"
},
"addToPlaylist": {
"success": "dodano {{message}} $t(entity.song_other) do {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "dodano do $t(entity.playlist_one)",
"input_skipDuplicates": "pomiń duplikaty",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "uaktualnij serwer",
"success": "serwer zaaktualizowany pomyślnie"
},
"queryEditor": {
"input_optionMatchAll": "dopasuj wszystkie",
"input_optionMatchAny": "dopasuj dowolne"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "wyszukiwanie tekstów"
},
"editPlaylist": {
"title": "edytuj $t(entity.playlist_one)"
}
},
"page": {
"fullscreenPlayer": {
"config": {
"showLyricMatch": "pokaż dopasowanie tekstu",
"dynamicBackground": "dynamiczne tło",
"synchronized": "zsynchronizowane",
"followCurrentLyric": "podążaj za aktualnym tekstem",
"opacity": "przezroczystość",
"lyricSize": "rozmiar tekstu",
"showLyricProvider": "pokaż dostawce tekstu",
"unsynchronized": "niezsynchronizowane",
"lyricAlignment": "wyrównaj tekst",
"useImageAspectRatio": "użyj współczynnika proporcji obrazu",
"lyricGap": "odstępy tekstu"
},
"upNext": "następny",
"lyrics": "tekst",
"related": "powiązane"
},
"appMenu": {
"selectServer": "wybierz serwer",
"version": "wersja {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "zarządzaj serwerami",
"expandSidebar": "rozwiń pasek boczny",
"collapseSidebar": "zwiń pasek boczny",
"openBrowserDevtools": "otwórz narzędzia deweloperskie przeglądarki",
"quit": "$t(common.quit)",
"goBack": "do tyłu",
"goForward": "do przodu"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "zaznaczono {{count}}",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"albumDetail": {
"moreFromArtist": "więcej od $t(entity.artist_one)",
"moreFromGeneric": "więcej od {{item}}"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
},
"sidebar": {
"nowPlaying": "teraz odtwarzane",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"home": {
"mostPlayed": "najczęściej odtwarzane",
"newlyAdded": "niedawno dodane",
"title": "$t(common.home)",
"explore": "przeglądaj z biblioteki",
"recentlyPlayed": "ostatnio odtwarzane"
},
"setting": {
"playbackTab": "odtworzenia",
"generalTab": "ogólne",
"hotkeysTab": "skróty klawiszowe",
"windowTab": "okno"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "komendy serwera",
"goToPage": "przejdź do strony",
"searchFor": "wyszukaj {{query}}"
},
"title": "komendy"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
}
},
"player": {
"repeat_all": "powtarzaj wszystkie",
"stop": "stop",
"repeat": "powtarzaj jeden",
"queue_remove": "usuń zaznaczone",
"playRandom": "odtwarzaj losowe",
"skip": "pomiń",
"previous": "poprzedni",
"toggleFullscreenPlayer": "przełącz odtwarzacz pełnoekranowy",
"skip_back": "przeskocz do tyłu",
"favorite": "ulubione",
"next": "następny",
"shuffle": "losowa kolejność",
"playbackFetchNoResults": "nie znaleziono utworów",
"playbackFetchInProgress": "wczytywanie utworów…",
"addNext": "dodaj następny",
"playbackSpeed": "prędkość odtwarzania",
"playbackFetchCancel": "to potrwa chwilę... zamknij powiadomienie aby anulować",
"play": "odtwarzaj",
"repeat_off": "powtarzanie wyłączone",
"pause": "wstrzymaj",
"queue_clear": "wyczyść kolejke",
"muted": "wyciszone",
"unfavorite": "usuń z ulubionych",
"queue_moveToTop": "przesuń zaznaczone na dół",
"queue_moveToBottom": "przesuń zaznaczone na górę",
"shuffle_off": "losowa kolejność wyłączona",
"addLast": "dodaj na końcu",
"mute": "wycisz",
"skip_forward": "przeskocz do przodu"
},
"setting": {
"crossfadeStyle_description": "wybierz styl przenikania, który ma być używany do odtwarzania dźwięku",
"hotkey_skipBackward": "przeskocz do tyłu",
"audioDevice_description": "wybierz urządzenie dźwiękowe używane do odtwarzania (tylko odtwarzacz przeglądarkowy)",
"hotkey_playbackPause": "wstrzymaj",
"hotkey_volumeUp": "podgłoś",
"discordIdleStatus_description": "kiedy włączony, aktualizuje stan kiedy odtwarzacz jest bezczynny",
"lyricFetch": "pobierz teksty z internetu",
"enableRemote_description": "umożliwia serwerowi zdalnego sterowania zezwalanie innym urządzeniom na sterowanie aplikacją",
"fontType_optionSystem": "czcionka systemowa",
"hotkey_favoriteCurrentSong": "ulubiona $t(common.currentSong)",
"crossfadeStyle": "styl przenikania",
"hotkey_zoomIn": "przybliż",
"hotkey_browserForward": "przeglądarka w przód",
"audioExclusiveMode_description": "włącz wyłączny tryb wyjścia. W tym trybie, system zwykle jest zablokowany i może odtwarzać tylko poprzez mpv",
"discordUpdateInterval": "{{discord}} interwał aktualizacji obszernej obecności",
"fontType_optionBuiltIn": "wbudowana czcionka",
"hotkey_playbackPlayPause": "odtwarzaj / wstrzymaj",
"hotkey_rate1": "oceń na 1 gwiazdkę",
"hotkey_skipForward": "przeskocz do przodu",
"disableLibraryUpdateOnStartup": "wyłącz wyszukiwanie aktualizacji podczas uruchamiania aplikacji",
"discordApplicationId_description": "id dla aplikacji {{discord}} obszernie obecne (domyślnie {{defaultId}})",
"gaplessAudio": "dźwięk bez przerw",
"hotkey_playbackPlay": "odtwarzaj",
"hotkey_togglePreviousSongFavorite": "dodaj $t(common.previousSong) do ulubionych",
"hotkey_volumeDown": "przycisz",
"hotkey_unfavoritePreviousSong": "usuń $t(common.previousSong) z ulubionych",
"audioPlayer_description": "wybierz odtwarzacz dźwięku który ma być używany do odtwarzania",
"globalMediaHotkeys": "globalne skróty klawiszowe multimediów",
"hotkey_globalSearch": "globalne wyszukiwanie",
"gaplessAudio_description": "ustaw dźwięk bez przerw dla mpv",
"disableAutomaticUpdates": "wyłącz automatyczne aktualizacje",
"exitToTray_description": "zamknij aplikację do zasobnika systemowego",
"followLyric_description": "przewiń tekst do obecnego momentu",
"hotkey_favoritePreviousSong": "ulubiona $t(common.previousSong)",
"lyricOffset": "opóźnienie tekstu (ms)",
"discordUpdateInterval_description": "czas w sekundach pomiędzy każdą aktualizacją (minimalnie 15 sekund)",
"fontType_optionCustom": "czcionka niestandardowa",
"audioExclusiveMode": "wyłączny tryb audio",
"lyricFetchProvider": "dostawcy tekstów internetowych",
"language_description": "ustaw język dla aplikacji ($t(common.restartRequired))",
"hotkey_rate3": "oceń na 3 gwiazdki",
"font": "czcionka",
"hotkey_toggleFullScreenPlayer": "przełącz tryb pełnoekranowy",
"hotkey_localSearch": "wyszukiwanie na stronie",
"hotkey_toggleQueue": "przełącz kolejkę",
"hotkey_rate5": "oceń na 5 gwiazdek",
"hotkey_playbackPrevious": "poprzedni utwór",
"crossfadeDuration_description": "ustaw czas trwania efektu przenikania",
"language": "język",
"hotkey_toggleShuffle": "przełącz kolejność losową",
"discordRichPresence_description": "włącz status odtwarzania w {{discord}} obszernie obecny. Klucze obrazów to {{icon}}, {{playing}} i {{paused}}. ",
"audioDevice": "urządzenia dźwiękowe",
"hotkey_rate2": "oceń na 2 gwiazdki",
"exitToTray": "zamknij do zasobnika",
"hotkey_rate4": "oceń na 4 gwiazdki",
"enableRemote": "włącz zdalną kontrolę serwera",
"fontType_description": "wbudowana czcionka pozwala na wybranie czcionki dostarczonej z Feishin. systemowa czcionka pozwala na wybranie czcionki dostarczonej przez system operacyjny. niestandardowa czcionka pozwala na wybranie własnej czcionki",
"accentColor": "kolor akcentujący",
"accentColor_description": "ustaw kolor akcentujący dla aplikacji",
"floatingQueueArea": "pokaż pływającą kolejkę podczas najechania kursorem",
"hotkey_toggleRepeat": "przełącz powtarzanie",
"lyricOffset_description": "opóźnienie tekstu przez podaną liczbę milisekund",
"fontType": "typ czcionki",
"applicationHotkeys": "skróty klawiszowe aplikacji",
"hotkey_playbackNext": "następny utwór",
"lyricFetch_description": "pobierz teksty z rozmaitych źródeł internetowych",
"lyricFetchProvider_description": "wybierz dostawców internetowych dla tekstów. zapytania będą wykonywane według podanej kolejności",
"globalMediaHotkeys_description": "włącz lub wyłącz używanie systemowych skrótów klawiszowych do kontroli odtwarzania",
"customFontPath": "niestandardowa ścieżka czcionki",
"followLyric": "podążaj za tekstem",
"crossfadeDuration": "czas trwania przenikania",
"discordIdleStatus": "pokaż obszerne informacje w stanie bezczynności",
"audioPlayer": "odtwarzacz dźwięku",
"hotkey_zoomOut": "oddal",
"hotkey_unfavoriteCurrentSong": "usuń $t(common.currentSong) z ulubionych",
"hotkey_rate0": "wyczyść oceny",
"discordApplicationId": "id aplikacji {{discord}}",
"applicationHotkeys_description": "ustaw skróty klawiszowe aplikacji. przełącz pole wyboru aby ustawić skrót globalny (tylko komputery)",
"floatingQueueArea_description": "wyświetl ikonę najechania kursorem po prawej stronie ekranu, aby wyświetlić kolejkę odtwarzania",
"hotkey_volumeMute": "wycisz",
"hotkey_toggleCurrentSongFavorite": "dodaj $t(common.currentSong) do ulubionych",
"hotkey_browserBack": "przeglądarka wstecz",
"minimizeToTray": "zminimalizuj do zasobnika",
"customFontPath_description": "ustaw ścieżkę dla niestandardowych czcionek dla aplikacji",
"gaplessAudio_optionWeak": "słabe (rekomendowane)",
"hotkey_playbackStop": "zatrzymaj",
"discordRichPresence": "{{discord}} obszernie obecny",
"font_description": "ustaw czcionkę dla aplikacji",
"mpvExecutablePath_help": "jedna na linnię",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "minimalny czas trwania scrobble (procentowy)",
"mpvExecutablePath_description": "ustaw ścieżkę dla plików wykonywalnych mpv",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"minimizeToTray_description": "zminimalizuj aplikację do zasobnika systemowego",
"remotePassword": "hasło dla serwera zdalnej kontroli",
"playbackStyle_optionCrossFade": "przenikanie",
"mpvExtraParameters": "parametry mpv",
"playbackStyle": "styl odtwarzania",
"playbackStyle_description": "wybierz styl odtwarzania dla odtwarzacza dźwięku",
"mpvExecutablePath": "ścieżka pliku wykonywalnego mpv",
"playButtonBehavior_description": "ustaw domyślne zachowanie dla przycisku odtwarzania kiedy piosenka zostanie dodana do kolejki",
"minimumScrobblePercentage_description": "minimalny czas odtwarzania piosenki który musi upłynąć aby uznać ją za scrobble",
"minimumScrobbleSeconds_description": "minimalny czas odtwarzania piosenki w sekundach jaki musi upłynąć aby uznać ją za scrobbling",
"playButtonBehavior": "zachowanie przycisku odtwarzania",
"playbackStyle_optionNormal": "normalny",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"minimumScrobbleSeconds": "minimalne scrobble (w sekundach)",
"remotePort_description": "ustaw port dla serwera zdalnej kontroli",
"replayGainMode_description": "dostosuj wzmocnienie dźwięku zgodnie z wartościami {{ReplayGain}} przechowywanymi w metadanych do pliku",
"replayGainFallback": "rezerwowy {{ReplayGain}}",
"sidebarCollapsedNavigation_description": "pokaż lub ukryj nawigację na zwiniętym pasku bocznym",
"skipDuration": "czas trwania pominięcia",
"showSkipButtons": "pokaż przyciski pomijania",
"scrobble": "scrobbling",
"skipDuration_description": "ustaw czas pominięcia kiedy zostanie użyty przycisk pominięcia na pasku odtwarzania",
"replayGainClipping_description": "Zapobiegaj wzmocnieniu spowodowanemu przez {{ReplayGain}} na automatyczne obniżanie wzmocnienia",
"replayGainPreamp": "przedwzmacniacz {{ReplayGain}} (db)",
"sampleRate": "częstotliwość próbkowania",
"sidePlayQueueStyle_optionAttached": "przyłączony",
"sidebarConfiguration": "konfiguracja paska bocznego",
"sampleRate_description": "wybierz wyjściową częstotliwość próbkowania, która ma być używana, jeśli wybrana częstotliwość próbkowania różni się od częstotliwości bieżącego utworu",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainClipping": "wzmocnienie {{ReplayGain}}",
"scrobble_description": "odtwarzanie scrobble na serwerze multimediów",
"sidePlayQueueStyle": "boczny styl kolejki odtwarzania",
"remoteUsername_description": "ustaw nazwę użytkownika dla serwera zdalnej kontroli. Jeśli nazwa użytkownika i hasło są puste, autoryzacja będzie wyłączona",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"remotePassword_description": "ustawia hasło dla serwera zdalnego sterowania. Te poświadczenia są domyślnie przesyłane w sposób niezabezpieczony, dlatego należy użyć unikalnego hasła na którym ci nie zależy",
"showSkipButtons_description": "pokaż lub ukryj przyciski pomijania na pasku odtwarzacza",
"showSkipButton_description": "pokaż lub ukryj przyciski pomijania na pasku odtwarzacza",
"savePlayQueue": "zapisz kolejkę odtwarzania",
"sidebarPlaylistList_description": "pokaż lub ukryj listę odtwarzania na pasku bocznym",
"sidePlayQueueStyle_description": "ustaw boczny styl kolejki odtwarzania",
"replayGainMode": "tryb {{ReplayGain}}",
"replayGainFallback_description": "wzmocnienie w db do użycia w przypadku kiedy plik nie ma tagu {{ReplayGain}}",
"replayGainPreamp_description": "dostosuj wzmocnienie przedwzmacniacza zastosowane do wartości {{ReplayGain}}",
"sidebarConfiguration_description": "wybierz pozycje i ustaw je w kolejności w jakiej mają się pokazywać na pasku bocznym",
"remotePort": "port dla serwera zdalnej kontroli",
"sidePlayQueueStyle_optionDetached": "odłączony",
"remoteUsername": "nazwa użytkownika serwera zdalnej kontroli",
"showSkipButton": "pokaż przyciski pomijania",
"sidebarPlaylistList": "lista odtwarzania na pasku bocznym",
"sidebarCollapsedNavigation": "nawigacja na pasku bocznym (zwinięta)",
"savePlayQueue_description": "zapisz kolejkę odtwarzania kiedy aplikacja jest zamykana i wznów ją kiedy aplikacja jest otwierana",
"volumeWheelStep_description": "wartość zmiany glośności w czasie używania pokrętła myszy na pasku głośności",
"theme_description": "ustaw motyw dla aplikacji",
"themeLight": "motyw (jasny)",
"zoom": "procentowe przybliżenie",
"themeDark_description": "ustaw ciemny motyw do używania w aplikacji",
"themeLight_description": "ustaw jasny motyw do używania w aplikacji",
"zoom_description": "ustaw procentowe przybliżenie dla aplikacji",
"theme": "motyw",
"skipPlaylistPage_description": "przechodząc do listy odtwarzania, przejdź do strony listy odtwarzania zamiast do strony domyślnej",
"volumeWheelStep": "krok pokrętła głośności",
"windowBarStyle": "styl paska okna",
"useSystemTheme_description": "podążaj za systemem z ustawieniami jasnego lub ciemnego motywu",
"skipPlaylistPage": "pomiń stronę list odtwarzania",
"themeDark": "motyw (ciemny)",
"windowBarStyle_description": "wybierz styl paska okna",
"useSystemTheme": "użyj motywu systemowego"
},
"table": {
"config": {
"view": {
"card": "karta",
"table": "tabela",
"poster": "plakat"
},
"general": {
"displayType": "typ wyświetlania",
"gap": "$t(common.gap)",
"tableColumns": "kolumny tabeli",
"autoFitColumns": "automatyczne dopasowanie kolumn",
"size": "$t(common.size)"
},
"label": {
"releaseDate": "data premiery",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (połączony)",
"dateAdded": "data dodania",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "ostatnio odtwarzane",
"trackNumber": "numer utworu",
"rowIndex": "indeks wiersza",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "liczba odtworzeń",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "numer płyty",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)"
}
},
"column": {
"comment": "komentarz",
"album": "album",
"rating": "ocena",
"favorite": "ulubione",
"playCount": "odtwarzane",
"albumCount": "$t(entity.album_other)",
"releaseYear": "rok",
"lastPlayed": "ostatnio odtwarzane",
"biography": "biografia",
"releaseDate": "data premiery",
"bitrate": "bitrate",
"title": "tytuł",
"bpm": "bpm",
"dateAdded": "data dodania",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "utwór",
"genre": "$t(entity.genre_one)",
"albumArtist": "artysta albumu",
"path": "ścieżka",
"discNumber": "płyta",
"channels": "$t(common.channel_other)"
}
}
}
+246
View File
@@ -0,0 +1,246 @@
{
"common": {
"backward": "voltar",
"areYouSure": "tem certeza?",
"add": "adicionar",
"ascending": "ascendente",
"center": "centro",
"cancel": "cancelar",
"bitrate": "taxa de bits",
"action_one": "ação",
"action_many": "ações",
"action_other": "ações",
"biography": "biografia",
"bpm": "bpm",
"edit": "editar",
"favorite": "favorito",
"currentSong": "$t(entity.track_one) atual",
"descending": "abaixar",
"dismiss": "liberar",
"duration": "duração",
"decrease": "diminuir",
"description": "descrição",
"configure": "configurar",
"enable": "habilitar",
"clear": "limpar",
"delete": "deletar",
"title": "titulo",
"create": "criar",
"confirm": "confirmar",
"home": "inicio",
"comingSoon": "em breve…",
"channel_one": "canal",
"channel_many": "canais",
"channel_other": "canais",
"disable": "desabilitar",
"expand": "expandir",
"disc": "disco",
"increase": "incrementar",
"rating": "classificação",
"refresh": "atualizar",
"unknown": "desconhecido",
"left": "esquerda",
"save": "salvar",
"right": "direita",
"collapse": "minimizar",
"trackNumber": "faixa",
"gap": "intervalo",
"year": "ano",
"manage": "gerenciar",
"limit": "limite",
"minimize": "minimizar",
"modified": "modificado",
"name": "nome",
"maximize": "maximizar",
"ok": "ok",
"path": "caminho",
"no": "não",
"owner": "dono",
"forward": "avançar",
"forceRestartRequired": "reinicie para aplicar as alterações… feche a notificação para reiniciar",
"setting": "contexto",
"version": "versão",
"filter_one": "filtro",
"filter_many": "filtros",
"filter_other": "filtros",
"filters": "filtros",
"saveAndReplace": "salvar e substituir",
"playerMustBePaused": "o player deve estar pausado",
"resetToDefault": "restaurar ao padrão",
"reset": "reiniciar",
"sortOrder": "ordem",
"none": "nenhum",
"menu": "menu",
"restartRequired": "é necessário reiniciar",
"previousSong": "anterior $t(entity.track_one)",
"noResultsFromQuery": "a consulta não retornou resultados",
"quit": "abandonar",
"search": "procurar",
"saveAs": "salvar como",
"yes": "sim",
"random": "aleatório",
"size": "tamanho",
"note": "observação"
},
"action": {
"goToPage": "vá para página",
"addToFavorites": "adicionar em $t(entity.favorite_other)",
"viewPlaylists": "ver $t(entity.playlist_other)",
"setRating": "definir classificação",
"moveToTop": "mover para o topo",
"refresh": "$t(common.refresh)",
"removeFromQueue": "remover da fila",
"moveToBottom": "mover para baixo",
"editPlaylist": "editar $t(entity.playlist_one)",
"clearQueue": "limpar fila",
"addToPlaylist": "adicionar à $t(entity.playlist_one)",
"createPlaylist": "criar $t(entity.playlist_one)",
"removeFromPlaylist": "remover da $t(entity.playlist_one)",
"deletePlaylist": "deletar $t(entity.playlist_one)",
"deselectAll": "desmarcar todos",
"removeFromFavorites": "remover de $t(entity.favorite_other)"
},
"form": {
"deletePlaylist": {
"title": "deletar $t(entity.playlist_one)"
},
"addServer": {
"title": "adicionar servidor"
},
"createPlaylist": {
"title": "criar $t(entity.playlist_one)"
},
"updateServer": {
"title": "atualizar servidor"
},
"editPlaylist": {
"title": "editar $t(entity.playlist_one)"
},
"addToPlaylist": {
"title": "adicionar à $t(entity.playlist_one)"
},
"lyricSearch": {
"title": "pesquisa de letras"
}
},
"setting": {
"discordIdleStatus_description": "quando ativado, atualiza o status enquanto o player está ocioso",
"discordUpdateInterval_description": "o tempo em segundos entre cada atualização (mínimo 15 segundos)",
"playButtonBehavior_description": "define o comportamento padrão do botão play ao adicionar músicas à fila",
"discordApplicationId": "{{discord}} ID do aplicativo"
},
"table": {
"config": {
"label": {
"title": "$t(common.title)",
"titleCombined": "$t(common.title) (combinado)",
"discNumber": "numero do disco"
}
},
"column": {
"title": "titulo",
"discNumber": "disco"
}
},
"page": {
"home": {
"mostPlayed": "mais tocado",
"newlyAdded": "lançamentos recém-adicionados",
"title": "$t(common.home)",
"explore": "explore a sua biblioteca",
"recentlyPlayed": "tocado recentemente"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"title": "comandos"
},
"sidebar": {
"home": "$t(common.home)"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"filter": {
"title": "titulo",
"disc": "disco",
"mostPlayed": "mais tocado"
},
"player": {
"playbackFetchNoResults": "nenhuma música encontrada",
"playbackFetchInProgress": "carregando músicas…"
},
"entity": {
"albumArtist_one": "artista do álbum",
"albumArtist_many": "artistas do álbum",
"albumArtist_other": "artistas do álbum",
"albumArtistCount_one": "{{count}} artista do álbum",
"albumArtistCount_many": "{{count}} artistas do álbum",
"albumArtistCount_other": "{{count}} artistas do álbum",
"album_one": "álbum",
"album_many": "álbuns",
"album_other": "álbuns",
"artist_one": "artista",
"artist_many": "artistas",
"artist_other": "artistas",
"albumWithCount_one": "{{count}} álbum",
"albumWithCount_many": "{{count}} álbuns",
"albumWithCount_other": "{{count}} álbuns",
"favorite_one": "favorito",
"favorite_many": "favoritos",
"favorite_other": "favoritos",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistas",
"artistWithCount_other": "{{count}} artistas",
"folder_one": "pasta",
"folder_many": "pastas",
"folder_other": "pastas",
"genre_one": "gênero",
"genre_many": "gêneros",
"genre_other": "gêneros",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlists",
"playlistWithCount_other": "{{count}} playlists",
"playlist_one": "playlist",
"playlist_many": "playlists",
"playlist_other": "playlists",
"folderWithCount_one": "{{count}} pasta",
"folderWithCount_many": "{{count}} pastas",
"folderWithCount_other": "{{count}} pastas",
"genreWithCount_one": "{{count}} gênero",
"genreWithCount_many": "{{count}} gêneros",
"genreWithCount_other": "{{count}} gêneros"
},
"error": {
"remotePortWarning": "reinicie o servidor para aplicar a nova porta",
"systemFontError": "ocorreu um erro ao tentar obter fontes do sistema",
"playbackError": "ocorreu um erro ao tentar reproduzir a mídia",
"endpointNotImplementedError": "endpoint {{endpoint}} não está implementado para {{serverType}}",
"remotePortError": "ocorreu um erro ao tentar definir a porta do servidor remoto",
"serverRequired": "servidor necessário",
"authenticationFailed": "falha na autenticação",
"apiRouteError": "não é possível encaminhar a solicitação",
"genericError": "um erro ocorreu",
"credentialsRequired": "credenciais necessárias",
"sessionExpiredError": "sua sessão expirou",
"remoteEnableError": "ocorreu um erro ao tentar $t(common.enable) o servidor remoto",
"localFontAccessDenied": "acesso negado a fontes locais",
"serverNotSelectedError": "nenhum servidor selecionado",
"remoteDisableError": "ocorreu um erro ao tentar $t(common.disable) o servidor remoto",
"mpvRequired": "MPV necessário",
"audioDeviceFetchError": "ocorreu um erro ao tentar obter dispositivos de áudio",
"invalidServer": "servidor inválido",
"loginRateError": "muitas tentativas de login, tente novamente em alguns segundos"
}
}
+488
View File
@@ -0,0 +1,488 @@
{
"action": {
"editPlaylist": "редактировать $t(entity.playlist_one)",
"goToPage": "перейти на страницу",
"moveToTop": "вверх",
"clearQueue": "очистить очередь",
"addToFavorites": "добавить в $t(entity.favorite_other)",
"addToPlaylist": "добавить в $t(entity.playlist_one)",
"createPlaylist": "создать $t(entity.playlist_one)",
"removeFromPlaylist": "удалить из $t(entity.playlist_one)",
"viewPlaylists": "просмотреть $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "удалить $t(entity.playlist_one)",
"removeFromQueue": "удалить из очереди",
"deselectAll": "снять выделение",
"moveToBottom": "вниз",
"setRating": "оценить",
"toggleSmartPlaylistEditor": "вкл/выкл $t(entity.smartPlaylist) редактор",
"removeFromFavorites": "удалить из $t(entity.favorite_other)"
},
"common": {
"backward": "назад",
"increase": "увеличить",
"rating": "рейтинг",
"bpm": "ударов в мин.",
"refresh": "обновить",
"unknown": "неизвестно",
"areYouSure": "вы уверены?",
"edit": "изменить",
"favorite": "любимый",
"left": "лево",
"save": "сохранить",
"right": "право",
"currentSong": "текущий $t(entity.track_one)",
"collapse": "закрыть",
"trackNumber": "трек",
"descending": "убывающий",
"add": "добавить",
"gap": "промежуток",
"ascending": "возрастающий",
"dismiss": "отклонить",
"year": "год",
"manage": "управлять",
"limit": "лимит",
"minimize": "минимизировать",
"modified": "изменено",
"duration": "продолжительность",
"name": "имя",
"maximize": "максимизировать",
"decrease": "уменьшить",
"ok": "ок",
"description": "описание",
"configure": "настроить",
"path": "путь",
"center": "центр",
"no": "нет",
"owner": "владелец",
"enable": "включить",
"clear": "очистить",
"forward": "вперёд",
"delete": "удалить",
"cancel": "отменить",
"forceRestartRequired": "перезапустите приложение, чтобы применить изменения... закройте уведомление, чтобы перезапустить приложение",
"setting": "настройка",
"version": "версия",
"title": "название",
"filter_one": "фильтр",
"filter_few": "фильтра",
"filter_many": "фильтров",
"filters": "фильтры",
"create": "создать",
"bitrate": "битрейт",
"saveAndReplace": "сохранить и заменить",
"action_one": "действие",
"action_few": "действия",
"action_many": "действий",
"playerMustBePaused": "воспроизведение должно быть остановлено",
"confirm": "подтвердить",
"resetToDefault": "сбросить к настройкам по умолчанию",
"home": "Главная страница",
"comingSoon": "скоро будет…",
"reset": "сбросить",
"channel_one": "канал",
"channel_few": "канала",
"channel_many": "каналов",
"disable": "выключить",
"sortOrder": "порядок",
"menu": "меню",
"restartRequired": "необходим перезапуск приложения",
"previousSong": "предыдущий $t(entity.track_one)",
"noResultsFromQuery": "нет результатов",
"quit": "выйти",
"expand": "расширить",
"search": "Поиск",
"saveAs": "сохранить как",
"disc": "диск",
"yes": "да",
"random": "случайный",
"size": "размер",
"biography": "биография",
"note": "заметка",
"none": "нет"
},
"entity": {
"album_one": "альбом",
"album_few": "альбома",
"album_many": "альбомов",
"genre_one": "жанр",
"genre_few": "жанра",
"genre_many": "жанров",
"playlistWithCount_one": "{{count}} плейлист",
"playlistWithCount_few": "{{count}} плейлиста",
"playlistWithCount_many": "{{count}} плейлистов",
"playlist_one": "плейлист",
"playlist_few": "плейлиста",
"playlist_many": "плейлистов",
"artist_one": "автор",
"artist_few": "автора",
"artist_many": "авторов",
"folderWithCount_one": "{{count}} папка",
"folderWithCount_few": "{{count}} папки",
"folderWithCount_many": "{{count}} папок",
"albumArtist_one": "автор альбома",
"albumArtist_few": "автора альбома",
"albumArtist_many": "авторов альбома",
"track_one": "трек",
"track_few": "трека",
"track_many": "треков",
"albumArtistCount_one": "{{count}} автор альбома",
"albumArtistCount_few": "{{count}} автора альбома",
"albumArtistCount_many": "{{count}} авторов альбома",
"albumWithCount_one": "{{count}} альбом",
"albumWithCount_few": "{{count}} альбома",
"albumWithCount_many": "{{count}} альбомов",
"favorite_one": "любимый",
"favorite_few": "любимых",
"favorite_many": "любимые",
"artistWithCount_one": "{{count}} автор",
"artistWithCount_few": "{{count}} автора",
"artistWithCount_many": "{{count}} авторов",
"folder_one": "папка",
"folder_few": "папки",
"folder_many": "папок",
"smartPlaylist": "умный $t(entity.playlist_one)",
"genreWithCount_one": "{{count}} жанр",
"genreWithCount_few": "{{count}} жанра",
"genreWithCount_many": "{{count}} жанров",
"trackWithCount_one": "{{count}} трек",
"trackWithCount_few": "{{count}} трека",
"trackWithCount_many": "{{count}} треков"
},
"table": {
"config": {
"view": {
"card": "карта",
"table": "таблица",
"poster": "постер"
},
"general": {
"displayType": "тип отображения",
"gap": "$t(common.gap)",
"tableColumns": "столбцы таблицы",
"autoFitColumns": "автоматически расставить столбцы",
"size": "$t(common.size)"
},
"label": {
"releaseDate": "дата выхода",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (комбинированный)",
"dateAdded": "дата добавления",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "последний",
"trackNumber": "номер трека",
"rowIndex": "индекс ряда",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "количество воспроизведений",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "номер диска",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)"
}
},
"column": {
"rating": "рейтинг",
"favorite": "любимый",
"playCount": "воспроизведений",
"releaseYear": "год",
"lastPlayed": "последний",
"releaseDate": "дата выхода",
"title": "название",
"songCount": "$t(entity.track_other)",
"trackNumber": "трек",
"genre": "$t(entity.genre_one)",
"path": "путь",
"discNumber": "диск"
}
},
"error": {
"remotePortWarning": "перезапустить сервер для применения нового порта",
"systemFontError": "произошла ошибка при попытке получить системные шрифты",
"playbackError": "произошла ошибка при попытке проиграть медиа",
"endpointNotImplementedError": "запрос {{endpoint}} is not implemented for {{serverType}}",
"remotePortError": "произошла ошибка при попытке установить порт удаленного сервера",
"serverRequired": "необходим сервер",
"authenticationFailed": "аутентификация завершилась с ошибкой",
"apiRouteError": "невозможно выполнить запрос",
"genericError": "произошла ошибка",
"credentialsRequired": "необходимы учётные данные",
"sessionExpiredError": "ваш сеанс истек",
"remoteEnableError": "ошибка произошла при попытке $t(common.enable) удаленного сервера",
"localFontAccessDenied": "не получилось получить доступ к шрифтам",
"serverNotSelectedError": "не выбран сервер",
"remoteDisableError": "ошибка произошла при попытке $t(common.disable) удаленного сервера",
"mpvRequired": "Необходим MPV",
"audioDeviceFetchError": "произошла ошибка с аудиоустройством",
"invalidServer": "недействительный сервер",
"loginRateError": "слишком много попыток входа, пожалуйста, попробуйте еще раз через несколько секунд"
},
"filter": {
"isCompilation": "сборник",
"isRated": "оценён",
"bitrate": "битрейт",
"dateAdded": "дата добавления",
"communityRating": "рейтинг сообщества",
"favorited": "любимый",
"albumArtist": "$t(entity.albumArtist_one)",
"isFavorited": "любимый",
"bpm": "ударов в мин.",
"disc": "диск",
"biography": "биография",
"artist": "$t(entity.artist_one)",
"duration": "продолжительность",
"fromYear": "из года",
"criticRating": "рейтинг критиков",
"mostPlayed": "наибольшое кол-во воспроизведений",
"comment": "комментировать",
"playCount": "кол-во воспроизведений",
"recentlyUpdated": "недавно обновлено",
"channels": "$t(common.channel_other)",
"recentlyPlayed": "недавно проиграно",
"owner": "$t(common.owner)",
"title": "название",
"rating": "рейтинг",
"search": "Поиск",
"genre": "$t(entity.genre_one)",
"recentlyAdded": "недавно добавлено",
"note": "заметка",
"name": "название",
"releaseDate": "дата выхода",
"albumCount": "$t(entity.album_other) кол-во",
"path": "путь",
"isRecentlyPlayed": "недавно проигрывалась",
"releaseYear": "год выхода",
"id": "#",
"songCount": "кол-во песен",
"isPublic": "публичный",
"random": "случайный",
"lastPlayed": "последний раз проигрывалась",
"toYear": "до года",
"album": "$t(entity.album_one)",
"trackNumber": "трек"
},
"player": {
"repeat_all": "повтор всех",
"stop": "остановить",
"repeat": "повтор",
"queue_remove": "удалить выделенные",
"playRandom": "случайные песни",
"skip": "пропустить",
"previous": "предыдущий",
"toggleFullscreenPlayer": "включить полноэкранный режим",
"skip_back": "назад",
"favorite": "любимый",
"next": "следующее",
"shuffle": "перемешать",
"playbackFetchNoResults": "нет песен",
"playbackFetchInProgress": "загрузка песен..",
"addNext": "добавить следующий",
"playbackSpeed": "скорость воспроизведения",
"playbackFetchCancel": "это занимает некоторое время... закрыть уведомление для отмены",
"play": "прослушать",
"repeat_off": "повтор выключен",
"pause": "пауза",
"queue_clear": "очистить очередь",
"muted": "звук отключён",
"unfavorite": "убрать из любимых",
"queue_moveToTop": "переместить выделение вниз",
"queue_moveToBottom": "переместить выделение вверх",
"shuffle_off": "перемешивание выключено",
"addLast": "добавить последний",
"mute": "отключить звук",
"skip_forward": "вперёд"
},
"page": {
"sidebar": {
"nowPlaying": "Cейчас проигрывается",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "показать слова песни",
"dynamicBackground": "динамический фон",
"synchronized": "синхронизировано",
"followCurrentLyric": "следовать за текущими словами песни",
"opacity": "непрозрачность",
"lyricSize": "размер слов",
"showLyricProvider": "показать провайдера слов",
"unsynchronized": "несинхронизировано",
"lyricAlignment": "выравнивание слов песни",
"useImageAspectRatio": "использовать соотношение сторон изображения",
"lyricGap": "пробел между словами"
},
"upNext": "следующее",
"lyrics": "слова песни",
"related": "схожие"
},
"appMenu": {
"selectServer": "выбрать сервер",
"version": "версия {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "настроить список серверов",
"expandSidebar": "развернуть",
"collapseSidebar": "Скрыть боковую панель",
"openBrowserDevtools": "открыть инструменты разработчика",
"quit": "$t(common.quit)",
"goBack": "назад",
"goForward": "вперёд"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} выбрано",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"home": {
"mostPlayed": "наибольшее кол-во воспроизведений",
"newlyAdded": "недавно добавленные релизы",
"title": "$t(common.home)",
"explore": "изучите вашу медиатеку",
"recentlyPlayed": "недавно прослушано"
},
"albumDetail": {
"moreFromArtist": "больше из жанра $t(entity.genre_one)",
"moreFromGeneric": "больше из {{item}}"
},
"setting": {
"playbackTab": "воспроизведение",
"generalTab": "общее",
"hotkeysTab": "горячие клавиши",
"windowTab": "окно"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "команды сервера",
"goToPage": "перейти на страницу",
"searchFor": "поиск {{query}}"
},
"title": "комманды"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"form": {
"deletePlaylist": {
"title": "удалить $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) успешно удалён",
"input_confirm": "напишите название $t(entity.playlist_one), чтобы подтвердить действие"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "создать $t(entity.playlist_one)",
"input_public": "публичный",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) успешно создан",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "добавить сервер",
"input_username": "пользователь",
"input_url": "url",
"input_password": "пароль",
"input_legacyAuthentication": "включить старую аутентификацию",
"input_name": "название сервера",
"success": "сервер добавлен успешно",
"input_savePassword": "сохранить пароль",
"ignoreSsl": "ignore ssl $t(common.restartRequired)",
"ignoreCors": "$t(common.restartRequired)",
"error_savePassword": "произошла ошибка во время попытки сохранения пароля"
},
"addToPlaylist": {
"success": "добавлено(а) {{message}} $t(entity.song_other) в {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "добавить в $t(entity.playlist_one)",
"input_skipDuplicates": "пропустить дубликаты",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "обновить сервер",
"success": "сервер успешно обновлён"
},
"queryEditor": {
"input_optionMatchAll": "сопоставить все",
"input_optionMatchAny": "сопоставить любой"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "поиск слов песни"
},
"editPlaylist": {
"title": "редактировать $t(entity.playlist_one)"
}
},
"setting": {
"accentColor": "цвет акцента",
"accentColor_description": "устанавливает цвет акцента для приложения",
"applicationHotkeys": "горячие клавиши приложения",
"crossfadeStyle_description": "Выберите вид эффекта crossfade для аудиоплеера",
"enableRemote_description": "Включает сервер удалённого управления для управления воспроизведением с помощью других устройств",
"fontType_optionSystem": "Системный шрифт",
"mpvExecutablePath_description": "Укажите папку, в которой находится исполняющий файл аудиоплеера MPV",
"crossfadeStyle": "Вид эффекта crossfade",
"fontType_optionBuiltIn": "Встроенный в приложение",
"disableLibraryUpdateOnStartup": "Отключить проверку новых версий при запуске приложения",
"minimizeToTray_description": "Сворачивать приложение в панель уведомлений",
"audioPlayer_description": "Укажите - какой аудиоплеер использовать для воспроизведения",
"disableAutomaticUpdates": "Отключить проверку обновлений",
"exitToTray_description": "При закрытии приложения - оно останется в панели уведомлений",
"fontType_optionCustom": "Пользовательский шрифт",
"remotePassword": "Пароль к серверу удалённого управления",
"font": "Шрифт",
"crossfadeDuration_description": "Укажите длительность эффекта crossfade",
"mpvExecutablePath": "Папка с аудиоплеером MPV",
"exitToTray": "Сворачивать в панель уведомлений при закрытии",
"enableRemote": "Включить сервер удалённого управления",
"fontType": "Источник шрифта",
"crossfadeDuration": "Длительность эффекта crossfade",
"audioPlayer": "Аудиоплеер",
"minimizeToTray": "Сворачивать в панель уведомлений",
"font_description": "Выберите - какой шрифт использовать в приложении",
"remoteUsername": "Имя пользователя для доступа к серверу удалённого управления"
}
}
+632
View File
@@ -0,0 +1,632 @@
{
"player": {
"repeat_all": "ponavljaj sve",
"stop": "zaustavi",
"repeat": "ponavljaj jednu",
"queue_remove": "ukloni izabrane",
"playRandom": "slučajna reprodukcija",
"skip": "preskoči",
"previous": "prethodna",
"toggleFullscreenPlayer": "prebaci u puni ekran",
"skip_back": "preskoči unazad",
"favorite": "omiljeno",
"next": "sledeća",
"shuffle": "mešaj",
"playbackFetchNoResults": "nema pronađenih pesama",
"playbackFetchInProgress": "učitavanje pesama…",
"addNext": "dodaj sledeći",
"playbackSpeed": "brzina reprodukcije",
"playbackFetchCancel": "ovo traje... zatvorite obaveštenje da biste otkazali",
"play": "pusti",
"repeat_off": "ponavljanje isključeno",
"pause": "pauziraj",
"queue_clear": "isprazni red",
"muted": "isključeno",
"unfavorite": "ukloni iz omiljenih",
"queue_moveToTop": "pomeri izabrane na dno",
"queue_moveToBottom": "pomeri izabrane na vrh",
"shuffle_off": "mešanje isključeno",
"addLast": "dodaj poslednji",
"mute": "isključi ton",
"skip_forward": "preskoči unapred"
},
"setting": {
"crossfadeStyle_description": "izaberite stil prelaska za audio plejer",
"remotePort_description": "postavlja port za daljinsku kontrolu servera",
"hotkey_skipBackward": "preskoči unazad",
"replayGainMode_description": "prilagođava jačinu glasnoće prema vrednostima {{ReplayGain}} koje se nalaze u metapodacima datoteke",
"volumeWheelStep_description": "količina promene glasnoće pri okretanju točkića miša na traci za glasnoću",
"audioDevice_description": "izaberite audio uređaj za reprodukciju (samo veb plejer)",
"theme_description": "postavlja temu za aplikaciju",
"hotkey_playbackPause": "pauza",
"replayGainFallback": "{{ReplayGain}} alternativa",
"sidebarCollapsedNavigation_description": "prikaži ili sakrij navigaciju u sklopljenoj bočnoj traci",
"mpvExecutablePath_help": "po jedna po liniji",
"hotkey_volumeUp": "pojačaj glasnoću",
"skipDuration": "dužina preskakanja",
"discordIdleStatus_description": "kada je omogućeno, ažurira status dok je plejer u mirovanju",
"showSkipButtons": "prikaži dugmad za preskakanje",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "minimum trajanja za bilježenje (u procentima)",
"lyricFetch": "preuzimanje tekstova sa interneta",
"scrobble": "bilježi",
"skipDuration_description": "postavlja dužinu preskakanja kada koristite dugmad za preskakanje na traci za reprodukciju",
"enableRemote_description": "omogućava daljinsku kontrolu servera kako bi omogućili drugim uređajima da kontrolišu aplikaciju",
"fontType_optionSystem": "sistemski font",
"mpvExecutablePath_description": "postavlja putanju do izvršne datoteke mpv player-a",
"replayGainClipping_description": "Smanjuje preklapanje uzrokovano {{ReplayGain}} automatskim smanjenjem glasnoće",
"replayGainPreamp": "{{ReplayGain}} pojačalo (dB)",
"hotkey_favoriteCurrentSong": "omiljena $t(common.currentSong)",
"sampleRate": "sample rate",
"crossfadeStyle": "stil prelaza",
"sidePlayQueueStyle_optionAttached": "priložena",
"sidebarConfiguration": "konfiguracija bočne trake",
"sampleRate_description": "izaberite izlazni sample rate koji će se koristiti ako je sample rate drugačiji od onog u trenutnom mediju",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainClipping": "{{ReplayGain}} smanjenje",
"hotkey_zoomIn": "uvećaj",
"scrobble_description": "bilježi reprodukciju na vašem serverskom uređaju",
"hotkey_browserForward": "napred u pregledaču",
"audioExclusiveMode_description": "omogućava ekskluzivan režim izlaza. U ovom režimu, sistem je obično zaključan, i samo mpv će moći da izlazi zvuk",
"discordUpdateInterval": "{{discord}} interval ažuriranja bogatog prikaza",
"themeLight": "tema (svetla)",
"fontType_optionBuiltIn": "ugrađeni font",
"hotkey_playbackPlayPause": "reprodukcija / pauza",
"hotkey_rate1": "oceni sa 1 zvezdicom",
"hotkey_skipForward": "preskoči unapred",
"disableLibraryUpdateOnStartup": "onemogući proveru za nove verzije pri pokretanju",
"discordApplicationId_description": "ID aplikacije za {{discord}} bogat prikaz (podrazumevano je {{defaultId}})",
"sidePlayQueueStyle": "stil bočne liste za reprodukciju",
"gaplessAudio": "bez pauze zvuka",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"zoom": "stepen zumiranja",
"minimizeToTray_description": "minimizira aplikaciju u sistemsku traku kada se zatvori i vraća je kada se ponovo otvori",
"hotkey_playbackPlay": "pusti",
"hotkey_togglePreviousSongFavorite": "promeni omiljenu pesmu $t(common.previousSong)",
"hotkey_volumeDown": "smanji glasnoću",
"hotkey_unfavoritePreviousSong": "ukloni omiljenu pesmu $t(common.previousSong)",
"audioPlayer_description": "izaberite audio plejer za reprodukciju",
"globalMediaHotkeys": "globalni medijski tasteri",
"hotkey_globalSearch": "globalno pretraživanje",
"gaplessAudio_description": "postavlja opciju bez pauze zvuka za mpv (preporučeno: slabo)",
"remoteUsername_description": "postavlja korisničko ime za daljinsku kontrolu servera. Ako su i korisničko ime i lozinka prazni, autentifikacija će biti onemogućena",
"disableAutomaticUpdates": "onemogući automatsko ažuriranje",
"exitToTray_description": "izlazak aplikacije u sistemsku traku",
"followLyric_description": "pomera tekst pesme na trenutnu poziciju reprodukcije",
"hotkey_favoritePreviousSong": "omiljena $t(common.previousSong)",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "pomeraj teksta (ms)",
"discordUpdateInterval_description": "vreme u sekundama između svakog ažuriranja (minimum 15 sekundi)",
"fontType_optionCustom": "prilagođeni font",
"themeDark_description": "postavlja tamnu temu za aplikaciju",
"audioExclusiveMode": "ekskluzivni audio režim",
"remotePassword": "lozinka za daljinsku kontrolu servera",
"lyricFetchProvider": "pružatelji tekstova za preuzimanje",
"language_description": "postavlja jezik za aplikaciju ($t(common.restartRequired))",
"playbackStyle_optionCrossFade": "prelazak sa preklapanjem",
"hotkey_rate3": "oceni sa 3 zvezdice",
"font": "font",
"mpvExtraParameters": "mpv parametri",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "postavlja svetlu temu za aplikaciju",
"hotkey_toggleFullScreenPlayer": "prebaci na prikaz na celom ekranu",
"hotkey_localSearch": "pretraživanje na stranici",
"hotkey_toggleQueue": "promeni listu za reprodukciju",
"zoom_description": "postavlja stepen zumiranja za aplikaciju",
"remotePassword_description": "postavlja lozinku za daljinsku kontrolu servera. Ove informacije se prenose nezaštićeno, pa biste trebali koristiti jedinstvenu lozinku koja vam nije važna.",
"hotkey_rate5": "oceni sa 5 zvezdica",
"hotkey_playbackPrevious": "prethodna pesma",
"showSkipButtons_description": "prikaži ili sakrij dugmad za preskakanje na traci za reprodukciju",
"crossfadeDuration_description": "postavi trajanje efekta prelaza",
"language": "jezik",
"playbackStyle": "stil reprodukcije",
"hotkey_toggleShuffle": "promeni slučajan redosled",
"theme": "tema",
"playbackStyle_description": "izaberite stil reprodukcije za audio plejer",
"discordRichPresence_description": "omogućava status reprodukcije u {{discord}} bogatom prikazu. Ključevi slika su: {{icon}}, {{playing}}, i {{paused}} ",
"mpvExecutablePath": "putanja do mpv izvršne datoteke",
"audioDevice": "audio uređaj",
"hotkey_rate2": "oceni sa 2 zvezdice",
"playButtonBehavior_description": "postavlja zadano ponašanje dugmeta za reprodukciju pri dodavanju pesama u listu za reprodukciju",
"minimumScrobblePercentage_description": "minimalni procenat pesme koji mora da bude reprodukovan pre nego što se zabeleži",
"exitToTray": "izlazak u oblast za traku",
"hotkey_rate4": "oceni sa 4 zvezdice",
"enableRemote": "omogući daljinsku kontrolu servera",
"showSkipButton_description": "prikaži ili sakrij dugmad za preskakanje na traci za reprodukciju",
"savePlayQueue": "sačuvaj listu za reprodukciju",
"minimumScrobbleSeconds_description": "minimalno trajanje pesme u sekundama koje mora biti reprodukovano pre nego što se zabeleži",
"skipPlaylistPage_description": "kada idete na plejlistu, idi na stranicu sa pesmama plejliste umesto na podrazumevanu stranicu",
"fontType_description": "ugrađeni font bira jedan od fontova koje pruža Feishin. sistemski font vam omogućava da izaberete bilo koji font koji nudi vaš operativni sistem. prilagođeni vam omogućava da koristite svoj font",
"playButtonBehavior": "ponašanje dugmeta za reprodukciju",
"volumeWheelStep": "korak točkića za glasnoću",
"sidebarPlaylistList_description": "prikaži ili sakrij listu plejlista na bočnoj traci",
"accentColor": "akcentna boja",
"sidePlayQueueStyle_description": "postavlja stil bočne liste za reprodukciju",
"accentColor_description": "postavi akcentnu boju za aplikaciju",
"replayGainMode": "{{ReplayGain}} režim",
"playbackStyle_optionNormal": "normalno",
"windowBarStyle": "stil trake prozora",
"floatingQueueArea": "prikaži područje plutajuće liste za reprodukciju",
"replayGainFallback_description": "jačina u dB koja će se primeniti ako datoteka nema {{ReplayGain}} oznake",
"replayGainPreamp_description": "prilagođava pojačalo za {{ReplayGain}} vrednosti",
"hotkey_toggleRepeat": "promeni ponavljanje",
"lyricOffset_description": "pomera tekst za navedeni broj milisekundi",
"sidebarConfiguration_description": "izaberite stavke i redosled u kojem se pojavljuju u bočnoj traci",
"fontType": "tip fonta",
"remotePort": "port za daljinsku kontrolu servera",
"applicationHotkeys": "prečice za aplikaciju",
"hotkey_playbackNext": "sledeća pesma",
"useSystemTheme_description": "prati sistemski određene postavke za svetlu ili tamnu temu",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "preuzimanje tekstova sa različitih izvora na internetu",
"lyricFetchProvider_description": "izaberite pružatelje tekstova za preuzimanje. Redosled pružatelja je redosled upita.",
"globalMediaHotkeys_description": "omogućava ili onemogućava korišćenje medijskih tastera sistema za kontrolu reprodukcije",
"customFontPath": "prilagođena putanja fonta",
"followLyric": "prati trenutni tekst pesme",
"crossfadeDuration": "trajanje prelaza",
"discordIdleStatus": "prikaži status u mirovanju na Diskordu",
"sidePlayQueueStyle_optionDetached": "odvojena",
"audioPlayer": "audio plejer",
"hotkey_zoomOut": "umanji",
"hotkey_unfavoriteCurrentSong": "ukloni omiljenu pesmu $t(common.currentSong)",
"hotkey_rate0": "obrisati ocenu",
"discordApplicationId": "{{discord}} ID aplikacije",
"applicationHotkeys_description": "konfiguriši prečice za aplikaciju. uključite opciju za postavljanje kao globalne prečice (samo na radnoj površini)",
"floatingQueueArea_description": "prikaz ikone na desnoj strani ekrana za pregled liste za reprodukciju",
"hotkey_volumeMute": "isključi zvuk",
"hotkey_toggleCurrentSongFavorite": "promeni omiljenu pesmu $t(common.currentSong)",
"remoteUsername": "korisničko ime za daljinsku kontrolu servera",
"hotkey_browserBack": "nazad u pregledaču",
"showSkipButton": "prikaži dugmad za preskakanje",
"sidebarPlaylistList": "lista plejlista na bočnoj traci",
"minimizeToTray": "minimiziraj u sistemsku traku",
"skipPlaylistPage": "preskoči stranicu plejliste",
"themeDark": "tema (tamna)",
"sidebarCollapsedNavigation": "navigacija (skupljena bočna traka)",
"customFontPath_description": "postavlja putanju do prilagođenog fonta za aplikaciju",
"gaplessAudio_optionWeak": "slabo (preporučeno)",
"minimumScrobbleSeconds": "minimalno trajanje za bilježenje (u sekundama)",
"hotkey_playbackStop": "zaustavi",
"windowBarStyle_description": "izaberite stil trake prozora",
"discordRichPresence": "{{discord}} bogat prikaz",
"font_description": "postavlja font koji se koristi za aplikaciju",
"savePlayQueue_description": "sačuva listu za reprodukciju kada se aplikacija zatvori i obnovi je pri ponovnom otvaranju aplikacije",
"useSystemTheme": "koristi sistemsku temu"
},
"action": {
"editPlaylist": "izmeni $t(entity.playlist_one)",
"goToPage": "idi na stranu",
"moveToTop": "idi na vrh",
"clearQueue": "očisti listu",
"addToFavorites": "dodaj u $t(entity.favorite_other)",
"addToPlaylist": "dodaj u $t(entity.playlist_one)",
"createPlaylist": "napravi $t(entity.playlist_one)",
"removeFromPlaylist": "ukloni iz $t(entity.playlist_one)",
"viewPlaylists": "vidi $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "obriši $t(entity.playlist_one)",
"removeFromQueue": "ukloni iz liste",
"deselectAll": "deselektuj sve",
"moveToBottom": "idi na dno",
"setRating": "oceni",
"toggleSmartPlaylistEditor": "pokreni $t(entity.smartPlaylist) editor",
"removeFromFavorites": "ukloni iz $t(entity.favorite_other)"
},
"common": {
"backward": "nazad",
"increase": "povećaj",
"rating": "ocena",
"bpm": "bpm",
"refresh": "osveži",
"unknown": "nepoznato",
"areYouSure": "da li si siguran/na?",
"edit": "izmeni",
"favorite": "favorit",
"left": "levo",
"save": "sačuvaj",
"right": "desno",
"currentSong": "trenutno $t(entity.track_one)",
"collapse": "sklopi",
"trackNumber": "pesma",
"descending": "silazno",
"add": "dodaj",
"gap": "procep",
"ascending": "uzlazno",
"dismiss": "odbaci",
"year": "godina",
"manage": "upravljaj",
"limit": "limit",
"minimize": "minimiziraj",
"modified": "modifikovan",
"duration": "trajanje",
"name": "ime",
"maximize": "maksimiziraj",
"decrease": "smanji",
"ok": "ok",
"description": "opis",
"configure": "konfiguriši",
"path": "putanja",
"center": "centar",
"no": "ne",
"owner": "vlasnik",
"enable": "uključi",
"clear": "očisti",
"forward": "napred",
"delete": "obriši",
"cancel": "otkaži",
"forceRestartRequired": "restartuj da primeniš izmene… zatvori notifikaciju za restart",
"setting": "podešavanje",
"version": "verzija",
"title": "naziv",
"filter_one": "filter",
"filter_few": "filteri",
"filter_other": "filtera",
"filters": "filteri",
"create": "napravi",
"bitrate": "bitrejt",
"saveAndReplace": "sačuvaj i zameni",
"action_one": "akcija",
"action_few": "akcije",
"action_other": "akcija",
"playerMustBePaused": "plejer mora biti pauziran",
"confirm": "potvrdi",
"resetToDefault": "reset na fabrička podešavanja",
"home": "kuća",
"comingSoon": "stiže uskoro…",
"reset": "reset",
"channel_one": "kanal",
"channel_few": "kanali",
"channel_other": "kanala",
"disable": "onemogući",
"sortOrder": "redosled",
"none": "nijedan",
"menu": "meni",
"restartRequired": "restart potreban",
"previousSong": "prethodna $t(entity.track_one)",
"noResultsFromQuery": "upit je bez rezultata",
"quit": "izađi",
"expand": "proširi",
"search": "pretraga",
"saveAs": "sačuvaj kao",
"disc": "disk",
"yes": "da",
"random": "nasumično",
"size": "veličina",
"biography": "biografija",
"note": "notacija"
},
"table": {
"config": {
"view": {
"card": "kartica",
"table": "tabela",
"poster": "poster"
},
"general": {
"displayType": "tip prikaza",
"gap": "$t(common.gap)",
"tableColumns": "tabela kolona",
"autoFitColumns": "automatski uklopi kolone",
"size": "$t(common.size)"
},
"label": {
"releaseDate": "datum objavljivanja",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"titleCombined": "$t(common.title) (kombinovano)",
"dateAdded": "datum dodavanja",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "zadnje puštana",
"trackNumber": "broj pesme",
"rowIndex": "indeks reda",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "broj puštanja",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "disk broj",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)"
}
},
"column": {
"comment": "komentar",
"album": "album",
"rating": "rejting",
"favorite": "favorit",
"playCount": "puštanja",
"albumCount": "$t(entity.album_other)",
"releaseYear": "godina",
"lastPlayed": "zadnje puštana",
"biography": "biografija",
"releaseDate": "datum objavljivanja",
"bitrate": "bitrate",
"title": "naziv",
"bpm": "bpm",
"dateAdded": "datum dodavanja",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "pesma",
"genre": "$t(entity.genre_one)",
"albumArtist": "album artist",
"path": "putanja",
"discNumber": "disk",
"channels": "$t(common.channel_other)"
}
},
"error": {
"remotePortWarning": "ponovo pokrenite server kako biste primenili novi port",
"systemFontError": "došlo je do greške prilikom pokušaja dobijanja sistema fontova",
"playbackError": "došlo je do greške prilikom pokušaja reprodukcije medija",
"endpointNotImplementedError": "krajnja tačka {{endpoint}} nije implementirana za {{serverType}}",
"remotePortError": "došlo je do greške prilikom postavljanja porta udaljenog servera",
"serverRequired": "potreban je server",
"authenticationFailed": "neuspešna autentikacija",
"apiRouteError": "nije moguće usmeriti zahtev",
"genericError": "došlo je do greške",
"credentialsRequired": "potrebni su pristupni podaci",
"sessionExpiredError": "vaša sesija je istekla",
"remoteEnableError": "došlo je do greške prilikom pokušaja omogućavanja udaljenog servera",
"localFontAccessDenied": "pristup lokalnim fontovima odbijen",
"serverNotSelectedError": "nije izabran nijedan server",
"remoteDisableError": "došlo je do greške prilikom pokušaja onemogućavanja udaljenog servera",
"mpvRequired": "MPV je obavezan",
"audioDeviceFetchError": "došlo je do greške prilikom pokušaja dobijanja audio uređaja",
"invalidServer": "neispravan server",
"loginRateError": "previše pokušaja prijave, molimo pokušajte ponovo za nekoliko sekundi"
},
"filter": {
"mostPlayed": "najviše puštana",
"comment": "komentar",
"playCount": "broj slušanja",
"recentlyUpdated": "skorije ažurirana",
"channels": "$t(common.channel_other)",
"isCompilation": "je kompilacija",
"recentlyPlayed": "skorije puštana",
"isRated": "je ocenjena",
"owner": "$t(common.owner)",
"title": "naziv",
"rating": "rejting",
"search": "pretraga",
"bitrate": "bitrejt",
"genre": "$t(entity.genre_one)",
"recentlyAdded": "skorije dodata",
"note": "notacija",
"name": "ime",
"dateAdded": "datum dodavanja",
"releaseDate": "datum izdavanja",
"albumCount": "$t(entity.album_other) albuma",
"communityRating": "ocena zajednice",
"path": "putanja",
"favorited": "favoriti",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "je skorije puštana",
"isFavorited": "je favorit",
"bpm": "bpm",
"releaseYear": "godina izdavanja",
"id": "id",
"disc": "disk",
"biography": "biografija",
"songCount": "broj pesama",
"artist": "$t(entity.artist_one)",
"duration": "trajanje",
"isPublic": "je javna",
"random": "nasumično",
"lastPlayed": "zadnje puštana",
"toYear": "do godine",
"fromYear": "iz godine",
"criticRating": "ocena kritičara",
"album": "$t(entity.album_one)",
"trackNumber": "pesma"
},
"page": {
"sidebar": {
"nowPlaying": "trenutno pušta",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "prikaži poklapanje teksta",
"dynamicBackground": "dinamička pozadina",
"synchronized": "s sinhronizacijom",
"followCurrentLyric": "prati trenutni tekst pesme",
"opacity": "providnost",
"lyricSize": "veličina teksta pesme",
"showLyricProvider": "prikaži pružatelja teksta pesme",
"unsynchronized": "bez sinhronizacije",
"lyricAlignment": "poravnanje teksta pesme",
"useImageAspectRatio": "koristi odnos stranica slike",
"lyricGap": "razmak između stihova"
},
"upNext": "sledi",
"lyrics": "tekst pesme",
"related": "povezano"
},
"appMenu": {
"selectServer": "izaberi server",
"version": "verzija {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "upravljaj serverima",
"expandSidebar": "proširi bočnu traku",
"collapseSidebar": "skloni bočnu traku",
"openBrowserDevtools": "otvori alatke za razvoj pretraživača",
"quit": "$t(common.quit)",
"goBack": "idi nazad",
"goForward": "idi napred"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} izabrano",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"home": {
"mostPlayed": "najviše puštano",
"newlyAdded": "nedavno dodate pesme",
"title": "$t(common.home)",
"explore": "istraži iz tvoje biblioteke",
"recentlyPlayed": "nedavno puštane pesme"
},
"albumDetail": {
"moreFromArtist": "još od ovog $t(entity.genre_one)",
"moreFromGeneric": "još od {{item}}"
},
"setting": {
"playbackTab": "reprodukcija",
"generalTab": "opšte",
"hotkeysTab": "prečice",
"windowTab": "prozor"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"globalSearch": {
"commands": {
"serverCommands": "komande servera",
"goToPage": "idi na stranicu",
"searchFor": "pretraži za {{query}}"
},
"title": "komande"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
}
},
"form": {
"deletePlaylist": {
"title": "obriši $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) uspešno obrisan",
"input_confirm": "unesite ime $t(entity.playlist_one) za potvrdu"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "kreiraj $t(entity.playlist_one)",
"input_public": "javno",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) uspešno kreiran",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "dodaj server",
"input_username": "korisničko ime",
"input_url": "URL",
"input_password": "lozinka",
"input_legacyAuthentication": "omogući staru autentikaciju",
"input_name": "ime servera",
"success": "server uspešno dodat",
"input_savePassword": "sačuvaj lozinku",
"ignoreSsl": "ignoriši SSL ($t(common.restartRequired))",
"ignoreCors": "ignoriši CORS ($t(common.restartRequired))",
"error_savePassword": "došlo je do greške prilikom pokušaja čuvanja lozinke"
},
"addToPlaylist": {
"success": "dodato {{message}} $t(entity.song_other) u {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "dodaj u $t(entity.playlist_one)",
"input_skipDuplicates": "preskoči duplikate",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "ažuriraj server",
"success": "server uspešno ažuriran"
},
"queryEditor": {
"input_optionMatchAll": "pronađi sve",
"input_optionMatchAny": "pronađi bilo koji"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "pretraga teksta pesme"
},
"editPlaylist": {
"title": "izmeni $t(entity.playlist_one)"
}
},
"entity": {
"genre_one": "žanr",
"genre_few": "žanrova",
"genre_other": "žanrova",
"playlistWithCount_one": "{{count}} plejlista",
"playlistWithCount_few": "{{count}} plejlista",
"playlistWithCount_other": "{{count}} plejlista",
"playlist_one": "plejlista",
"playlist_few": "plejlista",
"playlist_other": "plejlista",
"artist_one": "umetnik",
"artist_few": "umetnika",
"artist_other": "umetnika",
"folderWithCount_one": "{{count}} folder",
"folderWithCount_few": "{{count}} foldera",
"folderWithCount_other": "{{count}} foldera",
"albumArtist_one": "album umetnika",
"albumArtist_few": "albuma umetnika",
"albumArtist_other": "albuma umetnika",
"track_one": "pesma",
"track_few": "pesama",
"track_other": "pesama",
"albumArtistCount_one": "{{count}} album umetnika",
"albumArtistCount_few": "{{count}} albuma umetnika",
"albumArtistCount_other": "{{count}} albuma umetnika",
"albumWithCount_one": "{{count}} album",
"albumWithCount_few": "{{count}} albuma",
"albumWithCount_other": "{{count}} albuma",
"favorite_one": "favorit",
"favorite_few": "favorita",
"favorite_other": "favorita",
"artistWithCount_one": "{{count}} umetnik",
"artistWithCount_few": "{{count}} umetnika",
"artistWithCount_other": "{{count}} umetnika",
"folder_one": "folder",
"folder_few": "foldera",
"folder_other": "foldera",
"smartPlaylist": "pametna $t(entity.playlist_one)",
"album_one": "album",
"album_few": "albumi",
"album_other": "albuma",
"genreWithCount_one": "{{count}} žanr",
"genreWithCount_few": "{{count}} žanrova",
"genreWithCount_other": "{{count}} žanrova",
"trackWithCount_one": "{{count}} pesma",
"trackWithCount_few": "{{count}} pesama",
"trackWithCount_other": "{{count}} pesama"
}
}
+345
View File
@@ -0,0 +1,345 @@
{
"action": {
"editPlaylist": "redigera $t(entity.playlist_one)",
"goToPage": "gå till sida",
"moveToTop": "flytta till toppen",
"clearQueue": "rensa kö",
"addToFavorites": "lägg till $t(entity.favorite_other)",
"addToPlaylist": "lägg till $t(entity.playlist_one)",
"createPlaylist": "skapa $t(entity.playlist_one)",
"removeFromPlaylist": "ta bort från $t(entity.playlist_one)",
"viewPlaylists": "visa $t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "ta bort $t(entity.playlist_one)",
"removeFromQueue": "ta bort från kö",
"deselectAll": "avmarkera alla",
"moveToBottom": "flytta till botten",
"setRating": "sätt betyg",
"toggleSmartPlaylistEditor": "växla $t(entity.smartPlaylist) redigerare",
"removeFromFavorites": "ta bort från $t(entity.favorite_other)"
},
"common": {
"backward": "bakåt",
"increase": "öka",
"rating": "betyg",
"bpm": "bpm",
"refresh": "laddaom",
"unknown": "okänd",
"areYouSure": "är du säker?",
"edit": "redigera",
"favorite": "favorit",
"left": "vänster",
"save": "spara",
"right": "höger",
"currentSong": "aktuell $t(entity.track_one)",
"collapse": "kollaps",
"trackNumber": "spår",
"descending": "fallande",
"add": "lägg till",
"gap": "avstånd",
"ascending": "stigande",
"dismiss": "avskeda",
"year": "år",
"manage": "hantera",
"limit": "gräns",
"minimize": "minimera",
"modified": "modifierad",
"duration": "längd",
"name": "namn",
"maximize": "maximera",
"decrease": "minska",
"ok": "ok",
"description": "beskrivning",
"configure": "konfigurera",
"path": "sökväg",
"no": "nej",
"owner": "ägare",
"enable": "aktivera",
"clear": "töm",
"forward": "framåt",
"delete": "ta bort",
"cancel": "avbryt",
"forceRestartRequired": "starta om för att tillämpa ändringar... Stäng meddelandet för att starta om",
"setting": "inställning",
"version": "version",
"title": "titel",
"filter_one": "filter",
"filter_other": "filter",
"filters": "filter",
"create": "skapa",
"bitrate": "bithastighet",
"saveAndReplace": "spara och skrivöver",
"action_one": "handling",
"action_other": "handlingar",
"playerMustBePaused": "spelaren måste pausas",
"confirm": "bekräfta",
"resetToDefault": "återställ till standard",
"home": "hem",
"comingSoon": "kommer snart…",
"reset": "nollställ",
"channel_one": "kanal",
"channel_other": "kanaler",
"disable": "inaktivera",
"sortOrder": "ordning",
"none": "ingen",
"menu": "meny",
"restartRequired": "omstart krävs",
"previousSong": "föregående $t(entity.track_one)",
"noResultsFromQuery": "frågan returnerade inga resultat",
"quit": "avsluta",
"expand": "expandera",
"search": "sök",
"saveAs": "spara som",
"disc": "skiva",
"yes": "ja",
"random": "slumpmässig",
"size": "storlek",
"biography": "biografi",
"note": "anteckning",
"center": "center"
},
"error": {
"remotePortWarning": "starta om servern för att tillämpa den nya porten",
"systemFontError": "ett fel uppstod vid försök att hämta systemteckensnitt",
"playbackError": "ett fel uppstod vid försök att spela upp media",
"endpointNotImplementedError": "endpoint {{endpoint}} är inte implementerad för {{serverType}}",
"remotePortError": "ett fel uppstod vid försök att ange serverporten",
"serverRequired": "server krävs",
"authenticationFailed": "autentiseringen misslyckades",
"apiRouteError": "det går inte att dirigera begäran",
"genericError": "ett fel uppstod",
"credentialsRequired": "autentiseringsuppgifter som krävs",
"sessionExpiredError": "din session har löpt ut",
"remoteEnableError": "Ett fel uppstod vid försök att $t(common.enable) servern",
"localFontAccessDenied": "åtkomst nekad till lokala teckensnitt",
"serverNotSelectedError": "ingen server vald",
"remoteDisableError": "ett fel uppstod vid försök av $t(common.disable) servern",
"mpvRequired": "MPV krävs",
"audioDeviceFetchError": "ett fel uppstod vid hämtning av ljudenheter",
"invalidServer": "ogiltig server",
"loginRateError": "för många inloggningsförsök, försök igen om några sekunder"
},
"filter": {
"mostPlayed": "mest spelade",
"comment": "kommentar",
"playCount": "antal spelningar",
"recentlyUpdated": "nyligen uppdaterad",
"channels": "$t(common.channel_other)",
"isCompilation": "är kompilering",
"recentlyPlayed": "nyligen spelad",
"isRated": "är betygsatt",
"owner": "$t(common.owner)",
"title": "titel",
"rating": "betyg",
"search": "sök",
"bitrate": "bithastighet",
"genre": "$t(entity.genre_one)",
"recentlyAdded": "nyligen tillagda",
"note": "anteckning",
"name": "namn",
"dateAdded": "datum tillagt",
"releaseDate": "utgivningsdag",
"communityRating": "betyg från communityn",
"path": "sökväg",
"favorited": "favoritmärkt",
"albumArtist": "$t(entity.albumArtist_one)",
"isRecentlyPlayed": "spelas nyligen",
"isFavorited": "är favoritmärkt",
"bpm": "bpm",
"releaseYear": "utgivningsår",
"id": "id",
"disc": "skiva",
"biography": "biografi",
"artist": "$t(entity.artist_one)",
"duration": "längd",
"isPublic": "är offentlig",
"random": "slumpmässig",
"lastPlayed": "senast spelad",
"toYear": "till år",
"fromYear": "från år",
"album": "$t(entity.album_one)",
"trackNumber": "spår",
"songCount": "sångräkning",
"criticRating": "kritikerbetyg"
},
"form": {
"deletePlaylist": {
"title": "ta bort $t(entity.playlist_one)",
"success": "$t(entity.playlist_one) har tagits bort",
"input_confirm": "Skriv namnet på $t(entity.playlist_one) för att bekräfta"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"title": "skapa $t(entity.playlist_one)",
"input_public": "offentlig",
"input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) skapad",
"input_owner": "$t(common.owner)"
},
"addServer": {
"title": "lägg till server",
"input_username": "användarnamn",
"input_url": "länk",
"input_password": "lösenord",
"input_legacyAuthentication": "aktivera äldre autentisering",
"input_name": "server namn",
"success": "servern har lagts till",
"input_savePassword": "spara lösenord",
"ignoreSsl": "ignorera ssl ($t(common.restartRequired))",
"ignoreCors": "ignorera cors ($t(common.restartRequired))",
"error_savePassword": "ett fel uppstod när lösenordet skulle sparas"
},
"addToPlaylist": {
"success": "tillade {{message}} $t(entity.song_other) til {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "lägg till i $t(entity.playlist_one)",
"input_skipDuplicates": "hoppa över dubbletter",
"input_playlists": "$t(entity.playlist_other)"
},
"updateServer": {
"title": "uppdatera server",
"success": "servern har uppdaterats"
},
"queryEditor": {
"input_optionMatchAll": "matcha alla",
"input_optionMatchAny": "matcha något"
},
"lyricSearch": {
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)",
"title": "sångtext sök"
},
"editPlaylist": {
"title": "redigera $t(entity.playlist_one)"
}
},
"page": {
"fullscreenPlayer": {
"config": {
"showLyricMatch": "Visa låttext matchning",
"dynamicBackground": "dynamisk bakgrund",
"followCurrentLyric": "följ aktuell låttext",
"opacity": "ogenomskinlighet",
"lyricSize": "låttext storlek",
"lyricAlignment": "låttext justering",
"lyricGap": "låttext mellanrum",
"synchronized": "synkroniserad",
"showLyricProvider": "visa sångtextleverantör",
"unsynchronized": "osynkroniserad"
},
"lyrics": "sångtext",
"related": "relaterad"
},
"appMenu": {
"selectServer": "välj server",
"version": "version {{version}}",
"settings": "$t(common.setting_other)",
"manageServers": "hantera servrar",
"expandSidebar": "expandera sidofältet",
"openBrowserDevtools": "öppna webbläsarens utvecklingsverktyg",
"quit": "$t(common.quit)",
"goBack": "gå tillbaka",
"goForward": "gå framåt",
"collapseSidebar": "växla sidofältet"
},
"contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"setRating": "$t(action.setRating)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} vald",
"removeFromQueue": "$t(action.removeFromQueue)"
},
"albumDetail": {
"moreFromArtist": "mer från $t(entity.genre_one)",
"moreFromGeneric": "mer från {{item}}"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
},
"sidebar": {
"nowPlaying": "nu spelas"
},
"home": {
"mostPlayed": "mest spelade",
"newlyAdded": "nytillkomna utgåvor",
"explore": "utforska från ditt bibliotek",
"recentlyPlayed": "nyligen spelat"
},
"setting": {
"playbackTab": "uppspelning",
"generalTab": "allmänt",
"hotkeysTab": "snabbtangenter",
"windowTab": "fönster"
},
"globalSearch": {
"commands": {
"serverCommands": "serverkommandon",
"goToPage": "gå till sidan",
"searchFor": "sök efter {{query}}"
},
"title": "kommandon"
}
},
"entity": {
"playlist_one": "spellista",
"playlist_other": "spellistor",
"artist_one": "artist",
"artist_other": "artister",
"albumArtist_one": "albumartist",
"albumArtist_other": "albumartister",
"albumArtistCount_one": "{{count}} Albumartist",
"albumArtistCount_other": "{{count}} Albumartister",
"albumWithCount_one": "{{count}} album",
"albumWithCount_other": "{{count}} album",
"favorite_one": "favorit",
"favorite_other": "favoriter",
"folder_one": "mapp",
"folder_other": "mappar",
"album_one": "album",
"album_other": "album",
"playlistWithCount_one": "{{count}} spellista",
"playlistWithCount_other": "{{count}} spellistor",
"folderWithCount_one": "{{count}} mapp",
"folderWithCount_other": "{{count}} mappar",
"track_one": "spår",
"track_other": "spår",
"trackWithCount_one": "{{count}} spår",
"trackWithCount_other": "{{count}} spår"
},
"player": {
"repeat_all": "repetera alla",
"repeat": "repetera",
"queue_remove": "ta bort markerad",
"playRandom": "spela slumpmässigt",
"previous": "föregående",
"favorite": "favorit",
"next": "nästa",
"shuffle": "blanda",
"playbackFetchNoResults": "inga låtar hittades",
"playbackFetchInProgress": "laddar låtar…",
"addNext": "lägg till nästa",
"playbackSpeed": "uppspelningshastighet",
"playbackFetchCancel": "det här tar ett tag... stäng aviseringen för att avbryta",
"play": "spela",
"repeat_off": "repetera inaktiverad",
"queue_clear": "rensa kö",
"muted": "mutad",
"queue_moveToTop": "flytta markerad till botten",
"queue_moveToBottom": "flytta markerad till toppen",
"addLast": "lägg till sist",
"mute": "muta"
}
}
+596
View File
@@ -0,0 +1,596 @@
{
"action": {
"editPlaylist": "编辑 $t(entity.playlist_one)",
"moveToTop": "跳至顶部",
"clearQueue": "清空播放队列",
"addToFavorites": "添加到$t(entity.favorite_other)",
"addToPlaylist": "添加到$t(entity.playlist_one)",
"createPlaylist": "创建$t(entity.playlist_one)",
"removeFromPlaylist": "从$t(entity.playlist_one)移除",
"viewPlaylists": "查看$t(entity.playlist_other)",
"refresh": "$t(common.refresh)",
"deletePlaylist": "删除$t(entity.playlist_one)",
"removeFromQueue": "从播放队列中移除",
"deselectAll": "取消全选",
"moveToBottom": "跳至底部",
"setRating": "评分",
"toggleSmartPlaylistEditor": "切换$t(entity.smartPlaylist)编辑器",
"removeFromFavorites": "从$t(entity.favorite_other)移除",
"goToPage": "转到页面"
},
"common": {
"increase": "增高",
"rating": "评分",
"bpm": "bpm",
"refresh": "刷新",
"unknown": "未知",
"edit": "编辑",
"favorite": "收藏",
"left": "左",
"save": "保存",
"right": "右",
"currentSong": "当前$t(entity.track_one)",
"collapse": "折叠",
"trackNumber": "音轨编号",
"descending": "降序",
"add": "添加",
"ascending": "升序",
"dismiss": "忽略",
"year": "年份",
"manage": "管理",
"minimize": "最小化",
"modified": "已修改",
"name": "名称",
"maximize": "最大化",
"decrease": "降低",
"description": "描述",
"configure": "配置",
"path": "路径",
"center": "中央",
"owner": "所有者",
"enable": "启用",
"clear": "清空",
"forward": "前进",
"delete": "删除",
"cancel": "取消",
"forceRestartRequired": "重启应用使更改生效…关闭通知即可重启",
"setting": "设置",
"version": "版本",
"title": "标题",
"filter_other": "筛选",
"filters": "筛选",
"create": "创建",
"bitrate": "比特率",
"saveAndReplace": "保存并替换",
"action_other": "操作",
"confirm": "确认",
"resetToDefault": "重置为默认",
"home": "主页",
"comingSoon": "即将上线…",
"reset": "重置",
"disable": "禁用",
"menu": "菜单",
"restartRequired": "需要重启应用",
"previousSong": "上一首$t(entity.track_one)",
"noResultsFromQuery": "未查询到匹配结果",
"quit": "退出",
"expand": "展开",
"search": "搜索",
"saveAs": "保存为",
"random": "随机",
"biography": "简介",
"sortOrder": "顺序",
"backward": "返回",
"gap": "空隙",
"limit": "限制",
"duration": "时长",
"ok": "好",
"no": "否",
"playerMustBePaused": "播放器须被暂停",
"channel_other": "频道",
"none": "无",
"disc": "盘",
"yes": "是",
"size": "大小",
"areYouSure": "是否继续?",
"note": "注释"
},
"entity": {
"albumArtist_other": "专辑艺术家",
"albumArtistCount_other": "{{count}} 位专辑艺术家",
"albumWithCount_other": "{{count}} 张专辑",
"album_other": "专辑",
"genre_other": "流派",
"playlistWithCount_other": "{{count}} 个播放列表",
"playlist_other": "播放列表",
"artist_other": "艺术家",
"folderWithCount_other": "{{count}} 个文件夹",
"track_other": "乐曲",
"favorite_other": "收藏",
"artistWithCount_other": "{{count}} 位艺术家",
"folder_other": "文件夹",
"smartPlaylist": "智能$t(entity.playlist_one)",
"genreWithCount_other": "{{count}} 种流派",
"trackWithCount_other": "{{count}} 首乐曲"
},
"player": {
"repeat_all": "全部循环",
"stop": "停止",
"repeat": "循环",
"queue_remove": "移除所选",
"playRandom": "随机播放",
"skip": "跳过",
"previous": "上一首",
"toggleFullscreenPlayer": "全屏",
"skip_back": "向后跳过",
"favorite": "收藏",
"next": "下一首",
"shuffle": "随机播放",
"playbackFetchNoResults": "未找到歌曲",
"playbackFetchInProgress": "正在加载歌曲…",
"addNext": "添加为播放列表下一首",
"playbackFetchCancel": "请稍等…关闭通知以取消操作",
"play": "播放",
"repeat_off": "不循环",
"queue_clear": "清空播放队列",
"muted": "已静音",
"unfavorite": "取消收藏",
"queue_moveToTop": "使所选置底",
"queue_moveToBottom": "使所选置顶",
"shuffle_off": "未启用随机播放",
"addLast": "添加到播放列表末尾",
"mute": "静音",
"skip_forward": "向前跳过",
"playbackSpeed": "播放速度",
"pause": "暂停"
},
"setting": {
"crossfadeStyle_description": "选择用于音频播放器的淡入淡出风格",
"hotkey_favoriteCurrentSong": "收藏 $t(common.currentSong)",
"crossfadeStyle": "淡入淡出风格",
"audioExclusiveMode_description": "启用独占输出模式。在此模式下,系统通常被锁定,只有 mpv 能够输出音频",
"disableLibraryUpdateOnStartup": "禁用启动时查找新版本",
"gaplessAudio": "无缝音频",
"audioPlayer_description": "选择用于播放的音频播放器",
"globalMediaHotkeys": "全局媒体快捷键",
"gaplessAudio_description": "调整 mpv 无缝音频设置",
"disableAutomaticUpdates": "禁用自动更新",
"followLyric_description": "滚动歌词到当前播放位置",
"audioExclusiveMode": "音频独占模式",
"font": "字体",
"crossfadeDuration_description": "设置淡入淡出持续时间",
"audioDevice": "音频设备",
"enableRemote": "启用远程控制服务器",
"fontType": "字体类型",
"applicationHotkeys": "应用快捷键",
"globalMediaHotkeys_description": "启用或禁用系统媒体快捷键以控制播放",
"customFontPath": "自定义字体路径",
"followLyric": "跟随当前歌词",
"crossfadeDuration": "淡入淡出持续时间",
"audioPlayer": "音频播放器",
"discordApplicationId": "{{discord}} 应用 id",
"applicationHotkeys_description": "配置应用快捷键。勾选设为全局快捷键(仅桌面端)",
"customFontPath_description": "设置应用使用的自定义字体路径",
"gaplessAudio_optionWeak": "弱(推荐)",
"font_description": "设置应用使用的字体",
"audioDevice_description": "选择用于播放的音频设备(仅 web 播放器)",
"enableRemote_description": "启用远程控制服务器,以允许其他设备控制此应用",
"remotePort_description": "设置远程服务器端口",
"hotkey_skipBackward": "向回跳过",
"replayGainMode_description": "根据乐曲元数据中存储的{{ReplayGain}}值调整音量增益",
"volumeWheelStep_description": "在音量滑块上滚动鼠标滚轮时要更改的音量大小",
"theme_description": "设置应用的主题",
"hotkey_playbackPause": "暂停",
"replayGainFallback": "{{ReplayGain}}后备替代",
"sidebarCollapsedNavigation_description": "在折叠的侧边栏中显示或隐藏导航",
"hotkey_volumeUp": "音量增高",
"skipDuration": "跳过时长",
"showSkipButtons": "显示跳过按钮",
"playButtonBehavior_optionPlay": "$t(player.play)",
"minimumScrobblePercentage": "最小 scrobble 时长(百分比)",
"lyricFetch": "从互联网获取歌词",
"scrobble": "记录播放信息(Scrobble",
"skipDuration_description": "设置每次按下跳过按钮将会跳过的时长",
"fontType_optionSystem": "系统字体",
"mpvExecutablePath_description": "设置 mpv 二进制文件的路径",
"sampleRate": "采样率",
"sidePlayQueueStyle_optionAttached": "吸附",
"sidebarConfiguration": "侧边栏设定",
"sampleRate_description": "所选的采样率与当前媒体的频率不同时,用于输出的采样率",
"replayGainMode_optionNone": "$t(common.none)",
"hotkey_zoomIn": "放大",
"scrobble_description": "在你的社交媒体中记录播放信息",
"hotkey_browserForward": "浏览器前进",
"themeLight": "主题(浅色)",
"fontType_optionBuiltIn": "内置字体",
"hotkey_playbackPlayPause": "播放/暂停",
"hotkey_rate1": "评为 1 星",
"hotkey_skipForward": "向后跳过",
"sidePlayQueueStyle": "侧边播放列表样式",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"zoom": "缩放率",
"minimizeToTray_description": "将应用程序最小化到系统托盘",
"hotkey_playbackPlay": "播放",
"hotkey_togglePreviousSongFavorite": "收藏 / 取消收藏$t(common.previousSong)",
"hotkey_volumeDown": "音量降低",
"hotkey_unfavoritePreviousSong": "取消收藏$t(common.previousSong)",
"hotkey_globalSearch": "全局搜索",
"remoteUsername_description": "设置远程控制服务器的用户名。如果用户名和密码都为空,则身份验证将被禁用",
"exitToTray_description": "退出应用时最小化到系统托盘而非关闭",
"hotkey_favoritePreviousSong": "收藏 $t(common.previousSong)",
"replayGainMode_optionAlbum": "$t(entity.album_one)",
"lyricOffset": "歌词偏移(毫秒)",
"fontType_optionCustom": "自定义字体",
"themeDark_description": "应用将使用深色主题",
"remotePassword": "远程控制服务器密码",
"lyricFetchProvider": "歌词源",
"language_description": "设置应用的语言($t(common.restartRequired)",
"playbackStyle_optionCrossFade": "交叉淡入淡出",
"hotkey_rate3": "评为 3 星",
"mpvExtraParameters": "mpv 参数",
"replayGainMode_optionTrack": "$t(entity.track_one)",
"themeLight_description": "应用将使用浅色主题",
"hotkey_toggleFullScreenPlayer": "全屏播放",
"hotkey_localSearch": "页面内搜索",
"hotkey_toggleQueue": "显示 / 隐藏播放队列",
"zoom_description": "设置应用程序的缩放率",
"remotePassword_description": "设置远程控制服务器的密码。这些凭据默认以不安全的方式传输,因此您应该使用一个您不在意的唯一密码",
"hotkey_rate5": "评为 5 星",
"hotkey_playbackPrevious": "上一曲",
"showSkipButtons_description": "在播放条显示/隐藏播放按钮",
"language": "语言",
"playbackStyle": "播放风格",
"hotkey_toggleShuffle": "切换随机播放设定",
"theme": "主题",
"playbackStyle_description": "选择播放器的播放风格",
"mpvExecutablePath": "mpv 二进制文件路径",
"hotkey_rate2": "评为 2 星",
"playButtonBehavior_description": "设置将歌曲添加到队列时播放按钮的默认行为",
"minimumScrobblePercentage_description": "歌曲被记录为已播放(scrobble)所需的最小播放百分比",
"exitToTray": "退出时最小化到托盘",
"hotkey_rate4": "评为 4 星",
"showSkipButton_description": "在播放条上显示/隐藏跳过按钮",
"savePlayQueue": "保存播放列表",
"minimumScrobbleSeconds_description": "歌曲被记录为已播放(scrobble)所需的最小播放时间",
"skipPlaylistPage_description": "打开歌单时,直接查看歌曲列表而非查看默认页面",
"fontType_description": "内置字体可以选择 Feishin 提供的字体之一。系统字体允许您选择操作系统提供的任何字体。自定义选项允许您使用自己的字体",
"playButtonBehavior": "播放按钮行为",
"volumeWheelStep": "音量滚轮步长",
"sidebarPlaylistList_description": "显示或隐藏侧边栏歌单列表",
"sidePlayQueueStyle_description": "设置侧边播放列表样式",
"replayGainMode": "{{ReplayGain}}模式",
"playbackStyle_optionNormal": "通常",
"windowBarStyle": "窗口顶栏风格",
"floatingQueueArea": "显示浮动队列悬停区域",
"replayGainFallback_description": "乐曲没有{{ReplayGain}}标签时应用的增益(以分贝为单位)",
"hotkey_toggleRepeat": "切换循环播放设定",
"lyricOffset_description": "将歌词偏移指定的毫秒数",
"sidebarConfiguration_description": "选择侧边栏包含的项目与顺序",
"remotePort": "远程服务器端口",
"hotkey_playbackNext": "下一曲",
"useSystemTheme_description": "使用系统定义的浅色或深色主题",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "从多个互联网源获取歌词",
"lyricFetchProvider_description": "选择歌词源。 歌词源顺序与查询顺序一致",
"sidePlayQueueStyle_optionDetached": "不吸附",
"hotkey_zoomOut": "缩小",
"hotkey_unfavoriteCurrentSong": "取消收藏$t(common.currentSong)",
"hotkey_rate0": "清除评分",
"floatingQueueArea_description": "在屏幕右侧显示一个悬停图标,以查看播放队列",
"hotkey_volumeMute": "静音",
"hotkey_toggleCurrentSongFavorite": "收藏 / 取消收藏$t(common.currentSong)",
"remoteUsername": "远程服务器用户名",
"hotkey_browserBack": "浏览器后退",
"showSkipButton": "显示跳过按钮",
"sidebarPlaylistList": "侧边栏歌单列表",
"minimizeToTray": "最小化到托盘",
"skipPlaylistPage": "跳过歌单页面",
"themeDark": "主题(深色)",
"sidebarCollapsedNavigation": "侧边栏(已折叠)导航",
"minimumScrobbleSeconds": "最小 scrobble 时间(秒)",
"hotkey_playbackStop": "停止",
"windowBarStyle_description": "选择窗口顶栏的风格",
"savePlayQueue_description": "当应用程序关闭时保存播放队列,并在应用程序打开时恢复它",
"useSystemTheme": "跟随系统",
"mpvExecutablePath_help": "每行一个",
"discordIdleStatus_description": "启用后将会在播放器闲置时更新状态",
"replayGainClipping_description": "自动降低增益以防止{{ReplayGain}}造成削波",
"replayGainPreamp": "{{ReplayGain}}前置放大(分贝)",
"replayGainClipping": "{{ReplayGain}}削波",
"discordUpdateInterval": "{{discord}} rich presence 更新间隔",
"discordApplicationId_description": "{{discord}} rich presence 应用 id(默认为 {{defaultId}}",
"discordUpdateInterval_description": "更新间隔秒数(至少 15 秒)",
"discordRichPresence_description": "在 {{discord}} rich presence 中显示播放状态。图片键为:{{icon}}、{{playing}} 和 {{paused}} ",
"accentColor": "强调色",
"accentColor_description": "设置应用的强调色",
"replayGainPreamp_description": "调整应用在{{ReplayGain}}值上的前置放大增益",
"discordIdleStatus": "显示 rich presence 闲置状态",
"discordRichPresence": "{{discord}} rich presence"
},
"error": {
"remotePortWarning": "重启服务器使新端口生效",
"systemFontError": "获取系统字体时出现错误",
"playbackError": "无法播放媒体",
"endpointNotImplementedError": "{{serverType}} 尚未实现端点 {{endpoint}}",
"remotePortError": "设置远程服务器端口时发生错误",
"serverRequired": "需要服务器",
"authenticationFailed": "认证失败",
"apiRouteError": "请求失败:无法路由",
"genericError": "发生了错误",
"credentialsRequired": "需要凭证",
"sessionExpiredError": "会话已过期",
"remoteEnableError": "$t(common.enable)远程服务器时出现错误",
"localFontAccessDenied": "无法获取本地字体",
"serverNotSelectedError": "未选择服务器",
"remoteDisableError": "$t(common.disable)远程服务器时出现错误",
"mpvRequired": "需要 MPV",
"audioDeviceFetchError": "无法获取音频设备",
"invalidServer": "无效的服务器",
"loginRateError": "登录请求尝试次数过多,请稍后再试"
},
"filter": {
"mostPlayed": "播放最多",
"playCount": "播放次数",
"recentlyPlayed": "最近播放",
"title": "标题",
"rating": "评分",
"search": "搜索",
"bitrate": "比特率",
"recentlyAdded": "最近添加",
"name": "名称",
"dateAdded": "已添加日期",
"releaseDate": "发布日期",
"communityRating": "社区评分",
"path": "路径",
"favorited": "已收藏",
"albumArtist": "$t(entity.albumArtist_one)",
"releaseYear": "发布年份",
"biography": "个人简介",
"songCount": "曲目数",
"random": "随机",
"lastPlayed": "上次播放过",
"toYear": "从年份",
"fromYear": "从年份",
"criticRating": "评论家评分",
"trackNumber": "曲目",
"bpm": "bpm",
"artist": "$t(entity.artist_one)",
"comment": "评论",
"isCompilation": "为合辑",
"isFavorited": "已收藏",
"isPublic": "已公开",
"recentlyUpdated": "最近更新",
"isRated": "已评分",
"isRecentlyPlayed": "最近播放过",
"channels": "$t(common.channel_other)",
"owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)",
"note": "注释",
"albumCount": "$t(entity.album_other)数",
"id": "id",
"disc": "盘",
"duration": "时长",
"album": "$t(entity.album_one)"
},
"page": {
"sidebar": {
"nowPlaying": "正在播放",
"playlists": "$t(entity.playlist_other)",
"search": "$t(common.search)",
"tracks": "$t(entity.track_other)",
"albums": "$t(entity.album_other)",
"genres": "$t(entity.genre_other)",
"folders": "$t(entity.folder_other)",
"settings": "$t(common.setting_other)",
"home": "$t(common.home)",
"artists": "$t(entity.artist_other)",
"albumArtists": "$t(entity.albumArtist_other)"
},
"fullscreenPlayer": {
"config": {
"showLyricMatch": "显示匹配的歌词",
"dynamicBackground": "动态背景",
"synchronized": "已同步",
"opacity": "透明度",
"lyricSize": "歌词字体大小",
"showLyricProvider": "显示歌词提供者",
"unsynchronized": "未同步",
"lyricAlignment": "歌词对齐",
"useImageAspectRatio": "使用图片纵横比",
"lyricGap": "歌词间距",
"followCurrentLyric": "跟随当前歌词"
},
"lyrics": "歌词",
"related": "相关",
"upNext": "即将播放"
},
"appMenu": {
"selectServer": "选择服务器",
"version": "版本 {{version}}",
"manageServers": "管理服务器",
"expandSidebar": "展开侧边栏",
"collapseSidebar": "折叠侧边栏",
"openBrowserDevtools": "打开浏览器开发者工具",
"goBack": "返回",
"goForward": "前进",
"settings": "$t(common.setting_other)",
"quit": "$t(common.quit)"
},
"home": {
"mostPlayed": "最多播放",
"newlyAdded": "最近添加的发布",
"explore": "从库中搜索",
"recentlyPlayed": "最近播放",
"title": "$t(common.home)"
},
"albumDetail": {
"moreFromArtist": "更多该$t(entity.artist_one)作品",
"moreFromGeneric": "更多{{item}}作品"
},
"setting": {
"playbackTab": "播放",
"generalTab": "通用",
"hotkeysTab": "快捷键",
"windowTab": "窗口"
},
"globalSearch": {
"commands": {
"serverCommands": "服务器命令",
"goToPage": "跳至页面",
"searchFor": "搜索 {{query}}"
},
"title": "命令"
},
"contextMenu": {
"setRating": "$t(action.setRating)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"play": "$t(player.play)",
"numberSelected": "{{count}} 已选择",
"removeFromQueue": "$t(action.removeFromQueue)",
"addToPlaylist": "$t(action.addToPlaylist)",
"addToFavorites": "$t(action.addToFavorites)",
"moveToTop": "$t(action.moveToTop)",
"deletePlaylist": "$t(action.deletePlaylist)",
"moveToBottom": "$t(action.moveToBottom)",
"createPlaylist": "$t(action.createPlaylist)",
"addNext": "$t(player.addNext)",
"deselectAll": "$t(action.deselectAll)",
"addLast": "$t(player.addLast)",
"addFavorite": "$t(action.addToFavorites)"
},
"trackList": {
"title": "$t(entity.track_other)"
},
"albumArtistList": {
"title": "$t(entity.albumArtist_other)"
},
"albumList": {
"title": "$t(entity.album_other)"
},
"genreList": {
"title": "$t(entity.genre_other)"
},
"playlistList": {
"title": "$t(entity.playlist_other)"
}
},
"form": {
"deletePlaylist": {
"title": "删除$t(entity.playlist_one)",
"success": "$t(entity.playlist_one)已成功删除",
"input_confirm": "输入$t(entity.playlist_one)的名称进行确认"
},
"addServer": {
"title": "添加服务器",
"input_username": "用户名",
"input_password": "密码",
"input_legacyAuthentication": "启用旧版认证方式",
"input_name": "服务器名",
"success": "服务器添加成功",
"input_savePassword": "保存密码",
"ignoreSsl": "忽略 ssl $t(common.restartRequired)",
"ignoreCors": "忽略 cors $t(common.restartRequired)",
"error_savePassword": "保存密码时出现错误",
"input_url": "url"
},
"addToPlaylist": {
"success": "添加 {{message}} $t(entity.song_other) 到 {{numOfPlaylists}} $t(entity.playlist_other)",
"title": "添加到$t(entity.playlist_one)",
"input_skipDuplicates": "跳过重复",
"input_playlists": "$t(entity.playlist_other)"
},
"createPlaylist": {
"title": "创建$t(entity.playlist_one)",
"input_public": "公开",
"success": "已成功创建 $t(entity.playlist_one)",
"input_description": "$t(common.description)",
"input_name": "$t(common.name)",
"input_owner": "$t(common.owner)"
},
"updateServer": {
"title": "更新服务器",
"success": "服务器已更新成功"
},
"queryEditor": {
"input_optionMatchAll": "匹配全部",
"input_optionMatchAny": "匹配任何"
},
"editPlaylist": {
"title": "编辑$t(entity.playlist_one)"
},
"lyricSearch": {
"title": "搜索歌词",
"input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)"
}
},
"table": {
"config": {
"general": {
"displayType": "显示风格",
"gap": "$t(common.gap)",
"tableColumns": "列",
"autoFitColumns": "列宽自适应",
"size": "$t(common.size)"
},
"view": {
"table": "表格",
"poster": "海报",
"card": "卡片"
},
"label": {
"releaseDate": "发布日期",
"title": "$t(common.title)",
"duration": "$t(common.duration)",
"dateAdded": "添加日期",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"lastPlayed": "最后播放",
"trackNumber": "音轨编号",
"rowIndex": "行号",
"rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)",
"album": "$t(entity.album_one)",
"note": "$t(common.note)",
"biography": "$t(common.biography)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"channels": "$t(common.channel_other)",
"playCount": "播放数",
"bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)",
"genre": "$t(entity.genre_one)",
"discNumber": "碟片编号",
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)",
"titleCombined": "$t(common.title)(合并)"
}
},
"column": {
"comment": "评论",
"album": "专辑",
"rating": "评价",
"favorite": "收藏",
"playCount": "播放次数",
"albumCount": "$t(entity.album_other)",
"releaseYear": "年份",
"lastPlayed": "最后播放",
"biography": "简介",
"releaseDate": "发布日期",
"bitrate": "比特率",
"title": "标题",
"bpm": "bpm",
"dateAdded": "添加日期",
"artist": "$t(entity.artist_one)",
"songCount": "$t(entity.track_other)",
"trackNumber": "音轨编号",
"genre": "$t(entity.genre_one)",
"albumArtist": "专辑艺术家",
"path": "路径",
"channels": "$t(common.channel_other)",
"discNumber": "盘"
}
}
}
+1
View File
@@ -0,0 +1 @@
{}
@@ -0,0 +1,63 @@
import { Client, SetActivity } from '@xhayper/discord-rpc';
import { ipcMain } from 'electron';
const FEISHIN_DISCORD_APPLICATION_ID = '1165957668758900787';
let client: Client | null = null;
const createClient = (clientId?: string) => {
client = new Client({
clientId: clientId || FEISHIN_DISCORD_APPLICATION_ID,
});
client.login();
return client;
};
const setActivity = (activity: SetActivity) => {
if (client) {
client.user?.setActivity({
...activity,
});
}
};
const clearActivity = () => {
if (client) {
client.user?.clearActivity();
}
};
const quit = () => {
if (client) {
client?.destroy();
}
};
ipcMain.handle('discord-rpc-initialize', (_event, clientId?: string) => {
createClient(clientId);
});
ipcMain.handle('discord-rpc-set-activity', (_event, activity: SetActivity) => {
if (client) {
setActivity(activity);
}
});
ipcMain.handle('discord-rpc-clear-activity', () => {
if (client) {
clearActivity();
}
});
ipcMain.handle('discord-rpc-quit', () => {
quit();
});
export const discordRpc = {
clearActivity,
createClient,
quit,
setActivity,
};
+3
View File
@@ -1,2 +1,5 @@
import './lyrics';
import './player';
import './remote';
import './settings';
import './discord-rpc';
+209
View File
@@ -0,0 +1,209 @@
import axios, { AxiosResponse } from 'axios';
import { load } from 'cheerio';
import { orderSearchResults } from './shared';
import {
LyricSource,
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '../../../../renderer/api/types';
const SEARCH_URL = 'https://genius.com/api/search/song';
// Adapted from https://github.com/NyaomiDEV/Sunamu/blob/master/src/main/lyricproviders/genius.ts
export interface GeniusResponse {
meta: Meta;
response: Response;
}
export interface Meta {
status: number;
}
export interface Response {
next_page: number;
sections: Section[];
}
export interface Section {
hits: Hit[];
type: string;
}
export interface Hit {
highlights: any[];
index: string;
result: Result;
type: string;
}
export interface Result {
_type: string;
annotation_count: number;
api_path: string;
artist_names: string;
featured_artists: any[];
full_title: string;
header_image_thumbnail_url: string;
header_image_url: string;
id: number;
instrumental: boolean;
language: string;
lyrics_owner_id: number;
lyrics_state: string;
lyrics_updated_at: number;
path: string;
primary_artist: PrimaryArtist;
pyongs_count: null;
relationships_index_url: string;
release_date_components: ReleaseDateComponents;
release_date_for_display: string;
release_date_with_abbreviated_month_for_display: string;
song_art_image_thumbnail_url: string;
song_art_image_url: string;
stats: Stats;
title: string;
title_with_featured: string;
updated_by_human_at: number;
url: string;
}
export interface PrimaryArtist {
_type: string;
api_path: string;
header_image_url: string;
id: number;
image_url: string;
index_character: string;
is_meme_verified: boolean;
is_verified: boolean;
name: string;
slug: string;
url: string;
}
export interface ReleaseDateComponents {
day: number;
month: number;
year: number;
}
export interface Stats {
hot: boolean;
unreviewed_annotations: number;
}
export async function getSearchResults(
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<GeniusResponse>;
const searchQuery = [params.artist, params.name].join(' ');
if (!searchQuery) {
return null;
}
try {
result = await axios.get(SEARCH_URL, {
params: {
per_page: '5',
q: searchQuery,
},
});
} catch (e) {
console.error('Genius search request got an error!', e);
return null;
}
const rawSongsResult = result.data.response?.sections?.[0]?.hits?.map((hit) => hit.result);
if (!rawSongsResult) return null;
const songResults: InternetProviderLyricSearchResponse[] = rawSongsResult.map((song) => {
return {
artist: song.artist_names,
id: song.url,
name: song.full_title,
source: LyricSource.GENIUS,
};
});
return orderSearchResults({ params, results: songResults });
}
async function getSongId(
params: LyricSearchQuery,
): Promise<Omit<InternetProviderLyricResponse, 'lyrics'> | null> {
let result: AxiosResponse<GeniusResponse>;
try {
result = await axios.get(SEARCH_URL, {
params: {
per_page: '1',
q: `${params.artist} ${params.name}`,
},
});
} catch (e) {
console.error('Genius search request got an error!', e);
return null;
}
const hit = result.data.response?.sections?.[0]?.hits?.[0]?.result;
if (!hit) {
return null;
}
return {
artist: hit.artist_names,
id: hit.url,
name: hit.full_title,
source: LyricSource.GENIUS,
};
}
export async function getLyricsBySongId(url: string): Promise<string | null> {
let result: AxiosResponse<string, any>;
try {
result = await axios.get<string>(url, { responseType: 'text' });
} catch (e) {
console.error('Genius lyrics request got an error!', e);
return null;
}
const $ = load(result.data.split('<br/>').join('\n'));
const lyricsDiv = $('div.lyrics');
if (lyricsDiv.length > 0) return lyricsDiv.text().trim();
const lyricSections = $('div[class^=Lyrics__Container]')
.map((_, e) => $(e).text())
.toArray()
.join('\n');
return lyricSections;
}
export async function query(
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
const response = await getSongId(params);
if (!response) {
console.error('Could not find the song on Genius!');
return null;
}
const lyrics = await getLyricsBySongId(response.id);
if (!lyrics) {
console.error('Could not get lyrics on Genius!');
return null;
}
return {
artist: response.artist,
id: response.id,
lyrics,
name: response.name,
source: LyricSource.GENIUS,
};
}
+149
View File
@@ -0,0 +1,149 @@
import { ipcMain } from 'electron';
import {
query as queryGenius,
getSearchResults as searchGenius,
getLyricsBySongId as getGenius,
} from './genius';
import {
query as queryLrclib,
getSearchResults as searchLrcLib,
getLyricsBySongId as getLrcLib,
} from './lrclib';
import {
query as queryNetease,
getSearchResults as searchNetease,
getLyricsBySongId as getNetease,
} from './netease';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
QueueSong,
LyricGetQuery,
LyricSource,
} from '../../../../renderer/api/types';
import { store } from '../settings/index';
type SongFetcher = (params: LyricSearchQuery) => Promise<InternetProviderLyricResponse | null>;
type SearchFetcher = (
params: LyricSearchQuery,
) => Promise<InternetProviderLyricSearchResponse[] | null>;
type GetFetcher = (id: string) => Promise<string | null>;
type CachedLyrics = Record<LyricSource, InternetProviderLyricResponse>;
const FETCHERS: Record<LyricSource, SongFetcher> = {
[LyricSource.GENIUS]: queryGenius,
[LyricSource.LRCLIB]: queryLrclib,
[LyricSource.NETEASE]: queryNetease,
};
const SEARCH_FETCHERS: Record<LyricSource, SearchFetcher> = {
[LyricSource.GENIUS]: searchGenius,
[LyricSource.LRCLIB]: searchLrcLib,
[LyricSource.NETEASE]: searchNetease,
};
const GET_FETCHERS: Record<LyricSource, GetFetcher> = {
[LyricSource.GENIUS]: getGenius,
[LyricSource.LRCLIB]: getLrcLib,
[LyricSource.NETEASE]: getNetease,
};
const MAX_CACHED_ITEMS = 10;
const lyricCache = new Map<string, CachedLyrics>();
const getRemoteLyrics = async (song: QueueSong) => {
const sources = store.get('lyrics', []) as LyricSource[];
const cached = lyricCache.get(song.id);
if (cached) {
for (const source of sources) {
const data = cached[source];
if (data) return data;
}
}
let lyricsFromSource = null;
for (const source of sources) {
const params = {
album: song.album || song.name,
artist: song.artistName,
duration: song.duration,
name: song.name,
};
const response = await FETCHERS[source](params);
if (response) {
const newResult = cached
? {
...cached,
[source]: response,
}
: ({ [source]: response } as CachedLyrics);
if (lyricCache.size === MAX_CACHED_ITEMS && cached === undefined) {
const toRemove = lyricCache.keys().next().value;
lyricCache.delete(toRemove);
}
lyricCache.set(song.id, newResult);
lyricsFromSource = response;
break;
}
}
return lyricsFromSource;
};
const searchRemoteLyrics = async (params: LyricSearchQuery) => {
const sources = store.get('lyrics', []) as LyricSource[];
const results: Record<LyricSource, InternetProviderLyricSearchResponse[]> = {
[LyricSource.GENIUS]: [],
[LyricSource.LRCLIB]: [],
[LyricSource.NETEASE]: [],
};
for (const source of sources) {
const response = await SEARCH_FETCHERS[source](params);
if (response) {
response.forEach((result) => {
results[source].push(result);
});
}
}
return results;
};
const getRemoteLyricsById = async (params: LyricGetQuery): Promise<string | null> => {
const { remoteSongId, remoteSource } = params;
const response = await GET_FETCHERS[remoteSource](remoteSongId);
if (!response) {
return null;
}
return response;
};
ipcMain.handle('lyric-by-song', async (_event, song: QueueSong) => {
const lyric = await getRemoteLyrics(song);
return lyric;
});
ipcMain.handle('lyric-search', async (_event, params: LyricSearchQuery) => {
const lyricResults = await searchRemoteLyrics(params);
return lyricResults;
});
ipcMain.handle('lyric-by-remote-id', async (_event, params: LyricGetQuery) => {
const lyricResults = await getRemoteLyricsById(params);
return lyricResults;
});
+119
View File
@@ -0,0 +1,119 @@
// Credits to https://github.com/tranxuanthang/lrcget for API implementation
import axios, { AxiosResponse } from 'axios';
import { orderSearchResults } from './shared';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '../../../../renderer/api/types';
const FETCH_URL = 'https://lrclib.net/api/get';
const SEEARCH_URL = 'https://lrclib.net/api/search';
const TIMEOUT_MS = 5000;
export interface LrcLibSearchResponse {
albumName: string;
artistName: string;
id: number;
name: string;
}
export interface LrcLibTrackResponse {
albumName: string;
artistName: string;
duration: number;
id: number;
instrumental: boolean;
isrc: string;
lang: string;
name: string;
plainLyrics: string | null;
releaseDate: string;
spotifyId: string;
syncedLyrics: string | null;
}
export async function getSearchResults(
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<LrcLibSearchResponse[]>;
if (!params.name) {
return null;
}
try {
result = await axios.get<LrcLibSearchResponse[]>(SEEARCH_URL, {
params: {
q: params.name,
},
});
} catch (e) {
console.error('LrcLib search request got an error!', e);
return null;
}
if (!result.data) return null;
const songResults: InternetProviderLyricSearchResponse[] = result.data.map((song) => {
return {
artist: song.artistName,
id: String(song.id),
name: song.name,
source: LyricSource.LRCLIB,
};
});
return orderSearchResults({ params, results: songResults });
}
export async function getLyricsBySongId(songId: string): Promise<string | null> {
let result: AxiosResponse<LrcLibTrackResponse, any>;
try {
result = await axios.get<LrcLibTrackResponse>(`${FETCH_URL}/${songId}`);
} catch (e) {
console.error('LrcLib lyrics request got an error!', e);
return null;
}
return result.data.syncedLyrics || result.data.plainLyrics || null;
}
export async function query(
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
let result: AxiosResponse<LrcLibTrackResponse, any>;
try {
result = await axios.get<LrcLibTrackResponse>(FETCH_URL, {
params: {
album_name: params.album,
artist_name: params.artist,
duration: params.duration,
track_name: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('LrcLib search request got an error!', e);
return null;
}
const lyrics = result.data.syncedLyrics || result.data.plainLyrics || null;
if (!lyrics) {
console.error(`Could not get lyrics on LrcLib!`);
return null;
}
return {
artist: result.data.artistName,
id: String(result.data.id),
lyrics,
name: result.data.name,
source: LyricSource.LRCLIB,
};
}
+167
View File
@@ -0,0 +1,167 @@
import axios, { AxiosResponse } from 'axios';
import { LyricSource } from '../../../../renderer/api/types';
import { orderSearchResults } from './shared';
import type {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '/@/renderer/api/types';
const SEARCH_URL = 'https://music.163.com/api/search/get';
const LYRICS_URL = 'https://music.163.com/api/song/lyric';
// Adapted from https://github.com/NyaomiDEV/Sunamu/blob/master/src/main/lyricproviders/netease.ts
export interface NetEaseResponse {
code: number;
result: Result;
}
export interface Result {
hasMore: boolean;
songCount: number;
songs: Song[];
}
export interface Song {
album: Album;
alias: string[];
artists: Artist[];
copyrightId: number;
duration: number;
fee: number;
ftype: number;
id: number;
mark: number;
mvid: number;
name: string;
rUrl: null;
rtype: number;
status: number;
transNames?: string[];
}
export interface Album {
artist: Artist;
copyrightId: number;
id: number;
mark: number;
name: string;
picId: number;
publishTime: number;
size: number;
status: number;
transNames?: string[];
}
export interface Artist {
albumSize: number;
alias: any[];
fansGroup: null;
id: number;
img1v1: number;
img1v1Url: string;
name: string;
picId: number;
picUrl: null;
trans: null;
}
export async function getSearchResults(
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<NetEaseResponse>;
const searchQuery = [params.artist, params.name].join(' ');
if (!searchQuery) {
return null;
}
try {
result = await axios.get(SEARCH_URL, {
params: {
limit: 5,
offset: 0,
s: searchQuery,
type: '1',
},
});
} catch (e) {
console.error('NetEase search request got an error!', e);
return null;
}
const rawSongsResult = result?.data.result?.songs;
if (!rawSongsResult) return null;
const songResults: InternetProviderLyricSearchResponse[] = rawSongsResult.map((song) => {
const artist = song.artists ? song.artists.map((artist) => artist.name).join(', ') : '';
return {
artist,
id: String(song.id),
name: song.name,
source: LyricSource.NETEASE,
};
});
return orderSearchResults({ params, results: songResults });
}
async function getMatchedLyrics(
params: LyricSearchQuery,
): Promise<Omit<InternetProviderLyricResponse, 'lyrics'> | null> {
const results = await getSearchResults(params);
const firstMatch = results?.[0];
if (!firstMatch || (firstMatch?.score && firstMatch.score > 0.5)) {
return null;
}
return firstMatch;
}
export async function getLyricsBySongId(songId: string): Promise<string | null> {
let result: AxiosResponse<any, any>;
try {
result = await axios.get(LYRICS_URL, {
params: {
id: songId,
kv: '-1',
lv: '-1',
},
});
} catch (e) {
console.error('NetEase lyrics request got an error!', e);
return null;
}
return result.data.klyric?.lyric || result.data.lrc?.lyric;
}
export async function query(
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
const lyricsMatch = await getMatchedLyrics(params);
if (!lyricsMatch) {
console.error('Could not find the song on NetEase!');
return null;
}
const lyrics = await getLyricsBySongId(lyricsMatch.id);
if (!lyrics) {
console.error('Could not get lyrics on NetEase!');
return null;
}
return {
artist: lyricsMatch.artist,
id: lyricsMatch.id,
lyrics,
name: lyricsMatch.name,
source: LyricSource.NETEASE,
};
}
+34
View File
@@ -0,0 +1,34 @@
import Fuse from 'fuse.js';
import {
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '../../../../renderer/api/types';
export const orderSearchResults = (args: {
params: LyricSearchQuery;
results: InternetProviderLyricSearchResponse[];
}) => {
const { params, results } = args;
const options: Fuse.IFuseOptions<InternetProviderLyricSearchResponse> = {
fieldNormWeight: 1,
includeScore: true,
keys: [
{ getFn: (song) => song.name, name: 'name', weight: 3 },
{ getFn: (song) => song.artist, name: 'artist' },
],
threshold: 1.0,
};
const fuse = new Fuse(results, options);
const searchResults = fuse.search<InternetProviderLyricSearchResponse>({
...(params.artist && { artist: params.artist }),
...(params.name && { name: params.name }),
});
return searchResults.map((result) => ({
...result.item,
score: result.score,
}));
};
+143 -68
View File
@@ -1,130 +1,205 @@
import console from 'console';
import { ipcMain } from 'electron';
import { getMainWindow, getMpvInstance } from '../../../main';
import { getMpvInstance } from '../../../main';
import { PlayerData } from '/@/renderer/store';
declare module 'node-mpv';
function wait(timeout: number) {
return new Promise((resolve) => {
setTimeout(() => {
resolve('resolved');
}, timeout);
});
}
// function wait(timeout: number) {
// return new Promise((resolve) => {
// setTimeout(() => {
// resolve('resolved');
// }, timeout);
// });
// }
ipcMain.handle('player-is-running', async () => {
return getMpvInstance()?.isRunning();
});
ipcMain.handle('player-clean-up', async () => {
getMpvInstance()?.stop();
getMpvInstance()?.clearPlaylist();
});
ipcMain.on('player-start', async () => {
await getMpvInstance()?.play();
await getMpvInstance()
?.play()
.catch((err) => {
console.log('MPV failed to play', err);
});
});
// Starts the player
ipcMain.on('player-play', async () => {
await getMpvInstance()?.play();
await getMpvInstance()
?.play()
.catch((err) => {
console.log('MPV failed to play', err);
});
});
// Pauses the player
ipcMain.on('player-pause', async () => {
await getMpvInstance()?.pause();
await getMpvInstance()
?.pause()
.catch((err) => {
console.log('MPV failed to pause', err);
});
});
// Stops the player
ipcMain.on('player-stop', async () => {
await getMpvInstance()?.stop();
await getMpvInstance()
?.stop()
.catch((err) => {
console.log('MPV failed to stop', err);
});
});
// Goes to the next track in the playlist
ipcMain.on('player-next', async () => {
await getMpvInstance()?.next();
await getMpvInstance()
?.next()
.catch((err) => {
console.log('MPV failed to go to next', err);
});
});
// Goes to the previous track in the playlist
ipcMain.on('player-previous', async () => {
await getMpvInstance()?.prev();
await getMpvInstance()
?.prev()
.catch((err) => {
console.log('MPV failed to go to previous', err);
});
});
// Seeks forward or backward by the given amount of seconds
ipcMain.on('player-seek', async (_event, time: number) => {
await getMpvInstance()?.seek(time);
await getMpvInstance()
?.seek(time)
.catch((err) => {
console.log('MPV failed to seek', err);
});
});
// Seeks to the given time in seconds
ipcMain.on('player-seek-to', async (_event, time: number) => {
await getMpvInstance()?.goToPosition(time);
await getMpvInstance()
?.goToPosition(time)
.catch((err) => {
console.log(`MPV failed to seek to ${time}`, err);
});
});
// Sets the queue in position 0 and 1 to the given data. Used when manually starting a song or using the next/prev buttons
ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean) => {
if (!data.queue.current && !data.queue.next) {
await getMpvInstance()?.clearPlaylist();
await getMpvInstance()?.pause();
return;
}
if (!data.queue.current && !data.queue.next) {
await getMpvInstance()
?.clearPlaylist()
.catch((err) => {
console.log('MPV failed to clear playlist', err);
});
let complete = false;
let tryAttempts = 0;
while (!complete) {
if (tryAttempts > 3) {
getMainWindow()?.webContents.send('renderer-player-error', 'Failed to load song');
complete = true;
} else {
try {
if (data.queue.current) {
await getMpvInstance()?.load(data.queue.current.streamUrl, 'replace');
}
if (data.queue.next) {
await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
}
complete = true;
} catch (err) {
console.error(err);
tryAttempts += 1;
await wait(500);
}
await getMpvInstance()
?.pause()
.catch((err) => {
console.log('MPV failed to pause', err);
});
return;
}
}
if (pause) {
await getMpvInstance()?.pause();
}
try {
if (data.queue.current) {
await getMpvInstance()
?.load(data.queue.current.streamUrl, 'replace')
.catch((err) => {
console.log('MPV failed to load song', err);
getMpvInstance()?.play();
});
if (data.queue.next) {
await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
}
}
} catch (err) {
console.error(err);
}
if (pause) {
getMpvInstance()?.pause();
}
});
// Replaces the queue in position 1 to the given data
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
const size = await getMpvInstance()?.getPlaylistSize();
const size = await getMpvInstance()
?.getPlaylistSize()
.catch((err) => {
console.log('MPV failed to get playlist size', err);
});
if (!size) {
return;
}
if (!size) {
return;
}
if (size > 1) {
await getMpvInstance()?.playlistRemove(1);
}
if (size > 1) {
await getMpvInstance()
?.playlistRemove(1)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
});
}
if (data.queue.next) {
await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
}
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
});
// Sets the next song in the queue when reaching the end of the queue
ipcMain.on('player-auto-next', async (_event, data: PlayerData) => {
// Always keep the current song as position 0 in the mpv queue
// This allows us to easily set update the next song in the queue without
// disturbing the currently playing song
await getMpvInstance()?.playlistRemove(0);
// Always keep the current song as position 0 in the mpv queue
// This allows us to easily set update the next song in the queue without
// disturbing the currently playing song
await getMpvInstance()
?.playlistRemove(0)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
getMpvInstance()?.pause();
});
if (data.queue.next) {
await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
}
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
});
// Sets the volume to the given value (0-100)
ipcMain.on('player-volume', async (_event, value: number) => {
await getMpvInstance()?.volume(value);
await getMpvInstance()
?.volume(value)
.catch((err) => {
console.log('MPV failed to set volume', err);
});
});
// Toggles the mute status
ipcMain.on('player-mute', async () => {
await getMpvInstance()?.mute();
ipcMain.on('player-mute', async (_event, mute: boolean) => {
await getMpvInstance()
?.mute(mute)
.catch((err) => {
console.log('MPV failed to toggle mute', err);
});
});
ipcMain.handle('player-get-time', async (): Promise<number | undefined> => {
return getMpvInstance()?.getTimePosition();
});
+16 -16
View File
@@ -2,26 +2,26 @@
import { BrowserWindow, globalShortcut } from 'electron';
export const enableMediaKeys = (window: BrowserWindow | null) => {
globalShortcut.register('MediaStop', () => {
window?.webContents.send('renderer-player-stop');
});
globalShortcut.register('MediaStop', () => {
window?.webContents.send('renderer-player-stop');
});
globalShortcut.register('MediaPlayPause', () => {
window?.webContents.send('renderer-player-play-pause');
});
globalShortcut.register('MediaPlayPause', () => {
window?.webContents.send('renderer-player-play-pause');
});
globalShortcut.register('MediaNextTrack', () => {
window?.webContents.send('renderer-player-next');
});
globalShortcut.register('MediaNextTrack', () => {
window?.webContents.send('renderer-player-next');
});
globalShortcut.register('MediaPreviousTrack', () => {
window?.webContents.send('renderer-player-previous');
});
globalShortcut.register('MediaPreviousTrack', () => {
window?.webContents.send('renderer-player-previous');
});
};
export const disableMediaKeys = () => {
globalShortcut.unregister('MediaStop');
globalShortcut.unregister('MediaPlayPause');
globalShortcut.unregister('MediaNextTrack');
globalShortcut.unregister('MediaPreviousTrack');
globalShortcut.unregister('MediaStop');
globalShortcut.unregister('MediaPlayPause');
globalShortcut.unregister('MediaNextTrack');
globalShortcut.unregister('MediaPreviousTrack');
};
+662
View File
@@ -0,0 +1,662 @@
import { Stats, promises } from 'fs';
import { readFile } from 'fs/promises';
import { IncomingMessage, Server, ServerResponse, createServer } from 'http';
import { join } from 'path';
import { deflate, gzip } from 'zlib';
import axios from 'axios';
import { app, ipcMain } from 'electron';
import { Server as WsServer, WebSocketServer, WebSocket } from 'ws';
import manifest from './manifest.json';
import { ClientEvent, ServerEvent } from '../../../../remote/types';
import { PlayerRepeat, SongUpdate } from '../../../../renderer/types';
import { getMainWindow } from '../../../main';
import { isLinux } from '../../../utils';
let mprisPlayer: any | undefined;
if (isLinux()) {
// eslint-disable-next-line global-require
mprisPlayer = require('../../linux/mpris').mprisPlayer;
}
interface RemoteConfig {
enabled: boolean;
password: string;
port: number;
username: string;
}
interface MimeType {
css: string;
html: string;
ico: string;
js: string;
}
interface StatefulWebSocket extends WebSocket {
alive: boolean;
auth: boolean;
}
let server: Server | undefined;
let wsServer: WsServer<StatefulWebSocket> | undefined;
const settings: RemoteConfig = {
enabled: false,
password: '',
port: 4333,
username: '',
};
type SendData = ServerEvent & {
client: StatefulWebSocket;
};
function send({ client, event, data }: SendData): void {
if (client.readyState === WebSocket.OPEN) {
if (client.alive && client.auth) {
client.send(JSON.stringify({ data, event }));
}
}
}
function broadcast(message: ServerEvent): void {
if (wsServer) {
for (const client of wsServer.clients) {
send({ client, ...message });
}
}
}
const shutdownServer = () => {
if (wsServer) {
wsServer.clients.forEach((client) => client.close(4000));
wsServer.close();
wsServer = undefined;
}
if (server) {
server.close();
server = undefined;
}
};
const MIME_TYPES: MimeType = {
css: 'text/css',
html: 'text/html; charset=UTF-8',
ico: 'image/x-icon',
js: 'application/javascript',
};
const PING_TIMEOUT_MS = 10000;
const UP_TIMEOUT_MS = 5000;
enum Encoding {
GZIP = 'gzip',
NONE = 'none',
ZLIB = 'deflate',
}
const GZIP_REGEX = /\bgzip\b/;
const ZLIB_REGEX = /bdeflate\b/;
let currentSong: SongUpdate = {
currentTime: 0,
};
const getEncoding = (encoding: string | string[]): Encoding => {
const encodingArray = Array.isArray(encoding) ? encoding : [encoding];
for (const code of encodingArray) {
if (code.match(GZIP_REGEX)) {
return Encoding.GZIP;
}
if (code.match(ZLIB_REGEX)) {
return Encoding.ZLIB;
}
}
return Encoding.NONE;
};
const cache = new Map<string, Map<Encoding, [number, Buffer]>>();
function setOk(
res: ServerResponse,
mtimeMs: number,
extension: keyof MimeType,
encoding: Encoding,
data?: Buffer,
) {
res.statusCode = data ? 200 : 304;
res.setHeader('Content-Type', MIME_TYPES[extension]);
res.setHeader('ETag', `"${mtimeMs}"`);
res.setHeader('Cache-Control', 'public');
if (encoding !== 'none') res.setHeader('Content-Encoding', encoding);
res.end(data);
}
async function serveFile(
req: IncomingMessage,
file: string,
extension: keyof MimeType,
res: ServerResponse,
): Promise<void> {
const fileName = `${file}.${extension}`;
const path = app.isPackaged
? join(__dirname, '../remote', fileName)
: join(__dirname, '../../../../../.erb/dll', fileName);
let stats: Stats;
try {
stats = await promises.stat(path);
} catch (error) {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end((error as Error).message);
// This is a resolve, even though it is an error, because we want specific (non 500) status
return Promise.resolve();
}
const encodings = req.headers['accept-encoding'] ?? '';
const selectedEncoding = getEncoding(encodings);
const ifMatch = req.headers['if-none-match'];
const fileInfo = cache.get(fileName);
let cached = fileInfo?.get(selectedEncoding);
if (cached && cached[0] !== stats.mtimeMs) {
cache.get(fileName)!.delete(selectedEncoding);
cached = undefined;
}
if (ifMatch && cached) {
const options = ifMatch.split(',');
for (const option of options) {
const mTime = Number(option.replaceAll('"', '').trim());
if (cached[0] === mTime) {
setOk(res, cached[0], extension, selectedEncoding);
return Promise.resolve();
}
}
}
if (!cached || cached[0] !== stats.mtimeMs) {
const content = await readFile(path);
switch (selectedEncoding) {
case Encoding.GZIP:
return new Promise((resolve, reject) => {
gzip(content, (error, result) => {
if (error) {
reject(error);
return;
}
const newEntry: [number, Buffer] = [stats.mtimeMs, result];
if (fileInfo) {
fileInfo.set(selectedEncoding, newEntry);
} else {
cache.set(fileName, new Map([[selectedEncoding, newEntry]]));
}
setOk(res, stats.mtimeMs, extension, selectedEncoding, result);
resolve();
});
});
case Encoding.ZLIB:
return new Promise((resolve, reject) => {
deflate(content, (error, result) => {
if (error) {
reject(error);
return;
}
const newEntry: [number, Buffer] = [stats.mtimeMs, result];
if (fileInfo) {
fileInfo.set(selectedEncoding, newEntry);
} else {
cache.set(fileName, new Map([[selectedEncoding, newEntry]]));
}
setOk(res, stats.mtimeMs, extension, selectedEncoding, result);
resolve();
});
});
default: {
const newEntry: [number, Buffer] = [stats.mtimeMs, content];
if (fileInfo) {
fileInfo.set(selectedEncoding, newEntry);
} else {
cache.set(fileName, new Map([[selectedEncoding, newEntry]]));
}
setOk(res, stats.mtimeMs, extension, selectedEncoding, content);
return Promise.resolve();
}
}
}
setOk(res, cached[0], extension, selectedEncoding, cached[1]);
return Promise.resolve();
}
function authorize(req: IncomingMessage): boolean {
if (settings.username || settings.password) {
// https://stackoverflow.com/questions/23616371/basic-http-authentication-with-node-and-express-4
const authorization = req.headers.authorization?.split(' ')[1] || '';
const [login, password] = Buffer.from(authorization, 'base64').toString().split(':');
return login === settings.username && password === settings.password;
}
return true;
}
const enableServer = (config: RemoteConfig): Promise<void> => {
return new Promise<void>((resolve, reject) => {
try {
if (server) {
server.close();
}
server = createServer({}, async (req, res) => {
if (!authorize(req)) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="401"');
res.end('Authorization required');
return;
}
try {
switch (req.url) {
case '/': {
await serveFile(req, 'index', 'html', res);
break;
}
case '/favicon.ico': {
await serveFile(req, 'favicon', 'ico', res);
break;
}
case '/remote.css': {
await serveFile(req, 'remote', 'css', res);
break;
}
case '/remote.js': {
await serveFile(req, 'remote', 'js', res);
break;
}
case '/manifest.json': {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(manifest));
break;
}
case '/credentials': {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(req.headers.authorization);
break;
}
default: {
if (req.url?.startsWith('/worker.js')) {
await serveFile(req, 'worker', 'js', res);
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Not Found');
}
}
}
} catch (error) {
res.statusCode = 500;
res.setHeader('Content-Type', 'text/plain');
res.end((error as Error).message);
}
});
server.listen(config.port, resolve);
wsServer = new WebSocketServer({ server });
wsServer.on('connection', (ws) => {
let authFail: number | undefined;
ws.alive = true;
if (!settings.username && !settings.password) {
ws.auth = true;
} else {
authFail = setTimeout(() => {
if (!ws.auth) {
ws.close();
}
}, 10000) as unknown as number;
}
ws.on('error', console.error);
ws.on('message', (data) => {
try {
const json = JSON.parse(data.toString()) as ClientEvent;
const event = json.event;
if (!ws.auth) {
if (event === 'authenticate') {
const auth = json.header.split(' ')[1];
const [login, password] = Buffer.from(auth, 'base64')
.toString()
.split(':');
if (login === settings.username && password === settings.password) {
ws.auth = true;
} else {
ws.close();
}
clearTimeout(authFail);
} else {
return;
}
}
switch (event) {
case 'pause': {
getMainWindow()?.webContents.send('renderer-player-pause');
break;
}
case 'play': {
getMainWindow()?.webContents.send('renderer-player-play');
break;
}
case 'next': {
getMainWindow()?.webContents.send('renderer-player-next');
break;
}
case 'previous': {
getMainWindow()?.webContents.send('renderer-player-previous');
break;
}
case 'proxy': {
const toFetch = currentSong.song?.imageUrl?.replaceAll(
/&(size|width|height=\d+)/g,
'',
);
if (!toFetch) return;
axios
.get(toFetch, { responseType: 'arraybuffer' })
.then((resp) => {
if (ws.readyState === WebSocket.OPEN) {
send({
client: ws,
data: Buffer.from(resp.data, 'binary').toString(
'base64',
),
event: 'proxy',
});
}
return null;
})
.catch((error) => {
if (ws.readyState === WebSocket.OPEN) {
send({
client: ws,
data: error.message,
event: 'error',
});
}
});
break;
}
case 'repeat': {
getMainWindow()?.webContents.send('renderer-player-toggle-repeat');
break;
}
case 'shuffle': {
getMainWindow()?.webContents.send('renderer-player-toggle-shuffle');
break;
}
case 'volume': {
let volume = Number(json.volume);
if (volume > 100) {
volume = 100;
} else if (volume < 0) {
volume = 0;
}
currentSong.volume = volume;
broadcast({ data: { volume }, event: 'song' });
getMainWindow()?.webContents.send('request-volume', {
volume,
});
if (mprisPlayer) {
mprisPlayer.volume = volume / 100;
}
break;
}
case 'favorite': {
const { favorite, id } = json;
if (id && id === currentSong.song?.id) {
getMainWindow()?.webContents.send('request-favorite', {
favorite,
id,
serverId: currentSong.song.serverId,
});
}
break;
}
case 'rating': {
const { rating, id } = json;
if (id && id === currentSong.song?.id) {
getMainWindow()?.webContents.send('request-rating', {
id,
rating,
serverId: currentSong.song.serverId,
});
}
break;
}
}
} catch (error) {
console.error(error);
}
});
ws.on('pong', () => {
ws.alive = true;
});
ws.send(JSON.stringify({ data: currentSong, event: 'song' }));
});
const heartBeat = setInterval(() => {
wsServer?.clients.forEach((ws) => {
if (!ws.alive) {
ws.terminate();
return;
}
ws.alive = false;
ws.ping();
});
}, PING_TIMEOUT_MS);
wsServer.on('close', () => {
clearInterval(heartBeat);
});
setTimeout(() => {
reject(new Error('Server did not come up'));
}, UP_TIMEOUT_MS);
} catch (error) {
reject(error);
shutdownServer();
}
});
};
ipcMain.handle('remote-enable', async (_event, enabled: boolean) => {
settings.enabled = enabled;
if (enabled) {
try {
await enableServer(settings);
} catch (error) {
return (error as Error).message;
}
} else {
shutdownServer();
}
return null;
});
ipcMain.handle('remote-port', async (_event, port: number) => {
settings.port = port;
});
ipcMain.on('remote-password', (_event, password: string) => {
settings.password = password;
wsServer?.clients.forEach((client) => client.close(4002));
});
ipcMain.handle(
'remote-settings',
async (_event, enabled: boolean, port: number, username: string, password: string) => {
settings.enabled = enabled;
settings.password = password;
settings.port = port;
settings.username = username;
if (enabled) {
try {
await enableServer(settings);
} catch (error) {
return (error as Error).message;
}
} else {
shutdownServer();
}
return null;
},
);
ipcMain.on('remote-username', (_event, username: string) => {
settings.username = username;
wsServer?.clients.forEach((client) => client.close(4002));
});
ipcMain.on('update-favorite', (_event, favorite: boolean, serverId: string, ids: string[]) => {
if (currentSong.song?.serverId !== serverId) return;
const id = currentSong.song.id;
for (const songId of ids) {
if (songId === id) {
currentSong.song.userFavorite = favorite;
broadcast({ data: { favorite, id: songId }, event: 'favorite' });
return;
}
}
});
ipcMain.on('update-rating', (_event, rating: number, serverId: string, ids: string[]) => {
if (currentSong.song?.serverId !== serverId) return;
const id = currentSong.song.id;
for (const songId of ids) {
if (songId === id) {
currentSong.song.userRating = rating;
broadcast({ data: { id: songId, rating }, event: 'rating' });
return;
}
}
});
ipcMain.on('update-repeat', (_event, repeat: PlayerRepeat) => {
currentSong.repeat = repeat;
broadcast({ data: { repeat }, event: 'song' });
});
ipcMain.on('update-shuffle', (_event, shuffle: boolean) => {
currentSong.shuffle = shuffle;
broadcast({ data: { shuffle }, event: 'song' });
});
ipcMain.on('update-song', (_event, data: SongUpdate) => {
const { song, ...rest } = data;
const songChanged = song?.id !== currentSong.song?.id;
if (!song?.id) {
currentSong = {
...currentSong,
...data,
song: undefined,
};
} else {
currentSong = {
...currentSong,
...data,
};
}
if (songChanged) {
broadcast({ data: { ...rest, song: song || null }, event: 'song' });
} else {
broadcast({ data: rest, event: 'song' });
}
});
ipcMain.on('update-volume', (_event, volume: number) => {
currentSong.volume = volume;
broadcast({ data: { volume }, event: 'song' });
});
if (mprisPlayer) {
mprisPlayer.on('loopStatus', (event: string) => {
const repeat =
event === 'Playlist'
? PlayerRepeat.ALL
: event === 'Track'
? PlayerRepeat.ONE
: PlayerRepeat.NONE;
currentSong.repeat = repeat;
broadcast({ data: { repeat }, event: 'song' });
});
mprisPlayer.on('shuffle', (shuffle: boolean) => {
currentSong.shuffle = shuffle;
broadcast({ data: { shuffle }, event: 'song' });
});
mprisPlayer.on('volume', (vol: number) => {
let volume = Math.round(vol * 100);
if (volume > 100) {
volume = 100;
} else if (volume < 0) {
volume = 0;
}
currentSong.volume = volume;
broadcast({ data: { volume }, event: 'song' });
});
}
@@ -0,0 +1,17 @@
{
"name": "Feishin Remote",
"short_name": "Feishin Remote",
"start_url": "/",
"background_color": "#000100",
"theme_color": "#E7E7E7",
"icons": [
{
"src": "favicon.ico",
"sizes": "32x32",
"type": "image/png",
"purpose": "maskable any"
}
],
"display": "standalone",
"orientation": "portrait"
}
+41 -3
View File
@@ -1,12 +1,50 @@
import { ipcMain } from 'electron';
import { ipcMain, safeStorage } from 'electron';
import Store from 'electron-store';
export const store = new Store();
ipcMain.handle('settings-get', (_event, data: { property: string }) => {
return store.get(`${data.property}`);
return store.get(`${data.property}`);
});
ipcMain.on('settings-set', (__event, data: { property: string; value: any }) => {
store.set(`${data.property}`, data.value);
store.set(`${data.property}`, data.value);
});
ipcMain.handle('password-get', (_event, server: string): string | null => {
if (safeStorage.isEncryptionAvailable()) {
const servers = store.get('server') as Record<string, string> | undefined;
if (!servers) {
return null;
}
const encrypted = servers[server];
if (!encrypted) return null;
const decrypted = safeStorage.decryptString(Buffer.from(encrypted, 'hex'));
return decrypted;
}
return null;
});
ipcMain.on('password-remove', (_event, server: string) => {
const passwords = store.get('server', {}) as Record<string, string>;
if (server in passwords) {
delete passwords[server];
}
store.set({ server: passwords });
});
ipcMain.handle('password-set', (_event, password: string, server: string) => {
if (safeStorage.isEncryptionAvailable()) {
const encrypted = safeStorage.encryptString(password);
const passwords = store.get('server', {}) as Record<string, string>;
passwords[server] = encrypted.toString('hex');
store.set({ server: passwords });
return true;
}
return false;
});
+103 -102
View File
@@ -1,168 +1,169 @@
import { ipcMain } from 'electron';
import Player from 'mpris-service';
import { QueueSong, RelatedArtist } from '../../../renderer/api/types';
import { PlayerRepeat, PlayerStatus, SongUpdate } from '../../../renderer/types';
import { getMainWindow } from '../../main';
import { PlayerRepeat, PlayerShuffle, PlayerStatus } from '/@/renderer/types';
const mprisPlayer = Player({
identity: 'Feishin',
maximumRate: 1.0,
minimumRate: 1.0,
name: 'Feishin',
rate: 1.0,
supportedInterfaces: ['player'],
supportedMimeTypes: ['audio/mpeg', 'application/ogg'],
supportedUriSchemes: ['file'],
identity: 'Feishin',
maximumRate: 1.0,
minimumRate: 1.0,
name: 'Feishin',
rate: 1.0,
supportedInterfaces: ['player'],
supportedMimeTypes: ['audio/mpeg', 'application/ogg'],
supportedUriSchemes: ['file'],
});
mprisPlayer.on('quit', () => {
process.exit();
process.exit();
});
mprisPlayer.on('stop', () => {
getMainWindow()?.webContents.send('renderer-player-stop');
mprisPlayer.playbackStatus = 'Paused';
getMainWindow()?.webContents.send('renderer-player-stop');
mprisPlayer.playbackStatus = 'Paused';
});
mprisPlayer.on('pause', () => {
getMainWindow()?.webContents.send('renderer-player-pause');
mprisPlayer.playbackStatus = 'Paused';
getMainWindow()?.webContents.send('renderer-player-pause');
mprisPlayer.playbackStatus = 'Paused';
});
mprisPlayer.on('play', () => {
getMainWindow()?.webContents.send('renderer-player-play');
mprisPlayer.playbackStatus = 'Playing';
getMainWindow()?.webContents.send('renderer-player-play');
mprisPlayer.playbackStatus = 'Playing';
});
mprisPlayer.on('playpause', () => {
getMainWindow()?.webContents.send('renderer-player-play-pause');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
} else {
mprisPlayer.playbackStatus = 'Paused';
}
getMainWindow()?.webContents.send('renderer-player-play-pause');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
} else {
mprisPlayer.playbackStatus = 'Paused';
}
});
mprisPlayer.on('next', () => {
getMainWindow()?.webContents.send('renderer-player-next');
getMainWindow()?.webContents.send('renderer-player-next');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
}
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
}
});
mprisPlayer.on('previous', () => {
getMainWindow()?.webContents.send('renderer-player-previous');
getMainWindow()?.webContents.send('renderer-player-previous');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING;
}
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING;
}
});
mprisPlayer.on('volume', (event: any) => {
getMainWindow()?.webContents.send('mpris-request-volume', {
volume: event,
});
mprisPlayer.on('volume', (vol: number) => {
let volume = Math.round(vol * 100);
if (volume > 100) {
volume = 100;
} else if (volume < 0) {
volume = 0;
}
getMainWindow()?.webContents.send('request-volume', {
volume,
});
});
mprisPlayer.on('shuffle', (event: boolean) => {
getMainWindow()?.webContents.send('mpris-request-toggle-shuffle', { shuffle: event });
mprisPlayer.shuffle = event;
getMainWindow()?.webContents.send('mpris-request-toggle-shuffle', { shuffle: event });
mprisPlayer.shuffle = event;
});
mprisPlayer.on('loopStatus', (event: string) => {
getMainWindow()?.webContents.send('mpris-request-toggle-repeat', { repeat: event });
mprisPlayer.loopStatus = event;
getMainWindow()?.webContents.send('mpris-request-toggle-repeat', { repeat: event });
mprisPlayer.loopStatus = event;
});
mprisPlayer.on('position', (event: any) => {
getMainWindow()?.webContents.send('mpris-request-position', {
position: event.position / 1e6,
});
getMainWindow()?.webContents.send('request-position', {
position: event.position / 1e6,
});
});
mprisPlayer.on('seek', (event: number) => {
getMainWindow()?.webContents.send('mpris-request-seek', {
offset: event / 1e6,
});
getMainWindow()?.webContents.send('request-seek', {
offset: event / 1e6,
});
});
ipcMain.on('mpris-update-position', (_event, arg) => {
mprisPlayer.getPosition = () => arg * 1e6;
mprisPlayer.getPosition = () => arg * 1e6;
});
ipcMain.on('mpris-update-seek', (_event, arg) => {
mprisPlayer.seeked(arg * 1e6);
mprisPlayer.seeked(arg * 1e6);
});
ipcMain.on('mpris-update-volume', (_event, arg) => {
mprisPlayer.volume = Number(arg);
ipcMain.on('update-volume', (_event, volume) => {
mprisPlayer.volume = Number(volume) / 100;
});
ipcMain.on('mpris-update-repeat', (_event, arg) => {
mprisPlayer.loopStatus = arg;
const REPEAT_TO_MPRIS: Record<PlayerRepeat, string> = {
[PlayerRepeat.ALL]: 'Playlist',
[PlayerRepeat.ONE]: 'Track',
[PlayerRepeat.NONE]: 'None',
};
ipcMain.on('update-repeat', (_event, arg: PlayerRepeat) => {
mprisPlayer.loopStatus = REPEAT_TO_MPRIS[arg];
});
ipcMain.on('mpris-update-shuffle', (_event, arg) => {
mprisPlayer.shuffle = arg;
ipcMain.on('update-shuffle', (_event, shuffle: boolean) => {
mprisPlayer.shuffle = shuffle;
});
ipcMain.on(
'mpris-update-song',
(
_event,
args: {
currentTime: number;
repeat: PlayerRepeat;
shuffle: PlayerShuffle;
song: QueueSong;
status: PlayerStatus;
},
) => {
ipcMain.on('update-song', (_event, args: SongUpdate) => {
const { song, status, repeat, shuffle } = args || {};
try {
mprisPlayer.playbackStatus = status;
mprisPlayer.playbackStatus = status === PlayerStatus.PLAYING ? 'Playing' : 'Paused';
if (repeat) {
mprisPlayer.loopStatus =
repeat === 'all' ? 'Playlist' : repeat === 'one' ? 'Track' : 'None';
}
if (repeat) {
mprisPlayer.loopStatus = REPEAT_TO_MPRIS[repeat];
}
if (shuffle) {
mprisPlayer.shuffle = shuffle;
}
if (shuffle) {
mprisPlayer.shuffle = shuffle;
}
if (!song) return;
if (!song) return;
const upsizedImageUrl = song.imageUrl
? song.imageUrl
?.replace(/&size=\d+/, '&size=300')
.replace(/\?width=\d+/, '?width=300')
.replace(/&height=\d+/, '&height=300')
: null;
const upsizedImageUrl = song.imageUrl
? song.imageUrl
?.replace(/&size=\d+/, '&size=300')
.replace(/\?width=\d+/, '?width=300')
.replace(/&height=\d+/, '&height=300')
: null;
mprisPlayer.metadata = {
'mpris:artUrl': upsizedImageUrl,
'mpris:length': song.duration ? Math.round((song.duration || 0) * 1e6) : null,
'mpris:trackid': song?.id
? mprisPlayer.objectPath(`track/${song.id?.replace('-', '')}`)
: '',
'xesam:album': song.album || null,
'xesam:albumArtist': song.albumArtists?.length ? song.albumArtists[0].name : null,
'xesam:artist':
song.artists?.length !== 0
? song.artists?.map((artist: RelatedArtist) => artist.name)
: null,
'xesam:discNumber': song.discNumber ? song.discNumber : null,
'xesam:genre': song.genres?.length ? song.genres.map((genre: any) => genre.name) : null,
'xesam:title': song.name || null,
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
'xesam:useCount':
song.playCount !== null && song.playCount !== undefined ? song.playCount : null,
};
mprisPlayer.metadata = {
'mpris:artUrl': upsizedImageUrl,
'mpris:length': song.duration ? Math.round((song.duration || 0) * 1e3) : null,
'mpris:trackid': song.id
? mprisPlayer.objectPath(`track/${song.id?.replace('-', '')}`)
: '',
'xesam:album': song.album || null,
'xesam:albumArtist': song.albumArtists?.length
? song.albumArtists.map((artist) => artist.name)
: null,
'xesam:artist': song.artists?.length ? song.artists.map((artist) => artist.name) : null,
'xesam:discNumber': song.discNumber ? song.discNumber : null,
'xesam:genre': song.genres?.length ? song.genres.map((genre: any) => genre.name) : null,
'xesam:title': song.name || null,
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
'xesam:useCount':
song.playCount !== null && song.playCount !== undefined ? song.playCount : null,
};
} catch (err) {
console.log(err);
console.log(err);
}
},
);
});
export { mprisPlayer };
+553 -458
View File
File diff suppressed because it is too large Load Diff
+255 -245
View File
@@ -1,269 +1,279 @@
import { app, Menu, shell, BrowserWindow, MenuItemConstructorOptions } from 'electron';
interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
selector?: string;
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
selector?: string;
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
}
export default class MenuBuilder {
mainWindow: BrowserWindow;
mainWindow: BrowserWindow;
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
buildMenu(): Menu {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
this.setupDevelopmentEnvironment();
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
const template =
process.platform === 'darwin' ? this.buildDarwinTemplate() : this.buildDefaultTemplate();
buildMenu(): Menu {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
this.setupDevelopmentEnvironment();
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
const template =
process.platform === 'darwin'
? this.buildDarwinTemplate()
: this.buildDefaultTemplate();
return menu;
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
setupDevelopmentEnvironment(): void {
this.mainWindow.webContents.on('context-menu', (_, props) => {
const { x, y } = props;
return menu;
}
Menu.buildFromTemplate([
{
click: () => {
this.mainWindow.webContents.inspectElement(x, y);
},
label: 'Inspect element',
},
]).popup({ window: this.mainWindow });
});
}
setupDevelopmentEnvironment(): void {
this.mainWindow.webContents.on('context-menu', (_, props) => {
const { x, y } = props;
buildDarwinTemplate(): MenuItemConstructorOptions[] {
const subMenuAbout: DarwinMenuItemConstructorOptions = {
label: 'Electron',
submenu: [
{
label: 'About ElectronReact',
selector: 'orderFrontStandardAboutPanel:',
},
{ type: 'separator' },
{ label: 'Services', submenu: [] },
{ type: 'separator' },
{
accelerator: 'Command+H',
label: 'Hide ElectronReact',
selector: 'hide:',
},
{
accelerator: 'Command+Shift+H',
label: 'Hide Others',
selector: 'hideOtherApplications:',
},
{ label: 'Show All', selector: 'unhideAllApplications:' },
{ type: 'separator' },
{
accelerator: 'Command+Q',
click: () => {
app.quit();
},
label: 'Quit',
},
],
};
const subMenuEdit: DarwinMenuItemConstructorOptions = {
label: 'Edit',
submenu: [
{ accelerator: 'Command+Z', label: 'Undo', selector: 'undo:' },
{ accelerator: 'Shift+Command+Z', label: 'Redo', selector: 'redo:' },
{ type: 'separator' },
{ accelerator: 'Command+X', label: 'Cut', selector: 'cut:' },
{ accelerator: 'Command+C', label: 'Copy', selector: 'copy:' },
{ accelerator: 'Command+V', label: 'Paste', selector: 'paste:' },
{
accelerator: 'Command+A',
label: 'Select All',
selector: 'selectAll:',
},
],
};
const subMenuViewDev: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Command+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: 'Reload',
},
{
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
{
accelerator: 'Alt+Command+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle Developer Tools',
},
],
};
const subMenuViewProd: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
],
};
const subMenuWindow: DarwinMenuItemConstructorOptions = {
label: 'Window',
submenu: [
{
accelerator: 'Command+M',
label: 'Minimize',
selector: 'performMiniaturize:',
},
{ accelerator: 'Command+W', label: 'Close', selector: 'performClose:' },
{ type: 'separator' },
{ label: 'Bring All to Front', selector: 'arrangeInFront:' },
],
};
const subMenuHelp: MenuItemConstructorOptions = {
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/tree/main/docs#readme');
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
};
const subMenuView =
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? subMenuViewDev
: subMenuViewProd;
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
}
buildDefaultTemplate() {
const templateDefault = [
{
label: '&File',
submenu: [
{
accelerator: 'Ctrl+O',
label: '&Open',
},
{
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
},
label: '&Close',
},
],
},
{
label: '&View',
submenu:
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? [
Menu.buildFromTemplate([
{
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: '&Reload',
click: () => {
this.mainWindow.webContents.inspectElement(x, y);
},
label: 'Inspect element',
},
]).popup({ window: this.mainWindow });
});
}
buildDarwinTemplate(): MenuItemConstructorOptions[] {
const subMenuAbout: DarwinMenuItemConstructorOptions = {
label: 'Electron',
submenu: [
{
label: 'About ElectronReact',
selector: 'orderFrontStandardAboutPanel:',
},
{ type: 'separator' },
{ label: 'Services', submenu: [] },
{ type: 'separator' },
{
accelerator: 'Command+H',
label: 'Hide ElectronReact',
selector: 'hide:',
},
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle &Full Screen',
accelerator: 'Command+Shift+H',
label: 'Hide Others',
selector: 'hideOtherApplications:',
},
{ label: 'Show All', selector: 'unhideAllApplications:' },
{ type: 'separator' },
{
accelerator: 'Command+Q',
click: () => {
app.quit();
},
label: 'Quit',
},
],
};
const subMenuEdit: DarwinMenuItemConstructorOptions = {
label: 'Edit',
submenu: [
{ accelerator: 'Command+Z', label: 'Undo', selector: 'undo:' },
{ accelerator: 'Shift+Command+Z', label: 'Redo', selector: 'redo:' },
{ type: 'separator' },
{ accelerator: 'Command+X', label: 'Cut', selector: 'cut:' },
{ accelerator: 'Command+C', label: 'Copy', selector: 'copy:' },
{ accelerator: 'Command+V', label: 'Paste', selector: 'paste:' },
{
accelerator: 'Command+A',
label: 'Select All',
selector: 'selectAll:',
},
],
};
const subMenuViewDev: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Command+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: 'Reload',
},
{
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle &Developer Tools',
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
]
: [
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle &Full Screen',
accelerator: 'Alt+Command+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle Developer Tools',
},
],
},
{
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/tree/main/docs#readme');
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
},
];
],
};
const subMenuViewProd: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
],
};
const subMenuWindow: DarwinMenuItemConstructorOptions = {
label: 'Window',
submenu: [
{
accelerator: 'Command+M',
label: 'Minimize',
selector: 'performMiniaturize:',
},
{ accelerator: 'Command+W', label: 'Close', selector: 'performClose:' },
{ type: 'separator' },
{ label: 'Bring All to Front', selector: 'arrangeInFront:' },
],
};
const subMenuHelp: MenuItemConstructorOptions = {
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal(
'https://github.com/electron/electron/tree/main/docs#readme',
);
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
};
return templateDefault;
}
const subMenuView =
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? subMenuViewDev
: subMenuViewProd;
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
}
buildDefaultTemplate() {
const templateDefault = [
{
label: '&File',
submenu: [
{
accelerator: 'Ctrl+O',
label: '&Open',
},
{
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
},
label: '&Close',
},
],
},
{
label: '&View',
submenu:
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? [
{
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: '&Reload',
},
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen(),
);
},
label: 'Toggle &Full Screen',
},
{
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle &Developer Tools',
},
]
: [
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen(),
);
},
label: 'Toggle &Full Screen',
},
],
},
{
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal(
'https://github.com/electron/electron/tree/main/docs#readme',
);
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
},
];
return templateDefault;
}
}
+13 -7
View File
@@ -1,17 +1,23 @@
import { contextBridge } from 'electron';
import { browser } from './preload/browser';
import { discordRpc } from './preload/discord-rpc';
import { ipc } from './preload/ipc';
import { localSettings } from './preload/local-settings';
import { lyrics } from './preload/lyrics';
import { mpris } from './preload/mpris';
import { mpvPlayer, mpvPlayerListener } from './preload/mpv-player';
import { remote } from './preload/remote';
import { utils } from './preload/utils';
contextBridge.exposeInMainWorld('electron', {
browser,
ipc,
localSettings,
mpris,
mpvPlayer,
mpvPlayerListener,
utils,
browser,
discordRpc,
ipc,
localSettings,
lyrics,
mpris,
mpvPlayer,
mpvPlayerListener,
remote,
utils,
});
+18 -10
View File
@@ -1,26 +1,34 @@
import { ipcRenderer } from 'electron';
const exit = () => {
ipcRenderer.send('window-close');
ipcRenderer.send('window-close');
};
const maximize = () => {
ipcRenderer.send('window-maximize');
ipcRenderer.send('window-maximize');
};
const minimize = () => {
ipcRenderer.send('window-minimize');
ipcRenderer.send('window-minimize');
};
const unmaximize = () => {
ipcRenderer.send('window-unmaximize');
ipcRenderer.send('window-unmaximize');
};
const quit = () => {
ipcRenderer.send('window-quit');
};
const devtools = () => {
ipcRenderer.send('window-dev-tools');
ipcRenderer.send('window-dev-tools');
};
export const browser = {
devtools,
exit,
maximize,
minimize,
unmaximize,
devtools,
exit,
maximize,
minimize,
quit,
unmaximize,
};
+28
View File
@@ -0,0 +1,28 @@
import { SetActivity } from '@xhayper/discord-rpc';
import { ipcRenderer } from 'electron';
const initialize = (clientId: string) => {
const client = ipcRenderer.invoke('discord-rpc-initialize', clientId);
return client;
};
const clearActivity = () => {
ipcRenderer.invoke('discord-rpc-clear-activity');
};
const setActivity = (activity: SetActivity) => {
ipcRenderer.invoke('discord-rpc-set-activity', activity);
};
const quit = () => {
ipcRenderer.invoke('discord-rpc-quit');
};
export const discordRpc = {
clearActivity,
initialize,
quit,
setActivity,
};
export type DiscordRpc = typeof discordRpc;
+6 -4
View File
@@ -1,14 +1,16 @@
import { ipcRenderer } from 'electron';
const removeAllListeners = (channel: string) => {
ipcRenderer.removeAllListeners(channel);
ipcRenderer.removeAllListeners(channel);
};
const send = (channel: string, ...args: any[]) => {
ipcRenderer.send(channel, ...args);
ipcRenderer.send(channel, ...args);
};
export const ipc = {
removeAllListeners,
send,
removeAllListeners,
send,
};
export type Ipc = typeof ipc;
+38 -11
View File
@@ -1,32 +1,59 @@
import { ipcRenderer } from 'electron';
import { IpcRendererEvent, ipcRenderer, webFrame } from 'electron';
import Store from 'electron-store';
const store = new Store();
const set = (property: string, value: string | Record<string, unknown> | boolean | string[]) => {
store.set(`${property}`, value);
store.set(`${property}`, value);
};
const get = (property: string) => {
return store.get(`${property}`);
return store.get(`${property}`);
};
const restart = () => {
ipcRenderer.send('app-restart');
ipcRenderer.send('app-restart');
};
const enableMediaKeys = () => {
ipcRenderer.send('global-media-keys-enable');
ipcRenderer.send('global-media-keys-enable');
};
const disableMediaKeys = () => {
ipcRenderer.send('global-media-keys-disable');
ipcRenderer.send('global-media-keys-disable');
};
const passwordGet = async (server: string): Promise<string | null> => {
return ipcRenderer.invoke('password-get', server);
};
const passwordRemove = (server: string) => {
ipcRenderer.send('password-remove', server);
};
const passwordSet = async (password: string, server: string): Promise<boolean> => {
return ipcRenderer.invoke('password-set', password, server);
};
const setZoomFactor = (zoomFactor: number) => {
webFrame.setZoomFactor(zoomFactor / 100);
};
const fontError = (cb: (event: IpcRendererEvent, file: string) => void) => {
ipcRenderer.on('custom-font-error', cb);
};
export const localSettings = {
disableMediaKeys,
enableMediaKeys,
get,
restart,
set,
disableMediaKeys,
enableMediaKeys,
fontError,
get,
passwordGet,
passwordRemove,
passwordSet,
restart,
set,
setZoomFactor,
};
export type LocalSettings = typeof localSettings;
+33
View File
@@ -0,0 +1,33 @@
import { ipcRenderer } from 'electron';
import {
InternetProviderLyricSearchResponse,
LyricGetQuery,
LyricSearchQuery,
LyricSource,
QueueSong,
} from '/@/renderer/api/types';
const getRemoteLyricsBySong = (song: QueueSong) => {
const result = ipcRenderer.invoke('lyric-by-song', song);
return result;
};
const searchRemoteLyrics = (
params: LyricSearchQuery,
): Promise<Record<LyricSource, InternetProviderLyricSearchResponse[]>> => {
const result = ipcRenderer.invoke('lyric-search', params);
return result;
};
const getRemoteLyricsByRemoteId = (id: LyricGetQuery) => {
const result = ipcRenderer.invoke('lyric-by-remote-id', id);
return result;
};
export const lyrics = {
getRemoteLyricsByRemoteId,
getRemoteLyricsBySong,
searchRemoteLyrics,
};
export type Lyrics = typeof lyrics;
+21 -50
View File
@@ -1,70 +1,41 @@
import { IpcRendererEvent, ipcRenderer } from 'electron';
import { QueueSong } from '/@/renderer/api/types';
const updateSong = (args: { currentTime: number; song: QueueSong }) => {
ipcRenderer.send('mpris-update-song', args);
};
import type { PlayerRepeat } from '/@/renderer/types';
const updatePosition = (timeSec: number) => {
ipcRenderer.send('mpris-update-position', timeSec);
ipcRenderer.send('mpris-update-position', timeSec);
};
const updateSeek = (timeSec: number) => {
ipcRenderer.send('mpris-update-seek', timeSec);
};
const updateVolume = (volume: number) => {
ipcRenderer.send('mpris-update-volume', volume);
};
const updateRepeat = (repeat: string) => {
ipcRenderer.send('mpris-update-repeat', repeat);
};
const updateShuffle = (shuffle: boolean) => {
ipcRenderer.send('mpris-update-shuffle', shuffle);
ipcRenderer.send('mpris-update-seek', timeSec);
};
const toggleRepeat = () => {
ipcRenderer.send('mpris-toggle-repeat');
ipcRenderer.send('mpris-toggle-repeat');
};
const toggleShuffle = () => {
ipcRenderer.send('mpris-toggle-shuffle');
ipcRenderer.send('mpris-toggle-shuffle');
};
const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => {
ipcRenderer.on('mpris-request-position', cb);
const requestToggleRepeat = (
cb: (event: IpcRendererEvent, data: { repeat: PlayerRepeat }) => void,
) => {
ipcRenderer.on('mpris-request-toggle-repeat', cb);
};
const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => {
ipcRenderer.on('mpris-request-seek', cb);
};
const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => {
ipcRenderer.on('mpris-request-volume', cb);
};
const requestToggleRepeat = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('mpris-request-toggle-repeat', cb);
};
const requestToggleShuffle = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('mpris-request-toggle-shuffle', cb);
const requestToggleShuffle = (
cb: (event: IpcRendererEvent, data: { shuffle: boolean }) => void,
) => {
ipcRenderer.on('mpris-request-toggle-shuffle', cb);
};
export const mpris = {
requestPosition,
requestSeek,
requestToggleRepeat,
requestToggleShuffle,
requestVolume,
toggleRepeat,
toggleShuffle,
updatePosition,
updateRepeat,
updateSeek,
updateShuffle,
updateSong,
updateVolume,
requestToggleRepeat,
requestToggleShuffle,
toggleRepeat,
toggleShuffle,
updatePosition,
updateSeek,
};
export type Mpris = typeof mpris;
+100 -80
View File
@@ -1,199 +1,219 @@
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { PlayerData } from '/@/renderer/store';
import { PlayerData, PlayerState } from '/@/renderer/store';
const initialize = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
ipcRenderer.send('player-initialize', data);
ipcRenderer.send('player-initialize', data);
};
const restart = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
ipcRenderer.send('player-restart', data);
ipcRenderer.send('player-restart', data);
};
const isRunning = () => {
return ipcRenderer.invoke('player-is-running');
};
const cleanup = () => {
return ipcRenderer.invoke('player-clean-up');
};
const setProperties = (data: Record<string, any>) => {
console.log('Setting property :>>', data);
ipcRenderer.send('player-set-properties', data);
console.log('Setting property :>>', data);
ipcRenderer.send('player-set-properties', data);
};
const autoNext = (data: PlayerData) => {
ipcRenderer.send('player-auto-next', data);
ipcRenderer.send('player-auto-next', data);
};
const currentTime = () => {
ipcRenderer.send('player-current-time');
ipcRenderer.send('player-current-time');
};
const mute = () => {
ipcRenderer.send('player-mute');
const mute = (mute: boolean) => {
ipcRenderer.send('player-mute', mute);
};
const next = () => {
ipcRenderer.send('player-next');
ipcRenderer.send('player-next');
};
const pause = () => {
ipcRenderer.send('player-pause');
ipcRenderer.send('player-pause');
};
const play = () => {
ipcRenderer.send('player-play');
ipcRenderer.send('player-play');
};
const previous = () => {
ipcRenderer.send('player-previous');
ipcRenderer.send('player-previous');
};
const restoreQueue = () => {
ipcRenderer.send('player-restore-queue');
ipcRenderer.send('player-restore-queue');
};
const saveQueue = (data: Record<string, any>) => {
ipcRenderer.send('player-save-queue', data);
ipcRenderer.send('player-save-queue', data);
};
const seek = (seconds: number) => {
ipcRenderer.send('player-seek', seconds);
ipcRenderer.send('player-seek', seconds);
};
const seekTo = (seconds: number) => {
ipcRenderer.send('player-seek-to', seconds);
ipcRenderer.send('player-seek-to', seconds);
};
const setQueue = (data: PlayerData, pause?: boolean) => {
ipcRenderer.send('player-set-queue', data, pause);
ipcRenderer.send('player-set-queue', data, pause);
};
const setQueueNext = (data: PlayerData) => {
ipcRenderer.send('player-set-queue-next', data);
ipcRenderer.send('player-set-queue-next', data);
};
const stop = () => {
ipcRenderer.send('player-stop');
ipcRenderer.send('player-stop');
};
const volume = (value: number) => {
ipcRenderer.send('player-volume', value);
ipcRenderer.send('player-volume', value);
};
const quit = () => {
ipcRenderer.send('player-quit');
ipcRenderer.send('player-quit');
};
const getCurrentTime = async () => {
return ipcRenderer.invoke('player-get-time');
};
const rendererAutoNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-auto-next', cb);
ipcRenderer.on('renderer-player-auto-next', cb);
};
const rendererCurrentTime = (cb: (event: IpcRendererEvent, data: number) => void) => {
ipcRenderer.on('renderer-player-current-time', cb);
ipcRenderer.on('renderer-player-current-time', cb);
};
const rendererNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-next', cb);
ipcRenderer.on('renderer-player-next', cb);
};
const rendererPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-pause', cb);
ipcRenderer.on('renderer-player-pause', cb);
};
const rendererPlay = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play', cb);
ipcRenderer.on('renderer-player-play', cb);
};
const rendererPlayPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play-pause', cb);
ipcRenderer.on('renderer-player-play-pause', cb);
};
const rendererPrevious = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-previous', cb);
ipcRenderer.on('renderer-player-previous', cb);
};
const rendererStop = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-stop', cb);
ipcRenderer.on('renderer-player-stop', cb);
};
const rendererSkipForward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-forward', cb);
ipcRenderer.on('renderer-player-skip-forward', cb);
};
const rendererSkipBackward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-backward', cb);
ipcRenderer.on('renderer-player-skip-backward', cb);
};
const rendererVolumeUp = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-up', cb);
ipcRenderer.on('renderer-player-volume-up', cb);
};
const rendererVolumeDown = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-down', cb);
ipcRenderer.on('renderer-player-volume-down', cb);
};
const rendererVolumeMute = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-mute', cb);
ipcRenderer.on('renderer-player-volume-mute', cb);
};
const rendererToggleRepeat = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-repeat', cb);
ipcRenderer.on('renderer-player-toggle-repeat', cb);
};
const rendererToggleShuffle = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-shuffle', cb);
ipcRenderer.on('renderer-player-toggle-shuffle', cb);
};
const rendererQuit = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-quit', cb);
ipcRenderer.on('renderer-player-quit', cb);
};
const rendererSaveQueue = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-save-queue', cb);
ipcRenderer.on('renderer-player-save-queue', cb);
};
const rendererRestoreQueue = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-restore-queue', cb);
const rendererRestoreQueue = (
cb: (event: IpcRendererEvent, data: Partial<PlayerState>) => void,
) => {
ipcRenderer.on('renderer-player-restore-queue', cb);
};
const rendererError = (cb: (event: IpcRendererEvent, data: string) => void) => {
ipcRenderer.on('renderer-player-error', cb);
ipcRenderer.on('renderer-player-error', cb);
};
export const mpvPlayer = {
autoNext,
currentTime,
initialize,
mute,
next,
pause,
play,
previous,
quit,
restart,
restoreQueue,
saveQueue,
seek,
seekTo,
setProperties,
setQueue,
setQueueNext,
stop,
volume,
autoNext,
cleanup,
currentTime,
getCurrentTime,
initialize,
isRunning,
mute,
next,
pause,
play,
previous,
quit,
restart,
restoreQueue,
saveQueue,
seek,
seekTo,
setProperties,
setQueue,
setQueueNext,
stop,
volume,
};
export const mpvPlayerListener = {
rendererAutoNext,
rendererCurrentTime,
rendererError,
rendererNext,
rendererPause,
rendererPlay,
rendererPlayPause,
rendererPrevious,
rendererQuit,
rendererRestoreQueue,
rendererSaveQueue,
rendererSkipBackward,
rendererSkipForward,
rendererStop,
rendererToggleRepeat,
rendererToggleShuffle,
rendererVolumeDown,
rendererVolumeMute,
rendererVolumeUp,
rendererAutoNext,
rendererCurrentTime,
rendererError,
rendererNext,
rendererPause,
rendererPlay,
rendererPlayPause,
rendererPrevious,
rendererQuit,
rendererRestoreQueue,
rendererSaveQueue,
rendererSkipBackward,
rendererSkipForward,
rendererStop,
rendererToggleRepeat,
rendererToggleShuffle,
rendererVolumeDown,
rendererVolumeMute,
rendererVolumeUp,
};
export type MpvPLayer = typeof mpvPlayer;
export type MpvPlayerListener = typeof mpvPlayerListener;
+101
View File
@@ -0,0 +1,101 @@
import { IpcRendererEvent, ipcRenderer } from 'electron';
import { SongUpdate } from '/@/renderer/types';
const requestFavorite = (
cb: (
event: IpcRendererEvent,
data: { favorite: boolean; id: string; serverId: string },
) => void,
) => {
ipcRenderer.on('request-favorite', cb);
};
const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => {
ipcRenderer.on('request-position', cb);
};
const requestRating = (
cb: (event: IpcRendererEvent, data: { id: string; rating: number; serverId: string }) => void,
) => {
ipcRenderer.on('request-rating', cb);
};
const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => {
ipcRenderer.on('request-seek', cb);
};
const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => {
ipcRenderer.on('request-volume', cb);
};
const setRemoteEnabled = (enabled: boolean): Promise<string | null> => {
const result = ipcRenderer.invoke('remote-enable', enabled);
return result;
};
const setRemotePort = (port: number): Promise<string | null> => {
const result = ipcRenderer.invoke('remote-port', port);
return result;
};
const updateFavorite = (favorite: boolean, serverId: string, ids: string[]) => {
ipcRenderer.send('update-favorite', favorite, serverId, ids);
};
const updatePassword = (password: string) => {
ipcRenderer.send('remote-password', password);
};
const updateSetting = (
enabled: boolean,
port: number,
username: string,
password: string,
): Promise<string | null> => {
return ipcRenderer.invoke('remote-settings', enabled, port, username, password);
};
const updateRating = (rating: number, serverId: string, ids: string[]) => {
ipcRenderer.send('update-rating', rating, serverId, ids);
};
const updateRepeat = (repeat: string) => {
ipcRenderer.send('update-repeat', repeat);
};
const updateShuffle = (shuffle: boolean) => {
ipcRenderer.send('update-shuffle', shuffle);
};
const updateSong = (args: SongUpdate) => {
ipcRenderer.send('update-song', args);
};
const updateUsername = (username: string) => {
ipcRenderer.send('remote-username', username);
};
const updateVolume = (volume: number) => {
ipcRenderer.send('update-volume', volume);
};
export const remote = {
requestFavorite,
requestPosition,
requestRating,
requestSeek,
requestVolume,
setRemoteEnabled,
setRemotePort,
updateFavorite,
updatePassword,
updateRating,
updateRepeat,
updateSetting,
updateShuffle,
updateSong,
updateUsername,
updateVolume,
};
export type Remote = typeof remote;
+5 -3
View File
@@ -1,7 +1,9 @@
import { isMacOS, isWindows, isLinux } from '../utils';
export const utils = {
isLinux,
isMacOS,
isWindows,
isLinux,
isMacOS,
isWindows,
};
export type Utils = typeof utils;
+27 -27
View File
@@ -6,47 +6,47 @@ import { URL } from 'url';
export let resolveHtmlPath: (htmlFileName: string) => string;
if (process.env.NODE_ENV === 'development') {
const port = process.env.PORT || 4343;
resolveHtmlPath = (htmlFileName: string) => {
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
};
const port = process.env.PORT || 4343;
resolveHtmlPath = (htmlFileName: string) => {
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
};
} else {
resolveHtmlPath = (htmlFileName: string) => {
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
};
resolveHtmlPath = (htmlFileName: string) => {
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
};
}
export const isMacOS = () => {
return process.platform === 'darwin';
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
return process.platform === 'linux';
};
export const hotkeyToElectronAccelerator = (hotkey: string) => {
let accelerator = hotkey;
let accelerator = hotkey;
const replacements = {
mod: 'CmdOrCtrl',
numpad: 'num',
numpadadd: 'numadd',
numpaddecimal: 'numdec',
numpaddivide: 'numdiv',
numpadenter: 'numenter',
numpadmultiply: 'nummult',
numpadsubtract: 'numsub',
};
const replacements = {
mod: 'CmdOrCtrl',
numpad: 'num',
numpadadd: 'numadd',
numpaddecimal: 'numdec',
numpaddivide: 'numdiv',
numpadenter: 'numenter',
numpadmultiply: 'nummult',
numpadsubtract: 'numsub',
};
Object.keys(replacements).forEach((key) => {
accelerator = accelerator.replace(key, replacements[key as keyof typeof replacements]);
});
Object.keys(replacements).forEach((key) => {
accelerator = accelerator.replace(key, replacements[key as keyof typeof replacements]);
});
return accelerator;
return accelerator;
};
+84
View File
@@ -0,0 +1,84 @@
import { useEffect } from 'react';
import { MantineProvider } from '@mantine/core';
import './styles/global.scss';
import { useIsDark, useReconnect } from '/@/remote/store';
import { Shell } from '/@/remote/components/shell';
export const App = () => {
const isDark = useIsDark();
const reconnect = useReconnect();
useEffect(() => {
reconnect();
}, [reconnect]);
return (
<MantineProvider
withGlobalStyles
withNormalizeCSS
theme={{
colorScheme: isDark ? 'dark' : 'light',
components: {
AppShell: {
styles: {
body: {
height: '100vh',
overflow: 'scroll',
},
},
},
Modal: {
styles: {
body: {
background: 'var(--modal-bg)',
height: '100vh',
},
close: { marginRight: '0.5rem' },
content: { borderRadius: '5px' },
header: {
background: 'var(--modal-header-bg)',
paddingBottom: '1rem',
},
title: { fontSize: 'medium', fontWeight: 500 },
},
},
},
defaultRadius: 'xs',
dir: 'ltr',
focusRing: 'auto',
focusRingStyles: {
inputStyles: () => ({
border: '1px solid var(--primary-color)',
}),
resetStyles: () => ({ outline: 'none' }),
styles: () => ({
outline: '1px solid var(--primary-color)',
outlineOffset: '-1px',
}),
},
fontFamily: 'var(--content-font-family)',
fontSizes: {
lg: '1.1rem',
md: '1rem',
sm: '0.9rem',
xl: '1.5rem',
xs: '0.8rem',
},
headings: {
fontFamily: 'var(--content-font-family)',
fontWeight: 700,
},
other: {},
spacing: {
lg: '2rem',
md: '1rem',
sm: '0.5rem',
xl: '4rem',
xs: '0rem',
},
}}
>
<Shell />
</MantineProvider>
);
};

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