Compare commits

...

470 Commits

Author SHA1 Message Date
jeffvli 5bab80b89b convert remaining locales to sentence case 2026-05-01 20:50:58 -07:00
jeffvli 066e5188f2 refactor some strings to sentence case 2026-05-01 20:50:57 -07:00
jeffvli 456f4d7f65 remove sentence case override configuration 2026-05-01 20:50:57 -07:00
jeffvli 02f5a1bd94 convert locales to new casing 2026-05-01 20:50:57 -07:00
jeffvli 4424e9ae33 convert EN localization to use proper casing, remove postprocessing from renderer 2026-05-01 20:47:24 -07:00
Hosted Weblate bc7ef0624b Translated using Weblate
Currently translated at 100.0% (1205 of 1205 strings) (Dutch)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/

Translated using Weblate

Currently translated at 100.0% (1205 of 1205 strings) (Dutch)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/

Co-authored-by: Joren Vansteenkiste <vansteenkiste.joren@telenet.be>
Co-authored-by: bokse <weblate@bokse.nl>
2026-05-01 21:32:24 +02:00
Pedro Daniel Reis 304ce8b881 [UI] Made sidebar image just use flex (#1975)
* made sidebar image just use flex

* force aspect ratio to be square

* prevent image container from expanding

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-05-01 12:32:18 -07:00
Jonne Saloranta 01011a49a2 Replace success toast with info when no songs are added (#1994) 2026-05-01 11:44:15 -07:00
York d24ca04878 fix: detect Homebrew mpv on macOS (#1989) 2026-05-01 11:43:07 -07:00
Hosted Weblate 640d38e5a9 Translated using Weblate
Currently translated at 100.0% (1205 of 1205 strings) (Catalan)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/

Co-authored-by: Ondo <SparkyOndo@proton.me>
2026-04-30 15:09:57 +00:00
jeffvli ac0c074d4b fix undefined / null parameter string for Subsonic (#1978) 2026-04-28 21:17:44 -07:00
jeffvli 6be5818493 migrate to mantine v9 2026-04-28 21:02:27 -07:00
Hosted Weblate 03edd5a639 Translated using Weblate
Currently translated at 100.0% (1205 of 1205 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1205 of 1205 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1205 of 1205 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 99.8% (1201 of 1203 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: York <goog10216922@gmail.com>
2026-04-29 04:45:44 +02:00
jeffvli f5eb3f1488 wrap useHotkeys to disable on command palette open (#1925) 2026-04-28 19:31:41 -07:00
jeffvli 8eab9edb15 fix performance issue related to blurred library header 2026-04-28 19:05:43 -07:00
Mitch Ray fcc69980e4 Stretch the wavesurfer waveform to the full height (#1962)
* Stretch the wavesurfer waveform to the full height

* Add waveform stretch setting
2026-04-27 20:28:03 -07:00
Hosted Weblate 053b78a3fd Translated using Weblate
Currently translated at 100.0% (1203 of 1203 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1203 of 1203 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 100.0% (1203 of 1203 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1203 of 1203 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: linger <linger0517@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-04-22 11:09:49 +02:00
mathew4 42ded966e4 fix: proper selection of next song when using shuffle and repeat-one (#1932) 2026-04-21 00:10:21 -07:00
Kendall Garner ea9119431c use urlsearchparams instead of qs (#1970) 2026-04-21 00:09:23 -07:00
vimae add0345f10 feat(lyrics): non-active lyric settings (#1954)
* feat: non-active lyric settings
2026-04-21 00:09:03 -07:00
Hosted Weblate e5a8324a79 Translated using Weblate
Currently translated at 99.3% (1193 of 1201 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Co-authored-by: yidaduizuoye <yidaduizuoye@outlook.com>
2026-04-19 06:09:51 +02:00
jeffvli cc4e933c07 fix missing path replacement transformations 2026-04-16 00:29:13 -07:00
Hosted Weblate 382d279dad Translated using Weblate
Currently translated at 100.0% (1201 of 1201 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1201 of 1201 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Translated using Weblate

Currently translated at 100.0% (1201 of 1201 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1201 of 1201 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 100.0% (1201 of 1201 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 79.9% (959 of 1200 strings) (Russian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/

Co-authored-by: Dylan MONTIGAUD <dylanmontigaud17@gmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Nick <n.grakhov08@gmail.com>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-04-15 06:10:05 +02:00
York b99899f128 fix MPV visualizer on macOS and handle exclusive mode UX (#1930) 2026-04-13 20:47:03 -07:00
korpseluv f5839bf39c normalize album release types and improve grouping logic (#1892) 2026-04-13 20:40:11 -07:00
Damien Erambert 914ed5b8f3 macOS 26-friendly icon (#1941) 2026-04-13 20:32:21 -07:00
Ross ca0a1569f8 Add everfrost dark and light themes (#1934)
Co-authored-by: Ross <ro@noirlab.edu>
2026-04-13 20:22:37 -07:00
Hosted Weblate 9f10fe398a Translated using Weblate
Currently translated at 58.1% (698 of 1200 strings) (Portuguese (Brazil))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pt_BR/

Translated using Weblate

Currently translated at 38.8% (466 of 1200 strings) (Ukrainian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/uk/

Co-authored-by: Yuri Shumyatsky <shumy260404@gmail.com>
Co-authored-by: albatrays <weblate.duct925@passmail.net>
2026-04-12 12:09:54 +00:00
Kendall Garner 8869278898 make theme selector serachable 2026-04-10 20:03:50 -07:00
Jeff 16c9e6cc1b Fix various build issues (#1942)
* remove dynamic import for platform features

* increase node memory limit on macOS build

* fix invalid dynamic imports in renderer

* remove discord-rpc import in renderer
2026-04-10 01:54:11 -07:00
Kendall Garner 2a6e9b6ad3 add extendInfo to alpha/beta builders 2026-04-08 07:28:34 -07:00
Kendall Garner 167b42df2b Merge pull request #1926 from noctuum/fix/wayland-screen-share-dialog
fix(linux): remove unnecessary screen capture from audio loopback handler
2026-04-08 02:11:36 +00:00
jeffvli e6a2bc3acf disable useTransition in router again 2026-04-07 18:21:32 -07:00
jeffvli ca3c7015c6 add fallback to direct streamURL if getTranscodeDecision fails 2026-04-07 18:14:47 -07:00
jeffvli c7c15d917a isolate item card control renders 2026-04-07 18:14:47 -07:00
noctuum 6adb29bc38 fix(linux): remove unnecessary desktopCapturer call from display media handler
The setDisplayMediaRequestHandler was calling desktopCapturer.getSources()
to provide a video source that the renderer never uses (it requests
video: false and only consumes audio tracks). On Wayland, this created a
new xdg-desktop-portal ScreenCast session on every launch, showing an
unavoidable screen share dialog because Electron does not persist
PipeWire restore tokens across desktopCapturer sessions.

Simplified the handler to return only { audio: 'loopback' }, which
captures system audio via PipeWire/PulseAudio monitor source without
any portal interaction.
2026-04-08 04:56:05 +07:00
Hosted Weblate 2c3cd7af24 Translated using Weblate
Currently translated at 94.6% (1136 of 1200 strings) (German)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/

Translated using Weblate

Currently translated at 100.0% (1200 of 1200 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1200 of 1200 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Translated using Weblate

Currently translated at 100.0% (1200 of 1200 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 100.0% (1200 of 1200 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1200 of 1200 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Translated using Weblate

Currently translated at 100.0% (1200 of 1200 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: Zarakkas <kaz@users.noreply.hosted.weblate.org>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: lorduskordus <lorduskordus@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-04-07 08:10:05 +02:00
jeffvli 3c442a2d40 update to v1.11.0 2026-04-06 17:10:18 -07:00
Andrzej Voss d67c185c93 feat: Make "Clear" button "Refresh" when there are no lyrics found. (#1920)
Ref: effvli/feishin#1919 - tl;dr: Button actually reloads/refreshes
lyrics info from the server too, it makes it, well, clearer what it does
in that case - allows to reread lyrics from server without clearing whole cache.
2026-04-06 16:59:01 -07:00
jeffvli ff96a5f121 lint 2026-04-06 12:06:55 -07:00
jeffvli 6fc7b6b271 support image drop for upload 2026-04-06 11:41:33 -07:00
jeffvli 918f453066 support navidrome artist image upload/delete 2026-04-06 11:41:26 -07:00
jeffvli 4a986069f8 set flac as default transcoding profile 2026-04-06 10:58:37 -07:00
jeffvli 11d26af893 remove arm/v7 from container build 2026-04-06 09:47:28 -07:00
jeffvli ad13fea033 update to v1.10.0 2026-04-05 22:41:06 -07:00
Hosted Weblate 8a75ec2558 Translated using Weblate
Currently translated at 100.0% (1196 of 1196 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: York <goog10216922@gmail.com>
2026-04-06 05:27:10 +00:00
Auzlex 895cbb4d16 fix(media-session): prevent handlers from being destroyed during playback (#1898)
Handlers were being registered and destroyed on state change/re-render,
causing media controls to vanish during rapid use or quick track skipping.
Persist handlers and add debounce for rapid track skipping.

Tested on Windows, Linux, and Android.
2026-04-05 22:27:04 -07:00
jeffvli 3f300c40cc add in-app prompt for system audio connection 2026-04-05 22:19:09 -07:00
jeffvli c8e8f58cce re-add useTransition to router 2026-04-05 21:54:07 -07:00
jeffvli 56cd50e0ed add react compiler 2026-04-05 18:38:06 -07:00
jeffvli 1b2a6dfc1f optimize item list controls 2026-04-05 18:21:28 -07:00
jeffvli 356f5487b0 reorder playlist context menu items 2026-04-05 14:10:07 -07:00
jeffvli 37501f2983 remove automatic autosize, use dummy fill column instead 2026-04-05 09:06:03 -07:00
jeffvli d61587b16f add automatic autosize columns when auto-fit is disabled 2026-04-05 08:12:10 -07:00
Kendall Garner 06b7b53dc9 feat(macos): add NSLocalNetworkUsageDescription 2026-04-05 08:03:28 -07:00
Kendall Garner 6c2cd1c274 fix(mpris): serve minimal metadata when playing radio
1. MPRIS (or `mpris-service`) is very fragile. If an invalid `mpris:trackid` (something with `-` or spaces) is passed in, it breaks. Use a minimal track id instead
2. Only populate album/artist/title
2026-04-05 08:01:28 -07:00
jeffvli ef129e4638 remove video from displayMedia request 2026-04-05 07:58:01 -07:00
jeffvli a01b4e664d add plex fork notice 2026-04-05 07:57:41 -07:00
jeffvli 0b45ab7f36 support real-time table column resizing 2026-04-05 07:48:54 -07:00
jeffvli 031d365262 decrease padding on list header 2026-04-05 03:49:31 -07:00
jeffvli 4fd56281d5 increase font size of smart playlist JSON editor 2026-04-05 02:42:05 -07:00
jeffvli 08ce8a4028 add nd v0.61.1 smart playlist fields 2026-04-05 02:38:40 -07:00
jeffvli e06877af76 make visualizer idle kill consistent for both 2026-04-05 02:35:32 -07:00
jeffvli 84395ce5b4 pass muted text props to JoinedArtists in left controls 2026-04-05 00:59:53 -07:00
jeffvli 94886a2d5a add system audio loopback for webaudio 2026-04-05 00:48:38 -07:00
jeffvli 25bb7f7069 fix scroll shadow z-indexing issue with table scrollbars 2026-04-04 23:48:48 -07:00
jeffvli 573fe5ee35 use external store for scroll shadow 2026-04-04 23:32:32 -07:00
jeffvli a868d4d539 combine wav codec check 2026-04-04 23:11:55 -07:00
jeffvli 564ee721c4 revert default transcoding profile to opus, add safari check for mp3 2026-04-04 23:08:53 -07:00
jeffvli a8d990db23 fix subsonic transcoding stream url to return raw string instead of fetch 2026-04-04 23:03:46 -07:00
jeffvli e21515f7fb add additional codec probes for transcoding profile, use mp3 instead of opus for default transcode 2026-04-04 23:03:29 -07:00
jeffvli 3e5a8ac78d re-add default suspense to album/artist routes 2026-04-04 22:25:21 -07:00
jeffvli 6c73d06dcf remove useTransition from router 2026-04-04 22:14:07 -07:00
jeffvli a8954bfa2a remove imageUrl in favor or imageId for artistInfo 2026-04-04 21:52:44 -07:00
jeffvli 19a1617a8d remove suspense spinner from router 2026-04-04 18:26:25 -07:00
jeffvli 1abae986f8 move server selector into app menu 2026-04-04 18:25:04 -07:00
jeffvli 43fa574dab add responsive breakpoint for queue control items 2026-04-04 17:37:47 -07:00
jeffvli 99530c670e redesign queue controls bar 2026-04-04 17:37:05 -07:00
jeffvli 3a0dfe59ce improve visibily of keyboard-focused search items 2026-04-04 17:37:05 -07:00
York d60ed0a793 feat: macOS menu enhancement (#1903) 2026-04-04 17:35:30 -07:00
Kendall Garner a32fed3bcf chore: upgrade dependencies (#1906)
* upgrade dependencies

* downgrade fast-average-color
2026-04-04 17:10:57 -07:00
Kendall Garner 132ac92984 chore: use consistent order of track / artist / ablum on full screen page 2026-04-04 14:46:13 -07:00
jeffvli 141a20f042 refactor item table props 2026-04-04 12:34:27 -07:00
jeffvli 1592204515 add fallback sort order for subsonic playlist list 2026-04-04 12:03:41 -07:00
jeffvli b9f5459725 fix layout shift on grid carousel page change 2026-04-03 20:25:12 -07:00
jeffvli d4e9b9b7a6 adjust bg loading on album detail page 2026-04-03 20:11:10 -07:00
jeffvli ec9e4b1339 fix type error due to new param on mediaStop 2026-04-03 19:09:42 -07:00
Hosted Weblate f09109b887 Translated using Weblate
Currently translated at 100.0% (1194 of 1194 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 100.0% (1194 of 1194 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 100.0% (1194 of 1194 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-04-04 04:05:50 +02:00
jeffvli 1494c8e044 fix mpv seek error on queue end 2026-04-03 19:05:34 -07:00
jeffvli f3a6027e6d fix mpv progress interval still running after queue ends 2026-04-03 18:58:58 -07:00
jeffvli 3c42355c1e attempt to fix mpv playback sync on song insertion (#1855) 2026-04-03 18:54:49 -07:00
jeffvli feda1bb06f remove square image param, default item id for image 2026-04-03 11:24:39 -07:00
jeffvli 72f1d2f9f9 improve date parsing for partial dates (#1683) 2026-04-02 19:39:08 -07:00
jeffvli ad11a9303c add playlist description to expanded header 2026-04-02 18:36:42 -07:00
jeffvli db06e7f601 add native nd radio endpoints, support radio station images 2026-04-02 18:26:26 -07:00
jeffvli fbf82c1ef0 add playlist image upload to edit playlist modal 2026-04-02 17:41:25 -07:00
jeffvli 92cea5dfda add log for direct play profiles 2026-04-02 01:27:14 -07:00
jeffvli 7442f9d3ca support navidrome playlist image upload 2026-04-02 01:23:09 -07:00
jeffvli 68dacea228 use resized images in artist header 2026-04-01 21:57:32 -07:00
jeffvli 51425b5e86 various performance refactors 2026-04-01 21:57:26 -07:00
jeffvli c60610cb42 lint files 2026-03-31 21:12:48 -07:00
jeffvli d3881ee3be support limitPercent for smart playlists 2026-03-31 21:09:13 -07:00
jeffvli de403ea6ac add new nd smart playlist fields
- averagerating

- albumdateloved
- albumlastplayed
- albumdaterated
- albumloved
- albumrating

- artistdateloved
 -artistlastplayed
- artistdaterated
- artistloved
- artistplaycount
2026-03-31 20:55:36 -07:00
jeffvli a30b1ec90b add OS transcoding extension 2026-03-31 20:45:22 -07:00
Hosted Weblate 7982c0e1bd Translated using Weblate
Currently translated at 100.0% (1193 of 1193 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Translated using Weblate

Currently translated at 83.4% (996 of 1193 strings) (Basque)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/eu/

Co-authored-by: Aitor Astorga <a.astorga.sdv@protonmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
2026-03-31 16:09:57 +02:00
dependabot[bot] baf4e7bc0b Bump fast-xml-parser in the npm_and_yarn group across 1 directory (#1777)
Bumps the npm_and_yarn group with 1 update in the / directory: [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser).


Updates `fast-xml-parser` from 5.3.6 to 5.3.8
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.6...v5.3.8)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.3.8
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-29 21:43:31 -07:00
Sutaai 74c44558fe fix: window bar disappearing in Glassy Dark (#1878)
* fix: glassy dark content container claiming entire width  (#1713)

* fix: apply container height fix only when using window bar

---------

Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
2026-03-29 21:43:08 -07:00
Pyx 4033619421 glassy dark theme improvements (#1844)
* glassy dark theme improvements
2026-03-29 21:27:56 -07:00
jeffvli 5d206bbb1f toggle fullscreen visualizer on left controls image (#1857) 2026-03-29 21:05:53 -07:00
Hosted Weblate 3db801f2de Translated using Weblate
Currently translated at 89.1% (1063 of 1193 strings) (German)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Czech)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Japanese)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Spanish)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate

Currently translated at 100.0% (1194 of 1194 strings) (Chinese (Traditional Han script))
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: PhillyMay <mein.alias@outlook.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: linger <linger0517@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translation: feishin/Translation
2026-03-30 05:34:25 +02:00
ebee04 0d3cf912d3 fix click on feishin icon in macos menu bar (tray) causing switch to feishin window (#1881) 2026-03-29 20:34:18 -07:00
jeffvli d81f30a8b5 disable lyrics on radio playback (#1885) 2026-03-29 20:33:31 -07:00
Kendall Garner a5c3b454f4 add flathub to readme 2026-03-28 21:33:51 -07:00
Kendall Garner 68e6e3cf65 feat(playlist): support updating playlist track order (#1875)
* feat(playlist): support updating playlist track order

* force track mode when editing

* use common confirmation for save

* remove en editPLaylist key
2026-03-27 21:36:08 -07:00
Romain VIGNERES 86e6b88555 feat(albums): show grouping tags on album detail page (#1872)
* feat(albums): show grouping tags on album detail page

---------

Co-authored-by: Romain VIGNERES <romain.vigneres@texa.fr>
2026-03-27 18:51:44 -07:00
jeffvli 5cdc45836f rework queue persistence (#1862) 2026-03-27 18:48:38 -07:00
Kendall Garner d438c802a4 fix(normalize): do not duplicate remixer when included in credit 2026-03-27 18:46:12 -07:00
Hosted Weblate a838bdebb7 Translated using Weblate
Currently translated at 79.5% (949 of 1193 strings) (Basque)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/eu/

Co-authored-by: Aitor Astorga <a.astorga.sdv@protonmail.com>
2026-03-27 20:09:51 +01:00
Hosted Weblate 8ff2f4dfb4 Translated using Weblate
Currently translated at 100.0% (1193 of 1193 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1193 of 1193 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
2026-03-26 08:09:54 +01:00
jeffvli ede47fbf8f fix missing artist name in lyrics search query for lrclib (#1871) 2026-03-25 17:49:50 -07:00
jeffvli 9eb64079f7 handle disabled features in a single flag (#1271) 2026-03-25 17:41:08 -07:00
Hosted Weblate 3b955bb319 Translated using Weblate
Currently translated at 100.0% (1191 of 1191 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Translated using Weblate

Currently translated at 100.0% (1191 of 1191 strings) (Dutch)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/

Co-authored-by: bokse <weblate@bokse.nl>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
2026-03-25 07:09:59 +01:00
Darius 816adfa6c7 Waveform playerbar improvements (#1781)
* Defer waveform loading & show default seek bar as fallback

* Add configurable waveform loading delay

* Add 2s default value for waveform loading delay

* disable transcoding config on waveform url

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-03-24 10:06:25 -07:00
Hosted Weblate f91dcc6af6 Translated using Weblate
Currently translated at 100.0% (1191 of 1191 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
2026-03-24 06:10:58 +01:00
kast3t 6dc58a3ff8 fix playlist sort by id (#1867) (#1868)
* fix playlist sort by id (#1867)
2026-03-23 18:27:21 -07:00
Kendall Garner 09fa10a4e9 fix(web): do not load umami if env is disabled 2026-03-22 15:08:21 -07:00
Hosted Weblate 6f45e1a814 Translated using Weblate
Currently translated at 100.0% (1191 of 1191 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Co-authored-by: skajmer <skajmer@protonmail.com>
2026-03-21 23:09:51 +01:00
Hosted Weblate 62ba721f26 Translated using Weblate
Currently translated at 100.0% (1191 of 1191 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1191 of 1191 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 100.0% (1191 of 1191 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1191 of 1191 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
2026-03-19 13:09:56 +01:00
jeffvli 67231753e4 add list search links to command palette 2026-03-18 02:51:27 -07:00
jeffvli c16eccaecb fix tab index on command palette play buttons 2026-03-18 02:17:31 -07:00
Hosted Weblate 0bdf1dcb75 Translated using Weblate
Currently translated at 100.0% (1190 of 1190 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: York <goog10216922@gmail.com>
2026-03-18 08:03:12 +00:00
jeffvli 598e9ca5c2 replace lastfm, musicbrainz, and listenbrainz logos 2026-03-18 01:02:57 -07:00
jeffvli 615f9c3515 refactor search into individual sections by itemtype, add infinite loader 2026-03-18 00:59:04 -07:00
jeffvli b7cbdb4d6c persist command palette collapsed sections to app store 2026-03-17 22:34:07 -07:00
jeffvli 3c562c1398 redesign command palette
- add collapsible search groups
- reduce modal padding
- reduce command item padding
- use breadcrumbs for pagination
- move page breadcrumbs to the bottom
2026-03-17 22:28:07 -07:00
jeffvli 3eafa73217 adjust padding / design of layout toggle 2026-03-17 21:40:16 -07:00
Hosted Weblate 74864d9621 Translated using Weblate
Currently translated at 100.0% (1184 of 1184 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: York <goog10216922@gmail.com>
2026-03-18 04:25:29 +00:00
jeffvli cb5562d32e decrease height of single feature carousel (#1850) 2026-03-17 21:25:18 -07:00
jeffvli e40a175e12 add qobuz and listenbrainz external links 2026-03-17 21:10:31 -07:00
jeffvli f996b111b9 add new external brand icons 2026-03-17 21:10:31 -07:00
Hosted Weblate 0cb5c49924 Translated using Weblate
Currently translated at 100.0% (1180 of 1180 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2026-03-18 02:06:56 +00:00
jeffvli c636029003 assert appstore state migration return type 2026-03-17 19:06:45 -07:00
jeffvli db88a6bc22 support vertical play queue layout 2026-03-17 19:01:01 -07:00
Hosted Weblate 8ccd97b574 Translated using Weblate
Currently translated at 100.0% (1180 of 1180 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1180 of 1180 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1180 of 1180 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Translated using Weblate

Currently translated at 45.5% (537 of 1180 strings) (Norwegian Bokmål)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Kristoffer <spinal-onto-rebel@duck.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: linger <linger0517@gmail.com>
2026-03-17 09:10:00 +01:00
jeffvli 3f99acf473 update to v1.9.0 2026-03-16 00:19:48 -07:00
Hosted Weblate 0cd37ce8ec Translated using Weblate
Currently translated at 100.0% (1180 of 1180 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: linger <linger0517@gmail.com>
2026-03-16 08:19:17 +01:00
jeffvli ee04878580 set mpv audio device to auto if undefined 2026-03-15 20:17:59 -07:00
jeffvli e987049f20 improve sleep timer ui 2026-03-15 18:20:12 -07:00
Hosted Weblate 122552287a Translated using Weblate
Currently translated at 100.0% (1175 of 1175 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Translated using Weblate

Currently translated at 100.0% (1175 of 1175 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Translated using Weblate

Currently translated at 100.0% (1175 of 1175 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Translated using Weblate

Currently translated at 10.1% (119 of 1175 strings) (Arabic)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ar/

Translated using Weblate

Currently translated at 89.0% (1046 of 1175 strings) (German)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/

Translated using Weblate

Currently translated at 100.0% (1175 of 1175 strings) (Catalan)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/

Co-authored-by: Benjamin <ben@iipython.dev>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: PhillyMay <mein.alias@outlook.com>
2026-03-16 01:01:08 +00:00
jeffvli d318e6d341 ensure no concurrent playback on non-transition state on web player (#1829) 2026-03-15 18:00:51 -07:00
riccardo d96b282cae feat: "open in spotify" button (#1839)
* feat: open in spotify

* fix: disable native spotify URI by default
2026-03-15 11:49:33 -07:00
jeffvli f2ab01199f disable WaylandFractionScaleV1 (#1271) 2026-03-15 11:42:31 -07:00
Kendall Garner 04b22431f4 fix(settings): proper description for sidebar configuration 2026-03-15 09:16:12 -07:00
Kendall Garner 31fce705ab feat(docker): rootless container) 2026-03-14 18:39:38 -07:00
jeffvli a28fab0ff3 optimize skeleton animation (#1832) 2026-03-14 15:31:13 -07:00
Hosted Weblate 0a1d4788ee Translated using Weblate
Currently translated at 81.1% (954 of 1175 strings) (Russian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/

Translated using Weblate

Currently translated at 100.0% (1175 of 1175 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 44.1% (519 of 1175 strings) (Norwegian Bokmål)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/

Co-authored-by: klodrik <klodrik@zoominn.no>
Co-authored-by: qvap <qvapelsin@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-03-14 14:09:54 +01:00
jeffvli fafb9d4f56 remove vercel.json due to incorrect edge request configuration 2026-03-13 17:23:03 -07:00
jeffvli 4fdc38caee remove duplicate only-built-dependencies 2026-03-13 17:15:12 -07:00
jeffvli 799cdb44d3 use pnpm v10 on runners 2026-03-13 17:11:24 -07:00
jeffvli 372892199f pin pnpm/action-setup to v4 2026-03-13 17:02:34 -07:00
jeffvli d16184fb25 bump actions dependencies to latest due to deprecated Node v20 2026-03-13 16:59:55 -07:00
jeffvli b8564f6d41 skip wait-for-lint on workflow dispatch 2026-03-13 16:38:32 -07:00
Hosted Weblate d474e60c51 Translated using Weblate
Currently translated at 100.0% (1175 of 1175 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Translated using Weblate

Currently translated at 87.5% (1029 of 1175 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Co-authored-by: Mario Gervais <social.m@riogervais.ca>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
2026-03-13 09:09:54 +00:00
Lyall dfdac28f53 Fix server queue saving/restoring on Navidrome and OpenSubsonic (#1828)
* fix server queue saving

* fix error when attempting to restore empty queue

* queue items optional

* make playQueueByIndex optional

* fix incorrect error message
2026-03-12 13:41:50 +00:00
jeffvli 16b713bc85 add missing suspense boundary around playlist filter sidebar 2026-03-11 22:00:39 -07:00
Kendall Garner 81cd0722b1 fix(mpv): replace mpv queue when restoring queue 2026-03-11 21:42:16 -07:00
jeffvli 1526f9b8d6 re-add session cache for loaded images 2026-03-11 21:20:31 -07:00
jeffvli 5b4da3bc29 use batched fetching for nd song list
- albumIds get culled from query parameters after a certain number, resulting in not all items fetched
2026-03-11 20:46:49 -07:00
jeffvli d78ea440cc set low fetchPriority for carousel images 2026-03-11 19:41:04 -07:00
jeffvli 1595805b83 add additional render optimizations to ArtistAlbums 2026-03-11 19:12:11 -07:00
jeffvli 00fa45f15d isolate render of sticky elements on item table 2026-03-11 19:07:18 -07:00
Luna M ab05be30c0 Fix typo in docker-compose config (#1827) 2026-03-12 01:08:13 +00:00
Hosted Weblate 3d407e5f24 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate

Currently translated at 99.8% (1173 of 1175 strings) (Polish)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 74.1% (871 of 1175 strings) (Russian)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/

Translated using Weblate

Currently translated at 74.1% (871 of 1175 strings) (Russian)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/

Translated using Weblate

Currently translated at 85.7% (1007 of 1175 strings) (Japanese)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Co-authored-by: DanisimoR <danisimogg22@gmail.com>
Co-authored-by: Gentor <gentor92@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translation: feishin/Translation
2026-03-11 21:10:02 +01:00
jeffvli 60776b5f02 fix missing list query invalidation on playlist create/delete 2026-03-11 02:04:51 -07:00
jeffvli 8699b1ffea add sort to artist favorite songs page (#1691) 2026-03-11 01:48:39 -07:00
riccardo 16ac536f93 feat(lyrics): simpmusic lyrics provider (#1820)
* feat(lyrics): simpmusic lyrics provider
2026-03-11 01:04:55 -07:00
jeffvli f51d3d5711 prevent no lyrics message fade out on fullscreen player (#1821) 2026-03-11 01:00:32 -07:00
jeffvli 17a4a14a4e fix isValid condition on REMOTE_URL for server lock (#1822) 2026-03-10 20:02:37 -07:00
jeffvli ecda918b46 update to v1.8.0 2026-03-09 21:53:26 -07:00
Tarulia 93834788b5 use electron-builder's static AppImage runtime (#1816)
electron-builder added compatibility with the AppImage Type2 runtime
(i.e. FUSE-less runtime). However, this is - for now anyway - an opt-in
feature, which this commit enables.
2026-03-09 21:52:48 -07:00
jeffvli 66e7b44d75 add client-side sort to artist favorite songs (#1691) 2026-03-09 20:58:02 -07:00
jeffvli 8825fc1e84 unhide image on error on libraryheader 2026-03-09 20:49:02 -07:00
jeffvli ac0cc19c04 prefer artistInfo artist image 2026-03-09 20:49:02 -07:00
jeffvli de29465b1f enable skeleton animations by default 2026-03-09 20:49:02 -07:00
jeffvli 31a201ca32 rewrite Image component
- remove react-image dependency
- use manual blob load
- abort load when exiting viewport
2026-03-09 20:49:02 -07:00
Hosted Weblate 3644ea2969 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate

Currently translated at 100.0% (1175 of 1175 strings) (Czech)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 100.0% (1175 of 1175 strings) (Spanish)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 98.2% (1155 of 1175 strings) (French)
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
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/
Translation: feishin/Translation
2026-03-10 03:08:35 +01:00
ashe e46c61e026 Remote URL hardcoding compatibility (#1810)
* Remote URL compatibility
---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-03-09 19:08:28 -07:00
Oliver Tzeng 3873218e94 fix translated zh-Hant and zh-Hans (#1807) 2026-03-09 17:08:07 -07:00
Jeff b4a61cbd6e use ad-hoc code signing for macOS build, disable hardenedRuntime (#1815) 2026-03-09 17:05:12 -07:00
jeffvli accc6e53f0 bump electron-builder for pnpm v10.3 ( #1796) 2026-03-09 12:54:32 -07:00
jeffvli adf48decc4 run PR publish on electron-builder config changes 2026-03-09 09:52:42 -07:00
jeffvli 49ff928414 refactor artist header to better handle artist image from info endpoint 2026-03-09 01:54:04 -07:00
Hosted Weblate 71efd4a6d7 Translated using Weblate
Currently translated at 97.8% (1149 of 1174 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Co-authored-by: 無情天 <kofzhanganguo@126.com>
2026-03-09 09:38:55 +01:00
jeffvli 01e4907295 update to v1.7.0 2026-03-09 01:36:19 -07:00
jeffvli 6a47e99680 add manual update notice for macOS, disable autoinstall (#1725) 2026-03-09 01:31:33 -07:00
jeffvli 078d8068e0 fix file path replacement to handle both add/replace (#1749) 2026-03-09 00:55:13 -07:00
jeffvli 58ae76ce2a use album order on artist page for queue add (#1754) 2026-03-09 00:47:55 -07:00
jeffvli bc6cd5b014 refactor grid columns internally into album artist sections to handle null cases 2026-03-08 22:15:54 -07:00
jeffvli 17deac8d65 decouple AlbumArtistInfo from AlbumArtistDetail (#1809) 2026-03-08 22:06:18 -07:00
jeffvli 7dbf8dd9fe add conditional render to queue/lyric state on mobile player (#1797) 2026-03-08 21:06:22 -07:00
Hosted Weblate 27ab9f89c9 Translated using Weblate
Currently translated at 100.0% (1174 of 1174 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1174 of 1174 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
2026-03-08 13:09:50 +00:00
jeffvli 602808c742 directly replace playlist rules on save and replace 2026-03-07 21:18:55 -08:00
jeffvli c1051956ad add size to album detail header 2026-03-07 20:25:04 -08:00
York 6d2c084355 fix: sleep timer end-of-song mode (#1706) 2026-03-07 20:23:19 -08:00
Hosted Weblate 6e3f0f2253 Translated using Weblate
Currently translated at 100.0% (1174 of 1174 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: York <goog10216922@gmail.com>
2026-03-07 10:09:54 +01:00
jeffvli d3d3688d60 add Symbola font for explicit status indicator 2026-03-07 00:50:00 -08:00
Kendall Garner 49c28299af remove package.lock, make lint happy 2026-03-06 21:04:51 -08:00
Kendall Garner 0bb6fea3db don't assume server exists in enable either 2026-03-06 20:59:13 -08:00
Kendall Garner 7027084394 fix(autosave): server isn't guaranteed to exist 2026-03-06 20:52:41 -08:00
Kendall Garner 7c4cbaad9a feat(player): add server-side autosave capability 2026-03-06 20:01:35 -08:00
jeffvli e603048a80 add settings override with env variables 2026-03-06 19:32:38 -08:00
jeffvli 9e08157517 add has_rating filter for Navidrome song list 2026-03-06 18:03:14 -08:00
Hosted Weblate d4c2b1e914 Translated using Weblate
Currently translated at 84.1% (985 of 1170 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Co-authored-by: UnknownExplorer13 <kyedylanfenton@gmail.com>
2026-03-06 19:09:49 +00:00
Hosted Weblate 65dd67ec96 Translated using Weblate
Currently translated at 100.0% (1170 of 1170 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: linger <linger0517@gmail.com>
2026-03-06 12:09:50 +00:00
Hosted Weblate 76259309af Translated using Weblate
Currently translated at 74.7% (875 of 1170 strings) (Finnish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fi/

Co-authored-by: jonoafi <joona@jonottaa.com>
2026-03-06 00:09:53 +01:00
Hosted Weblate 3e5a9db279 Translated using Weblate
Currently translated at 100.0% (1170 of 1170 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1170 of 1170 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 100.0% (1170 of 1170 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-03-05 15:09:54 +00:00
Hosted Weblate c155bbdb37 Translated using Weblate
Currently translated at 100.0% (1169 of 1169 strings) (Indonesian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/id/

Co-authored-by: Arif Budiman <arifpedia@gmail.com>
2026-03-05 07:39:54 +01:00
jeffvli ecbc03f052 add dummy onErrorPause handler for radio player 2026-03-04 22:39:46 -08:00
jeffvli 6ef9efc8bf add 10s retry for playback on network err (#1779) 2026-03-04 22:32:33 -08:00
jeffvli 513e9e822d attempt to fix viewport size for mobile browsers (#1787) 2026-03-04 21:51:04 -08:00
jeffvli dbc215c44f add toggle visibility behavior to tray icon (#1793) 2026-03-04 21:38:11 -08:00
jeffvli 43c5cf4275 fix primary color css variable to use new shade value 2026-03-04 21:26:47 -08:00
Benjamin 41f1f376bc feat: customizable item layout on fullscreen player (#1769)
* change container display to release type, readd badge styling to improve contrast

* make everything customizable
2026-03-04 21:23:14 -08:00
jeffvli cad7fef454 add type assertion for primaryShade 2026-03-04 21:01:56 -08:00
jeffvli 93791aea15 add setting to override theme primary shade (#1791) 2026-03-04 20:58:30 -08:00
Hosted Weblate 884dcde289 Translated using Weblate
Currently translated at 67.2% (782 of 1163 strings) (Finnish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fi/

Co-authored-by: Lauri Koo <late91@gmail.com>
2026-03-05 05:09:50 +01:00
Hosted Weblate 21f993a951 Translated using Weblate
Currently translated at 100.0% (1163 of 1163 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: linger <linger0517@gmail.com>
2026-03-04 05:09:49 +00:00
Hosted Weblate 4784228831 Translated using Weblate
Currently translated at 100.0% (1163 of 1163 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Co-authored-by: skajmer <skajmer@protonmail.com>
2026-03-03 21:09:48 +00:00
Hosted Weblate c6e3e0c07e Translated using Weblate
Currently translated at 100.0% (1163 of 1163 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1163 of 1163 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
2026-03-03 15:09:50 +00:00
Kendall Garner 30685e7252 fix(subsonic): do favorite filter processing on artist favorite 2026-03-03 06:54:59 -08:00
Kendall Garner 12a398a65c fix(server selector): better handling for large server/folder list 2026-03-02 22:30:14 -08:00
Kendall Garner 71360e37de fix(favorites): filter favorite artists on favorite page for Jellyfin 2026-03-02 21:02:24 -08:00
Kendall Garner f99b8ea9ee feat(queue): add go to current button 2026-03-02 20:30:35 -08:00
Kendall Garner 2854b928f6 fix(player): handle items in the queue moved to next before current index 2026-03-02 20:05:01 -08:00
Hosted Weblate 14e1f1d003 Translated using Weblate
Currently translated at 73.7% (857 of 1162 strings) (Russian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/

Translated using Weblate

Currently translated at 100.0% (1162 of 1162 strings) (Tamil)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ta/

Co-authored-by: Платон Петров <pplaton6@gmail.com>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
2026-03-01 23:10:04 +01:00
Hosted Weblate 85c490bd06 Translated using Weblate
Currently translated at 100.0% (1162 of 1162 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: York <goog10216922@gmail.com>
2026-03-01 08:09:52 +01:00
Hosted Weblate 96d78f8bda Translated using Weblate
Currently translated at 44.6% (519 of 1162 strings) (Norwegian Bokmål)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/

Co-authored-by: klodrik <klodrik@zoominn.no>
2026-02-28 14:09:50 +00:00
jeffvli 5f65aebe63 convert nd album rating filter to yes/no (#1775) 2026-02-27 19:30:33 -08:00
Hosted Weblate cd96da9cd5 Translated using Weblate
Currently translated at 100.0% (1162 of 1162 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 100.0% (1162 of 1162 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Translated using Weblate

Currently translated at 100.0% (1162 of 1162 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-02-27 23:09:53 +00:00
Hosted Weblate 674b66b682 Translated using Weblate
Currently translated at 84.6% (984 of 1162 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
2026-02-27 15:09:56 +01:00
jeffvli 934851456b prevent click propagation on detached queue button (#1762) 2026-02-27 00:53:09 -08:00
jeffvli afca396654 attempt to fix mpv autoNext behavior (#1768) 2026-02-27 00:39:27 -08:00
jeffvli b62f62671d fix table config drag and drop interaction on bottom edge 2026-02-26 20:59:28 -08:00
Norman eb8913479b Add album grouping column (#1722)
* Add album grouping column

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-26 20:34:55 -08:00
Alexander Welsing 4918b412b2 Album radio (#1759)
* added album radio feature

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-26 20:33:00 -08:00
jeffvli a78f5803a5 fix mediasession play hotkey on after pause on web (#1758)
- chrome (and other browsers) determine that the audio element is inactive if the volume is set to 0 when paused, leading to the resume (play) mediasession event to no longer be available
2026-02-26 01:11:24 -08:00
dependabot[bot] 0d1799cbf7 Bump the npm_and_yarn group across 1 directory with 6 updates (#1763)
Bumps the npm_and_yarn group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [axios](https://github.com/axios/axios) | `1.13.2` | `1.13.5` |
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.3.3` | `5.3.6` |
| [lodash](https://github.com/lodash/lodash) | `4.17.21` | `4.17.23` |
| [qs](https://github.com/ljharb/qs) | `6.14.1` | `6.14.2` |
| [minimatch](https://github.com/isaacs/minimatch) | `3.1.2` | `3.1.5` |
| [tar](https://github.com/isaacs/node-tar) | `6.2.1` | `7.5.9` |



Updates `axios` from 1.13.2 to 1.13.5
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.13.2...v1.13.5)

Updates `fast-xml-parser` from 5.3.3 to 5.3.6
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.3...v5.3.6)

Updates `lodash` from 4.17.21 to 4.17.23
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

Updates `qs` from 6.14.1 to 6.14.2
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

Updates `tar` from 6.2.1 to 7.5.9
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.1...v7.5.9)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.13.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: fast-xml-parser
  dependency-version: 5.3.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tar
  dependency-version: 7.5.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-25 22:22:53 -08:00
jeffvli 438db40d0e remove audio prefix from subsonic contentType property 2026-02-25 22:22:13 -08:00
dependabot[bot] aca5e1fe87 Bump react-router in the npm_and_yarn group across 1 directory (#1534)
Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.11.0 to 7.12.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.12.0/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.12.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-25 22:06:26 -08:00
Tarulia 8aaf24ff87 Redo Icons in SVG (#1731)
Also includes exports from SVG for all relevant files:
- `assets/icons/` in 16-1024px size PNGs
- `assets/icons/icon.ico` using same sizes as the old one
- `assets/icons/` IconTemplate
- `media/` black and white logo-only variants
- `resource/icon.png`
2026-02-25 21:35:13 -08:00
Hosted Weblate 75dbea1ab7 Translated using Weblate
Currently translated at 100.0% (1160 of 1160 strings) (Catalan)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/

Co-authored-by: HUMET <ressaguer@proton.me>
2026-02-25 16:09:51 +00:00
jeffvli 61616dd2b9 move remote hook to global scope (#1752) 2026-02-24 00:45:43 -08:00
jeffvli ceef7964af add originalyear to smart playlist filters (#1746) 2026-02-24 00:34:07 -08:00
jeffvli ec12e89653 fix missing blur config from expanded album image 2026-02-23 23:48:56 -08:00
Hosted Weblate 386ca41a5d Translated using Weblate
Currently translated at 99.3% (1152 of 1160 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
2026-02-24 01:09:49 +00:00
Hosted Weblate a8fd5a4f46 Translated using Weblate
Currently translated at 100.0% (1160 of 1160 strings) (Dutch)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/

Co-authored-by: bokse <weblate@bokse.nl>
2026-02-23 16:09:54 +01:00
Hosted Weblate 4590383a97 Translated using Weblate
Currently translated at 83.1% (964 of 1160 strings) (Japanese)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/

Translated using Weblate

Currently translated at 100.0% (1160 of 1160 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
2026-02-22 14:09:50 +01:00
Hosted Weblate 405208cf92 Translated using Weblate
Currently translated at 97.8% (1135 of 1160 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Co-authored-by: 無情天 <kofzhanganguo@126.com>
2026-02-22 00:09:48 +01:00
Hosted Weblate f19c37276a Translated using Weblate
Currently translated at 100.0% (1160 of 1160 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Co-authored-by: skajmer <skajmer@protonmail.com>
2026-02-21 15:09:49 +01:00
Hosted Weblate 3269034bfb Translated using Weblate
Currently translated at 88.9% (1032 of 1160 strings) (German)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/

Translated using Weblate

Currently translated at 99.1% (1150 of 1160 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Translated using Weblate

Currently translated at 42.4% (492 of 1160 strings) (Norwegian Bokmål)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/

Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: PhillyMay <mein.alias@outlook.com>
Co-authored-by: klodrik <klodrik@zoominn.no>
2026-02-20 22:09:52 +00:00
Lyosha e5f99af43b Fix image resolution setting value checking (#1741) 2026-02-20 13:10:26 -08:00
Hosted Weblate 2866ca9537 Translated using Weblate
Currently translated at 100.0% (1160 of 1160 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 40.6% (471 of 1160 strings) (Norwegian Bokmål)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/

Translated using Weblate

Currently translated at 99.1% (1150 of 1160 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: klodrik <klodrik@zoominn.no>
2026-02-20 17:09:51 +00:00
Kendall Garner a377eae2f4 fix(build): do not add hash to favicon and assets 2026-02-20 08:48:36 -08:00
Hosted Weblate dbd8ce2380 Translated using Weblate
Currently translated at 100.0% (1160 of 1160 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: linger <linger0517@gmail.com>
2026-02-20 12:09:53 +01:00
Kendall Garner 0741351318 fix(share): only copy to clipboard if available and secure 2026-02-19 19:55:10 -08:00
Hosted Weblate ab9e02adfc Translated using Weblate
Currently translated at 39.6% (459 of 1158 strings) (Norwegian Bokmål)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/

Co-authored-by: klodrik <klodrik@zoominn.no>
2026-02-19 19:09:47 +01:00
Kendall Garner 12ff690619 fix(window): recreate window on macos when closed but not quit 2026-02-18 21:50:44 -08:00
jeffvli 5039012fcb catch errors on desktop scrobble notification failure (#1723) 2026-02-18 20:54:31 -08:00
jeffvli 75af57a7b3 use correct player seek event for mpris updater (#1740) 2026-02-18 20:37:18 -08:00
jeffvli 9027eee99f fix lyrics type in player sidebar 2026-02-18 20:28:14 -08:00
jeffvli 50fe373f1e simplify lyrics implementation
- removes complex lyrics fetch and override logic, and instead uses a single query as a source of truth for the lyrics
- properly handles loading state, invalidation, and refetch
2026-02-18 20:25:52 -08:00
Alexander Welsing 2c546867a8 change "Fields" query parameter to array (#1733)
* change "Fields" query parameter to array

* platformToTarget.key() -> to array
2026-02-17 18:45:31 -08:00
Kendall Garner 9dad934a40 transcode player bar waveform 2026-02-17 14:30:26 -08:00
Hosted Weblate 34dbb4c794 Translated using Weblate
Currently translated at 77.2% (894 of 1158 strings) (Italian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/it/

Co-authored-by: Giuseppe Filomeno <giusefilo@gmail.com>
2026-02-17 19:10:04 +00:00
Hosted Weblate 7472af66ef Translated using Weblate
Currently translated at 99.3% (1150 of 1158 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
2026-02-17 00:09:52 +01:00
Adam f293fb287d Add hook script to update and publish appstream metainfo on electron build (#1719) 2026-02-16 12:54:11 -08:00
libussa 7656e84c20 fix stale SERVER_URL when changing env var in Docker (#1714)
settings.js (which injects SERVER_URL into the browser) was served
without Cache-Control headers, causing Cloudflare and other reverse
proxies to cache the old value indefinitely. Additionally, when
SERVER_LOCK is enabled, the persisted server URL in localStorage was
never compared against the current window.SERVER_URL, so same-browser
sessions kept using the old server even after settings.js was updated.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 12:44:29 -08:00
Kendall Garner c524e8d3b7 fix(left-controls): use joined artsts instead of array by artist id 2026-02-16 07:52:36 -08:00
Hosted Weblate 7f13ce491b Translated using Weblate
Currently translated at 92.0% (1066 of 1158 strings) (French)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/

Co-authored-by: Nicolas DERIVE <kalon33@ubuntu.com>
2026-02-16 15:09:54 +00:00
Hosted Weblate 6c16aabce0 Translated using Weblate
Currently translated at 88.3% (1023 of 1158 strings) (German)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/

Co-authored-by: PhillyMay <mein.alias@outlook.com>
2026-02-15 13:10:11 +01:00
Hosted Weblate f5240b1766 Translated using Weblate
Currently translated at 78.7% (912 of 1158 strings) (German)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/

Co-authored-by: PhillyMay <mein.alias@outlook.com>
2026-02-15 04:09:48 +01:00
Hosted Weblate cc5a95d725 Translated using Weblate
Currently translated at 100.0% (1158 of 1158 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1158 of 1158 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: York <goog10216922@gmail.com>
2026-02-14 20:09:53 +01:00
Hosted Weblate 8ba63988d8 Translated using Weblate
Currently translated at 97.8% (1133 of 1158 strings) (Chinese (Simplified Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/

Translated using Weblate

Currently translated at 100.0% (1158 of 1158 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Translated using Weblate

Currently translated at 100.0% (1158 of 1158 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
2026-02-14 15:09:49 +01:00
jeffvli 289f307a52 update to v1.6.0 2026-02-13 23:08:33 -08:00
jeffvli 91ac36c835 fix Subsonic root folder condition (#1686)
- some older subsonic servers used numeric ids which causes the rootFolderId regex to fail which resulted in the getFolder endpoint to always attempt to fetch the root
2026-02-13 23:03:24 -08:00
jeffvli 503e4b2bac fix list refresh not working on discography/genre pages 2026-02-13 21:38:12 -08:00
jeffvli c39ddc3b45 refactor PlaylistQueryEditor to new file 2026-02-13 21:05:37 -08:00
jeffvli 1163c4ad5e add JSON editor for playlist query builder (#1711) 2026-02-13 21:05:34 -08:00
jeffvli e497734c07 allow all rule groups to be empty (#1710) 2026-02-13 20:26:58 -08:00
Kendall Garner 77fef33cbf improve album artist favorite performance and search (#1709)
* improve album artist favorite performance and search

* adjust top songs / favorite songs sections

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-13 20:13:28 -08:00
Hosted Weblate 81189db1e1 Translated using Weblate
Currently translated at 100.0% (1156 of 1156 strings) (Catalan)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/

Co-authored-by: HUMET <ressaguer@proton.me>
2026-02-14 03:46:02 +00:00
Maevi 054a3d005e Listen on IPv6 by default in docker image (#1707) 2026-02-13 19:45:55 -08:00
jeffvli dfbff64430 fix duplicate server add when SERVER_LOCK is configured (#1623) 2026-02-13 19:42:36 -08:00
jeffvli 2b4046a82e fix double click propagation on the table expand button 2026-02-13 17:49:49 -08:00
jeffvli 9eb879fc37 hide favorite / rating from playlist album view items 2026-02-13 17:39:35 -08:00
jeffvli 9e63ee2735 add loading spinners for some lazy loaded content 2026-02-13 15:04:54 -08:00
jeffvli 9950e51d45 remove lazy loading from context menu initialization 2026-02-13 15:01:18 -08:00
jeffvli 70fdd4bdc3 refactor album expansion to global scope 2026-02-13 14:59:15 -08:00
jeffvli e855f7dd01 remove invalid detail view type for song list config 2026-02-13 12:19:14 -08:00
jeffvli 123842dfda attempt to optimize the tagList query
- this query in some cases can return a very large amount of data, depending on the size of the user's library
- increasing the cacheTime reduces the frequency of fetches while disabling structuralSharing reduces the need for react-query to do a deep equality comparison for the cache
2026-02-13 11:53:42 -08:00
Hosted Weblate 1338513f82 Translated using Weblate
Currently translated at 100.0% (1156 of 1156 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@alius.cz>
2026-02-13 19:09:45 +01:00
Hosted Weblate c9c88dd82d Translated using Weblate
Currently translated at 100.0% (1156 of 1156 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Translated using Weblate

Currently translated at 100.0% (1156 of 1156 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: York <goog10216922@gmail.com>
2026-02-13 14:09:48 +01:00
York 02a5395453 fix: regenerate macOS icon (.icns) to fix glitched small icons (#1705) 2026-02-13 00:15:12 -08:00
jeffvli 7ba2f6b827 fix removed filter functions 2026-02-12 22:04:08 -08:00
jeffvli f1b5dc8ef3 add additional client-side filters to playlist songs 2026-02-12 22:00:07 -08:00
Kendall Garner 78875572e9 add explicit blurring to left expanded image and full screen (#1701)
* add explicit blurring to left expanded image and full screen
2026-02-12 18:49:57 -08:00
jeffvli f487560ec5 fix vite web build again for hashed assets and PWA cache clear 2026-02-12 18:21:27 -08:00
jeffvli f752090c78 Revert "attempt fix for web/docker cache busting on new release"
This reverts commit 91e7c7434c.
2026-02-12 18:17:49 -08:00
jeffvli 96f5b2b82a Revert "fix vite web build to work with subpath"
This reverts commit 1a9f36ce9e.
2026-02-12 18:17:44 -08:00
jeffvli 80292ae579 fix alpha autoupdater logic to use correct config for latest 2026-02-12 18:17:08 -08:00
Hosted Weblate 1d156ac506 Translated using Weblate
Currently translated at 100.0% (1154 of 1154 strings) (Czech)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/

Co-authored-by: Fjuro <fjuro@alius.cz>
2026-02-13 00:09:58 +00:00
jeffvli dc5586f859 adjuat audioDevice description to remove web player disclaimer 2026-02-12 11:18:55 -08:00
jeffvli 1a9f36ce9e fix vite web build to work with subpath 2026-02-12 11:18:55 -08:00
York 203c8a6588 fix: restore original macOS squircle icon and regenerate icns properly (#1703) 2026-02-12 10:03:23 -08:00
Hosted Weblate 2e6cf8d869 Translated using Weblate
Currently translated at 100.0% (1154 of 1154 strings) (Spanish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/

Co-authored-by: Fordas <fordas15@gmail.com>
2026-02-12 17:09:48 +00:00
Hosted Weblate b1827dd352 Translated using Weblate
Currently translated at 100.0% (1154 of 1154 strings) (Polish)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/

Translated using Weblate

Currently translated at 100.0% (1154 of 1154 strings) (Chinese (Traditional Han script))
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/

Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
2026-02-12 11:09:50 +01:00
jeffvli 0d2dddddbc fix invalid comparison on detail rating column hide condition 2026-02-12 01:40:01 -08:00
jeffvli 1d8e1957ba handle image drag from item detail list 2026-02-12 01:37:59 -08:00
jeffvli dc957cb3cc hide detail rating column on zero value 2026-02-12 00:57:24 -08:00
jeffvli c314fa0bf3 properly handle context menu in playlist album view 2026-02-12 00:54:45 -08:00
jeffvli c5ebfac647 fix playlist grid view itemType to PLAYLIST_SONG to support remove from playlist 2026-02-12 00:28:39 -08:00
jeffvli 4adea11a93 support select all hotkey in detail list view 2026-02-12 00:13:32 -08:00
Jeff e6f49b9f1f Add album view for playlists (#1700)
* update client side song ordering to include album order

* add compact styling to LibraryHeader

* move search button to top right of LibraryHeader
2026-02-11 21:48:25 -08:00
York 9cde569c7d Add option to show playing icon in Discord RPC (#1699)
* feat: add option to show playing/paused icon in Discord RPC
2026-02-11 21:14:58 -08:00
jeffvli 91e7c7434c attempt fix for web/docker cache busting on new release
- remove static asset filenames
- add cache clear config to PWA
- move PWA to base dir instead of assets
2026-02-11 20:52:28 -08:00
Yoshua Wakeham ffef5dfdee fix: actually show Jellyfin recently played songs carousel (#1697) 2026-02-11 20:35:23 -08:00
Kendall Garner 409dd69fcb reduce explicit indicator spacing slightly 2026-02-11 19:34:20 -08:00
Hosted Weblate 064cf5103a Translated using Weblate
Currently translated at 100.0% (1152 of 1152 strings)

Co-authored-by: Ondo <SparkyOndo@proton.me>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/
Translation: feishin/Translation
2026-02-11 23:09:47 +01:00
Hosted Weblate 7e3a613a93 Translated using Weblate
Currently translated at 100.0% (1152 of 1152 strings)

Translated using Weblate

Currently translated at 100.0% (1152 of 1152 strings)

Translated using Weblate

Currently translated at 99.9% (1151 of 1152 strings)

Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translation: feishin/Translation
2026-02-11 16:09:52 +01:00
Hosted Weblate e7c49f6d67 Translated using Weblate
Currently translated at 36.6% (422 of 1152 strings)

Translated using Weblate

Currently translated at 33.5% (387 of 1152 strings)

Translated using Weblate

Currently translated at 100.0% (1152 of 1152 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 96.3% (1101 of 1143 strings)

Translated using Weblate (Ukrainian)

Currently translated at 32.1% (368 of 1143 strings)

Translated using Weblate (French)

Currently translated at 91.3% (1044 of 1143 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1143 of 1143 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (1143 of 1143 strings)

Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Raphael <raphael.margueron@gmail.com>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: Yurii <04_hours.lambing@icloud.com>
Co-authored-by: linger <linger0517@gmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/uk/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/
Translation: feishin/Translation
2026-02-11 11:02:22 +01:00
jeffvli 022b83ab32 fix playlist add returning zero results on modal menu (#1695) 2026-02-11 00:35:22 -08:00
jeffvli 551d705ee1 adjust fixed-width columns on the Item Detail list and prevent text wrapping 2026-02-10 21:52:22 -08:00
jeffvli 83f73c7fa9 remove unused enableAnimation from ImageContainer 2026-02-10 21:46:54 -08:00
York cc8cb4f4f1 Add sleep timer to player bar (#1671)
* feat: add sleep timer to player bar

- Add sleep timer button in player bar right controls
- Preset options: End of song, 5/10/15/30/45 min, 1 hr, 2 hrs
- Custom timer with HH:MM:SS input fields
- Timer only counts down while music is playing
- Timer pauses playback when it expires
- End-of-song mode pauses at the next track change
- Uses theme-aware styling (--theme-colors-surface)
- Add sleepTimer/sleepTimerOff icons (LuTimer/LuTimerOff)
- Add i18n strings for sleep timer UI

---------

Co-authored-by: York <york@BonecharMac.local>
Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-10 21:19:37 -08:00
York 496eab7d09 fix: regenerate macOS icon (.icns) to fix glitched small icons (#1688)
Co-authored-by: York <york@BonecharMac.local>
2026-02-10 21:11:10 -08:00
York 5197c967c2 fix: use theme mode property for macOS native window theme (#1685)
Co-authored-by: York <york@BonecharMac.local>
2026-02-10 21:09:32 -08:00
jeffvli 74b615dba7 include stable version check on alpha update 2026-02-10 20:20:37 -08:00
jeffvli b67ee797cb move arm64 build configuration to electron-builder config (#1689) 2026-02-10 19:25:26 -08:00
Hosted Weblate 4228084810 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (French)

Currently translated at 90.7% (1034 of 1139 strings)

Translated using Weblate (Ukrainian)

Currently translated at 29.0% (331 of 1139 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (1139 of 1139 strings)

Translated using Weblate (Ukrainian)

Currently translated at 26.5% (302 of 1139 strings)

Translated using Weblate (Ukrainian)

Currently translated at 22.0% (251 of 1139 strings)

Translated using Weblate (Danish)

Currently translated at 90.0% (1026 of 1139 strings)

Translated using Weblate (Danish)

Currently translated at 28.1% (321 of 1139 strings)

Translated using Weblate (Ukrainian)

Currently translated at 9.5% (109 of 1139 strings)

Translated using Weblate (Ukrainian)

Currently translated at 5.7% (66 of 1139 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (1139 of 1139 strings)

Translated using Weblate (Czech)

Currently translated at 99.7% (1136 of 1139 strings)

Translated using Weblate (Danish)

Currently translated at 4.8% (55 of 1139 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (1139 of 1139 strings)

Translated using Weblate (Danish)

Currently translated at 0.4% (5 of 1139 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1139 of 1139 strings)

Translated using Weblate (Polish)

Currently translated at 99.9% (1138 of 1139 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (1135 of 1135 strings)

Translated using Weblate (Dutch)

Currently translated at 99.8% (1133 of 1135 strings)

Translated using Weblate (Dutch)

Currently translated at 84.4% (959 of 1135 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (1135 of 1135 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1135 of 1135 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (1135 of 1135 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1131 of 1131 strings)

Translated using Weblate (German)

Currently translated at 78.9% (893 of 1131 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (1131 of 1131 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 92.7% (1047 of 1129 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (1129 of 1129 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1129 of 1129 strings)

Translated using Weblate (Japanese)

Currently translated at 85.1% (961 of 1129 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1129 of 1129 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (1129 of 1129 strings)

Co-authored-by: Alexander Welsing <kontakt@a-wels.de>
Co-authored-by: Denisa Alicia Rissa <denisarissa@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: Ondo <SparkyOndo@proton.me>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: Yurii <04_hours.lambing@icloud.com>
Co-authored-by: bokse <weblate@bokse.nl>
Co-authored-by: haha4ni <haha4ni@hotmail.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/da/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/de/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/uk/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/
Translation: feishin/Translation
2026-02-10 09:22:15 +01:00
jeffvli b514c7972d update to v1.5.0 2026-02-10 00:22:00 -08:00
jeffvli 83d9042a47 decouple playlist song sort order from search results (#1650) 2026-02-09 22:28:37 -08:00
jeffvli a28c403093 adjust wording and toggles for analytics/updates (#1654) 2026-02-09 22:19:49 -08:00
jeffvli 2927fa5ff7 set default font to Inter
- prefer to use monospace font due to new item detail columns
2026-02-09 22:00:46 -08:00
Jeff f39a7f8d6f Add album detail list view (#1681) 2026-02-09 21:56:08 -08:00
Kai Gritun 397610d8ab fix: remove duplicate CommandPalette in mobile layout (#1669)
The CommandPalette component was being rendered twice when in mobile view:
1. In ResponsiveLayout via LayoutHotkeys (which handles all layouts)
2. In MobileLayout directly

This caused two overlapping command menus to open when pressing Ctrl+K
in mobile view, with keyboard input going to the background menu.

The fix removes the duplicate CommandPalette from MobileLayout since
LayoutHotkeys already provides it for all layouts (both desktop and mobile).

Fixes #1666

Co-authored-by: Kai Gritun <kai@kaigritun.com>
2026-02-07 19:22:46 -08:00
Ahmed ElSayed fb170bb7c4 Add win-arm64 target (#1665) 2026-02-07 15:39:57 -08:00
Mateleo d93f6e8720 feat: enable scrobbling on song repeat and fix package name typo (#1662)
- Add `handleScrobbleFromRepeat` callback to reset scrobble state and send 'start' event when player repeats a song, ensuring accurate scrobbling in repeat mode.
- Fix typo in `web.vite.config.ts` by correcting '@tanstack_react-query-persist-client' to '@tanstack/react-query-persist-client' for proper package reference.
2026-02-07 15:25:29 -08:00
Martín González Gómez 668de93829 Open settings with shortcut (#1655)
* Open settings with shortcut. Also add settings to menubar.
2026-02-07 15:19:05 -08:00
jeffvli 7cecd859ae add mbzReleaseGroupId to Album type 2026-02-06 05:22:56 -08:00
jeffvli fea2966f62 refactor jellyfin field properties and include ProviderIds 2026-02-06 05:17:40 -08:00
jeffvli 6efa308e85 fix release channel input value 2026-02-06 04:13:53 -08:00
Pyx 82b50a60bc Implement Glassy Dark theme (#1388)
* implement theme

* refactor theme stylesheets to load inline to simplify vite bundling

* add missing css module scope name for web build

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-06 02:01:32 -08:00
jeffvli f52c4f7900 fix alpha release notes to compare to development instead of alpha tag 2026-02-06 00:16:11 -08:00
jeffvli 2fb621993d rename electron-builder config to alpha instead of nightly 2026-02-05 23:52:38 -08:00
jeffvli cf663de2fc add handlers and setting for nightly release 2026-02-05 23:45:32 -08:00
jeffvli 65c215fa9c fix R2_ENDPOINT_URL reference 2026-02-05 20:58:15 -08:00
jeffvli 8af972c20b add nightly publish build 2026-02-05 20:48:05 -08:00
jeffvli 027e4046a2 handle radio metadata in discord rpc / fullscreen player (#1649) 2026-02-05 19:14:30 -08:00
jeffvli 4c256348fc add configuration to blur explicit album/song art 2026-02-04 01:20:31 -08:00
jeffvli 6e3275c05c add explicit / clean indicators for album and song titles (#1634) 2026-02-04 00:35:35 -08:00
Jake Klingler 3518a3f3b6 populate bit depth from jellyfin (#1648) 2026-02-04 00:10:28 -08:00
jeffvli 2b6b0cb38b fix artist favorite songs (subsonic) 2026-02-03 23:58:44 -08:00
jeffvli f56a836ffd add personal/community toggle for artist top songs (#1372) 2026-02-03 23:58:44 -08:00
jeffvli 2d963a9d23 use correct filters for album song sort options 2026-02-03 23:58:44 -08:00
rushii 4423b06807 fix(Settings): mpv path selector (#1641)
An unnecessary default value appears to be stringifying a Promise when a separate useEffect hook is supposed to properly load the setting value.
2026-02-03 22:56:09 -08:00
T 1f9223b476 Fix: ratings display on player bar and mobile player (#1646)
* fix(playerbar): use settings to display ratings

* fix(mobile player): use settings to display ratings
2026-02-03 22:50:43 -08:00
Alexander Welsing b4ecf5d257 Update instant mix to use the new items endpoint instead of songs (#1642)
songs/:itemId:/InstantMix -> items/:itemId/InstantMix
2026-02-03 22:34:34 -08:00
jeffvli 0dd13cbab1 add release notes modal to appmenu 2026-02-03 01:06:52 -08:00
jeffvli 48e50430fe add play button group to artist top / favorite song sections 2026-02-02 22:54:10 -08:00
jeffvli ac5611fdca add favorite songs section to artist page (#1604) 2026-02-02 22:23:38 -08:00
jeffvli 50c3dbc0a0 set first item of track radio to the triggering item 2026-02-02 21:18:29 -08:00
jeffvli ddd840d2df fix inconsistent size of musicbrainz icon on album page 2026-02-02 21:14:03 -08:00
jeffvli c0c9878fad add cors / ssl ignore switches to all login components (#1606) 2026-02-02 21:05:01 -08:00
jeffvli c4fc8a8aef fix overlayscrollbars init on loading state 2026-02-02 20:53:11 -08:00
Kendall Garner 0620b096db fix(mpv): only check player time when there is an item in the track (#1639) 2026-02-02 20:49:34 -08:00
Kendall Garner f998491beb fix(playlist): optimistically update rating for playlist song list 2026-02-02 20:48:44 -08:00
Damien Erambert 55a6ea4fca Prevent double fetching when force refreshing paginated views (#1637)
* Prevent double fetching when force refreshing paginated views

* remove await from infinite list loader query invalidation

* add mutation and loading state to list refresh

* add non-suspense query to list genre filters to add loading state

* remove list count data set on random queries

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-02 20:25:19 -08:00
Damien Erambert 72fc5beb98 Use a re-usable Intl.Collator instance for locale compare when possible (#1638)
* Use a re-usable Intl.Collator instance for locale compare
2026-02-02 18:28:01 -08:00
jeffvli a45b607fe7 add Noto Sans Hebrew to default font configuration 2026-02-02 18:14:19 -08:00
jeffvli adfdf04240 update to v1.4.2 2026-02-02 01:45:02 -08:00
jeffvli faa7281993 add datetime to release notes 2026-02-02 01:45:02 -08:00
Hosted Weblate 2d0f4e7881 Translated using Weblate (Polish)
Currently translated at 99.9% (1128 of 1129 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 92.7% (1047 of 1129 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 97.6% (1102 of 1129 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 95.9% (1083 of 1129 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 90.5% (1022 of 1129 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: haha4ni <haha4ni@hotmail.com>
Co-authored-by: skajmer <skajmer@protonmail.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/pl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/
Translation: feishin/Translation
2026-02-02 10:44:51 +01:00
jeffvli ce9183ffd6 revert black background on visualizer container 2026-02-01 22:36:21 -08:00
jeffvli 3a5508653b add missing CrossfadeStyle types 2026-02-01 22:13:59 -08:00
jeffvli 7e4e28037c add typecheck to lint 2026-02-01 22:13:48 -08:00
Kendall Garner d2d8ea8249 misc type fixes, album artist header page favorite/rating work now 2026-02-01 22:04:46 -08:00
jeffvli ba835bec3e update to v1.4.1 2026-02-01 20:38:17 -08:00
jeffvli 9850874dfd support viewing up to 5 previous releases in the release notes modal 2026-02-01 20:37:51 -08:00
jeffvli 51a8285ba2 adjust fullscreen player z-indexes back
- the modal needs to appear above
- instead, move the titlebar controls z-index under the fullscreen players
2026-02-01 20:28:47 -08:00
jeffvli e12150d026 match Save as Collection popover width to its target 2026-02-01 20:26:02 -08:00
jeffvli 54bc241984 add Save as Collection button to the filters modal 2026-02-01 20:25:43 -08:00
jeffvli a698f83c45 fix butterchurn preset display not updating 2026-02-01 20:14:25 -08:00
jeffvli 854e7cc67e update to v1.4.0 2026-02-01 19:11:27 -08:00
Hosted Weblate c3920cbcc0 Translated using Weblate (Indonesian)
Currently translated at 100.0% (1126 of 1126 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (1126 of 1126 strings)

Translated using Weblate (Russian)

Currently translated at 73.3% (826 of 1126 strings)

Translated using Weblate (Russian)

Currently translated at 73.3% (826 of 1126 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1126 of 1126 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 92.5% (1042 of 1126 strings)

Translated using Weblate (Japanese)

Currently translated at 85.3% (961 of 1126 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 92.2% (1039 of 1126 strings)

Translated using Weblate (Catalan)

Currently translated at 98.8% (1113 of 1126 strings)

Translated using Weblate (Arabic)

Currently translated at 10.0% (113 of 1126 strings)

Translated using Weblate (Swedish)

Currently translated at 33.6% (379 of 1126 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 91.6% (1032 of 1126 strings)

Translated using Weblate (Tamil)

Currently translated at 58.3% (657 of 1126 strings)

Translated using Weblate (Indonesian)

Currently translated at 56.1% (632 of 1126 strings)

Translated using Weblate (Korean)

Currently translated at 38.0% (428 of 1126 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 61.1% (689 of 1126 strings)

Translated using Weblate (Spanish)

Currently translated at 99.3% (1119 of 1126 strings)

Translated using Weblate (Polish)

Currently translated at 99.3% (1119 of 1126 strings)

Translated using Weblate (Italian)

Currently translated at 60.2% (678 of 1126 strings)

Translated using Weblate (Hungarian)

Currently translated at 81.9% (923 of 1126 strings)

Translated using Weblate (Basque)

Currently translated at 82.9% (934 of 1126 strings)

Translated using Weblate (Japanese)

Currently translated at 84.7% (954 of 1126 strings)

Translated using Weblate (Portuguese)

Currently translated at 36.6% (413 of 1126 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 36.3% (409 of 1126 strings)

Translated using Weblate (Serbian)

Currently translated at 44.4% (501 of 1126 strings)

Translated using Weblate (Slovenian)

Currently translated at 43.7% (493 of 1126 strings)

Translated using Weblate (Turkish)

Currently translated at 61.7% (695 of 1126 strings)

Translated using Weblate (Czech)

Currently translated at 99.3% (1119 of 1126 strings)

Translated using Weblate (Dutch)

Currently translated at 73.3% (826 of 1126 strings)

Translated using Weblate (Persian)

Currently translated at 44.8% (505 of 1126 strings)

Translated using Weblate (Slovak)

Currently translated at 59.6% (672 of 1126 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 85.5% (963 of 1126 strings)

Translated using Weblate (Finnish)

Currently translated at 62.5% (704 of 1126 strings)

Co-authored-by: Aitor Astorga <a.astorga.sdv@protonmail.com>
Co-authored-by: Alexander Temnikov <sachat2006@gmail.com>
Co-authored-by: Arif Budiman <arifpedia@gmail.com>
Co-authored-by: Aurora <arci@anche.no>
Co-authored-by: Darwin <daerwendeshu@outlook.com>
Co-authored-by: Fadilah Riczky <friczky@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hadi <xhopeter@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ilija <zojka2g@gmail.com>
Co-authored-by: Joao <joaohirasawaa@gmail.com>
Co-authored-by: Jonne Saloranta <saloranta.jonne@gmail.com>
Co-authored-by: Joren Vansteenkiste <vansteenkiste.joren@telenet.be>
Co-authored-by: Martin Stojanoski <martin.stojanoski2000@gmail.com>
Co-authored-by: Mattias <mattiasghodsian@gmail.com>
Co-authored-by: Mücahit Kaya <kaya-mucahit@outlook.com>
Co-authored-by: Ondo <SparkyOndo@proton.me>
Co-authored-by: WilliamNT <hwbendeguz@gmail.com>
Co-authored-by: York <goog10216922@gmail.com>
Co-authored-by: aorinngoDo <aorinngo@email.cz>
Co-authored-by: haha4ni <haha4ni@hotmail.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: klodrik <klodrik@zoominn.no>
Co-authored-by: peter cerny <posli.to.semka@gmail.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: tgp0625 <tgp0625@naver.com>
Co-authored-by: xB <abxb19@gmail.com>
Co-authored-by: Роман <romkaeliseev@gmail.com>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ar/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/eu/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fa/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fi/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/hu/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/id/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/it/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ko/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pt/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sk/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sr/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/sv/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ta/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/tr/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/
Translation: feishin/Translation
2026-02-02 04:10:51 +01:00
jeffvli 6a68cebc93 increase fullscreen overlay z-index above titlebar 2026-02-01 19:10:38 -08:00
jeffvli ba474205c8 bump electron to v39.4.0 2026-02-01 18:59:46 -08:00
jeffvli dffe5bfffb fix duplicate import merge error 2026-02-01 18:56:48 -08:00
jeffvli 7615c0d2ba track image viewport state in sessionStorage 2026-02-01 18:55:08 -08:00
jeffvli 216353837c decrease zIndex of error boundary ServerSelector to prevent display above FullScreenPlayer 2026-02-01 18:55:08 -08:00
jeffvli 7f180b2c50 add bottom padding to fullscreenplayer header 2026-02-01 18:55:08 -08:00
Exceen ce46e0f351 feat: add regex filter setting for sidebar playlists (#1589)
* added regex filter for sidebar playlists

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2026-02-01 18:25:37 -08:00
jeffvli 0999b93b47 add missing border radius to item card container 2026-01-31 02:41:25 -08:00
jeffvli 63c21cf7c2 add 2 line clamp to genre image placeholder 2026-01-31 01:42:51 -08:00
jeffvli 72ab1dc8a0 add download context menu action for browser 2026-01-31 00:41:48 -08:00
jeffvli 131e1ec11d support filter pinning on the genre detail list 2026-01-30 21:42:17 -08:00
jeffvli 4d60f5b8d9 add genre image card placeholder 2026-01-30 21:39:05 -08:00
jeffvli 6962a05c96 fix icon and wrap on collection title 2026-01-30 21:29:33 -08:00
jeffvli 25ee57da1c remove filter modal from playlist list header 2026-01-30 21:12:18 -08:00
jeffvli 37e41c9c8e re-add background to modal header 2026-01-30 21:10:59 -08:00
jeffvli 69b6821deb add secondary sort for album release date 2026-01-30 21:07:44 -08:00
jeffvli 8e6078515a add spinner for color loading fallback 2026-01-30 21:07:07 -08:00
jeffvli 73868dbcbf add translation keys for window bar 2026-01-30 20:51:24 -08:00
jeffvli 49ae774d48 fix translation files to use correct plurality references 2026-01-30 20:51:12 -08:00
Hosted Weblate 5b3e2da95b Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 85.8% (962 of 1120 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (1120 of 1120 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (1120 of 1120 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1120 of 1120 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (1119 of 1119 strings)

Translated using Weblate (Japanese)

Currently translated at 85.3% (955 of 1119 strings)

Translated using Weblate (Russian)

Currently translated at 67.1% (751 of 1119 strings)

Translated using Weblate (Basque)

Currently translated at 78.0% (869 of 1114 strings)

Translated using Weblate (Basque)

Currently translated at 77.9% (868 of 1114 strings)

Translated using Weblate (Japanese)

Currently translated at 84.0% (936 of 1114 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1114 of 1114 strings)

Translated using Weblate (Japanese)

Currently translated at 83.9% (935 of 1114 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1114 of 1114 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (1114 of 1114 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (1114 of 1114 strings)

Translated using Weblate (Czech)

Currently translated at 97.3% (1084 of 1114 strings)

Translated using Weblate (Danish)

Currently translated at 0.3% (4 of 1114 strings)

Added translation using Weblate (Danish)

Translated using Weblate (Czech)

Currently translated at 100.0% (1114 of 1114 strings)

Translated using Weblate (Basque)

Currently translated at 84.5% (942 of 1114 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1114 of 1114 strings)

Translated using Weblate (Basque)

Currently translated at 84.5% (938 of 1110 strings)

Translated using Weblate (Basque)

Currently translated at 84.3% (936 of 1110 strings)

Translated using Weblate (Dutch)

Currently translated at 74.5% (828 of 1110 strings)

Translated using Weblate (Turkish)

Currently translated at 63.0% (700 of 1110 strings)

Translated using Weblate (French)

Currently translated at 92.9% (1032 of 1110 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1110 of 1110 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (1110 of 1110 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1110 of 1110 strings)

Translated using Weblate (Russian)

Currently translated at 72.0% (800 of 1110 strings)

Co-authored-by: Aitor Astorga <a.astorga.sdv@protonmail.com>
Co-authored-by: Denisa Alicia Rissa <denisarissa@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fordas <fordas15@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Koray HATIRNAZ <hatirnazkoray@gmail.com>
Co-authored-by: KosmoMoustache <kosmomoustache@users.noreply.hosted.weblate.org>
Co-authored-by: Ondo <SparkyOndo@proton.me>
Co-authored-by: haha4ni <haha4ni@hotmail.com>
Co-authored-by: jay <jayma13222@gmail.com>
Co-authored-by: karigane <169052233+karigane-cha@users.noreply.github.com>
Co-authored-by: skajmer <skajmer@protonmail.com>
Co-authored-by: Роман <romkaeliseev@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/cs/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/da/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/es/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/eu/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/fr/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ja/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/nl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/pl/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/tr/
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/
Translation: feishin/Translation
2026-01-31 04:03:41 +00:00
jeffvli 1a5e513526 add list filter collections 2026-01-30 20:03:27 -08:00
jeffvli ad83e95a46 decouple audio device setting property depending on player type (#1617) 2026-01-30 17:55:25 -08:00
jeffvli bdd5c78d39 add custom skeleton component, remove dependency 2026-01-30 17:50:34 -08:00
Damien Erambert 6a0c2213a5 Prevent scrollbar appearing in album metadata column on large viewport (#1629)
* Prevent layout shifting due to scrollbar appearing in album metadata column
2026-01-30 17:48:55 -08:00
jeffvli 99515591f1 conditionally load analytics script based on disabled status (#1630) 2026-01-30 12:20:04 -08:00
Steffen Martinsen b8228844df feat: Add support for player controls in macOS dock menu (#1627)
* Added simple macOS dock menu similar to tray menu

* Enhanced and moved dock menu to darwin folder and enabled mpris on macOS to support play/pause state

* Added missing property sortName to silence TS error
2026-01-30 12:01:02 -08:00
jeffvli 7613bc32c2 decrease image debounce duration 2026-01-29 22:28:01 -08:00
jeffvli 4d81dc49fa fix global hotkey volume step (#1614) 2026-01-29 22:23:09 -08:00
jeffvli 8c65775310 fix selection state on ItemGridList when single select mode 2026-01-29 21:34:51 -08:00
jeffvli 74c9d0ca6f remove eager loading prop from Image 2026-01-29 21:32:10 -08:00
jeffvli 871bb9da3a update lyrics autofetcher to async search 2026-01-29 20:52:36 -08:00
jeffvli 8ae1fb4fdb fix type errors 2026-01-29 20:46:07 -08:00
jeffvli 1a39182d80 fix spoiler line breaks (#1622) 2026-01-29 20:43:57 -08:00
jeffvli a1a837298f fix title/description of home feature style setting 2026-01-29 19:58:54 -08:00
jeffvli 74b0e38f7e remove direct references to plural translation keys 2026-01-29 19:52:42 -08:00
jeffvli c1f48b21a4 add config to show/hide table header 2026-01-29 19:43:00 -08:00
jeffvli a496c04701 persist image render after entering viewport 2026-01-29 19:12:07 -08:00
das_ 590a7c7dfa Fix Discord details url exceeding max length (#1620)
* fix discord details url exceeding max length
2026-01-28 21:08:00 -08:00
Mike Benz 796629b4e6 Enable Playlist in sidebar to be sorted (#1542)
* add playlist reorder in sidebar
2026-01-28 20:54:20 -08:00
jeffvli ced3b491ff add SortName client side sort option (#1612) 2026-01-28 20:45:47 -08:00
jeffvli 78aebd7c5d add configuration to revert to single-select ItemGrid behavior 2026-01-28 20:14:05 -08:00
jeffvli 9532601efe make compact image container full width 2026-01-28 19:54:14 -08:00
jeffvli bd34a729b3 set debounce / viewport for images used in the app 2026-01-28 19:49:37 -08:00
jeffvli 23990b581b add enableDebounce prop to Image to conditionally debounce image loading 2026-01-24 18:08:44 -08:00
jeffvli f786da52bb add custom useDebouncedValue hook to handle initial set value 2026-01-24 17:42:53 -08:00
jeffvli ee145d6f65 add enableViewport prop to Image to conditionally lazy load 2026-01-24 17:27:32 -08:00
jeffvli c310ad65ef handle i18n plurality references with context instead of direct key access (#1591) 2026-01-24 17:20:11 -08:00
jeffvli 26284e2412 decrease long animation durations (#1553) 2026-01-24 17:08:06 -08:00
Kendall Garner 6e4cd6912b enable refresh for all home carousels, and fix favorite/rating on home 2026-01-24 14:54:29 -08:00
jeffvli 974313c30b add hebrew font 2026-01-24 11:57:14 -08:00
marank 8ed586e2f3 add support for disc subtitles from subsonic discTitles field (#1609) 2026-01-23 21:30:09 -08:00
marank f016d2cdf4 Cover art support for folder view (Subsonic servers only) (#1608)
* add coverArt support for subsonic folders
2026-01-23 21:26:51 -08:00
Jeff c79e041777 Fix macOS tray icon (#1600)
* add 32x32 icon for macOS tray

* add 16x16 image

* add black/transparent icon
2026-01-23 21:10:49 -08:00
jeffvli a50984d2cb remove memoization from individual table columns
- causes issues with drag/drop state
2026-01-23 20:43:28 -08:00
jeffvli 819945dcba fix issue where the first row would not show row border 2026-01-23 19:12:07 -08:00
jeffvli 9f81bff020 fix swipe area on grid carousel 2026-01-23 19:10:36 -08:00
jeffvli b45594515e retry autofit calculation to prevent zero value on initial render and memoization 2026-01-22 02:53:25 -08:00
jeffvli 3a5d701195 fix genres detail content being constrained to maxWidth 2026-01-22 02:10:13 -08:00
jeffvli 34e037725c disable autoscroll on non-list tables 2026-01-22 02:08:09 -08:00
jeffvli b4fd7b6a66 add prop to disable custom autoscroll on ItemTableList 2026-01-22 02:07:24 -08:00
jeffvli 81af324260 handle radio metadata for mpris / mediasession (#1586) 2026-01-22 01:30:45 -08:00
jeffvli a5541745c3 add toggle for single/multiple home feature carousel (#1581) (#1412) 2026-01-22 01:18:34 -08:00
jay 39d691d528 fix: stop volume slider clicks from opening full screen (#1585)
* fix volume slider clicks from opening full screen
2026-01-22 01:01:06 -08:00
jeffvli 0c1537e5ef prevent duplicate audio device in selector (#1598) 2026-01-22 00:33:52 -08:00
jeffvli 3405f853e3 allow swiping on the the grid carousel title area (#1521) 2026-01-22 00:33:52 -08:00
jeffvli 8b0b53029c fix missing autoscroll cleanup on ScrollArea 2026-01-22 00:33:51 -08:00
jeffvli f6012d3b03 add more memoization to the ItemTableList 2026-01-22 00:33:51 -08:00
jeffvli f4072c183b refactor feature hooks to be conditionally initialized 2026-01-21 02:23:25 -08:00
jeffvli 9a4a8eb742 refactor some usePlayerSong consumers to only fetch needed properties 2026-01-21 02:02:49 -08:00
jeffvli dbfb547af9 remove favorite/rating handlers from PlayerContext to prevent rerenders from consumers 2026-01-21 01:57:47 -08:00
jeffvli 0492b867aa optimize rating/favorite to prevent blocking UI 2026-01-21 01:38:11 -08:00
jeffvli b7cbb3055a fix memory leak on table autoscroll 2026-01-21 00:32:48 -08:00
jeffvli c3c18e22ea add butterchurn presets ignore constant for broken presets 2026-01-20 18:02:59 -08:00
jeffvli 9318d00a5b refactor butterchurn state to prevent rerender flicker on cycle 2026-01-20 17:58:58 -08:00
jeffvli a5940a9124 fix item grid calling onScrollEnd on initial render 2026-01-20 15:41:19 -08:00
jeffvli 9a8986834e remove wait for color calc on detail pages causing extra render 2026-01-20 15:40:48 -08:00
674 changed files with 55072 additions and 47045 deletions
+189
View File
@@ -0,0 +1,189 @@
# Alpha builds published to Cloudflare R2 with date versioning (e.g. 1.0.0-alpha-20260205).
# Required repo secrets: R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY (from R2 API token in Cloudflare dashboard).
name: Publish Alpha
on:
workflow_dispatch:
inputs:
version:
description: 'Semantic version number (e.g., 1.0.0) - alpha suffix will be added automatically'
required: false
type: string
schedule:
# Run at 3:00 AM PST daily (11:00 UTC; PST = UTC-8)
- cron: '0 11 * * *'
jobs:
check-new-commits:
runs-on: ubuntu-latest
outputs:
has_new_commits: ${{ steps.manual.outputs.has_new_commits || steps.check.outputs['has-new-commits'] }}
steps:
- name: Set has new commits (manual trigger)
id: manual
if: github.event_name == 'workflow_dispatch'
run: echo "has_new_commits=true" >> "$GITHUB_OUTPUT"
- name: Check for new commits (24 hr interval)
id: check
if: github.event_name != 'workflow_dispatch'
uses: adriangl/check-new-commits-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
seconds: 86400
prepare:
needs: check-new-commits
if: needs.check-new-commits.outputs.has_new_commits == 'true'
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout git repo
uses: actions/checkout@v6
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
- name: Set date-based alpha version
id: version
shell: pwsh
run: |
$inputVersion = "${{ github.event.inputs.version }}"
Write-Host "Input version: $inputVersion"
if ($inputVersion -eq "" -or $inputVersion -eq "null") {
# No input version provided (scheduled run or manual without input), auto-increment patch version
Write-Host "No version provided, auto-incrementing patch version..."
$currentVersion = (Get-Content package.json | ConvertFrom-Json).version
Write-Host "Current version: $currentVersion"
$cleanVersion = $currentVersion -replace '-.*$', ''
$versionParts = $cleanVersion.Split('.')
if ($versionParts.Length -ne 3) {
Write-Error "Current version format is invalid: $cleanVersion"
exit 1
}
$major = [int]$versionParts[0]
$minor = [int]$versionParts[1]
$patch = [int]$versionParts[2]
$newPatch = $patch + 1
$inputVersion = "$major.$minor.$newPatch"
Write-Host "Auto-generated version: $inputVersion"
} else {
# Validate semantic version format (major.minor.patch)
$versionPattern = '^\d+\.\d+\.\d+$'
if ($inputVersion -notmatch $versionPattern) {
Write-Error "Invalid version format. Expected semantic version (e.g., 1.0.0), got: $inputVersion"
exit 1
}
}
# Date in YYYYMMDD (PST / America/Los_Angeles)
$pst = [TimeZoneInfo]::FindSystemTimeZoneById('America/Los_Angeles')
$dateInPst = [TimeZoneInfo]::ConvertTimeFromUtc([DateTime]::UtcNow, $pst)
$dateStr = $dateInPst.ToString("yyyyMMdd")
$alphaVersion = "$inputVersion-alpha-$dateStr"
Write-Host "Alpha version: $alphaVersion"
# Update package.json
$packageJson = Get-Content package.json | ConvertFrom-Json
$packageJson.version = $alphaVersion
$packageJson | ConvertTo-Json -Depth 10 | Set-Content package.json
echo "version=$alphaVersion" >> $env:GITHUB_OUTPUT
cleanup:
needs: prepare
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT_URL: ${{ secrets.R2_ENDPOINT_URL }}
steps:
- name: Delete all objects in R2 bucket
run: |
aws s3 rm s3://feishin-nightly --recursive --endpoint-url $R2_ENDPOINT_URL
publish:
needs: [prepare, cleanup]
runs-on: ${{ matrix.os }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
strategy:
matrix:
os: [windows-latest, macos-26, ubuntu-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v6
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
- name: Set version from prepare job
shell: pwsh
run: |
$version = "${{ needs.prepare.outputs.version }}"
Write-Host "Setting version: $version"
$packageJson = Get-Content package.json | ConvertFrom-Json
$packageJson.version = $version
$packageJson | ConvertTo-Json -Depth 10 | Set-Content package.json
- name: Build and Publish to R2 (Windows)
if: matrix.os == 'windows-latest'
uses: nick-invision/retry@v3.0.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
pnpm run publish:win:alpha
on_retry_command: pnpm cache delete
- name: Build and Publish to R2 (macOS)
if: matrix.os == 'macos-26'
uses: nick-invision/retry@v3.0.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
pnpm run publish:mac:alpha
on_retry_command: pnpm cache delete
- name: Build and Publish to R2 (Linux)
if: matrix.os == 'ubuntu-latest'
uses: nick-invision/retry@v3.0.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
pnpm run publish:linux:alpha
on_retry_command: pnpm cache delete
- name: Build and Publish to R2 (Linux ARM64)
if: matrix.os == 'ubuntu-latest'
uses: nick-invision/retry@v3.0.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
pnpm run publish:linux-arm64:alpha
on_retry_command: pnpm cache delete
+14 -14
View File
@@ -15,12 +15,12 @@ jobs:
version: ${{ steps.version.outputs.version }} version: ${{ steps.version.outputs.version }}
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@@ -115,16 +115,16 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [windows-latest, macos-latest, ubuntu-latest] os: [windows-latest, macos-26, ubuntu-latest]
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@@ -146,7 +146,7 @@ jobs:
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -156,10 +156,10 @@ jobs:
on_retry_command: pnpm cache delete on_retry_command: pnpm cache delete
- name: Build and Publish releases (macOS) - name: Build and Publish releases (macOS)
if: matrix.os == 'macos-latest' if: matrix.os == 'macos-26'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -172,7 +172,7 @@ jobs:
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -185,7 +185,7 @@ jobs:
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -199,7 +199,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Edit release with commits and title - name: Edit release with commits and title
shell: pwsh shell: pwsh
@@ -346,7 +346,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Delete existing prereleases - name: Delete existing prereleases
shell: pwsh shell: pwsh
+1 -2
View File
@@ -20,7 +20,7 @@ jobs:
packages: write packages: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with: with:
@@ -51,5 +51,4 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
platforms: | platforms: |
linux/amd64 linux/amd64
linux/arm/v7
linux/arm64/v8 linux/arm64/v8
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
packages: write packages: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with: with:
+5 -5
View File
@@ -12,12 +12,12 @@ jobs:
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@@ -25,7 +25,7 @@ jobs:
- name: Build and Publish releases - name: Build and Publish releases
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -37,7 +37,7 @@ jobs:
- name: Build and Publish releases (arm64) - name: Build and Publish releases (arm64)
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
+6 -5
View File
@@ -8,24 +8,25 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-latest] os: [macos-26]
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
- name: Build and Publish releases - name: Build and Publish releases
env: env:
NODE_OPTIONS: --max-old-space-size=4096
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
+17 -13
View File
@@ -1,14 +1,17 @@
name: Publish (PR) name: Publish (PR)
on: on:
workflow_dispatch:
pull_request: pull_request:
branches: branches:
- development - development
paths: paths:
- 'src/**' - 'src/**'
- 'electron-builder*.yml'
jobs: jobs:
wait-for-lint: wait-for-lint:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Wait for Test workflow to complete - name: Wait for Test workflow to complete
@@ -22,27 +25,28 @@ jobs:
publish: publish:
needs: wait-for-lint needs: wait-for-lint
if: always() && (needs.wait-for-lint.result == 'success' || needs.wait-for-lint.result == 'skipped')
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [macos-latest, ubuntu-latest, windows-latest] os: [macos-26, ubuntu-latest, windows-latest]
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
- name: Build for Windows - name: Build for Windows
if: ${{ matrix.os == 'windows-latest' }} if: ${{ matrix.os == 'windows-latest' }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -52,7 +56,7 @@ jobs:
- name: Build for Linux - name: Build for Linux
if: ${{ matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'ubuntu-latest' }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -61,8 +65,8 @@ jobs:
pnpm run package:linux:pr pnpm run package:linux:pr
- name: Build for MacOS - name: Build for MacOS
if: ${{ matrix.os == 'macos-latest' }} if: ${{ matrix.os == 'macos-26' }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -82,27 +86,27 @@ jobs:
zip -r dist/linux-binaries.zip dist/*.{AppImage,deb,rpm} zip -r dist/linux-binaries.zip dist/*.{AppImage,deb,rpm}
- name: Zip MacOS Binaries - name: Zip MacOS Binaries
if: ${{ matrix.os == 'macos-latest' }} if: ${{ matrix.os == 'macos-26' }}
run: | run: |
zip -r dist/macos-binaries.zip dist/*.dmg zip -r dist/macos-binaries.zip dist/*.dmg
- name: Upload Windows Binaries - name: Upload Windows Binaries
if: ${{ matrix.os == 'windows-latest' }} if: ${{ matrix.os == 'windows-latest' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: windows-binaries name: windows-binaries
path: dist/windows-binaries.zip path: dist/windows-binaries.zip
- name: Upload Linux Binaries - name: Upload Linux Binaries
if: ${{ matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'ubuntu-latest' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: linux-binaries name: linux-binaries
path: dist/linux-binaries.zip path: dist/linux-binaries.zip
- name: Upload MacOS Binaries - name: Upload MacOS Binaries
if: ${{ matrix.os == 'macos-latest' }} if: ${{ matrix.os == 'macos-26' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: macos-binaries name: macos-binaries
path: dist/macos-binaries.zip path: dist/macos-binaries.zip
+4 -4
View File
@@ -12,12 +12,12 @@ jobs:
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@@ -25,7 +25,7 @@ jobs:
- name: Build and Publish releases - name: Build and Publish releases
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
+1 -2
View File
@@ -16,6 +16,5 @@ jobs:
- uses: vedantmgoyal9/winget-releaser@main - uses: vedantmgoyal9/winget-releaser@main
with: with:
identifier: jeffvli.Feishin identifier: jeffvli.Feishin
installers-regex: 'Feishin-*-win-x64\.exe' installers-regex: 'Feishin-*-win-(x64|arm64)\.exe'
token: ${{ secrets.WINGET_ACC_TOKEN }} token: ${{ secrets.WINGET_ACC_TOKEN }}
+9 -9
View File
@@ -8,16 +8,16 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [windows-latest, macos-latest, ubuntu-latest] os: [windows-latest, macos-26, ubuntu-latest]
steps: steps:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node and PNPM - name: Install Node and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@@ -26,7 +26,7 @@ jobs:
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -36,10 +36,10 @@ jobs:
on_retry_command: pnpm cache delete on_retry_command: pnpm cache delete
- name: Build and Publish releases (macOS) - name: Build and Publish releases (macOS)
if: matrix.os == 'macos-latest' if: matrix.os == 'macos-26'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -52,7 +52,7 @@ jobs:
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
@@ -65,7 +65,7 @@ jobs:
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2 uses: nick-invision/retry@v3.0.2
with: with:
timeout_minutes: 30 timeout_minutes: 30
max_attempts: 3 max_attempts: 3
+3 -3
View File
@@ -8,12 +8,12 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@v1 uses: actions/checkout@v6
- name: Install Node.js and PNPM - name: Install Node.js and PNPM
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
-1
View File
@@ -1,2 +1 @@
legacy-peer-deps=true legacy-peer-deps=true
only-built-dependencies=electron,esbuild
+5 -5
View File
@@ -1,5 +1,5 @@
# --- Builder stage # --- Builder stage
FROM node:23-alpine as builder FROM node:23-alpine AS builder
WORKDIR /app WORKDIR /app
# Copy package.json first to cache node_modules # Copy package.json first to cache node_modules
@@ -14,13 +14,13 @@ COPY . .
RUN pnpm run build:web RUN pnpm run build:web
# --- Production stage # --- Production stage
FROM nginx:alpine-slim FROM nginxinc/nginx-unprivileged:alpine-slim
COPY --chown=nginx:nginx --from=builder /app/out/web /usr/share/nginx/html COPY --chown=nginx:nginx --from=builder /app/out/web /usr/share/nginx/html
COPY ./settings.js.template /etc/nginx/templates/settings.js.template COPY --chown=nginx:nginx ./settings.js.template /etc/nginx/templates/settings.js.template
COPY ng.conf.template /etc/nginx/templates/default.conf.template COPY --chown=nginx:nginx ng.conf.template /etc/nginx/templates/default.conf.template
ENV SERVER_LOCK=false SERVER_NAME="" SERVER_TYPE="" SERVER_URL="" ENV SERVER_LOCK=false SERVER_NAME="" SERVER_TYPE="" SERVER_URL="" REMOTE_URL=""
ENV LEGACY_AUTHENTICATION="" ANALYTICS_DISABLED="" PUBLIC_PATH="/" ENV LEGACY_AUTHENTICATION="" ANALYTICS_DISABLED="" PUBLIC_PATH="/"
EXPOSE 9180 EXPOSE 9180
+15 -2
View File
@@ -59,7 +59,11 @@ For media keys to work, you will be prompted to allow Feishin to be a Trusted Ac
#### Linux Notes #### Linux Notes
We provide a small install script to download the latest `.AppImage`, make it executable, and also download the icons required by Desktop Environments. Finally, it generates a `.desktop` file to add Feishin to your Application Launcher. Feishin is available in [Flathub](https://flathub.org/en/apps/org.jeffvli.feishin).
Alternatively, you can install it as an Appimage.
We provide a small install script to download the latest `.AppImage`, make it executable, and also download the icons required by Desktop Environments.
Finally, it generates a `.desktop` file to add Feishin to your Application Launcher.
Simply run the installer like this: Simply run the installer like this:
@@ -114,6 +118,7 @@ services:
- SERVER_LOCK=true # When true AND name/type/url are set, only username/password can be toggled - SERVER_LOCK=true # When true AND name/type/url are set, only username/password can be toggled
- SERVER_TYPE=jellyfin # the allowed types are: jellyfin, navidrome, subsonic. These values are case insensitive - SERVER_TYPE=jellyfin # the allowed types are: jellyfin, navidrome, subsonic. These values are case insensitive
- SERVER_URL= # http://address:port or https://address:port - SERVER_URL= # http://address:port or https://address:port
- REMOTE_URL= # http://address or https://address
- LEGACY_AUTHENTICATION=false # When SERVER_LOCK is true, sets the legacy (plaintext) authentication flag for Subsonic/OpenSubsonic servers - LEGACY_AUTHENTICATION=false # When SERVER_LOCK is true, sets the legacy (plaintext) authentication flag for Subsonic/OpenSubsonic servers
- ANALYTICS_DISABLED=true # Set to true to disable Umami analytics tracking - ANALYTICS_DISABLED=true # Set to true to disable Umami analytics tracking
ports: ports:
@@ -134,7 +139,11 @@ services:
4. _Optional_ - To hard code the server url, pass the following environment variables: `SERVER_NAME`, `SERVER_TYPE` (one of `jellyfin` or `navidrome` or `subsonic`), `SERVER_URL`. To prevent users from changing these settings, pass `SERVER_LOCK=true`. This can only be set if all three of the previous values are set. When `SERVER_LOCK=true`, you can also set `LEGACY_AUTHENTICATION=true` or `LEGACY_AUTHENTICATION=false` to configure the legacy authentication flag for the server (only applicable for Subsonic/OpenSubsonic servers). 4. _Optional_ - To hard code the server url, pass the following environment variables: `SERVER_NAME`, `SERVER_TYPE` (one of `jellyfin` or `navidrome` or `subsonic`), `SERVER_URL`. To prevent users from changing these settings, pass `SERVER_LOCK=true`. This can only be set if all three of the previous values are set. When `SERVER_LOCK=true`, you can also set `LEGACY_AUTHENTICATION=true` or `LEGACY_AUTHENTICATION=false` to configure the legacy authentication flag for the server (only applicable for Subsonic/OpenSubsonic servers).
5. _Optional_ - To disable Umami analytics tracking in the Docker/web version, set the environment variable `ANALYTICS_DISABLED=true`. When enabled, the analytics script will not be loaded and all tracking will be disabled. 5. _Optional_ - If your server uses a separate public-facing URL than what integrating applications use internally to communicate with your server, such as a separate Navidrome `ShareURL`, set `REMOTE_URL` to said public-facing URL.
6. _Optional_ - To disable Umami analytics tracking in the Docker/web version, set the environment variable `ANALYTICS_DISABLED=true`. When enabled, the analytics script will not be loaded and all tracking will be disabled.
7. _Optional_ - App settings (theme, language, sidebar options, etc.) can be overridden with environment variables on first run. The variables use the `FS_` prefix (e.g. `FS_GENERAL_THEME=defaultDark`, `FS_GENERAL_LANGUAGE=de`). See [the settings environment variable documentation](docs/ENV_SETTINGS.md) for the full list.
## FAQ ## FAQ
@@ -160,6 +169,10 @@ Feishin supports any music server that implements a [Navidrome](https://www.navi
- [Qm-Music](https://github.com/chenqimiao/qm-music) - [Qm-Music](https://github.com/chenqimiao/qm-music)
- More (?) - More (?)
- [Plex](https://www.plex.tv/media-server-downloads)
- [Feishin fork by lux032](https://github.com/lux032/feishin) - Plex is not natively supported. Use the fork by lux032 to use Plex with Feishin.
### I have the issue "The SUID sandbox helper binary was found, but is not configured correctly" on Linux ### I have the issue "The SUID sandbox helper binary was found, but is not configured correctly" on Linux
This happens when you have user (unprivileged) namespaces disabled (`sysctl kernel.unprivileged_userns_clone` returns 0). You can fix this by either enabling unprivileged namespaces, or by making the `chrome-sandbox` Setuid. This happens when you have user (unprivileged) namespaces disabled (`sysctl kernel.unprivileged_userns_clone` returns 0). You can fix this by either enabling unprivileged namespaces, or by making the `chrome-sandbox` Setuid.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 32 KiB

+1
View File
@@ -8,6 +8,7 @@ services:
- SERVER_LOCK=false # When true AND name/type/url are set, only username/password can be toggled - SERVER_LOCK=false # When true AND name/type/url are set, only username/password can be toggled
- SERVER_TYPE=jellyfin # the allowed types are: jellyfin, navidrome, subsonic. These values are case insensitive - SERVER_TYPE=jellyfin # the allowed types are: jellyfin, navidrome, subsonic. These values are case insensitive
- SERVER_URL=http://localhost:8096 # http://address:port or https://address:port - SERVER_URL=http://localhost:8096 # http://address:port or https://address:port
# - REMOTE_URL=http://share.localhost # Used for compatibility with external functionality, such as custom sharing URLs on Navidrome
- LEGACY_AUTHENTICATION=false # When SERVER_LOCK is true, sets the legacyauth flag for server authentication (true or false) - LEGACY_AUTHENTICATION=false # When SERVER_LOCK is true, sets the legacyauth flag for server authentication (true or false)
- ANALYTICS_DISABLED=false # Set to true to disable Umami analytics tracking - ANALYTICS_DISABLED=false # Set to true to disable Umami analytics tracking
ports: ports:
+129
View File
@@ -0,0 +1,129 @@
# Environment variables for settings (web / Docker)
These variables override app settings **on first run** when no persisted settings exist. They are injected via `settings.js` (from `settings.js.template`) and only apply to the **web** build.
**Format:** All values are strings; booleans use `true`/`false`, numbers are numeric strings. Leave unset or empty to use the default.
---
## General
| Setting | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `general.accent` | `rgb(53, 116, 252)` | `FS_GENERAL_ACCENT` | CSS `rgb(r, g, b)` string (e.g. `rgb(53, 116, 252)`). Invalid values are ignored. |
| `general.albumBackground` | `false` | `FS_GENERAL_ALBUM_BACKGROUND` | `true` / `false` — Show album background image. |
| `general.albumBackgroundBlur` | `3` | `FS_GENERAL_ALBUM_BACKGROUND_BLUR` | Blur amount for album background (number). |
| `general.artistBackground` | `true` | `FS_GENERAL_ARTIST_BACKGROUND` | `true` / `false` — Show artist background image. |
| `general.artistBackgroundBlur` | `3` | `FS_GENERAL_ARTIST_BACKGROUND_BLUR` | Blur amount for artist background (number). |
| `general.blurExplicitImages` | `false` | `FS_GENERAL_BLUR_EXPLICIT_IMAGES` | `true` / `false` — Blur explicit images. |
| `general.combinedLyricsAndVisualizer` | `false` | `FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER` | `true` / `false` — Combine lyrics and visualizer panel. |
| `general.enableGridMultiSelect` | `false` | `FS_GENERAL_ENABLE_GRID_MULTI_SELECT` | `true` / `false` — Enable multi-select in grid views. |
| `general.externalLinks` | `true` | `FS_GENERAL_EXTERNAL_LINKS` | `true` / `false` — Show external links in UI. |
| `general.followCurrentSong` | `true` | `FS_GENERAL_FOLLOW_CURRENT_SONG` | `true` / `false` — Follow current song in list. |
| `general.followSystemTheme` | `false` | `FS_GENERAL_FOLLOW_SYSTEM_THEME` | `true` / `false` — Use OS light/dark preference. |
| `general.homeFeature` | `true` | `FS_GENERAL_HOME_FEATURE` | `true` / `false` — Show home featured carousel. |
| `general.homeFeatureStyle` | `single` | `FS_GENERAL_HOME_FEATURE_STYLE` | `multiple` / `single` — Home featured carousel style. |
| `general.language` | `en` | `FS_GENERAL_LANGUAGE` | UI language code (e.g. `en`, `de`, `fr`). |
| `general.theme` | `defaultDark` | `FS_GENERAL_THEME` | One of: `ayuDark`, `ayuLight`, `catppuccinLatte`, `catppuccinMocha`, `defaultDark`, `defaultLight`, `dracula`, `githubDark`, `githubLight`, `glassyDark`, `gruvboxDark`, `gruvboxLight`, `highContrastDark`, `highContrastLight`, `materialDark`, `materialLight`, `monokai`, `nightOwl`, `nord`, `oneDark`, `rosePine`, `rosePineDawn`, `rosePineMoon`, `shadesOfPurple`, `solarizedDark`, `solarizedLight`, `tokyoNight`, `vscodeDarkPlus`, `vscodeLightPlus`. |
| `general.themeDark` | `defaultDark` | `FS_GENERAL_THEME_DARK` | Same as theme (used when system is dark). |
| `general.themeLight` | `defaultLight` | `FS_GENERAL_THEME_LIGHT` | Same as theme (used when system is light). |
| `general.lastfmApiKey` | *(empty)* | `FS_GENERAL_LASTFM_API_KEY` | Last.fm API key. |
| `general.lastFM` | `true` | `FS_GENERAL_LAST_FM` | `true` / `false` — Enable Last.fm. |
| `general.listenBrainz` | `true` | `FS_GENERAL_LISTEN_BRAINZ` | `true` / `false` — ListenBrainz links. |
| `general.musicBrainz` | `true` | `FS_GENERAL_MUSIC_BRAINZ` | `true` / `false` — MusicBrainz links. |
| `general.nativeAspectRatio` | `false` | `FS_GENERAL_NATIVE_ASPECT_RATIO` | `true` / `false` — Use native cover art aspect ratio. |
| `general.pathReplace` | *(empty)* | `FS_GENERAL_PATH_REPLACE` | Path pattern to replace (e.g. server path in Docker). |
| `general.pathReplaceWith` | *(empty)* | `FS_GENERAL_PATH_REPLACE_WITH` | Replacement path. |
| `general.playerbarOpenDrawer` | `false` | `FS_GENERAL_PLAYERBAR_OPEN_DRAWER` | `true` / `false` — Open queue/lyrics as drawer from player bar. |
| `general.primaryShade` | `6` | `FS_GENERAL_PRIMARY_SHADE` | Mantine primary shade 09 (number). |
| `general.qobuz` | `true` | `FS_GENERAL_QOBUZ` | `true` / `false` — Qobuz links. |
| `general.resume` | `true` | `FS_GENERAL_RESUME` | `true` / `false` — Resume playback on load. |
| `general.showLyricsInSidebar` | `true` | `FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR` | `true` / `false` — Show lyrics in sidebar. |
| `general.showRatings` | `true` | `FS_GENERAL_SHOW_RATINGS` | `true` / `false` — Show star ratings. |
| `general.showVisualizerInSidebar` | `true` | `FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR` | `true` / `false` — Show visualizer in sidebar. |
| `general.sidebarCollapsedNavigation` | `true` | `FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION` | `true` / `false` — Start with collapsed sidebar nav. |
| `general.sidebarCollapseShared` | `false` | `FS_GENERAL_SIDEBAR_COLLAPSE_SHARED` | `true` / `false` — Share sidebar collapse state. |
| `general.sidebarPlaylistList` | `true` | `FS_GENERAL_SIDEBAR_PLAYLIST_LIST` | `true` / `false` — Show playlist list in sidebar. |
| `general.sidebarPlaylistSorting` | `false` | `FS_GENERAL_SIDEBAR_PLAYLIST_SORTING` | `true` / `false` — Enable playlist sorting in sidebar. |
| `general.sideQueueType` | `sideQueue` | `FS_GENERAL_SIDE_QUEUE_TYPE` | `sideDrawerQueue` / `sideQueue` — Side play queue style. |
| `general.sideQueueLayout` | `horizontal` | `FS_GENERAL_SIDE_QUEUE_LAYOUT` | `horizontal` / `vertical` — Attached side queue layout orientation. |
| `general.useThemeAccentColor` | `false` | `FS_GENERAL_USE_THEME_ACCENT_COLOR` | `true` / `false` — Use themes accent color instead of custom. |
| `general.useThemePrimaryShade` | `true` | `FS_GENERAL_USE_THEME_PRIMARY_SHADE` | `true` / `false` — Use themes primary shade. |
| `general.zoomFactor` | `100` | `FS_GENERAL_ZOOM_FACTOR` | UI zoom percentage (number). |
---
## Playback
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `playback.mediaSession` | `false` | `FS_PLAYBACK_MEDIA_SESSION` | `true` / `false` — Media Session API (e.g. browser/media keys). |
| `playback.webAudio` | `true` | `FS_PLAYBACK_WEB_AUDIO` | `true` / `false` — Use Web Audio for playback. |
| `playback.audioFadeOnStatusChange` | `true` | `FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE` | `true` / `false` — Fade on play/pause. |
| `playback.preservePitch` | `true` | `FS_PLAYBACK_PRESERVE_PITCH` | `true` / `false` — Preserve pitch when changing speed. |
| `playback.scrobble.enabled` | `true` | `FS_PLAYBACK_SCROBBLE_ENABLED` | `true` / `false` — Enable scrobbling. |
| `playback.scrobble.notify` | `false` | `FS_PLAYBACK_SCROBBLE_NOTIFY` | `true` / `false` — Scrobble notifications. |
| `playback.scrobble.scrobbleAtDuration` | `240` | `FS_PLAYBACK_SCROBBLE_AT_DURATION` | Seconds of playback before scrobble. |
| `playback.scrobble.scrobbleAtPercentage` | `75` | `FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE` | Percentage of track before scrobble. |
| `playback.transcode.enabled` | `false` | `FS_PLAYBACK_TRANSCODE_ENABLED` | `true` / `false` — Enable transcoding. |
---
## Discord
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `discord.enabled` | `false` | `FS_DISCORD_ENABLED` | `true` / `false` — Discord rich presence. |
| `discord.clientId` | *(built-in)* | `FS_DISCORD_CLIENT_ID` | Custom Discord application ID. |
| `discord.displayType` | `feishin` | `FS_DISCORD_DISPLAY_TYPE` | `artist` / `feishin` / `song`. |
| `discord.linkType` | `none` | `FS_DISCORD_LINK_TYPE` | `last_fm` / `musicbrainz` / `musicbrainz_last_fm` / `none`. |
| `discord.showAsListening` | `false` | `FS_DISCORD_SHOW_AS_LISTENING` | `true` / `false`. |
| `discord.showPaused` | `true` | `FS_DISCORD_SHOW_PAUSED` | `true` / `false` — Show paused state. |
| `discord.showServerImage` | `false` | `FS_DISCORD_SHOW_SERVER_IMAGE` | `true` / `false`. |
| `discord.showStateIcon` | `true` | `FS_DISCORD_SHOW_STATE_ICON` | `true` / `false`. |
---
## Lyrics
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `lyrics.fetch` | `true` | `FS_LYRICS_FETCH` | `true` / `false` — Fetch lyrics. |
| `lyrics.follow` | `true` | `FS_LYRICS_FOLLOW` | `true` / `false` — Follow current line. |
| `lyrics.delayMs` | `0` | `FS_LYRICS_DELAY_MS` | Sync delay in milliseconds. |
| `lyrics.preferLocalLyrics` | `true` | `FS_LYRICS_PREFER_LOCAL` | `true` / `false` — Prefer local lyric files. |
| `lyrics.showMatch` | `true` | `FS_LYRICS_SHOW_MATCH` | `true` / `false`. |
| `lyrics.showProvider` | `true` | `FS_LYRICS_SHOW_PROVIDER` | `true` / `false`. |
| `lyrics.enableAutoTranslation` | `false` | `FS_LYRICS_ENABLE_AUTO_TRANSLATION` | `true` / `false`. |
| `lyrics.translationApiKey` | *(empty)* | `FS_LYRICS_TRANSLATION_API_KEY` | API key for lyric translation. |
| `lyrics.translationTargetLanguage` | `en` | `FS_LYRICS_TRANSLATION_TARGET_LANGUAGE` | Target language code. |
| `lyrics.alignment` | `center` | `FS_LYRICS_ALIGNMENT` | `center` / `left` / `right`. |
---
## Auto DJ
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `autoDJ.enabled` | `false` | `FS_AUTO_DJ_ENABLED` | `true` / `false`. |
| `autoDJ.itemCount` | `5` | `FS_AUTO_DJ_ITEM_COUNT` | Number of items to add. |
| `autoDJ.timing` | `1` | `FS_AUTO_DJ_TIMING` | Timing value (number). |
---
## CSS
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `css.content` | *(empty)* | `FS_CSS_CONTENT` | Custom CSS string (sanitized like in-app custom CSS). Set `FS_CSS_ENABLED=true` to apply. |
| `css.enabled` | `false` | `FS_CSS_ENABLED` | `true` / `false` — Enable custom CSS. |
---
## Font
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `font.type` | `builtIn` | `FS_FONT_TYPE` | `builtIn` / `system` / `custom`. |
| `font.builtIn` | `Inter` | `FS_FONT_BUILT_IN` | Built-in font name. |
| `font.system` | *(empty)* | `FS_FONT_SYSTEM` | System font name (when type is `system`). |
+74
View File
@@ -0,0 +1,74 @@
appId: org.jeffvli.feishin
productName: Feishin
artifactName: ${productName}-${version}-${os}-${arch}.${ext}
electronVersion: 39.4.0
directories:
buildResources: assets
files:
- 'out/**/*'
- 'package.json'
extraResources:
- assets/**
asarUnpack:
- resources/**
win:
target:
- target: zip
arch:
- x64
- arm64
- target: nsis
arch:
- x64
- arm64
icon: assets/icons/icon.ico
nsis:
allowToChangeInstallationDirectory: true
oneClick: false
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
target:
- target: dmg
arch:
- arm64
- x64
- target: zip
arch:
- arm64
- x64
icon: media/feishin.icon
type: distribution
hardenedRuntime: false
identity: '-'
gatekeeperAssess: false
notarize: false
extendInfo:
NSAudioCaptureUsageDescription: "System audio access is required for mpv visualizer capture in Feishin"
NSLocalNetworkUsageDescription: 'Local network is necessary for accessing servers hosted on the same system as Feishin'
dmg:
contents: [{ x: 130, y: 220 }, { x: 410, y: 220, type: link, path: /Applications }]
linux:
target:
- AppImage
- deb
- tar.xz
category: AudioVideo;Audio;Player
icon: assets/icons/icon.png
artifactName: ${productName}-${os}-${arch}.${ext}
toolsets:
appimage: '1.0.2'
npmRebuild: false
publish:
provider: s3
bucket: feishin-nightly
channel: alpha
endpoint: https://065f090c64de2dc707dd70ac72db9669.r2.cloudflarestorage.com
+27 -12
View File
@@ -1,7 +1,7 @@
appId: org.jeffvli.feishin appId: org.jeffvli.feishin
productName: Feishin productName: Feishin
artifactName: ${productName}-${version}-${os}-${arch}.${ext} artifactName: ${productName}-${version}-${os}-${arch}.${ext}
electronVersion: 39.2.7 electronVersion: 39.4.0
directories: directories:
buildResources: assets buildResources: assets
files: files:
@@ -13,9 +13,15 @@ asarUnpack:
- resources/** - resources/**
win: win:
target: target:
- zip - target: zip
- nsis arch:
icon: assets/icons/icon.png - x64
- arm64
- target: nsis
arch:
- x64
- arm64
icon: assets/icons/icon.ico
nsis: nsis:
allowToChangeInstallationDirectory: true allowToChangeInstallationDirectory: true
@@ -26,17 +32,23 @@ nsis:
mac: mac:
target: target:
target: default - target: dmg
arch: arch:
- arm64 - arm64
- x64 - x64
icon: assets/icons/icon.icns - target: zip
arch:
- arm64
- x64
icon: media/feishin.icon
type: distribution type: distribution
hardenedRuntime: true hardenedRuntime: false
entitlements: assets/entitlements.mac.plist identity: '-'
entitlementsInherit: assets/entitlements.mac.plist
gatekeeperAssess: false gatekeeperAssess: false
notarize: false notarize: false
extendInfo:
NSAudioCaptureUsageDescription: "System audio access is required for mpv visualizer capture in Feishin"
NSLocalNetworkUsageDescription: 'Local network is necessary for accessing servers hosted on the same system as Feishin'
dmg: dmg:
contents: [{ x: 130, y: 220 }, { x: 410, y: 220, type: link, path: /Applications }] contents: [{ x: 130, y: 220 }, { x: 410, y: 220, type: link, path: /Applications }]
@@ -50,6 +62,9 @@ linux:
icon: assets/icons/icon.png icon: assets/icons/icon.png
artifactName: ${productName}-${os}-${arch}.${ext} artifactName: ${productName}-${os}-${arch}.${ext}
toolsets:
appimage: '1.0.2'
npmRebuild: false npmRebuild: false
publish: publish:
provider: github provider: github
+27 -11
View File
@@ -1,7 +1,7 @@
appId: org.jeffvli.feishin appId: org.jeffvli.feishin
productName: Feishin productName: Feishin
artifactName: ${productName}-${version}-${os}-${arch}.${ext} artifactName: ${productName}-${version}-${os}-${arch}.${ext}
electronVersion: 39.2.7 electronVersion: 39.4.0
directories: directories:
buildResources: assets buildResources: assets
files: files:
@@ -13,8 +13,14 @@ asarUnpack:
- resources/** - resources/**
win: win:
target: target:
- zip - target: zip
- nsis arch:
- x64
- arm64
- target: nsis
arch:
- x64
- arm64
icon: assets/icons/icon.ico icon: assets/icons/icon.ico
nsis: nsis:
@@ -26,17 +32,23 @@ nsis:
mac: mac:
target: target:
target: default - target: dmg
arch: arch:
- arm64 - arm64
- x64 - x64
icon: assets/icons/icon.icns - target: zip
arch:
- arm64
- x64
icon: media/feishin.icon
type: distribution type: distribution
hardenedRuntime: true hardenedRuntime: false
entitlements: assets/entitlements.mac.plist identity: '-'
entitlementsInherit: assets/entitlements.mac.plist
gatekeeperAssess: false gatekeeperAssess: false
notarize: false notarize: false
extendInfo:
NSAudioCaptureUsageDescription: 'System audio access is required for mpv visualizer capture in Feishin'
NSLocalNetworkUsageDescription: 'Local network is necessary for accessing servers hosted on the same system as Feishin'
dmg: dmg:
contents: [{ x: 130, y: 220 }, { x: 410, y: 220, type: link, path: /Applications }] contents: [{ x: 130, y: 220 }, { x: 410, y: 220, type: link, path: /Applications }]
@@ -50,7 +62,11 @@ linux:
icon: assets/icons/icon.png icon: assets/icons/icon.png
artifactName: ${productName}-${os}-${arch}.${ext} artifactName: ${productName}-${os}-${arch}.${ext}
toolsets:
appimage: '1.0.2'
npmRebuild: false npmRebuild: false
afterAllArtifactBuild: scripts/after-all-artifact-build.mjs
publish: publish:
provider: github provider: github
owner: jeffvli owner: jeffvli
+3 -2
View File
@@ -1,10 +1,11 @@
import react from '@vitejs/plugin-react';
import { externalizeDepsPlugin, UserConfig } from 'electron-vite'; import { externalizeDepsPlugin, UserConfig } from 'electron-vite';
import { resolve } from 'path'; import { resolve } from 'path';
import conditionalImportPlugin from 'vite-plugin-conditional-import'; import conditionalImportPlugin from 'vite-plugin-conditional-import';
import dynamicImportPlugin from 'vite-plugin-dynamic-import'; import dynamicImportPlugin from 'vite-plugin-dynamic-import';
import { ViteEjsPlugin } from 'vite-plugin-ejs'; import { ViteEjsPlugin } from 'vite-plugin-ejs';
import { createReactPlugin } from './vite.react-plugin';
const currentOSEnv = process.platform; const currentOSEnv = process.platform;
const electronRendererTarget = 'chrome87'; const electronRendererTarget = 'chrome87';
@@ -64,7 +65,7 @@ const config: UserConfig = {
localsConvention: 'camelCase', localsConvention: 'camelCase',
}, },
}, },
plugins: [react(), ViteEjsPlugin({ web: false })], plugins: [createReactPlugin(), ViteEjsPlugin({ web: false })],
resolve: { resolve: {
alias: { alias: {
'/@/i18n': resolve('src/i18n'), '/@/i18n': resolve('src/i18n'),
+1 -1
View File
@@ -25,7 +25,7 @@ export default tseslint.config(
'react-refresh': eslintPluginReactRefresh, 'react-refresh': eslintPluginReactRefresh,
}, },
rules: { rules: {
...eslintPluginReactHooks.configs.recommended.rules, ...eslintPluginReactHooks.configs['recommended-latest'].rules,
...eslintPluginReactRefresh.configs.vite.rules, ...eslintPluginReactRefresh.configs.vite.rules,
'@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-duplicate-enum-values': 'off', '@typescript-eslint/no-duplicate-enum-values': 'off',
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512"><g style="display:inline" transform="translate(-53.452 -43.352)scale(1.11813)"><circle cx="256" cy="240.312" r="21.5" style="opacity:1;fill:#000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.19597;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;filter:url(#filter249)"/><path d="M220.85 277.951 183.5 315.6l36 36.1 20-19.7s5.856-6.2 16.5-6.2 16.5 6.2 16.5 6.2l20 19.7 36-36.1-37.35-37.649A51.5 51.5 0 0 1 256 291.812a51.5 51.5 0 0 1-35.15-13.86" style="opacity:1;fill:#000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter249)"/><path d="M256 145.4a25.7 25.7 0 0 0-18.229 7.551L66.97 323.47A25.42 25.42 0 0 0 59.5 341.5c0 14.083 11.417 25.5 25.5 25.5a25.42 25.42 0 0 0 18.031-7.469l103.895-103.597a51.5 51.5 0 0 1-2.426-15.621 51.5 51.5 0 0 1 51.5-51.5 51.5 51.5 0 0 1 51.5 51.5 51.5 51.5 0 0 1-2.426 15.62L408.97 359.532A25.42 25.42 0 0 0 427 367c14.083 0 25.5-11.417 25.5-25.5a25.42 25.42 0 0 0-7.469-18.031L274.23 152.95a25.7 25.7 0 0 0-18.229-7.55" style="display:inline;opacity:1;fill:#000;fill-opacity:1;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;filter:url(#filter249)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+202
View File
@@ -0,0 +1,202 @@
{
"fill-specializations" : [
{
"value" : {
"linear-gradient" : [
"display-p3:0.87416,0.87416,0.87416,1.00000",
"display-p3:0.99575,0.99575,0.99575,1.00000"
],
"orientation" : {
"start" : {
"x" : 0.5,
"y" : 1
},
"stop" : {
"x" : 0.5,
"y" : 0.3
}
}
}
},
{
"appearance" : "dark",
"value" : "system-dark"
}
],
"groups" : [
{
"blend-mode-specializations" : [
{
"appearance" : "tinted",
"value" : "normal"
}
],
"blur-material-specializations" : [
{
"value" : 0.7
},
{
"appearance" : "dark",
"value" : 0.7
},
{
"appearance" : "tinted",
"value" : null
}
],
"hidden" : false,
"layers" : [
{
"blend-mode-specializations" : [
{
"appearance" : "tinted",
"value" : "normal"
}
],
"fill-specializations" : [
{
"value" : {
"solid" : "extended-gray:0.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : {
"linear-gradient" : [
"display-p3:0.78674,0.78674,0.78674,1.00000",
"display-p3:0.87416,0.87416,0.87416,1.00000"
],
"orientation" : {
"start" : {
"x" : 0.5,
"y" : 1
},
"stop" : {
"x" : 0.5,
"y" : 0
}
}
}
},
{
"appearance" : "tinted",
"value" : {
"solid" : "gray:1.00000,1.00000"
}
}
],
"glass-specializations" : [
{
"value" : true
},
{
"appearance" : "dark",
"value" : true
},
{
"appearance" : "tinted",
"value" : true
}
],
"hidden" : false,
"image-name" : "feishin.svg",
"name" : "feishin",
"opacity-specializations" : [
{
"value" : 1
},
{
"appearance" : "tinted",
"value" : 1
}
],
"position" : {
"scale" : 0.79,
"translation-in-points" : [
18,
-2
]
}
}
],
"lighting-specializations" : [
{
"value" : "individual"
},
{
"appearance" : "tinted",
"value" : "combined"
}
],
"position" : {
"scale" : 2.2,
"translation-in-points" : [
0,
0
]
},
"shadow-specializations" : [
{
"value" : {
"kind" : "neutral",
"opacity" : 1
}
},
{
"appearance" : "dark",
"value" : {
"kind" : "layer-color",
"opacity" : 0.5
}
},
{
"appearance" : "tinted",
"value" : {
"kind" : "neutral",
"opacity" : 1
}
}
],
"specular-specializations" : [
{
"value" : false
},
{
"appearance" : "dark",
"value" : false
},
{
"appearance" : "tinted",
"value" : true
}
],
"translucency-specializations" : [
{
"value" : {
"enabled" : true,
"value" : 0.29
}
},
{
"appearance" : "dark",
"value" : {
"enabled" : false,
"value" : 0.29
}
},
{
"appearance" : "tinted",
"value" : {
"enabled" : true,
"value" : 0.5
}
}
]
}
],
"supported-platforms" : {
"squares" : [
"macOS"
]
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 214 KiB

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

Before

Width:  |  Height:  |  Size: 101 KiB

+104
View File
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1"><linearGradient
id="linearGradient1"><stop
style="stop-color:#dfdfdf;stop-opacity:1;"
offset="0"
id="stop1" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop2" /></linearGradient><filter
style="color-interpolation-filters:sRGB"
id="filter249"
x="-0.61395349"
y="-0.61395349"
width="2.227907"
height="2.5069767"><feFlood
result="flood"
in="SourceGraphic"
flood-opacity="0.498039"
flood-color="rgb(0,0,0)"
id="feFlood247" /><feGaussianBlur
result="blur"
in="SourceGraphic"
stdDeviation="1.000000"
id="feGaussianBlur247" /><feOffset
result="offset"
in="blur"
dx="0.000000"
dy="2"
id="feOffset247" /><feComposite
result="comp1"
operator="in"
in="flood"
in2="offset"
id="feComposite248" /><feComposite
result="fbSourceGraphic"
operator="over"
in="SourceGraphic"
id="feComposite249"
in2="comp1" /><feColorMatrix
result="fbSourceGraphicAlpha"
in="fbSourceGraphic"
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
id="feColorMatrix122" /><feFlood
id="feFlood122"
result="flood"
in="fbSourceGraphic"
flood-opacity="0.196078"
flood-color="rgb(0,0,0)" /><feGaussianBlur
id="feGaussianBlur122"
result="blur"
in="fbSourceGraphic"
stdDeviation="10.000000" /><feOffset
id="feOffset122"
result="offset"
in="blur"
dx="0.000000"
dy="10.000000" /><feComposite
id="feComposite122"
result="comp1"
operator="in"
in="flood"
in2="offset" /><feComposite
id="feComposite123"
result="comp2"
operator="over"
in="fbSourceGraphic"
in2="comp1" /></filter><linearGradient
xlink:href="#linearGradient1"
id="linearGradient2"
x1="256"
y1="0"
x2="256"
y2="512"
gradientUnits="userSpaceOnUse" /></defs><g
id="layer1"
style="display:inline"><circle
style="display:inline;fill:url(#linearGradient2);stroke-width:25;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke"
id="background"
cx="256"
cy="256"
r="256" /><circle
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.19597;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;filter:url(#filter249)"
id="dot"
cx="256"
cy="240.31155"
r="21.5" /><path
id="bottom"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter249)"
d="M 220.84961,277.95117 183.5,315.59961 219.5,351.69922 239.5,332 c 0,0 5.85615,-6.19922 16.5,-6.19922 10.64385,0 16.5,6.19922 16.5,6.19922 l 20,19.69922 36,-36.09961 -37.34961,-37.64844 A 51.5,51.5 0 0 1 256,291.8125 51.5,51.5 0 0 1 220.84961,277.95117 Z" /><path
id="main"
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;filter:url(#filter249)"
d="m 256,145.40039 c -7.11895,0 -13.56326,2.88552 -18.22852,7.55078 L 66.96875,323.46875 C 62.354158,328.08334 59.5,334.45837 59.5,341.5 c 0,14.08326 11.416739,25.5 25.5,25.5 7.04163,0 13.41666,-2.85416 18.03125,-7.46875 L 206.92578,255.93359 A 51.5,51.5 0 0 1 204.5,240.3125 a 51.5,51.5 0 0 1 51.5,-51.5 51.5,51.5 0 0 1 51.5,51.5 51.5,51.5 0 0 1 -2.42578,15.62109 L 408.96875,359.53125 C 413.58334,364.14585 419.95837,367 427,367 c 14.08326,0 25.5,-11.41674 25.5,-25.5 0,-7.04163 -2.85415,-13.41666 -7.46875,-18.03125 L 274.22852,152.95117 C 269.56326,148.2859 263.11895,145.40039 256,145.40039 Z" /></g></svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+3
View File
@@ -1,5 +1,6 @@
server { server {
listen 9180; listen 9180;
listen [::]:9180;
sendfile on; sendfile on;
default_type application/octet-stream; default_type application/octet-stream;
@@ -19,9 +20,11 @@ server {
location ${PUBLIC_PATH}settings.js { location ${PUBLIC_PATH}settings.js {
alias /etc/nginx/conf.d/settings.js; alias /etc/nginx/conf.d/settings.js;
add_header Cache-Control "no-store";
} }
location ${PUBLIC_PATH}/settings.js { location ${PUBLIC_PATH}/settings.js {
alias /etc/nginx/conf.d/settings.js; alias /etc/nginx/conf.d/settings.js;
add_header Cache-Control "no-store";
} }
} }
-18321
View File
File diff suppressed because it is too large Load Diff
+85 -75
View File
@@ -1,6 +1,6 @@
{ {
"name": "feishin", "name": "feishin",
"version": "1.3.0", "version": "1.11.0",
"description": "A modern self-hosted music player.", "description": "A modern self-hosted music player.",
"keywords": [ "keywords": [
"subsonic", "subsonic",
@@ -30,29 +30,37 @@
"dev:watch": "electron-vite dev --watch", "dev:watch": "electron-vite dev --watch",
"i18next": "i18next -c src/i18n/i18next-parser.config.js", "i18next": "i18next -c src/i18n/i18next-parser.config.js",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"lint": "pnpm run lint-code && pnpm run lint-styles", "lint": "pnpm run typecheck && pnpm run lint-code && pnpm run lint-styles",
"lint:fix": "pnpm run lint-code:fix && pnpm run lint-styles:fix",
"lint-code": "eslint --max-warnings=0 --cache .", "lint-code": "eslint --max-warnings=0 --cache .",
"lint-code:fix": "eslint --cache --fix .", "lint-code:fix": "eslint --cache --fix .",
"lint-styles": "stylelint --max-warnings=0 'src/**/*.{css,scss}'", "lint-styles": "stylelint --max-warnings=0 'src/**/*.{css,scss}'",
"lint-styles:fix": "stylelint 'src/**/*.{css,scss}' --fix", "lint-styles:fix": "stylelint 'src/**/*.{css,scss}' --fix",
"lint:fix": "pnpm run lint-code:fix && pnpm run lint-styles:fix",
"package": "pnpm run build && electron-builder", "package": "pnpm run build && electron-builder",
"package:dev": "pnpm run build && electron-builder --dir", "package:dev": "pnpm run build && electron-builder --dir",
"package:linux": "pnpm run build && electron-builder --linux", "package:linux": "pnpm run build && electron-builder --linux",
"package:linux-arm64:pr": "pnpm run build && electron-builder --linux --arm64 --publish never",
"package:linux:pr": "pnpm run build && electron-builder --linux --publish never", "package:linux:pr": "pnpm run build && electron-builder --linux --publish never",
"package:linux-arm64:pr": "pnpm run build && electron-builder --linux --arm64 --publish never",
"package:mac": "pnpm run build && electron-builder --mac", "package:mac": "pnpm run build && electron-builder --mac",
"package:mac:pr": "pnpm run build && electron-builder --mac --publish never", "package:mac:pr": "pnpm run build && electron-builder --mac --publish never",
"package:win": "pnpm run build && electron-builder --win", "package:win": "pnpm run build && electron-builder --win",
"package:win:pr": "pnpm run build && electron-builder --win --publish never", "package:win:pr": "pnpm run build && electron-builder --win --publish never",
"package:win-arm64:pr": "pnpm run build && electron-builder --win --arm64 --publish never",
"publish:linux": "pnpm run build && electron-builder --publish always --linux", "publish:linux": "pnpm run build && electron-builder --publish always --linux",
"publish:linux-arm64": "pnpm run build && electron-builder --publish always --linux --arm64", "publish:linux:alpha": "pnpm run build && electron-builder --config electron-builder-alpha.yml --publish always --linux",
"publish:linux-arm64:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --linux --arm64",
"publish:linux:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --linux", "publish:linux:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --linux",
"publish:linux-arm64": "pnpm run build && electron-builder --publish always --linux --arm64",
"publish:linux-arm64:alpha": "pnpm run build && electron-builder --config electron-builder-alpha.yml --publish always --linux --arm64",
"publish:linux-arm64:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --linux --arm64",
"publish:mac": "pnpm run build && electron-builder --publish always --mac", "publish:mac": "pnpm run build && electron-builder --publish always --mac",
"publish:mac:alpha": "pnpm run build && electron-builder --config electron-builder-alpha.yml --publish always --mac",
"publish:mac:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --mac", "publish:mac:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --mac",
"publish:win": "pnpm run build && electron-builder --publish always --win", "publish:win": "pnpm run build && electron-builder --publish always --win",
"publish:win:alpha": "pnpm run build && electron-builder --config electron-builder-alpha.yml --publish always --win",
"publish:win:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --win", "publish:win:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --win",
"publish:win-arm64": "pnpm run build && electron-builder --publish always --win --arm64",
"publish:win-arm64:alpha": "pnpm run build && electron-builder --config electron-builder-alpha.yml --publish always --win --arm64",
"publish:win-arm64:beta": "pnpm run build && electron-builder --config electron-builder-beta.yml --publish always --win --arm64",
"start": "electron-vite preview", "start": "electron-vite preview",
"typecheck": "pnpm run typecheck:node && pnpm run typecheck:web", "typecheck": "pnpm run typecheck:node && pnpm run typecheck:web",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
@@ -60,121 +68,123 @@
"version": "pnpm version --no-git-tag-version", "version": "pnpm version --no-git-tag-version",
"postversion": "node ./scripts/update-app-stream.mjs" "postversion": "node ./scripts/update-app-stream.mjs"
}, },
"resolutions": {
"react-router": "7.14.0",
"xml2js": "0.5.0"
},
"dependencies": { "dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "1.7.7", "@atlaskit/pragmatic-drag-and-drop": "1.7.7",
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.2", "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.5",
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
"@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0", "@electron-toolkit/utils": "^4.0.0",
"@mantine/colors-generator": "^8.3.8", "@mantine/colors-generator": "^9.1.1",
"@mantine/core": "^8.3.8", "@mantine/core": "^9.1.1",
"@mantine/dates": "^8.3.8", "@mantine/dates": "^9.1.1",
"@mantine/form": "^8.3.8", "@mantine/form": "^9.1.1",
"@mantine/hooks": "^8.3.8", "@mantine/hooks": "^9.1.1",
"@mantine/modals": "^8.3.8", "@mantine/modals": "^9.1.1",
"@mantine/notifications": "^8.3.8", "@mantine/notifications": "^9.1.1",
"@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-context-menu": "^2.2.16",
"@tanstack/react-query": "^5.90.9", "@tanstack/react-query": "^5.96.2",
"@tanstack/react-query-devtools": "^5.90.2", "@tanstack/react-query-devtools": "^5.96.2",
"@tanstack/react-query-persist-client": "^5.90.11", "@tanstack/react-query-persist-client": "^5.96.2",
"@ts-rest/core": "^3.52.1", "@ts-rest/core": "^3.52.1",
"@wavesurfer/react": "^1.0.11", "@wavesurfer/react": "^1.0.12",
"@xhayper/discord-rpc": "^1.3.0", "@xhayper/discord-rpc": "^1.3.3",
"audiomotion-analyzer": "^4.5.1", "audiomotion-analyzer": "^4.5.4",
"axios": "^1.13.2", "axios": "^1.14.0",
"butterchurn": "^3.0.0-beta.5", "butterchurn": "3.0.0-beta.5",
"butterchurn-presets": "^3.0.0-beta.4", "butterchurn-presets": "3.0.0-beta.4",
"cheerio": "^1.1.2", "cheerio": "^1.2.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"dayjs": "^1.11.19", "dayjs": "^1.11.20",
"dompurify": "^3.3.0", "dompurify": "^3.3.3",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
"electron-localshortcut": "^3.2.1", "electron-localshortcut": "^3.2.1",
"electron-log": "^5.4.3", "electron-log": "^5.4.3",
"electron-store": "^8.2.0", "electron-store": "^8.2.0",
"electron-updater": "^6.6.2", "electron-updater": "^6.8.3",
"fast-average-color": "^9.5.0", "fast-average-color": "9.5.0",
"fast-xml-parser": "^5.3.2", "fast-xml-parser": "^5.5.10",
"format-duration": "^3.0.2", "format-duration": "^3.0.2",
"fuse.js": "^7.1.0", "fuse.js": "^7.2.0",
"i18next": "^25.6.2", "i18next": "^25.10.10",
"icecast-metadata-stats": "^0.1.12", "icecast-metadata-stats": "^0.1.12",
"idb-keyval": "^6.2.2", "idb-keyval": "^6.2.2",
"immer": "^10.2.0", "immer": "^10.2.0",
"is-electron": "^2.2.2", "is-electron": "^2.2.2",
"lodash": "^4.17.21", "lodash": "^4.18.1",
"md5": "^2.3.0", "md5": "^2.3.0",
"motion": "^12.23.24", "motion": "^12.38.0",
"mpris-service": "^2.1.2", "mpris-service": "^2.1.2",
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"node-mpv": "github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f", "node-mpv": "github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f",
"nuqs": "^2.7.1", "overlayscrollbars": "^2.14.0",
"overlayscrollbars": "^2.11.1",
"overlayscrollbars-react": "^0.5.6", "overlayscrollbars-react": "^0.5.6",
"qs": "^6.14.1", "qs": "^6.15.0",
"react": "^19.1.0", "react": "^19.2.4",
"react-call": "^1.8.1", "react-call": "^1.8.2",
"react-dom": "^19.1.0", "react-dom": "^19.2.4",
"react-error-boundary": "^5.0.0", "react-error-boundary": "^5.0.0",
"react-i18next": "^16.3.3", "react-i18next": "^16.6.6",
"react-icons": "^5.5.0", "react-icons": "^5.6.0",
"react-image": "^4.1.0", "react-player": "^2.16.1",
"react-loading-skeleton": "^3.5.0", "react-router": "^7.14.0",
"react-player": "^2.16.0", "react-split-pane": "^3.2.0",
"react-router": "^7.9.6",
"react-split-pane": "^3.0.4",
"react-virtualized-auto-sizer": "^1.0.26", "react-virtualized-auto-sizer": "^1.0.26",
"react-window": "1.8.11", "react-window": "1.8.11",
"react-window-v2": "npm:react-window@^2.2.3", "react-window-v2": "npm:react-window@^2.2.7",
"semver": "^7.5.4", "semver": "^7.7.4",
"string-to-color": "^2.2.2", "string-to-color": "^2.2.2",
"wavesurfer.js": "^7.11.1", "wavesurfer.js": "^7.12.5",
"ws": "^8.18.2", "ws": "^8.20.0",
"zod": "^3.22.3", "zod": "^3.25.76",
"zustand": "^5.0.5" "zustand": "^5.0.12"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^3.0.0", "@electron-toolkit/eslint-config-prettier": "^3.0.0",
"@electron-toolkit/eslint-config-ts": "^3.0.0", "@electron-toolkit/eslint-config-ts": "^3.1.0",
"@electron-toolkit/tsconfig": "^2.0.0", "@electron-toolkit/tsconfig": "^2.0.0",
"@types/electron-localshortcut": "^3.1.0", "@types/electron-localshortcut": "^3.1.3",
"@types/lodash": "^4.17.18", "@types/lodash": "^4.17.24",
"@types/md5": "^2.3.5", "@types/md5": "^2.3.6",
"@types/node": "^24.10.1", "@types/node": "^24.12.2",
"@types/react": "^19.2.5", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-window": "^1.8.8", "@types/react-window": "^1.8.8",
"@types/source-map-support": "^0.5.10", "@types/source-map-support": "^0.5.10",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^5.1.1", "@vitejs/plugin-react": "^5.2.0",
"babel-plugin-react-compiler": "^1.0.0",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"electron": "^39.2.7", "electron": "^39.8.6",
"electron-builder": "^26.0.12", "electron-builder": "^26.8.2",
"electron-devtools-installer": "^4.0.0", "electron-devtools-installer": "^4.0.0",
"electron-vite": "^4.0.1", "electron-vite": "^4.0.1",
"eslint": "^9.24.0", "eslint": "^9.39.4",
"eslint-plugin-perfectionist": "^4.13.0", "eslint-plugin-perfectionist": "^4.15.1",
"eslint-plugin-prettier": "^5.4.0", "eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-react-refresh": "^0.4.26",
"i18next-parser": "^9.3.0", "i18next-parser": "^9.4.0",
"postcss-preset-mantine": "^1.18.0", "postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"prettier": "^3.6.2", "prettier": "^3.8.1",
"prettier-plugin-packagejson": "^2.5.19", "prettier-plugin-packagejson": "^2.5.22",
"stylelint": "^16.25.0", "stylelint": "^16.26.1",
"stylelint-config-css-modules": "^4.5.1", "stylelint-config-css-modules": "^4.6.0",
"stylelint-config-recess-order": "^7.4.0", "stylelint-config-recess-order": "^7.7.0",
"stylelint-config-standard": "^39.0.1", "stylelint-config-standard": "^39.0.1",
"typescript": "^5.8.3", "typescript": "^5.9.3",
"vite": "^7.2.2", "vite": "^7.3.1",
"vite-plugin-conditional-import": "^0.1.7", "vite-plugin-conditional-import": "^0.1.7",
"vite-plugin-dynamic-import": "^1.6.0", "vite-plugin-dynamic-import": "^1.6.0",
"vite-plugin-ejs": "^1.7.0", "vite-plugin-ejs": "^1.7.0",
"vite-plugin-pwa": "^1.1.0" "vite-plugin-pwa": "^1.2.0"
}, },
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
+3083 -3084
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,9 +1,9 @@
import react from '@vitejs/plugin-react';
import path from 'path'; import path from 'path';
import { defineConfig, normalizePath } from 'vite'; import { defineConfig, normalizePath } from 'vite';
import { ViteEjsPlugin } from 'vite-plugin-ejs'; import { ViteEjsPlugin } from 'vite-plugin-ejs';
import { version } from './package.json'; import { version } from './package.json';
import { createReactPlugin } from './vite.react-plugin';
export default defineConfig({ export default defineConfig({
build: { build: {
@@ -35,7 +35,7 @@ export default defineConfig({
}, },
}, },
plugins: [ plugins: [
react(), createReactPlugin(),
ViteEjsPlugin({ ViteEjsPlugin({
prod: process.env.NODE_ENV === 'production', prod: process.env.NODE_ENV === 'production',
root: normalizePath(path.resolve(__dirname, './src/remote')), root: normalizePath(path.resolve(__dirname, './src/remote')),
Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 32 KiB

+45
View File
@@ -0,0 +1,45 @@
import { execSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/**
* Electron-builder afterAllArtifactBuild hook
* Runs the app stream update script only for Linux builds
* Returns the metainfo file path to be included in published artifacts
*/
// This is not a typescript file, and is called by electron-builder, so we cannot use typescript features here.
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default async function afterAllArtifactBuild(buildResult) {
// Check if this build includes Linux as a target
const isLinux = Array.from(buildResult.platformToTargets.keys()).some(
(platform) => platform.name === 'linux',
);
if (isLinux) {
const updateScriptPath = path.join(__dirname, 'update-app-stream.mjs');
const projectRoot = path.resolve(__dirname, '..');
const metainfoFile = path.resolve(projectRoot, 'org.jeffvli.feishin.metainfo.xml');
console.log('Running app stream update for Linux build...');
try {
execSync(`node ${updateScriptPath} --replace-if-version-missing`, {
cwd: projectRoot,
stdio: 'inherit',
});
// Return the metainfo file to be included in published artifacts
return [metainfoFile];
} catch (error) {
console.error('Failed to update app stream:', error.message);
throw error;
}
}
// Return empty array if not a Linux build
return [];
}
+32 -11
View File
@@ -3,30 +3,51 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
const args = process.argv.slice(2); const args = process.argv.slice(2);
if (args.length > 3) {
console.error('Usage: node update-app-stream.js [package-file] [date] [metainfo-file]'); // Parse flags and positional arguments
const flags = args.filter((arg) => arg.startsWith('--'));
const positionalArgs = args.filter((arg) => !arg.startsWith('--'));
const replaceIfVersionMissing = flags.includes('--replace-if-version-missing');
if (positionalArgs.length > 3) {
console.error(
'Usage: node update-app-stream.js [package-file] [date] [metainfo-file] [--replace-if-version-missing]',
);
process.exit(1); process.exit(1);
} }
const packageFile = args[0] || path.resolve(process.cwd(), 'package.json'); const packageFile = positionalArgs[0] || path.resolve(process.cwd(), 'package.json');
const packageContent = fs.readFileSync(packageFile, 'utf8'); const packageContent = fs.readFileSync(packageFile, 'utf8');
const packageJson = JSON.parse(packageContent); const packageJson = JSON.parse(packageContent);
const version = packageJson.version; const version = packageJson.version;
const time = Math.floor((Date.parse(args[1]) || Date.now()) / 1000); const time = Math.floor((Date.parse(positionalArgs[1]) || Date.now()) / 1000);
const metainfoFile = args[2] || path.resolve(process.cwd(), 'org.jeffvli.feishin.metainfo.xml'); const metainfoFile =
positionalArgs[2] || path.resolve(process.cwd(), 'org.jeffvli.feishin.metainfo.xml');
const parser = new XMLParser({ ignoreAttributes: false }); const parser = new XMLParser({ ignoreAttributes: false });
const metainfoContent = fs.readFileSync(metainfoFile, 'utf8'); const metainfoContent = fs.readFileSync(metainfoFile, 'utf8');
const metainfo = parser.parse(metainfoContent); const metainfo = parser.parse(metainfoContent);
if (!metainfo.component.releases.release.find((release) => release['@_version'] === version)) { const newRelease = {
metainfo.component.releases.release.unshift({ '@_date': new Date(time * 1000).toISOString().split('T')[0],
'@_date': new Date(time * 1000).toISOString().split('T')[0], '@_type': version.includes('-') ? 'development' : 'stable',
'@_type': version.includes('-') ? 'development' : 'stable', '@_version': version,
'@_version': version, };
});
if (replaceIfVersionMissing) {
// Replace all releases with only the current version
metainfo.component.releases.release = [newRelease];
} else {
// Default behavior: add new release if it doesn't exist
const releaseExists =
metainfo.component.releases.release.findIndex(
(release) => release['@_version'] === version,
) !== -1;
if (!releaseExists) {
metainfo.component.releases.release.unshift(newRelease);
}
} }
const builder = new XMLBuilder({ format: true, ignoreAttributes: false, indentBy: ' ' }); const builder = new XMLBuilder({ format: true, ignoreAttributes: false, indentBy: ' ' });
+90 -1
View File
@@ -1 +1,90 @@
"use strict";window.SERVER_URL="${SERVER_URL}";window.SERVER_NAME="${SERVER_NAME}";window.SERVER_TYPE="${SERVER_TYPE}";window.SERVER_LOCK="${SERVER_LOCK}";window.LEGACY_AUTHENTICATION="${LEGACY_AUTHENTICATION}";window.ANALYTICS_DISABLED="${ANALYTICS_DISABLED}"; "use strict";
window.SERVER_URL = "${SERVER_URL}";
window.REMOTE_URL = "${REMOTE_URL}";
window.SERVER_NAME = "${SERVER_NAME}";
window.SERVER_TYPE = "${SERVER_TYPE}";
window.SERVER_LOCK = "${SERVER_LOCK}";
window.LEGACY_AUTHENTICATION = "${LEGACY_AUTHENTICATION}";
window.ANALYTICS_DISABLED = "${ANALYTICS_DISABLED}";
window.FS_GENERAL_ACCENT = "${FS_GENERAL_ACCENT}";
window.FS_GENERAL_ALBUM_BACKGROUND = "${FS_GENERAL_ALBUM_BACKGROUND}";
window.FS_GENERAL_ALBUM_BACKGROUND_BLUR = "${FS_GENERAL_ALBUM_BACKGROUND_BLUR}";
window.FS_GENERAL_ARTIST_BACKGROUND = "${FS_GENERAL_ARTIST_BACKGROUND}";
window.FS_GENERAL_ARTIST_BACKGROUND_BLUR = "${FS_GENERAL_ARTIST_BACKGROUND_BLUR}";
window.FS_GENERAL_BLUR_EXPLICIT_IMAGES = "${FS_GENERAL_BLUR_EXPLICIT_IMAGES}";
window.FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER = "${FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER}";
window.FS_GENERAL_ENABLE_GRID_MULTI_SELECT = "${FS_GENERAL_ENABLE_GRID_MULTI_SELECT}";
window.FS_GENERAL_EXTERNAL_LINKS = "${FS_GENERAL_EXTERNAL_LINKS}";
window.FS_GENERAL_FOLLOW_CURRENT_SONG = "${FS_GENERAL_FOLLOW_CURRENT_SONG}";
window.FS_GENERAL_FOLLOW_SYSTEM_THEME = "${FS_GENERAL_FOLLOW_SYSTEM_THEME}";
window.FS_GENERAL_HOME_FEATURE = "${FS_GENERAL_HOME_FEATURE}";
window.FS_GENERAL_HOME_FEATURE_STYLE = "${FS_GENERAL_HOME_FEATURE_STYLE}";
window.FS_GENERAL_LANGUAGE = "${FS_GENERAL_LANGUAGE}";
window.FS_GENERAL_LAST_FM = "${FS_GENERAL_LAST_FM}";
window.FS_GENERAL_LASTFM_API_KEY = "${FS_GENERAL_LASTFM_API_KEY}";
window.FS_GENERAL_LISTEN_BRAINZ = "${FS_GENERAL_LISTEN_BRAINZ}";
window.FS_GENERAL_MUSIC_BRAINZ = "${FS_GENERAL_MUSIC_BRAINZ}";
window.FS_GENERAL_NATIVE_ASPECT_RATIO = "${FS_GENERAL_NATIVE_ASPECT_RATIO}";
window.FS_GENERAL_PATH_REPLACE = "${FS_GENERAL_PATH_REPLACE}";
window.FS_GENERAL_PATH_REPLACE_WITH = "${FS_GENERAL_PATH_REPLACE_WITH}";
window.FS_GENERAL_PLAYERBAR_OPEN_DRAWER = "${FS_GENERAL_PLAYERBAR_OPEN_DRAWER}";
window.FS_GENERAL_PRIMARY_SHADE = "${FS_GENERAL_PRIMARY_SHADE}";
window.FS_GENERAL_QOBUZ = "${FS_GENERAL_QOBUZ}";
window.FS_GENERAL_RESUME = "${FS_GENERAL_RESUME}";
window.FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR = "${FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR}";
window.FS_GENERAL_SHOW_RATINGS = "${FS_GENERAL_SHOW_RATINGS}";
window.FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR = "${FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR}";
window.FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION = "${FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION}";
window.FS_GENERAL_SIDEBAR_COLLAPSE_SHARED = "${FS_GENERAL_SIDEBAR_COLLAPSE_SHARED}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_LIST = "${FS_GENERAL_SIDEBAR_PLAYLIST_LIST}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_SORTING = "${FS_GENERAL_SIDEBAR_PLAYLIST_SORTING}";
window.FS_GENERAL_SIDE_QUEUE_TYPE = "${FS_GENERAL_SIDE_QUEUE_TYPE}";
window.FS_GENERAL_SIDE_QUEUE_LAYOUT = "${FS_GENERAL_SIDE_QUEUE_LAYOUT}";
window.FS_GENERAL_THEME = "${FS_GENERAL_THEME}";
window.FS_GENERAL_THEME_DARK = "${FS_GENERAL_THEME_DARK}";
window.FS_GENERAL_THEME_LIGHT = "${FS_GENERAL_THEME_LIGHT}";
window.FS_GENERAL_USE_THEME_ACCENT_COLOR = "${FS_GENERAL_USE_THEME_ACCENT_COLOR}";
window.FS_GENERAL_USE_THEME_PRIMARY_SHADE = "${FS_GENERAL_USE_THEME_PRIMARY_SHADE}";
window.FS_GENERAL_ZOOM_FACTOR = "${FS_GENERAL_ZOOM_FACTOR}";
window.FS_PLAYBACK_MEDIA_SESSION = "${FS_PLAYBACK_MEDIA_SESSION}";
window.FS_PLAYBACK_WEB_AUDIO = "${FS_PLAYBACK_WEB_AUDIO}";
window.FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE = "${FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE}";
window.FS_PLAYBACK_PRESERVE_PITCH = "${FS_PLAYBACK_PRESERVE_PITCH}";
window.FS_PLAYBACK_SCROBBLE_ENABLED = "${FS_PLAYBACK_SCROBBLE_ENABLED}";
window.FS_PLAYBACK_SCROBBLE_NOTIFY = "${FS_PLAYBACK_SCROBBLE_NOTIFY}";
window.FS_PLAYBACK_SCROBBLE_AT_DURATION = "${FS_PLAYBACK_SCROBBLE_AT_DURATION}";
window.FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE = "${FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE}";
window.FS_PLAYBACK_TRANSCODE_ENABLED = "${FS_PLAYBACK_TRANSCODE_ENABLED}";
window.FS_DISCORD_ENABLED = "${FS_DISCORD_ENABLED}";
window.FS_DISCORD_CLIENT_ID = "${FS_DISCORD_CLIENT_ID}";
window.FS_DISCORD_DISPLAY_TYPE = "${FS_DISCORD_DISPLAY_TYPE}";
window.FS_DISCORD_LINK_TYPE = "${FS_DISCORD_LINK_TYPE}";
window.FS_DISCORD_SHOW_AS_LISTENING = "${FS_DISCORD_SHOW_AS_LISTENING}";
window.FS_DISCORD_SHOW_PAUSED = "${FS_DISCORD_SHOW_PAUSED}";
window.FS_DISCORD_SHOW_SERVER_IMAGE = "${FS_DISCORD_SHOW_SERVER_IMAGE}";
window.FS_DISCORD_SHOW_STATE_ICON = "${FS_DISCORD_SHOW_STATE_ICON}";
window.FS_LYRICS_FETCH = "${FS_LYRICS_FETCH}";
window.FS_LYRICS_FOLLOW = "${FS_LYRICS_FOLLOW}";
window.FS_LYRICS_DELAY_MS = "${FS_LYRICS_DELAY_MS}";
window.FS_LYRICS_PREFER_LOCAL = "${FS_LYRICS_PREFER_LOCAL}";
window.FS_LYRICS_SHOW_MATCH = "${FS_LYRICS_SHOW_MATCH}";
window.FS_LYRICS_SHOW_PROVIDER = "${FS_LYRICS_SHOW_PROVIDER}";
window.FS_LYRICS_ENABLE_AUTO_TRANSLATION = "${FS_LYRICS_ENABLE_AUTO_TRANSLATION}";
window.FS_LYRICS_TRANSLATION_API_KEY = "${FS_LYRICS_TRANSLATION_API_KEY}";
window.FS_LYRICS_TRANSLATION_TARGET_LANGUAGE = "${FS_LYRICS_TRANSLATION_TARGET_LANGUAGE}";
window.FS_LYRICS_ALIGNMENT = "${FS_LYRICS_ALIGNMENT}";
window.FS_AUTO_DJ_ENABLED = "${FS_AUTO_DJ_ENABLED}";
window.FS_AUTO_DJ_ITEM_COUNT = "${FS_AUTO_DJ_ITEM_COUNT}";
window.FS_AUTO_DJ_TIMING = "${FS_AUTO_DJ_TIMING}";
window.FS_CSS_CONTENT = "${FS_CSS_CONTENT}";
window.FS_CSS_ENABLED = "${FS_CSS_ENABLED}";
window.FS_FONT_TYPE = "${FS_FONT_TYPE}";
window.FS_FONT_BUILT_IN = "${FS_FONT_BUILT_IN}";
window.FS_FONT_SYSTEM = "${FS_FONT_SYSTEM}";
+4 -12
View File
@@ -1,4 +1,4 @@
import { PostProcessorModule, TOptions } from 'i18next'; import { PostProcessorModule } from 'i18next';
import i18n from 'i18next'; import i18n from 'i18next';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
@@ -203,25 +203,17 @@ const titleCasePostProcessor: PostProcessorModule = {
type: 'postProcessor', type: 'postProcessor',
}; };
const ignoreSentenceCaseLanguages = ['de']; // const ignoreSentenceCaseLanguages = ['de'];
const sentenceCasePostProcessor: PostProcessorModule = { const sentenceCasePostProcessor: PostProcessorModule = {
name: 'sentenceCase', name: 'sentenceCase',
process: ( process: (value: string) => {
value: string,
_key: string,
_options: TOptions<Record<string, string>>,
translator: any,
) => {
const sentences = value.split('. '); const sentences = value.split('. ');
return sentences return sentences
.map((sentence) => { .map((sentence) => {
return ( return (
sentence.charAt(0).toLocaleUpperCase() + sentence.charAt(0).toLocaleUpperCase() + sentence.slice(1).toLocaleLowerCase()
(!ignoreSentenceCaseLanguages.includes(translator.language)
? sentence.slice(1).toLocaleLowerCase()
: sentence.slice(1))
); );
}) })
.join('. '); .join('. ');
+23 -12
View File
@@ -1,27 +1,33 @@
{ {
"action": { "action": {
"addToFavorites": "إضافة الى $t(entity.favorite_other)", "addToFavorites": "إضافة الى $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "إضافة الى $t(entity.playlist_one)", "addToPlaylist": "إضافة الى $t(entity.playlist, {\"count\": 1})",
"clearQueue": "مسح قائمة الإنتظار", "clearQueue": "مسح قائمة الإنتظار",
"createPlaylist": "إنشاء $t(entity.playlist_one)", "createPlaylist": "إنشاء $t(entity.playlist, {\"count\": 1})",
"deletePlaylist": "حذف $t(entity.playlist_one)", "deletePlaylist": "حذف $t(entity.playlist, {\"count\": 1})",
"deselectAll": "إلغاء تحديد الكل", "deselectAll": "إلغاء تحديد الكل",
"editPlaylist": "تعديل $t(entity.playlist_one)", "editPlaylist": "تعديل $t(entity.playlist, {\"count\": 1})",
"goToPage": "اذهب الى صفحة", "goToPage": "اذهب الى صفحة",
"moveToNext": "الذهاب الى التالي", "moveToNext": "الذهاب الى التالي",
"moveToBottom": "الذهاب الى الأسفل", "moveToBottom": "الذهاب الى الأسفل",
"moveToTop": "الذهاب الى الأعلى", "moveToTop": "الذهاب الى الأعلى",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"removeFromFavorites": "حذف من $t(entity.favorite_other)", "removeFromFavorites": "حذف من $t(entity.favorite, {\"count\": 2})",
"removeFromPlaylist": "حذف من $t(entity.playlist_one)", "removeFromPlaylist": "حذف من $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "حذف من قائمة الإنتظار", "removeFromQueue": "حذف من قائمة الإنتظار",
"setRating": "تحديد التقييم", "setRating": "تحديد التقييم",
"toggleSmartPlaylistEditor": "تشغيل / إطفاء وضع التعديل لـ $t(entity.smartPlaylist)", "toggleSmartPlaylistEditor": "تشغيل / إطفاء وضع التعديل لـ $t(entity.smartPlaylist)",
"viewPlaylists": "إظهار $t(entity.playlist_other)", "viewPlaylists": "إظهار $t(entity.playlist, {\"count\": 2})",
"openIn": { "openIn": {
"lastfm": "فتح في Last.fm", "lastfm": "فتح في Last.fm",
"musicbrainz": "فتح في MusicBrainz" "musicbrainz": "فتح في MusicBrainz"
} },
"addOrRemoveFromSelection": "إضافة أو إزالة من الإختيارات",
"selectRangeOfItems": "اختر مجموعة من العناصر",
"goToCurrent": "الانتقال إلى العنصر الحالي",
"createRadioStation": "يخلق $t(entity.radioStation, {\"count\": 1})",
"deleteRadioStation": "يمسح $t(entity.radioStation, {\"count\": 1})",
"selectAll": "تحديد الكل"
}, },
"common": { "common": {
"action_zero": "عملية", "action_zero": "عملية",
@@ -59,7 +65,7 @@
"configure": "تعديل", "configure": "تعديل",
"confirm": "تأكيد", "confirm": "تأكيد",
"create": "إنشاء", "create": "إنشاء",
"currentSong": "$t(entity.track_one) الحالي", "currentSong": "$t(entity.track, {\"count\": 1}) الحالي",
"decrease": "تنقيص", "decrease": "تنقيص",
"delete": "حذف", "delete": "حذف",
"descending": "تنازلي", "descending": "تنازلي",
@@ -102,7 +108,7 @@
"path": "المسار", "path": "المسار",
"playerMustBePaused": "يجب إيقاف المشغل", "playerMustBePaused": "يجب إيقاف المشغل",
"preview": "معاينة", "preview": "معاينة",
"previousSong": "$t(entity.track_one) السابق", "previousSong": "$t(entity.track, {\"count\": 1}) السابق",
"quit": "خروج", "quit": "خروج",
"random": "عشوائي", "random": "عشوائي",
"rating": "التقييم", "rating": "التقييم",
@@ -117,7 +123,12 @@
"saveAndReplace": "حفظ واستبدال", "saveAndReplace": "حفظ واستبدال",
"saveAs": "حفظ بإسم", "saveAs": "حفظ بإسم",
"search": "بحث", "search": "بحث",
"setting": "إعداد", "setting_zero": "إعداد",
"setting_one": "",
"setting_two": "",
"setting_few": "",
"setting_many": "",
"setting_other": "",
"share": "نشر", "share": "نشر",
"size": "حجم", "size": "حجم",
"sortOrder": "الترتيب", "sortOrder": "الترتيب",
+1028 -923
View File
File diff suppressed because it is too large Load Diff
+1012 -907
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+690 -424
View File
File diff suppressed because it is too large Load Diff
+998 -895
View File
File diff suppressed because it is too large Load Diff
+691 -586
View File
File diff suppressed because it is too large Load Diff
+880 -730
View File
File diff suppressed because it is too large Load Diff
+63 -63
View File
@@ -12,7 +12,7 @@
"unfavorite": "حذف از موردعلاقه‌ها", "unfavorite": "حذف از موردعلاقه‌ها",
"shuffle_off": "پخش تصادفی غیر فعال", "shuffle_off": "پخش تصادفی غیر فعال",
"skip_forward": "برو جلو", "skip_forward": "برو جلو",
"queue_moveToTop": "جابجا کردن انتخاب شده به پایین", "queue_moveToTop": "جابجا کردن انتخاب شده به بالا",
"queue_clear": "خالی کردن صف", "queue_clear": "خالی کردن صف",
"queue_remove": "حذف انتخاب شده", "queue_remove": "حذف انتخاب شده",
"addLast": "افزودن به پایان", "addLast": "افزودن به پایان",
@@ -24,7 +24,7 @@
"mute": "بی‌صدا کردن", "mute": "بی‌صدا کردن",
"playbackFetchCancel": "دارد طول می‌کشد... برای لفو کردن اعلان را ببندید", "playbackFetchCancel": "دارد طول می‌کشد... برای لفو کردن اعلان را ببندید",
"playbackFetchInProgress": "بارگذاری قطعه‌ها…", "playbackFetchInProgress": "بارگذاری قطعه‌ها…",
"queue_moveToBottom": "جابجا کردن انتخاب شده به بالا", "queue_moveToBottom": "جابجا کردن انتخاب شده به پایین",
"addNext": "افزودن به پسین", "addNext": "افزودن به پسین",
"favorite": "مورد علاقه", "favorite": "مورد علاقه",
"playSimilarSongs": "پخش آهنگ‌های همگون", "playSimilarSongs": "پخش آهنگ‌های همگون",
@@ -33,23 +33,23 @@
"muted": "بی‌صدا" "muted": "بی‌صدا"
}, },
"action": { "action": {
"editPlaylist": "ویرایش $t(entity.playlist_one)", "editPlaylist": "ویرایش $t(entity.playlist, {\"count\": 1})",
"goToPage": "برو به صفحهٔ", "goToPage": "برو به صفحهٔ",
"moveToTop": "انتقال به بالا", "moveToTop": "انتقال به بالا",
"clearQueue": "خالی کردن صف", "clearQueue": "خالی کردن صف",
"addToFavorites": "افزودن به $t(entity.favorite_other)", "addToFavorites": "افزودن به $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "افزودن به $t(entity.playlist_one)", "addToPlaylist": "افزودن به $t(entity.playlist, {\"count\": 1})",
"createPlaylist": "ساخت $t(entity.playlist_one)", "createPlaylist": "ساخت $t(entity.playlist, {\"count\": 1})",
"removeFromPlaylist": "حذف از $t(entity.playlist_one)", "removeFromPlaylist": "حذف از $t(entity.playlist, {\"count\": 1})",
"viewPlaylists": "نمایش $t(entity.playlist_other)", "viewPlaylists": "نمایش $t(entity.playlist, {\"count\": 2})",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"deletePlaylist": "حذف $t(entity.playlist_one)", "deletePlaylist": "حذف $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "حذف از صف", "removeFromQueue": "حذف از صف",
"deselectAll": "لغو انتخاب همه", "deselectAll": "لغو انتخاب همه",
"moveToBottom": "انتقال به پایین", "moveToBottom": "انتقال به پایین",
"setRating": "تعیین امتیاز", "setRating": "تعیین امتیاز",
"toggleSmartPlaylistEditor": "تغییر ویرایشگر $t(entity.smartPlaylist)", "toggleSmartPlaylistEditor": "تغییر ویرایشگر $t(entity.smartPlaylist)",
"removeFromFavorites": "حذف از $t(entity.favorite_other)", "removeFromFavorites": "حذف از $t(entity.favorite, {\"count\": 2})",
"openIn": { "openIn": {
"lastfm": "باز کردن در Last.fm", "lastfm": "باز کردن در Last.fm",
"musicbrainz": "باز کردن در MusicBranz" "musicbrainz": "باز کردن در MusicBranz"
@@ -70,22 +70,21 @@
"hotkey_rate1": "امتیاز ۱ ستاره", "hotkey_rate1": "امتیاز ۱ ستاره",
"hotkey_skipForward": "برو جلو", "hotkey_skipForward": "برو جلو",
"disableLibraryUpdateOnStartup": "غیرفعال کردن بررسی آخرین نسخه در آغاز به کار برنامه", "disableLibraryUpdateOnStartup": "غیرفعال کردن بررسی آخرین نسخه در آغاز به کار برنامه",
"discordApplicationId_description": "the application id for {{discord}} rich presence (defaults to {{defaultId}})", "discordApplicationId_description": "the application ID for {{discord}} Rich Presence (defaults to {{defaultId}})",
"playButtonBehavior_optionAddLast": "$t(player.addLast)", "playButtonBehavior_optionAddLast": "$t(player.addLast)",
"hotkey_playbackPlay": "پخش", "hotkey_playbackPlay": "پخش",
"hotkey_volumeDown": "کم کردن صدا", "hotkey_volumeDown": "کم کردن صدا",
"audioPlayer_description": "پخش‌کنندهٔ صدا را برای پخش انتخاب کنید", "audioPlayer_description": "پخش‌کنندهٔ صدا را برای پخش انتخاب کنید",
"hotkey_globalSearch": "جست و جوی سراسری", "hotkey_globalSearch": "جست و جوی سراسری",
"disableAutomaticUpdates": "غیرفعال کردن به‌‌روزرسانی خودکار",
"exitToTray_description": "خروج از اپلیکیشن به system tray", "exitToTray_description": "خروج از اپلیکیشن به system tray",
"replayGainMode_optionAlbum": "$t(entity.album_one)", "replayGainMode_optionAlbum": "$t(entity.album, {\"count\": 1})",
"discordUpdateInterval_description": "فاصلهٔ بین هر به روزرسانی به ثانیه (حداقل ۱۵ ثانیه)", "discordUpdateInterval_description": "فاصلهٔ بین هر به روزرسانی به ثانیه (حداقل ۱۵ ثانیه)",
"audioExclusiveMode": "حالت اختصاصی صدا", "audioExclusiveMode": "حالت اختصاصی صدا",
"remotePassword": "رمز عبور کنترل از راه دور", "remotePassword": "رمز عبور کنترل از راه دور",
"language_description": "زبان اپلیکیشن را معین می‌کند $t(common.restartRequired)", "language_description": "زبان اپلیکیشن را معین می‌کند $t(common.restartRequired)",
"hotkey_rate3": "امتیاز ۳ ستاره", "hotkey_rate3": "امتیاز ۳ ستاره",
"font": "قلم", "font": "قلم",
"replayGainMode_optionTrack": "$t(entity.track_one)", "replayGainMode_optionTrack": "$t(entity.track, {\"count\": 1})",
"hotkey_toggleFullScreenPlayer": "تغییر به پخش‌کنندهٔ تمام‌صفحه", "hotkey_toggleFullScreenPlayer": "تغییر به پخش‌کنندهٔ تمام‌صفحه",
"hotkey_localSearch": "جست و جو در صفحه", "hotkey_localSearch": "جست و جو در صفحه",
"hotkey_toggleQueue": "تغییر صف", "hotkey_toggleQueue": "تغییر صف",
@@ -110,7 +109,7 @@
"customFontPath": "مسیر قلم سفارشی", "customFontPath": "مسیر قلم سفارشی",
"audioPlayer": "پخش‌کنندهٔ صدا", "audioPlayer": "پخش‌کنندهٔ صدا",
"hotkey_rate0": "حذف امتیاز", "hotkey_rate0": "حذف امتیاز",
"discordApplicationId": "{{discord}} application id", "discordApplicationId": "{{discord}} application ID",
"hotkey_volumeMute": "بستن صدا", "hotkey_volumeMute": "بستن صدا",
"showSkipButton": "نمایش دکمهٔ رد کردن", "showSkipButton": "نمایش دکمهٔ رد کردن",
"customFontPath_description": "مسیر قلم سفارشی را برای استفاده در اپلیکیشن مشخص کنید", "customFontPath_description": "مسیر قلم سفارشی را برای استفاده در اپلیکیشن مشخص کنید",
@@ -133,7 +132,7 @@
"buttonSize": "اندازه‌ی دکمه‌ی پخش نوار", "buttonSize": "اندازه‌ی دکمه‌ی پخش نوار",
"contextMenu": "پیکربندی فهرست زمینه (کلیک راست)", "contextMenu": "پیکربندی فهرست زمینه (کلیک راست)",
"buttonSize_description": "اندازه‌ی دکمه‌های پخش نوار", "buttonSize_description": "اندازه‌ی دکمه‌های پخش نوار",
"audioExclusiveMode_description": "حالت اختصاصی خروجی را فعال می‌کند. در این حالت، سامانه معمولاً قفل است و فقط mpv می‌تواند خروجی صدا دهد", "audioExclusiveMode_description": "حالت اختصاصی خروجی را فعال می‌کند. در این حالت، سامانه معمولاً قفل است و فقط MPV می‌تواند خروجی صدا دهد",
"clearQueryCache_description": "یک 'پاک‌سازی نرم' از فیشین. این فهرست‌های پخش و فراداده‌ی قطعه‌ها را تازه می‌کند و متن شعرهای ذخیره شده را بازنشانی می‌کند. پیکربندی‌ها، اعتبارنامه‌های سرویس‌دهنده و نگاره‌های کَش شده حفظ می‌شوند", "clearQueryCache_description": "یک 'پاک‌سازی نرم' از فیشین. این فهرست‌های پخش و فراداده‌ی قطعه‌ها را تازه می‌کند و متن شعرهای ذخیره شده را بازنشانی می‌کند. پیکربندی‌ها، اعتبارنامه‌های سرویس‌دهنده و نگاره‌های کَش شده حفظ می‌شوند",
"clearCache_description": "یک 'پاک‌سازی سخت' فیشین. افزون بر پاک‌سازی کَش فیشین، کَش مرورگر هم تهی می‌شود (نگاره‌های ذخیره شده و باقی دارایی‌ها). اعتبارنامه‌ها و پیکربندی‌ها حفظ می‌شوند", "clearCache_description": "یک 'پاک‌سازی سخت' فیشین. افزون بر پاک‌سازی کَش فیشین، کَش مرورگر هم تهی می‌شود (نگاره‌های ذخیره شده و باقی دارایی‌ها). اعتبارنامه‌ها و پیکربندی‌ها حفظ می‌شوند",
"contextMenu_description": "به شما اجازه می‌دهد که آیتم‌های نمایش داده شده در فهرستی که وقتی روی یک آیتم کلیک راست می‌کنید پدیدار می‌شود، را پنهان کنید. آیتم‌هایی که منتخب نیستند پنهان می‌شوند", "contextMenu_description": "به شما اجازه می‌دهد که آیتم‌های نمایش داده شده در فهرستی که وقتی روی یک آیتم کلیک راست می‌کنید پدیدار می‌شود، را پنهان کنید. آیتم‌هایی که منتخب نیستند پنهان می‌شوند",
@@ -177,7 +176,7 @@
"backward": "به عقب", "backward": "به عقب",
"increase": "افزایش", "increase": "افزایش",
"rating": "امتیاز", "rating": "امتیاز",
"bpm": "bpm", "bpm": "BPM",
"refresh": "تازه‌سازی", "refresh": "تازه‌سازی",
"unknown": "ناشناخته", "unknown": "ناشناخته",
"areYouSure": "مطمئنید؟", "areYouSure": "مطمئنید؟",
@@ -186,7 +185,7 @@
"left": "چپ", "left": "چپ",
"save": "ذخیره", "save": "ذخیره",
"right": "راست", "right": "راست",
"currentSong": "فعلی $t(entity.track_one)", "currentSong": "فعلی $t(entity.track, {\"count\": 1})",
"collapse": "بستن", "collapse": "بستن",
"trackNumber": "قطعه", "trackNumber": "قطعه",
"descending": "نزولی", "descending": "نزولی",
@@ -239,7 +238,7 @@
"none": "هیچ", "none": "هیچ",
"menu": "منو", "menu": "منو",
"restartRequired": "راه‌اندازی دوباره لازم است", "restartRequired": "راه‌اندازی دوباره لازم است",
"previousSong": "$t(entity.track_one) پیشین", "previousSong": "$t(entity.track, {\"count\": 1}) پیشین",
"noResultsFromQuery": "جست‌وجو نتیجه‌ای نداشت", "noResultsFromQuery": "جست‌وجو نتیجه‌ای نداشت",
"quit": "خروج", "quit": "خروج",
"expand": "گسترش", "expand": "گسترش",
@@ -256,7 +255,8 @@
"albumPeak": "اوج آلبوم", "albumPeak": "اوج آلبوم",
"mbid": "شناسه‌ی MusicBrainz", "mbid": "شناسه‌ی MusicBrainz",
"reload": "بارگذاری مجدد", "reload": "بارگذاری مجدد",
"setting": "پیکربندی", "setting_one": "پیکربندی",
"setting_other": "",
"trackGain": "گین قطعه", "trackGain": "گین قطعه",
"trackPeak": "اوج قطعه", "trackPeak": "اوج قطعه",
"translation": "ترجمه", "translation": "ترجمه",
@@ -301,25 +301,25 @@
"rating": "امتیاز", "rating": "امتیاز",
"search": "جست‌وجو", "search": "جست‌وجو",
"bitrate": "بیت‌ریت", "bitrate": "بیت‌ریت",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"recentlyAdded": "به تازگی افزوده شده", "recentlyAdded": "به تازگی افزوده شده",
"note": "توجه", "note": "توجه",
"name": "نام", "name": "نام",
"dateAdded": "تاریخ افزوده شدن", "dateAdded": "تاریخ افزوده شدن",
"releaseDate": "تاریخ انتشار", "releaseDate": "تاریخ انتشار",
"albumCount": "$t(entity.album_other) عدد", "albumCount": "$t(entity.album, {\"count\": 2}) عدد",
"path": "مسیر", "path": "مسیر",
"favorited": "موردعلاقه", "favorited": "موردعلاقه",
"albumArtist": "$t(entity.albumArtist_one)", "albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"isRecentlyPlayed": "به تازگی پخش شده است", "isRecentlyPlayed": "به تازگی پخش شده است",
"isFavorited": "موردعلاقه است", "isFavorited": "موردعلاقه است",
"bpm": "bpm", "bpm": "BPM",
"releaseYear": "سال انتشار", "releaseYear": "سال انتشار",
"id": "id", "id": "ID",
"disc": "دیسک", "disc": "دیسک",
"biography": "زندگی‌نامه", "biography": "زندگی‌نامه",
"songCount": "تعداد ترانه", "songCount": "تعداد ترانه",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"duration": "مدت", "duration": "مدت",
"isPublic": "عمومی است", "isPublic": "عمومی است",
"random": "تصادفی", "random": "تصادفی",
@@ -327,23 +327,23 @@
"toYear": "تا سال", "toYear": "تا سال",
"fromYear": "از سال", "fromYear": "از سال",
"criticRating": "امتیاز منتقدین", "criticRating": "امتیاز منتقدین",
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"trackNumber": "قطعه", "trackNumber": "قطعه",
"communityRating": "رتبه بندی جامعه", "communityRating": "رتبه بندی جامعه",
"isCompilation": "مخلوط است" "isCompilation": "مخلوط است"
}, },
"form": { "form": {
"deletePlaylist": { "deletePlaylist": {
"title": "حذف $t(entity.playlist_one)", "title": "حذف $t(entity.playlist, {\"count\": 1})",
"success": "$t(entity.playlist_one) حذف شد", "success": "$t(entity.playlist, {\"count\": 1}) حذف شد",
"input_confirm": "برای تایید، نام $t(entity.playlist_one) را وارد کنید" "input_confirm": "برای تایید، نام $t(entity.playlist, {\"count\": 1}) را وارد کنید"
}, },
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
"title": "ساخت $t(entity.playlist_one)", "title": "ساخت $t(entity.playlist, {\"count\": 1})",
"input_public": "عمومی", "input_public": "عمومی",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) ساخته شد", "success": "$t(entity.playlist, {\"count\": 1}) ساخته شد",
"input_owner": "$t(common.owner)" "input_owner": "$t(common.owner)"
}, },
"addServer": { "addServer": {
@@ -360,19 +360,19 @@
"ignoreSsl": "نادیده گرفتن ssl ($t(common.restartRequired))" "ignoreSsl": "نادیده گرفتن ssl ($t(common.restartRequired))"
}, },
"addToPlaylist": { "addToPlaylist": {
"success": "$t(entity.song_other) به {{numOfPlaylists}}$t(entity.playlist_other) افزوده شد", "success": "$t(entity.song, {\"count\": 2}) به {{numOfPlaylists}}$t(entity.playlist, {\"count\": 2}) افزوده شد",
"title": "افزودن به $t(entity.playlist_one)", "title": "افزودن به $t(entity.playlist, {\"count\": 1})",
"input_playlists": "$t(entity.playlist_other)", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"input_skipDuplicates": "پرش از تکراری‌ها" "input_skipDuplicates": "پرش از تکراری‌ها"
}, },
"lyricSearch": { "lyricSearch": {
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)", "input_artist": "$t(entity.artist, {\"count\": 1})",
"title": "جست‌وجو در متن شعر" "title": "جست‌وجو در متن شعر"
}, },
"editPlaylist": { "editPlaylist": {
"title": "ویرایش $t(entity.playlist_one)", "title": "ویرایش $t(entity.playlist, {\"count\": 1})",
"success": "$t(entity.playlist_one) با موفقیت بروزرسانی شد", "success": "$t(entity.playlist, {\"count\": 1}) با موفقیت بروزرسانی شد",
"publicJellyfinNote": "جلی‌فین به دلیلی این‌که فهرست پخش عمومی‌ست یا خصوصی را فاش نمی‌کند. اگر می‌خواهید این عمومی باقی بماند، لطفاٌ ورودی پیش‌رو را منتخب داشته باشید" "publicJellyfinNote": "جلی‌فین به دلیلی این‌که فهرست پخش عمومی‌ست یا خصوصی را فاش نمی‌کند. اگر می‌خواهید این عمومی باقی بماند، لطفاٌ ورودی پیش‌رو را منتخب داشته باشید"
}, },
"queryEditor": { "queryEditor": {
@@ -417,7 +417,7 @@
"artistWithCount_other": "{{count}} هنرمند", "artistWithCount_other": "{{count}} هنرمند",
"folder_one": "پوشه", "folder_one": "پوشه",
"folder_other": "پوشه‌ها", "folder_other": "پوشه‌ها",
"smartPlaylist": "$t(entity.playlist_one) هوشمند", "smartPlaylist": "$t(entity.playlist, {\"count\": 1}) هوشمند",
"album_one": "آلبوم", "album_one": "آلبوم",
"album_other": "آلبوم‌ها", "album_other": "آلبوم‌ها",
"genreWithCount_one": "{{count}} ژانر", "genreWithCount_one": "{{count}} ژانر",
@@ -431,12 +431,12 @@
}, },
"page": { "page": {
"albumList": { "albumList": {
"title": "$t(entity.album_other)", "title": "$t(entity.album, {\"count\": 2})",
"artistAlbums": "آلبوم‌های {{artist}}", "artistAlbums": "آلبوم‌های {{artist}}",
"genreAlbums": "\"{{genre}}\" $t(entity.album_other)" "genreAlbums": "\"{{genre}}\" $t(entity.album, {\"count\": 2})"
}, },
"appMenu": { "appMenu": {
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"selectServer": "گزینش سرویس‌دهنده", "selectServer": "گزینش سرویس‌دهنده",
"expandSidebar": "گسترش نوار کناری", "expandSidebar": "گسترش نوار کناری",
"collapseSidebar": "فروکش نوار کناری", "collapseSidebar": "فروکش نوار کناری",
@@ -451,11 +451,11 @@
"appearsOn": "مشاهده می‌شود در", "appearsOn": "مشاهده می‌شود در",
"about": "درباره‌ی {{artist}}", "about": "درباره‌ی {{artist}}",
"recentReleases": "عرضه‌های اخیر", "recentReleases": "عرضه‌های اخیر",
"viewAllTracks": "نمایش همه‌ی $t(entity.track_other)", "viewAllTracks": "نمایش همه‌ی $t(entity.track, {\"count\": 2})",
"topSongsFrom": "قطعه‌های برتر از {{title}}", "topSongsFrom": "قطعه‌های برتر از {{title}}",
"viewAll": "نمایش همه", "viewAll": "نمایش همه",
"viewDiscography": "نمایش کاتالوگ", "viewDiscography": "نمایش کاتالوگ",
"relatedArtists": "$t(entity.artist_other) مربوطه", "relatedArtists": "$t(entity.artist, {\"count\": 2}) مربوطه",
"topSongs": "قطعه‌های برتر" "topSongs": "قطعه‌های برتر"
}, },
"contextMenu": { "contextMenu": {
@@ -523,21 +523,21 @@
"playbackTab": "پخش" "playbackTab": "پخش"
}, },
"sidebar": { "sidebar": {
"genres": "$t(entity.genre_other)", "genres": "$t(entity.genre, {\"count\": 2})",
"playlists": "$t(entity.playlist_other)", "playlists": "$t(entity.playlist, {\"count\": 2})",
"search": "$t(common.search)", "search": "$t(common.search)",
"albumArtists": "$t(entity.albumArtist_other)", "albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"albums": "$t(entity.album_other)", "albums": "$t(entity.album, {\"count\": 2})",
"folders": "$t(entity.folder_other)", "folders": "$t(entity.folder, {\"count\": 2})",
"artists": "$t(entity.artist_other)", "artists": "$t(entity.artist, {\"count\": 2})",
"home": "$t(common.home)", "home": "$t(common.home)",
"nowPlaying": "پخش کنونی", "nowPlaying": "پخش کنونی",
"tracks": "$t(entity.track_other)", "tracks": "$t(entity.track, {\"count\": 2})",
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"shared": "$t(entity.playlist_other) اشتراک‌گذاری شده" "shared": "$t(entity.playlist, {\"count\": 2}) اشتراک‌گذاری شده"
}, },
"albumDetail": { "albumDetail": {
"moreFromArtist": "موارد بیشتر از این $t(entity.artist_one)", "moreFromArtist": "موارد بیشتر از این $t(entity.artist, {\"count\": 1})",
"moreFromGeneric": "موارد بیشتر از {{item}}", "moreFromGeneric": "موارد بیشتر از {{item}}",
"released": "عرضه شده" "released": "عرضه شده"
}, },
@@ -550,9 +550,9 @@
"editServerDetailsTooltip": "ویرایش ریزگان سرویس‌دهنده" "editServerDetailsTooltip": "ویرایش ریزگان سرویس‌دهنده"
}, },
"genreList": { "genreList": {
"showAlbums": "نمایش $t(entity.genre_one) $t(entity.album_other)", "showAlbums": "نمایش $t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})",
"title": "$t(entity.genre_other)", "title": "$t(entity.genre, {\"count\": 2})",
"showTracks": "نمایش $t(entity.genre_one) $t(entity.track_other)" "showTracks": "نمایش $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})"
}, },
"globalSearch": { "globalSearch": {
"commands": { "commands": {
@@ -563,15 +563,15 @@
"title": "فرمان‌ها" "title": "فرمان‌ها"
}, },
"playlistList": { "playlistList": {
"title": "$t(entity.playlist_other)" "title": "$t(entity.playlist, {\"count\": 2})"
}, },
"trackList": { "trackList": {
"title": "$t(entity.track_other)", "title": "$t(entity.track, {\"count\": 2})",
"artistTracks": "قطعه‌های {{artist}}", "artistTracks": "قطعه‌های {{artist}}",
"genreTracks": "$t(entity.track_other) \"{{genre}}\"" "genreTracks": "$t(entity.track, {\"count\": 2}) \"{{genre}}\""
}, },
"albumArtistList": { "albumArtistList": {
"title": "$t(entity.albumArtist_other)" "title": "$t(entity.albumArtist, {\"count\": 2})"
}, },
"itemDetail": { "itemDetail": {
"copyPath": "کپی کردن مسیر در کلیپ‌بورد", "copyPath": "کپی کردن مسیر در کلیپ‌بورد",
@@ -584,11 +584,11 @@
"size": "$t(common.size)", "size": "$t(common.size)",
"lastPlayed": "آخرین بار پخش شده", "lastPlayed": "آخرین بار پخش شده",
"discNumber": "دیسک", "discNumber": "دیسک",
"songCount": "$t(entity.track_other)", "songCount": "$t(entity.track, {\"count\": 2})",
"title": "عنوان", "title": "عنوان",
"trackNumber": "قطعه", "trackNumber": "قطعه",
"favorite": "مورد علاقه", "favorite": "مورد علاقه",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"comment": "دیدگاه", "comment": "دیدگاه",
"playCount": "تعداد پخش", "playCount": "تعداد پخش",
"rating": "امتیاز", "rating": "امتیاز",
+841 -643
View File
File diff suppressed because it is too large Load Diff
+1143 -953
View File
File diff suppressed because it is too large Load Diff
+784 -785
View File
File diff suppressed because it is too large Load Diff
+1097 -516
View File
File diff suppressed because it is too large Load Diff
+870 -622
View File
File diff suppressed because it is too large Load Diff
+508 -175
View File
File diff suppressed because it is too large Load Diff
+46 -46
View File
@@ -1,31 +1,31 @@
{ {
"action": { "action": {
"createPlaylist": "$t(entity.playlist_one) 생성", "createPlaylist": "$t(entity.playlist, {\"count\": 1}) 생성",
"addToFavorites": "$t(entity.favorite_other)에 추가", "addToFavorites": "$t(entity.favorite, {\"count\": 2})에 추가",
"addToPlaylist": "$t(entity.playlist_one)에 추가", "addToPlaylist": "$t(entity.playlist, {\"count\": 1})에 추가",
"clearQueue": "대기열 지우기", "clearQueue": "대기열 지우기",
"deletePlaylist": "$t(entity.playlist_one) 삭제", "deletePlaylist": "$t(entity.playlist, {\"count\": 1}) 삭제",
"deselectAll": "모두 선택 해제", "deselectAll": "모두 선택 해제",
"editPlaylist": "$t(entity.playlist_one) 편집", "editPlaylist": "$t(entity.playlist, {\"count\": 1}) 편집",
"goToPage": "페이지 이동", "goToPage": "페이지 이동",
"moveToBottom": "맨 아래로 이동", "moveToBottom": "맨 아래로 이동",
"moveToTop": "맨 위로 이동", "moveToTop": "맨 위로 이동",
"moveToNext": "다음으로 이동", "moveToNext": "다음으로 이동",
"removeFromQueue": "대기열에서 제거", "removeFromQueue": "대기열에서 제거",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"removeFromFavorites": "$t(entity.favorite_other)에서 제거", "removeFromFavorites": "$t(entity.favorite, {\"count\": 2})에서 제거",
"removeFromPlaylist": "$t(entity.playlist_one)에서 제거", "removeFromPlaylist": "$t(entity.playlist, {\"count\": 1})에서 제거",
"openIn": { "openIn": {
"musicbrainz": "MusicBrainz에서 보기", "musicbrainz": "MusicBrainz에서 보기",
"lastfm": "Last.fm에서 보기" "lastfm": "Last.fm에서 보기"
}, },
"viewPlaylists": "$t(entity.playlist_other) 보기", "viewPlaylists": "$t(entity.playlist, {\"count\": 2}) 보기",
"setRating": "평점 지정", "setRating": "평점 지정",
"toggleSmartPlaylistEditor": "$t(entity.smartPlaylist) 편집기 펼치기", "toggleSmartPlaylistEditor": "$t(entity.smartPlaylist) 편집기 펼치기",
"addOrRemoveFromSelection": "선택항목에서 추가 또는 제거", "addOrRemoveFromSelection": "선택항목에서 추가 또는 제거",
"selectRangeOfItems": "항목의 범위 선택", "selectRangeOfItems": "항목의 범위 선택",
"createRadioStation": "$t(entity.radioStation_one) 생성", "createRadioStation": "$t(entity.radioStation, {\"count\": 1}) 생성",
"deleteRadioStation": "$t(entity.radioStation_one) 삭제", "deleteRadioStation": "$t(entity.radioStation, {\"count\": 1}) 삭제",
"selectAll": "전부 선택", "selectAll": "전부 선택",
"downloadStarted": "{{count}}개 항목 다운로드 시작했습니다", "downloadStarted": "{{count}}개 항목 다운로드 시작했습니다",
"moveUp": "위로 옮기기", "moveUp": "위로 옮기기",
@@ -59,7 +59,7 @@
"backward": "뒤로", "backward": "뒤로",
"saveAs": "(으)로 저장하기", "saveAs": "(으)로 저장하기",
"search": "검색", "search": "검색",
"setting": "설정", "setting_other": "설정",
"share": "공유", "share": "공유",
"size": "크기", "size": "크기",
"sortOrder": "순서", "sortOrder": "순서",
@@ -74,7 +74,7 @@
"comingSoon": "조만간…", "comingSoon": "조만간…",
"configure": "설정", "configure": "설정",
"confirm": "확인", "confirm": "확인",
"currentSong": "현재 $t(entity.track_one)", "currentSong": "현재 $t(entity.track, {\"count\": 1})",
"decrease": "감소", "decrease": "감소",
"delete": "삭제", "delete": "삭제",
"descending": "내림차순", "descending": "내림차순",
@@ -96,7 +96,7 @@
"path": "경로", "path": "경로",
"playerMustBePaused": "플레이어가 일시정지 되어야 합니다", "playerMustBePaused": "플레이어가 일시정지 되어야 합니다",
"preview": "미리보기", "preview": "미리보기",
"previousSong": "이전곡 $t(entity.track_one)", "previousSong": "이전곡 $t(entity.track, {\"count\": 1})",
"quit": "종료", "quit": "종료",
"refresh": "새로고침", "refresh": "새로고침",
"reload": "리로드", "reload": "리로드",
@@ -106,7 +106,7 @@
"ascending": "오름차순", "ascending": "오름차순",
"areYouSure": "확실한가요?", "areYouSure": "확실한가요?",
"bitrate": "비트 전송률", "bitrate": "비트 전송률",
"bpm": "bpm", "bpm": "BPM",
"biography": "바이오그래피", "biography": "바이오그래피",
"center": "중앙", "center": "중앙",
"channel_other": "채널", "channel_other": "채널",
@@ -127,7 +127,7 @@
"filters": "필터", "filters": "필터",
"noResultsFromQuery": "쿼리 결과가 없습니다", "noResultsFromQuery": "쿼리 결과가 없습니다",
"note": "노트", "note": "노트",
"ok": "OK", "ok": "Ok",
"owner": "소유자", "owner": "소유자",
"sampleRate": "샘플레이트", "sampleRate": "샘플레이트",
"tags": "태그", "tags": "태그",
@@ -168,7 +168,7 @@
"song_other": "곡", "song_other": "곡",
"play_other": "{{count}} 재생", "play_other": "{{count}} 재생",
"playlistWithCount_other": "{{count}} 재생목록", "playlistWithCount_other": "{{count}} 재생목록",
"smartPlaylist": "스마트 $t(entity.playlist_one)", "smartPlaylist": "스마트 $t(entity.playlist, {\"count\": 1})",
"track_other": "트랙", "track_other": "트랙",
"radioStation_other": "라디오 방송국", "radioStation_other": "라디오 방송국",
"radioStationWithCount_other": "{{count}}개 라디오 방송국" "radioStationWithCount_other": "{{count}}개 라디오 방송국"
@@ -214,9 +214,9 @@
"dateAdded": "추가된 날짜", "dateAdded": "추가된 날짜",
"lastPlayed": "마지막으로 재생한", "lastPlayed": "마지막으로 재생한",
"mostPlayed": "가장 많이 재생한", "mostPlayed": "가장 많이 재생한",
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"albumArtist": "$t(entity.albumArtist_one)", "albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"communityRating": "커뮤니티 평점", "communityRating": "커뮤니티 평점",
"criticRating": "비평가 평점", "criticRating": "비평가 평점",
"disc": "디스크", "disc": "디스크",
@@ -224,12 +224,12 @@
"biography": "바이오그래피", "biography": "바이오그래피",
"channels": "$t(common.channel_other)", "channels": "$t(common.channel_other)",
"duration": "길이", "duration": "길이",
"bpm": "bpm", "bpm": "BPM",
"albumCount": "$t(entity.album_other) 앨범수", "albumCount": "$t(entity.album, {\"count\": 2}) 앨범수",
"comment": "코멘트", "comment": "코멘트",
"favorited": "즐겨찾기", "favorited": "즐겨찾기",
"fromYear": "시작 년도", "fromYear": "시작 년도",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"id": "아이디", "id": "아이디",
"isCompilation": "편집앨범 여부", "isCompilation": "편집앨범 여부",
"isFavorited": "즐겨찾기 여부", "isFavorited": "즐겨찾기 여부",
@@ -251,7 +251,7 @@
"input_name": "서버 이름", "input_name": "서버 이름",
"input_password": "비밀번호", "input_password": "비밀번호",
"input_savePassword": "비밀번호 저장하기", "input_savePassword": "비밀번호 저장하기",
"input_url": "url", "input_url": "URL",
"error_savePassword": "비밀번호를 저장하는 도중 오류가 발생했습니다", "error_savePassword": "비밀번호를 저장하는 도중 오류가 발생했습니다",
"ignoreCors": "CORS 무시 ($t(common.restartRequired))", "ignoreCors": "CORS 무시 ($t(common.restartRequired))",
"ignoreSsl": "SSL 무시 ($t(common.restartRequired))", "ignoreSsl": "SSL 무시 ($t(common.restartRequired))",
@@ -262,16 +262,16 @@
}, },
"addToPlaylist": { "addToPlaylist": {
"input_skipDuplicates": "중복 건너뛰기", "input_skipDuplicates": "중복 건너뛰기",
"title": "$t(entity.playlist_one) 에 추가", "title": "$t(entity.playlist, {\"count\": 1}) 에 추가",
"input_playlists": "$t(entity.playlist_other)", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"success": "$t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })에 $t(entity.trackWithCount, {\"count\": {{message}} })가 추가되었습니다", "success": "$t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })에 $t(entity.trackWithCount, {\"count\": {{message}} })가 추가되었습니다",
"create": "$t(entity.playlist_one) {{playlist}} 생성", "create": "$t(entity.playlist, {\"count\": 1}) {{playlist}} 생성",
"searchOrCreate": "$t(entity.playlist_other) 검색 또는 입력하여 새로 만들기" "searchOrCreate": "$t(entity.playlist, {\"count\": 2}) 검색 또는 입력하여 새로 만들기"
}, },
"lyricSearch": { "lyricSearch": {
"title": "가사 검색", "title": "가사 검색",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)" "input_artist": "$t(entity.artist, {\"count\": 1})"
}, },
"queryEditor": { "queryEditor": {
"input_optionMatchAll": "모두 일치", "input_optionMatchAll": "모두 일치",
@@ -279,9 +279,9 @@
"title": "쿼리 편집기" "title": "쿼리 편집기"
}, },
"editPlaylist": { "editPlaylist": {
"title": "$t(entity.playlist_one) 편집", "title": "$t(entity.playlist, {\"count\": 1}) 편집",
"publicJellyfinNote": "Jellyfin은 재생목록 공개 여부를 노출하지 않습니다. 만약 공개되길 원한다면 다음을 선택하세요", "publicJellyfinNote": "Jellyfin은 재생목록 공개 여부를 노출하지 않습니다. 만약 공개되길 원한다면 다음을 선택하세요",
"success": "$t(entity.playlist_one) 업데이트 되었습니다" "success": "$t(entity.playlist, {\"count\": 1}) 업데이트 되었습니다"
}, },
"shareItem": { "shareItem": {
"allowDownloading": "다운로드 허용", "allowDownloading": "다운로드 허용",
@@ -298,15 +298,15 @@
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"success": "$t(entity.playlist_one)를 생성했습니다", "success": "$t(entity.playlist, {\"count\": 1})를 생성했습니다",
"input_owner": "$t(common.owner)", "input_owner": "$t(common.owner)",
"input_public": "공개", "input_public": "공개",
"title": "$t(entity.playlist_one) 생성" "title": "$t(entity.playlist, {\"count\": 1}) 생성"
}, },
"deletePlaylist": { "deletePlaylist": {
"input_confirm": "확인을 위해 $t(entity.playlist_one)의 이름을 적어주세요", "input_confirm": "확인을 위해 $t(entity.playlist, {\"count\": 1})의 이름을 적어주세요",
"success": "$t(entity.playlist_one)가 삭제되었습니다", "success": "$t(entity.playlist, {\"count\": 1})가 삭제되었습니다",
"title": "$t(entity.playlist_one) 삭제" "title": "$t(entity.playlist, {\"count\": 1}) 삭제"
}, },
"privateMode": { "privateMode": {
"enabled": "프라이빗 모드가 활성화되었습니다. 재생상태가 외부 서비스에 지금부터 노출되지 않습니다", "enabled": "프라이빗 모드가 활성화되었습니다. 재생상태가 외부 서비스에 지금부터 노출되지 않습니다",
@@ -362,8 +362,8 @@
"download": "다운로드", "download": "다운로드",
"numberSelected": "{{count}}개 선택됨", "numberSelected": "{{count}}개 선택됨",
"shareItem": "공유", "shareItem": "공유",
"goToAlbum": "$t(entity.album_one)으로 이동", "goToAlbum": "$t(entity.album, {\"count\": 1})으로 이동",
"goToAlbumArtist": "$t(entity.albumArtist_one)으로 이동", "goToAlbumArtist": "$t(entity.albumArtist, {\"count\": 1})으로 이동",
"showDetails": "추가정보" "showDetails": "추가정보"
}, },
"albumArtistDetail": { "albumArtistDetail": {
@@ -371,17 +371,17 @@
"viewDiscography": "디스코그래피 보기", "viewDiscography": "디스코그래피 보기",
"appearsOn": "참여 앨범", "appearsOn": "참여 앨범",
"recentReleases": "최근 앨범", "recentReleases": "최근 앨범",
"relatedArtists": "연관 $t(entity.artist_other)", "relatedArtists": "연관 $t(entity.artist, {\"count\": 2})",
"topSongs": "최고의 곡들", "topSongs": "최고의 곡들",
"topSongsFrom": "{{title}}이 포함된 최고의 곡들", "topSongsFrom": "{{title}}이 포함된 최고의 곡들",
"viewAll": "전부 보이기", "viewAll": "전부 보이기",
"viewAllTracks": "$t(entity.track_other) 전부 보이기" "viewAllTracks": "$t(entity.track, {\"count\": 2}) 전부 보이기"
}, },
"albumArtistList": { "albumArtistList": {
"title": "$t(entity.albumArtist_other)" "title": "$t(entity.albumArtist, {\"count\": 2})"
}, },
"albumDetail": { "albumDetail": {
"moreFromArtist": "$t(entity.artist_one) 더 보기", "moreFromArtist": "$t(entity.artist, {\"count\": 1}) 더 보기",
"moreFromGeneric": "{{item}} 더 보기", "moreFromGeneric": "{{item}} 더 보기",
"released": "발매" "released": "발매"
}, },
@@ -389,8 +389,8 @@
"artistAlbums": "{{artist}}의 앨범" "artistAlbums": "{{artist}}의 앨범"
}, },
"genreList": { "genreList": {
"showAlbums": "$t(entity.genre_one) $t(entity.album_other) 표시", "showAlbums": "$t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2}) 표시",
"showTracks": "$t(entity.genre_one) $t(entity.track_other) 표시" "showTracks": "$t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2}) 표시"
}, },
"globalSearch": { "globalSearch": {
"commands": { "commands": {
@@ -425,7 +425,7 @@
"sidebar": { "sidebar": {
"myLibrary": "내 라이브러리", "myLibrary": "내 라이브러리",
"nowPlaying": "재생중", "nowPlaying": "재생중",
"shared": "공유 $t(entity.playlist_other)" "shared": "공유 $t(entity.playlist, {\"count\": 2})"
}, },
"trackList": { "trackList": {
"artistTracks": "{{artist}}의 음악" "artistTracks": "{{artist}}의 음악"
@@ -458,8 +458,8 @@
"playSimilarSongs": "비슷한 곡 재생", "playSimilarSongs": "비슷한 곡 재생",
"previous": "이전", "previous": "이전",
"queue_clear": "재생 대기열 지우기", "queue_clear": "재생 대기열 지우기",
"queue_moveToBottom": "선택한 곡을 가장 로 이동", "queue_moveToBottom": "선택한 곡을 가장 아래로 이동",
"queue_moveToTop": "선택한 곡을 가장 아래로 이동", "queue_moveToTop": "선택한 곡을 가장 로 이동",
"queue_remove": "선택한 항목 삭제", "queue_remove": "선택한 항목 삭제",
"repeat": "반복", "repeat": "반복",
"repeat_all": "모두 반복하기", "repeat_all": "모두 반복하기",
File diff suppressed because it is too large Load Diff
+1174 -772
View File
File diff suppressed because it is too large Load Diff
+996 -893
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+383 -381
View File
@@ -1,353 +1,356 @@
{ {
"action": { "action": {
"addToFavorites": "adicionar a $t(entity.favorite_other)", "addToFavorites": "Adicionar a $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "adicionar a $t(entity.playlist_one)", "addToPlaylist": "Adicionar a $t(entity.playlist, {\"count\": 1})",
"clearQueue": "limpar fila", "clearQueue": "Limpar fila",
"createPlaylist": "criar $t(entity.playlist_one)", "createPlaylist": "Criar $t(entity.playlist, {\"count\": 1})",
"deletePlaylist": "apagar $t(entity.playlist_one)", "deletePlaylist": "Apagar $t(entity.playlist, {\"count\": 1})",
"deselectAll": "desmarcar todos", "deselectAll": "Desmarcar todos",
"editPlaylist": "editar $t(entity.playlist_one)", "editPlaylist": "Editar $t(entity.playlist, {\"count\": 1})",
"goToPage": "vá para página", "goToPage": "Vá para página",
"moveToNext": "mover para o próximo", "moveToNext": "Mover para o próximo",
"moveToBottom": "mover para baixo", "moveToBottom": "Mover para baixo",
"moveToTop": "mover para o topo", "moveToTop": "Mover para o topo",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"removeFromFavorites": "remover de $t(entity.favorite_other)", "removeFromFavorites": "Remover de $t(entity.favorite, {\"count\": 2})",
"removeFromPlaylist": "remover da $t(entity.playlist_one)", "removeFromPlaylist": "Remover da $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "remover da fila", "removeFromQueue": "Remover da fila",
"setRating": "definir classificação", "setRating": "Definir classificação",
"toggleSmartPlaylistEditor": "alternar editor $t(entity.smartPlaylist)", "toggleSmartPlaylistEditor": "Alternar editor $t(entity.smartPlaylist)",
"viewPlaylists": "ver $t(entity.playlist_other)", "viewPlaylists": "Ver $t(entity.playlist, {\"count\": 2})",
"openIn": { "openIn": {
"lastfm": "Abrir em Last.fm", "lastfm": "Abrir em Last.fm",
"musicbrainz": "Abrir em MusicBrainz" "musicbrainz": "Abrir em MusicBrainz"
} }
}, },
"common": { "common": {
"action_one": "ação", "action_one": "Ação",
"action_many": "ações", "action_many": "ações",
"action_other": "ações", "action_other": "Ações",
"add": "adicionar", "add": "Adicionar",
"additionalParticipants": "participantes adicionais", "additionalParticipants": "Participantes adicionais",
"newVersion": "uma nova versão foi instalada ({{version}})", "newVersion": "Uma nova versão foi instalada ({{version}})",
"viewReleaseNotes": "ver notas de lançamento", "viewReleaseNotes": "Ver notas de lançamento",
"albumGain": "ganho do álbum", "albumGain": "Ganho do álbum",
"albumPeak": "pico do álbum", "albumPeak": "Pico do álbum",
"areYouSure": "tem certeza?", "areYouSure": "Tem certeza?",
"ascending": "ascendente", "ascending": "Ascendente",
"backward": "para trás", "backward": "Para trás",
"biography": "biografia", "biography": "Biografia",
"bitrate": "taxa de bits", "bitrate": "Taxa de bits",
"bpm": "bpm", "bpm": "Bpm",
"cancel": "cancelar", "cancel": "Cancelar",
"center": "centro", "center": "Centro",
"channel_one": "canal", "channel_one": "Canal",
"channel_many": "canais", "channel_many": "canais",
"channel_other": "canais", "channel_other": "Canais",
"clear": "limpar", "clear": "Limpar",
"close": "fechar", "close": "Fechar",
"codec": "codec", "codec": "Codec",
"collapse": "minimizar", "collapse": "Minimizar",
"comingSoon": "em breve…", "comingSoon": "Em breve…",
"configure": "configurar", "configure": "Configurar",
"confirm": "confirmar", "confirm": "Confirmar",
"create": "criar", "create": "Criar",
"currentSong": "$t(entity.track_one) atual", "currentSong": "$t(entity.track, {\"count\": 1}) atual",
"decrease": "diminuir", "decrease": "Diminuir",
"delete": "apagar", "delete": "Apagar",
"descending": "abaixar", "descending": "Abaixar",
"description": "descrição", "description": "Descrição",
"disable": "desativar", "disable": "Desativar",
"disc": "disco", "disc": "Disco",
"dismiss": "liberar", "dismiss": "Liberar",
"duration": "duração", "duration": "Duração",
"edit": "editar", "edit": "Editar",
"enable": "ativar", "enable": "Ativar",
"expand": "expandir", "expand": "Expandir",
"favorite": "favorito", "favorite": "Favorito",
"filter_one": "filtro", "filter_one": "Filtro",
"filter_many": "filtros", "filter_many": "filtros",
"filter_other": "filtros", "filter_other": "Filtros",
"filters": "filtros", "filters": "Filtros",
"forceRestartRequired": "reinicie para aplicar as alterações… feche a notificação para reiniciar", "forceRestartRequired": "Reinicie para aplicar as alterações… feche a notificação para reiniciar",
"forward": "para frente", "forward": "Para frente",
"gap": "intervalo", "gap": "Intervalo",
"home": "início", "grouping": "Agrupamento",
"increase": "incrementar", "home": "Início",
"left": "esquerda", "increase": "Incrementar",
"limit": "limite", "left": "Esquerda",
"manage": "gerir", "limit": "Limite",
"maximize": "maximizar", "manage": "Gerir",
"menu": "menu", "maximize": "Maximizar",
"minimize": "minimizar", "menu": "Menu",
"modified": "modificado", "minimize": "Minimizar",
"modified": "Modificado",
"mbid": "ID no MusicBrainz", "mbid": "ID no MusicBrainz",
"name": "nome", "name": "Nome",
"no": "não", "no": "Não",
"none": "nenhum", "none": "Nenhum",
"noResultsFromQuery": "a consulta não retornou resultados", "noResultsFromQuery": "A consulta não retornou resultados",
"note": "observação", "note": "Observação",
"ok": "ok", "ok": "Ok",
"owner": "dono", "owner": "Dono",
"path": "caminho", "path": "Caminho",
"playerMustBePaused": "o player deve estar pausado", "playerMustBePaused": "O player deve estar pausado",
"preview": "pré-visualizar", "preview": "Pré-visualizar",
"previousSong": "anterior $t(entity.track_one)", "previousSong": "Anterior $t(entity.track, {\"count\": 1})",
"quit": "sair", "quit": "Sair",
"random": "aleatório", "random": "Aleatório",
"rating": "classificação", "rating": "Classificação",
"refresh": "atualizar", "refresh": "Atualizar",
"reload": "recarregar", "reload": "Recarregar",
"reset": "reiniciar", "reset": "Reiniciar",
"resetToDefault": "restaurar ao padrão", "resetToDefault": "Restaurar ao padrão",
"restartRequired": "é necessário reiniciar", "restartRequired": "É necessário reiniciar",
"right": "direita", "right": "Direita",
"save": "gravar", "save": "Gravar",
"saveAndReplace": "gravar e substituir", "saveAndReplace": "Gravar e substituir",
"saveAs": "gravar como", "saveAs": "Gravar como",
"search": "procurar", "search": "Procurar",
"setting": "configuração", "setting_one": "Configuração",
"share": "partilhar", "setting_many": "",
"size": "tamanho", "setting_other": "",
"sortOrder": "ordem", "share": "Partilhar",
"tags": "tags", "size": "Tamanho",
"title": "titulo", "sortOrder": "Ordem",
"trackNumber": "faixa", "tags": "Tags",
"trackGain": "ganho da faixa", "title": "Titulo",
"trackPeak": "pico da faixa", "trackNumber": "Faixa",
"translation": "tradução", "trackGain": "Ganho da faixa",
"unknown": "desconhecido", "trackPeak": "Pico da faixa",
"version": "versão", "translation": "Tradução",
"year": "ano", "unknown": "Desconhecido",
"yes": "sim" "version": "Versão",
"year": "Ano",
"yes": "Sim"
}, },
"entity": { "entity": {
"album_one": "álbum", "album_one": "Álbum",
"album_many": "álbuns", "album_many": "álbuns",
"album_other": "álbuns", "album_other": "Álbuns",
"albumArtist_one": "artista do álbum", "albumArtist_one": "Artista do álbum",
"albumArtist_many": "artistas do álbum", "albumArtist_many": "artistas do álbum",
"albumArtist_other": "artistas do álbum", "albumArtist_other": "Artistas do álbum",
"albumArtistCount_one": "{{count}} artista do álbum", "albumArtistCount_one": "{{count}} artista do álbum",
"albumArtistCount_many": "{{count}} artistas do álbum", "albumArtistCount_many": "{{count}} artistas do álbum",
"albumArtistCount_other": "{{count}} artistas do álbum", "albumArtistCount_other": "{{count}} artistas do álbum",
"albumWithCount_one": "{{count}} álbum", "albumWithCount_one": "{{count}} álbum",
"albumWithCount_many": "{{count}} álbuns", "albumWithCount_many": "{{count}} álbuns",
"albumWithCount_other": "{{count}} álbuns", "albumWithCount_other": "{{count}} álbuns",
"artist_one": "artista", "artist_one": "Artista",
"artist_many": "artistas", "artist_many": "artistas",
"artist_other": "artistas", "artist_other": "Artistas",
"artistWithCount_one": "{{count}} artista", "artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistas", "artistWithCount_many": "{{count}} artistas",
"artistWithCount_other": "{{count}} artistas", "artistWithCount_other": "{{count}} artistas",
"favorite_one": "favorito", "favorite_one": "Favorito",
"favorite_many": "favoritos", "favorite_many": "favoritos",
"favorite_other": "favoritos", "favorite_other": "Favoritos",
"folder_one": "pasta", "folder_one": "Pasta",
"folder_many": "pastas", "folder_many": "pastas",
"folder_other": "pastas", "folder_other": "Pastas",
"folderWithCount_one": "{{count}} pasta", "folderWithCount_one": "{{count}} pasta",
"folderWithCount_many": "{{count}} pastas", "folderWithCount_many": "{{count}} pastas",
"folderWithCount_other": "{{count}} pastas", "folderWithCount_other": "{{count}} pastas",
"genre_one": "gênero", "genre_one": "Gênero",
"genre_many": "gêneros", "genre_many": "gêneros",
"genre_other": "gêneros", "genre_other": "Gêneros",
"genreWithCount_one": "{{count}} gênero", "genreWithCount_one": "{{count}} gênero",
"genreWithCount_many": "{{count}} gêneros", "genreWithCount_many": "{{count}} gêneros",
"genreWithCount_other": "{{count}} gêneros", "genreWithCount_other": "{{count}} gêneros",
"playlist_one": "playlist", "playlist_one": "Playlist",
"playlist_many": "playlists", "playlist_many": "playlists",
"playlist_other": "playlists", "playlist_other": "Playlists",
"play_one": "{{count}} reprodução", "play_one": "{{count}} reprodução",
"play_many": "{{count}} reproduções", "play_many": "{{count}} reproduções",
"play_other": "{{count}} reproduções", "play_other": "{{count}} reproduções",
"playlistWithCount_one": "{{count}} playlist", "playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlists", "playlistWithCount_many": "{{count}} playlists",
"playlistWithCount_other": "{{count}} playlists", "playlistWithCount_other": "{{count}} playlists",
"smartPlaylist": "$t(entity.playlist_one) inteligente", "smartPlaylist": "$t(entity.playlist, {\"count\": 1}) inteligente",
"track_one": "faixa", "track_one": "Faixa",
"track_many": "faixas", "track_many": "faixas",
"track_other": "faixas", "track_other": "Faixas",
"song_one": "música", "song_one": "Música",
"song_many": "músicas", "song_many": "músicas",
"song_other": "músicas", "song_other": "Músicas",
"trackWithCount_one": "{{count}} faixa", "trackWithCount_one": "{{count}} faixa",
"trackWithCount_many": "{{count}} faixas", "trackWithCount_many": "{{count}} faixas",
"trackWithCount_other": "{{count}} faixas" "trackWithCount_other": "{{count}} faixas"
}, },
"error": { "error": {
"apiRouteError": "não é possível encaminhar a solicitação", "apiRouteError": "Não é possível encaminhar a solicitação",
"audioDeviceFetchError": "ocorreu um erro ao tentar obter dispositivos de áudio", "audioDeviceFetchError": "Ocorreu um erro ao tentar obter dispositivos de áudio",
"authenticationFailed": "falha na autenticação", "authenticationFailed": "Falha na autenticação",
"badAlbum": "está a ver este erro por que está música não é parte de algum album. um motivo comum para si estar a ver este erro é se a sua música estiver na raiz da sua pasta de músicas. o Jellyfin apenas agrupa as músicas se elas estiveram na mesma pasta", "badAlbum": "Está a ver este erro por que está música não é parte de algum album. um motivo comum para si estar a ver este erro é se a sua música estiver na raiz da sua pasta de músicas. o Jellyfin apenas agrupa as músicas se elas estiveram na mesma pasta",
"badValue": "opção inválida \"{{value}}\". este valor não existe no momento", "badValue": "Opção inválida \"{{value}}\". este valor não existe no momento",
"credentialsRequired": "credenciais necessárias", "credentialsRequired": "Credenciais necessárias",
"endpointNotImplementedError": "endpoint {{endpoint}} não está implementado para {{serverType}}", "endpointNotImplementedError": "Endpoint {{endpoint}} não está implementado para {{serverType}}",
"genericError": "um erro ocorreu", "genericError": "Um erro ocorreu",
"invalidServer": "servidor inválido", "invalidServer": "Servidor inválido",
"localFontAccessDenied": "acesso a fontes locais rejeitado", "localFontAccessDenied": "Acesso a fontes locais rejeitado",
"loginRateError": "muitas tentativas de login, tente novamente em alguns segundos", "loginRateError": "Muitas tentativas de login, tente novamente em alguns segundos",
"mpvRequired": "MPV necessário", "mpvRequired": "MPV necessário",
"networkError": "ocorreu um erro na internet", "networkError": "Ocorreu um erro na internet",
"openError": "não foi possível abrir o ficheiro", "openError": "Não foi possível abrir o ficheiro",
"playbackError": "ocorreu um erro ao tentar reproduzir a média", "playbackError": "Ocorreu um erro ao tentar reproduzir a média",
"remoteDisableError": "ocorreu um erro ao tentar $t(common.disable) o servidor remoto", "remoteDisableError": "Ocorreu um erro ao tentar $t(common.disable) o servidor remoto",
"remoteEnableError": "ocorreu um erro ao tentar $t(common.enable) o servidor remoto", "remoteEnableError": "Ocorreu um erro ao tentar $t(common.enable) o servidor remoto",
"remotePortError": "ocorreu um erro ao tentar definir a porta do servidor remoto", "remotePortError": "Ocorreu um erro ao tentar definir a porta do servidor remoto",
"remotePortWarning": "reinicie o servidor para aplicar a nova porta", "remotePortWarning": "Reinicie o servidor para aplicar a nova porta",
"serverNotSelectedError": "nenhum servidor selecionado", "serverNotSelectedError": "Nenhum servidor selecionado",
"serverRequired": "servidor necessário", "serverRequired": "Servidor necessário",
"sessionExpiredError": "a sua sessão expirou", "sessionExpiredError": "A sua sessão expirou",
"systemFontError": "ocorreu um erro ao tentar obter fontes do sistema" "systemFontError": "Ocorreu um erro ao tentar obter fontes do sistema"
}, },
"filter": { "filter": {
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"albumArtist": "$t(entity.albumArtist_one)", "albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"albumCount": "número de $t(entity.album_other)", "albumCount": "Número de $t(entity.album, {\"count\": 2})",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"biography": "bibliografia", "biography": "Bibliografia",
"bitrate": "bitrate", "bitrate": "Bitrate",
"bpm": "bpm", "bpm": "Bpm",
"channels": "$t(common.channel_other)", "channels": "$t(common.channel_other)",
"comment": "comentário", "comment": "Comentário",
"communityRating": "Nota da comunidade", "communityRating": "Nota da comunidade",
"criticRating": "avaliação da crítica", "criticRating": "Avaliação da crítica",
"dateAdded": "data de adição", "dateAdded": "Data de adição",
"disc": "disco", "disc": "Disco",
"duration": "duração", "duration": "Duração",
"favorited": "favoritado", "favorited": "Favoritado",
"fromYear": "a partir do ano", "fromYear": "A partir do ano",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"id": "id", "id": "Id",
"isCompilation": "é compilação", "isCompilation": "É compilação",
"isFavorited": "é favoritado", "isFavorited": "É favoritado",
"isPublic": "é público", "isPublic": "É público",
"isRated": "possui avaliação", "isRated": "Possui avaliação",
"isRecentlyPlayed": "foi tocado recentemente", "isRecentlyPlayed": "Foi tocado recentemente",
"lastPlayed": "última tocada", "lastPlayed": "Última tocada",
"mostPlayed": "mais tocado", "mostPlayed": "Mais tocado",
"name": "nome", "name": "Nome",
"note": "nota", "note": "Nota",
"owner": "$t(common.owner)", "owner": "$t(common.owner)",
"path": "caminho", "path": "Caminho",
"playCount": "contador de reproduções", "playCount": "Contador de reproduções",
"random": "aleatório", "random": "Aleatório",
"rating": "avaliação", "rating": "Avaliação",
"recentlyAdded": "adicionado recentemente", "recentlyAdded": "Adicionado recentemente",
"recentlyPlayed": "tocado recentemente", "recentlyPlayed": "Tocado recentemente",
"recentlyUpdated": "atualizado recentemente", "recentlyUpdated": "Atualizado recentemente",
"releaseDate": "data de lançamento", "releaseDate": "Data de lançamento",
"releaseYear": "ano de lançamento", "releaseYear": "Ano de lançamento",
"search": "buscar", "search": "Buscar",
"songCount": "contador de músicas", "songCount": "Contador de músicas",
"title": "titulo", "title": "Titulo",
"toYear": "até o ano", "toYear": "Até o ano",
"trackNumber": "faixa" "trackNumber": "Faixa"
}, },
"form": { "form": {
"addServer": { "addServer": {
"error_savePassword": "um erro ocorreu ao tentar gravar a palavra-passe", "error_savePassword": "Um erro ocorreu ao tentar gravar a palavra-passe",
"ignoreCors": "ignorar CORS ($t(common.restartRequired))", "ignoreCors": "Ignorar CORS ($t(common.restartRequired))",
"ignoreSsl": "ignorar ssl ($t(common.restartRequired))", "ignoreSsl": "Ignorar ssl ($t(common.restartRequired))",
"input_legacyAuthentication": "ativar autenticação legada", "input_legacyAuthentication": "Ativar autenticação legada",
"input_name": "nome do servidor", "input_name": "Nome do servidor",
"input_password": "palavra-passe", "input_password": "Palavra-passe",
"input_savePassword": "gravar palavra-passe", "input_savePassword": "Gravar palavra-passe",
"input_url": "url", "input_url": "Url",
"input_username": "nome de utilizador", "input_username": "Nome de utilizador",
"success": "servidor adicionado com sucesso", "success": "Servidor adicionado com sucesso",
"title": "adicionar servidor" "title": "Adicionar servidor"
}, },
"addToPlaylist": { "addToPlaylist": {
"input_playlists": "$t(entity.playlist_other)", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"input_skipDuplicates": "pular duplicadas", "input_skipDuplicates": "Pular duplicadas",
"success": "adicionado $t(entity.trackWithCount, {\"count\": {{message}} }) para $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })", "success": "Adicionado $t(entity.trackWithCount, {\"count\": {{message}} }) para $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"title": "adicionar à $t(entity.playlist_one)" "title": "Adicionar à $t(entity.playlist, {\"count\": 1})"
}, },
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"input_owner": "$t(common.owner)", "input_owner": "$t(common.owner)",
"input_public": "público", "input_public": "Público",
"success": "$t(entity.playlist_one) criada com sucesso", "success": "$t(entity.playlist, {\"count\": 1}) criada com sucesso",
"title": "criar $t(entity.playlist_one)" "title": "Criar $t(entity.playlist, {\"count\": 1})"
}, },
"deletePlaylist": { "deletePlaylist": {
"input_confirm": "escreva o nome da $t(entity.playlist_one) para confirmar", "input_confirm": "Escreva o nome da $t(entity.playlist, {\"count\": 1}) para confirmar",
"success": "$t(entity.playlist_one) apagada com sucesso", "success": "$t(entity.playlist, {\"count\": 1}) apagada com sucesso",
"title": "apagar $t(entity.playlist_one)" "title": "Apagar $t(entity.playlist, {\"count\": 1})"
}, },
"editPlaylist": { "editPlaylist": {
"publicJellyfinNote": "O Jellyfin por algum motivo não expõe se uma playlist é pública ou não. Se deseja que ela permaneça pública, por favor selecione a seguinte entrada", "publicJellyfinNote": "O Jellyfin por algum motivo não expõe se uma playlist é pública ou não. Se deseja que ela permaneça pública, por favor selecione a seguinte entrada",
"success": "$t(entity.playlist_one) atualizada com sucesso", "success": "$t(entity.playlist, {\"count\": 1}) atualizada com sucesso",
"title": "editar $t(entity.playlist_one)" "title": "Editar $t(entity.playlist, {\"count\": 1})"
}, },
"lyricSearch": { "lyricSearch": {
"input_artist": "$t(entity.artist_one)", "input_artist": "$t(entity.artist, {\"count\": 1})",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"title": "pesquisa de letras" "title": "Pesquisa de letras"
}, },
"queryEditor": { "queryEditor": {
"input_optionMatchAll": "corresponder todos", "input_optionMatchAll": "Corresponder todos",
"input_optionMatchAny": "corresponder qualquer um" "input_optionMatchAny": "Corresponder qualquer um"
}, },
"shareItem": { "shareItem": {
"allowDownloading": "permitir descargas", "allowDownloading": "Permitir descargas",
"description": "descrição", "description": "Descrição",
"setExpiration": "definir expiração", "setExpiration": "Definir expiração",
"success": "ligação de compartilhamento copiado para a área de transferência (ou clique aqui para abrir)", "success": "Ligação de compartilhamento copiado para a área de transferência (ou clique aqui para abrir)",
"expireInvalid": "a expiração deve ser uma data futura", "expireInvalid": "A expiração deve ser uma data futura",
"createFailed": "falha ao criar compartilhamento (o compartilhamento está ativado?)" "createFailed": "Falha ao criar compartilhamento (o compartilhamento está ativado?)"
}, },
"updateServer": { "updateServer": {
"success": "servidor atualizado com sucesso", "success": "Servidor atualizado com sucesso",
"title": "atualizar servidor" "title": "Atualizar servidor"
} }
}, },
"page": { "page": {
"albumArtistDetail": { "albumArtistDetail": {
"about": "Sobre {{artist}}", "about": "Sobre {{artist}}",
"appearsOn": "aparece em", "appearsOn": "Aparece em",
"recentReleases": "lançamentos recentes", "recentReleases": "Lançamentos recentes",
"viewDiscography": "ver discografia", "viewDiscography": "Ver discografia",
"relatedArtists": "$t(entity.artist_other) relacionados", "relatedArtists": "$t(entity.artist, {\"count\": 2}) relacionados",
"topSongs": "músicas mais tocadas", "topSongs": "Músicas mais tocadas",
"topSongsFrom": "músicas mais tocadas de {{title}}", "topSongsFrom": "Músicas mais tocadas de {{title}}",
"viewAll": "ver tudo", "viewAll": "Ver tudo",
"viewAllTracks": "ver todas as $t(entity.track_other)" "viewAllTracks": "Ver todas as $t(entity.track, {\"count\": 2})"
}, },
"albumArtistList": { "albumArtistList": {
"title": "$t(entity.albumArtist_other)" "title": "$t(entity.albumArtist, {\"count\": 2})"
}, },
"albumDetail": { "albumDetail": {
"moreFromArtist": "mais deste $t(entity.artist_one)", "moreFromArtist": "Mais deste $t(entity.artist, {\"count\": 1})",
"moreFromGeneric": "mais que {{item}}", "moreFromGeneric": "Mais que {{item}}",
"released": "lançado" "released": "Lançado"
}, },
"albumList": { "albumList": {
"artistAlbums": "álbuns de {{artist}}", "artistAlbums": "Álbuns de {{artist}}",
"genreAlbums": "\"{{genre}}\" $t(entity.album_other)", "genreAlbums": "\"{{genre}}\" $t(entity.album, {\"count\": 2})",
"title": "$t(entity.album_other)" "title": "$t(entity.album, {\"count\": 2})"
}, },
"appMenu": { "appMenu": {
"collapseSidebar": "recolher barra lateral", "collapseSidebar": "Recolher barra lateral",
"expandSidebar": "expandir barra lateral", "expandSidebar": "Expandir barra lateral",
"goBack": "voltar", "goBack": "Voltar",
"goForward": "avançar", "goForward": "Avançar",
"manageServers": "gerir servidores", "manageServers": "Gerir servidores",
"openBrowserDevtools": "abrir ferramentas do programador", "openBrowserDevtools": "Abrir ferramentas do programador",
"quit": "$t(common.quit)", "quit": "$t(common.quit)",
"selectServer": "selecionar servidor", "selectServer": "Selecionar servidor",
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"version": "versão {{version}}" "version": "Versão {{version}}"
}, },
"manageServers": { "manageServers": {
"title": "gerir servidores", "title": "Gerir servidores",
"serverDetails": "pormenores do servidor", "serverDetails": "Pormenores do servidor",
"url": "URL", "url": "URL",
"username": "nome de utilizador", "username": "Nome de utilizador",
"editServerDetailsTooltip": "editar pormenores do servidor", "editServerDetailsTooltip": "Editar pormenores do servidor",
"removeServer": "remover servidor" "removeServer": "Remover servidor"
}, },
"contextMenu": { "contextMenu": {
"addFavorite": "$t(action.addToFavorites)", "addFavorite": "$t(action.addToFavorites)",
@@ -358,7 +361,7 @@
"createPlaylist": "$t(action.createPlaylist)", "createPlaylist": "$t(action.createPlaylist)",
"deletePlaylist": "$t(action.deletePlaylist)", "deletePlaylist": "$t(action.deletePlaylist)",
"deselectAll": "$t(action.deselectAll)", "deselectAll": "$t(action.deselectAll)",
"download": "descarregar", "download": "Descarregar",
"moveToNext": "$t(action.moveToNext)", "moveToNext": "$t(action.moveToNext)",
"moveToBottom": "$t(action.moveToBottom)", "moveToBottom": "$t(action.moveToBottom)",
"moveToTop": "$t(action.moveToTop)", "moveToTop": "$t(action.moveToTop)",
@@ -370,173 +373,172 @@
"removeFromQueue": "$t(action.removeFromQueue)", "removeFromQueue": "$t(action.removeFromQueue)",
"setRating": "$t(action.setRating)", "setRating": "$t(action.setRating)",
"playShuffled": "$t(player.shuffle)", "playShuffled": "$t(player.shuffle)",
"shareItem": "partilhar elemento", "shareItem": "Partilhar elemento",
"showDetails": "obter informações" "showDetails": "Obter informações"
}, },
"fullscreenPlayer": { "fullscreenPlayer": {
"config": { "config": {
"dynamicBackground": "fundo dinâmico", "dynamicBackground": "Fundo dinâmico",
"dynamicImageBlur": "tamanho do desfoque da imagem", "dynamicImageBlur": "Tamanho do desfoque da imagem",
"dynamicIsImage": "ativar imagem de fundo", "dynamicIsImage": "Ativar imagem de fundo",
"followCurrentLyric": "acompanhar letra", "followCurrentLyric": "Acompanhar letra",
"lyricAlignment": "alinhamento da letra", "lyricAlignment": "Alinhamento da letra",
"lyricOffset": "deslocamento da letra (ms)", "lyricOffset": "Deslocamento da letra (ms)",
"lyricGap": "espaçamento da letra", "lyricGap": "Espaçamento da letra",
"lyricSize": "tamanho da letra", "lyricSize": "Tamanho da letra",
"opacity": "opacidade", "opacity": "Opacidade",
"showLyricMatch": "exibir correspondência da letra", "showLyricMatch": "Exibir correspondência da letra",
"showLyricProvider": "exibir origem da letra", "showLyricProvider": "Exibir origem da letra",
"synchronized": "sincronizado", "synchronized": "Sincronizado",
"unsynchronized": "não sincronizado", "unsynchronized": "Não sincronizado",
"useImageAspectRatio": "usar proporção da imagem" "useImageAspectRatio": "Usar proporção da imagem"
}, },
"lyrics": "letra", "lyrics": "Letra",
"related": "relacionado", "related": "Relacionado",
"upNext": "a seguir", "upNext": "A seguir",
"visualizer": "visualizador", "visualizer": "Visualizador",
"noLyrics": "nenhuma letra encontrada" "noLyrics": "Nenhuma letra encontrada"
}, },
"genreList": { "genreList": {
"showAlbums": "mostrar $t(entity.genre_one) $t(entity.album_other)", "showAlbums": "Mostrar $t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})",
"showTracks": "mostrar $t(entity.genre_one) $t(entity.track_other)", "showTracks": "Mostrar $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})",
"title": "$t(entity.genre_other)" "title": "$t(entity.genre, {\"count\": 2})"
}, },
"globalSearch": { "globalSearch": {
"commands": { "commands": {
"goToPage": "ir à página", "goToPage": "Ir à página",
"searchFor": "procurar {{query}}", "searchFor": "Procurar {{query}}",
"serverCommands": "comandos do servidor" "serverCommands": "Comandos do servidor"
}, },
"title": "comandos" "title": "Comandos"
}, },
"home": { "home": {
"explore": "explore a sua biblioteca", "explore": "Explore a sua biblioteca",
"mostPlayed": "mais tocado", "mostPlayed": "Mais tocado",
"newlyAdded": "lançamentos recém-adicionados", "newlyAdded": "Lançamentos recém-adicionados",
"recentlyPlayed": "tocado recentemente", "recentlyPlayed": "Tocado recentemente",
"title": "$t(common.home)" "title": "$t(common.home)"
}, },
"itemDetail": { "itemDetail": {
"copyPath": "copiar caminho para a área de transferência", "copyPath": "Copiar caminho para a área de transferência",
"copiedPath": "caminho copiado com sucesso", "copiedPath": "Caminho copiado com sucesso",
"openFile": "mostrar faixa no gestor de ficheiros" "openFile": "Mostrar faixa no gestor de ficheiros"
}, },
"playlist": { "playlist": {
"reorder": "reordenar apenas disponível quando ordenado pelo id" "reorder": "Reordenar apenas disponível quando ordenado pelo ID"
}, },
"playlistList": { "playlistList": {
"title": "$t(entity.playlist_other)" "title": "$t(entity.playlist, {\"count\": 2})"
}, },
"setting": { "setting": {
"advanced": "avançado", "advanced": "Avançado",
"generalTab": "geral", "generalTab": "Geral",
"hotkeysTab": "teclas de atalho", "hotkeysTab": "Teclas de atalho",
"playbackTab": "reprodução", "playbackTab": "Reprodução",
"windowTab": "janela" "windowTab": "Janela"
}, },
"sidebar": { "sidebar": {
"albumArtists": "$t(entity.albumArtist_other)", "albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"albums": "$t(entity.album_other)", "albums": "$t(entity.album, {\"count\": 2})",
"artists": "$t(entity.artist_other)", "artists": "$t(entity.artist, {\"count\": 2})",
"folders": "$t(entity.folder_other)", "folders": "$t(entity.folder, {\"count\": 2})",
"genres": "$t(entity.genre_other)", "genres": "$t(entity.genre, {\"count\": 2})",
"home": "$t(common.home)", "home": "$t(common.home)",
"myLibrary": "a minha biblioteca", "myLibrary": "A minha biblioteca",
"nowPlaying": "agora a tocar", "nowPlaying": "Agora a tocar",
"playlists": "$t(entity.playlist_other)", "playlists": "$t(entity.playlist, {\"count\": 2})",
"search": "$t(common.search)", "search": "$t(common.search)",
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"shared": "$t(entity.playlist_other) partilhada", "shared": "$t(entity.playlist, {\"count\": 2}) partilhada",
"tracks": "$t(entity.track_other)" "tracks": "$t(entity.track, {\"count\": 2})"
}, },
"trackList": { "trackList": {
"artistTracks": "faixas de {{artist}}", "artistTracks": "Faixas de {{artist}}",
"genreTracks": "\"{{genre}}\" $t(entity.track_other)", "genreTracks": "\"{{genre}}\" $t(entity.track, {\"count\": 2})",
"title": "$t(entity.track_other)" "title": "$t(entity.track, {\"count\": 2})"
} }
}, },
"player": { "player": {
"addLast": "adicionar no final", "addLast": "Adicionar no final",
"addNext": "adicionar a seguir", "addNext": "Adicionar a seguir",
"favorite": "favorito", "favorite": "Favorito",
"mute": "mudo", "mute": "Mudo",
"muted": "mudo", "muted": "Mudo",
"next": "próximo", "next": "Próximo",
"play": "tocar", "play": "Tocar",
"playbackFetchCancel": "isto demora um pouco... feche a notificação para cancelar", "playbackFetchCancel": "Isto demora um pouco... feche a notificação para cancelar",
"playbackFetchInProgress": "a carregar músicas…", "playbackFetchInProgress": "A carregar músicas…",
"playbackFetchNoResults": "nenhuma música encontrada", "playbackFetchNoResults": "Nenhuma música encontrada",
"playbackSpeed": "velocidade de reprodução", "playbackSpeed": "Velocidade de reprodução",
"playRandom": "tocar aleatório", "playRandom": "Tocar aleatório",
"playSimilarSongs": "tocar músicas similares", "playSimilarSongs": "Tocar músicas similares",
"previous": "anterior", "previous": "Anterior",
"queue_clear": "limpar fila", "queue_clear": "Limpar fila",
"queue_moveToBottom": "mover selecionados para o topo", "queue_moveToBottom": "Mover selecionados para o fim",
"queue_moveToTop": "mover selecionados para o fim", "queue_moveToTop": "Mover selecionados para o topo",
"queue_remove": "remover selecionados", "queue_remove": "Remover selecionados",
"repeat": "repetir", "repeat": "Repetir",
"repeat_all": "repetir tudo", "repeat_all": "Repetir tudo",
"repeat_off": "repetição desativada", "repeat_off": "Repetição desativada",
"shuffle": "tocar aleatório", "shuffle": "Tocar aleatório",
"shuffle_off": "aleatório desativado", "shuffle_off": "Aleatório desativado",
"skip": "pular", "skip": "Pular",
"skip_back": "retroceder", "skip_back": "Retroceder",
"skip_forward": "avançar", "skip_forward": "Avançar",
"stop": "parar", "stop": "Parar",
"toggleFullscreenPlayer": "alternar player de ecrã cheio", "toggleFullscreenPlayer": "Alternar player de ecrã cheio",
"unfavorite": "remover favorito", "unfavorite": "Remover favorito",
"pause": "pausar", "pause": "Pausar",
"viewQueue": "ver fila" "viewQueue": "Ver fila"
}, },
"setting": { "setting": {
"accentColor": "cor de realce", "accentColor": "Cor de realce",
"accentColor_description": "define a cor de realce para a aplicação", "accentColor_description": "Define a cor de realce para a aplicação",
"albumBackground": "imagem de fundo do álbum", "albumBackground": "Imagem de fundo do álbum",
"albumBackground_description": "adiciona uma imagem de fundo contendo a arte do álbum para a página de álbum", "albumBackground_description": "Adiciona uma imagem de fundo contendo a arte do álbum para a página de álbum",
"albumBackgroundBlur": "tamanho de desfoque da imagem de fundo do álbum", "albumBackgroundBlur": "Tamanho de desfoque da imagem de fundo do álbum",
"albumBackgroundBlur_description": "ajusta a quantidade de desfoque aplicada para a imagem de fundo do álbum", "albumBackgroundBlur_description": "Ajusta a quantidade de desfoque aplicada para a imagem de fundo do álbum",
"applicationHotkeys": "teclas de atalho da aplicação", "applicationHotkeys": "Teclas de atalho da aplicação",
"applicationHotkeys_description": "configure as teclas de atalho da aplicação. clique na caixa de seleção para definir como tecla de atalho global (somente desktop)", "applicationHotkeys_description": "Configure as teclas de atalho da aplicação. clique na caixa de seleção para definir como tecla de atalho global (somente desktop)",
"artistConfiguration": "configuração da página de artista de álbum", "artistConfiguration": "Configuração da página de artista de álbum",
"artistConfiguration_description": "configure quais elementos serão mostrados, e em qual ordem, na página de artista de álbum", "artistConfiguration_description": "Configure quais elementos serão mostrados, e em qual ordem, na página de artista de álbum",
"audioDevice": "dispositivo de áudio", "audioDevice": "Dispositivo de áudio",
"audioDevice_description": "selecione o dispositivo de áudio usado para reprodução (somente player web)", "audioDevice_description": "Selecione o dispositivo de áudio usado para reprodução (somente player web)",
"audioExclusiveMode": "modo de áudio exclusivo", "audioExclusiveMode": "Modo de áudio exclusivo",
"audioExclusiveMode_description": "ativar modo de saída exclusiva. Neste modo, o sistema é geralmente bloqueado, e apenas mpv terá saída de áudio", "audioExclusiveMode_description": "Ativar modo de saída exclusiva. Neste modo, o sistema é geralmente bloqueado, e apenas mpv terá saída de áudio",
"audioPlayer": "player de áudio", "audioPlayer": "Player de áudio",
"audioPlayer_description": "selecione o player de áudio usado para reprodução", "audioPlayer_description": "Selecione o player de áudio usado para reprodução",
"buttonSize": "tamanho do botão da barra de reprodução", "buttonSize": "Tamanho do botão da barra de reprodução",
"buttonSize_description": "o tamanho dos botões da barra de reprodução", "buttonSize_description": "O tamanho dos botões da barra de reprodução",
"clearCache": "limpar cache do navegador", "clearCache": "Limpar cache do navegador",
"clearCache_description": "uma 'limpeza geral' do feishin. em adição a limpar o cache do feishin, limpa o cache do navegador (imagens gravadas e outros recursos). as credenciais de servidor e as configurações serão mantidas", "clearCache_description": "Uma 'limpeza geral' do Feishin. Em adição a limpar o cache do Feishin, limpa o cache do navegador (imagens gravadas e outros recursos). As credenciais de servidor e as configurações serão mantidas",
"clearQueryCache": "limpar cache do feishin", "clearQueryCache": "Limpar cache do Feishin",
"clearQueryCache_description": "uma 'limpeza leve' do feishin. isto irá renovar playlists, metadados de faixas, e resetar letras gravadas. as configurações, as credenciais de servidor e o cache de imagens serão mantidos", "clearQueryCache_description": "Uma 'limpeza leve' do Feishin. Isto irá renovar playlists, metadados de faixas, e resetar letras gravadas. As configurações, as credenciais de servidor e o cache de imagens serão mantidos",
"clearCacheSuccess": "cache limpo com sucesso", "clearCacheSuccess": "Cache limpo com sucesso",
"contextMenu": "configuração do menu de contexto (clique do botão direito do rato)", "contextMenu": "Configuração do menu de contexto (clique do botão direito do rato)",
"contextMenu_description": "permite esconder elementos exibidos no menu quando clica num elemento com o botão direito. elementos não selecionados serão escondidos", "contextMenu_description": "Permite esconder elementos exibidos no menu quando clica num elemento com o botão direito. elementos não selecionados serão escondidos",
"crossfadeDuration": "duraçao de crossfade", "crossfadeDuration": "Duraçao de crossfade",
"crossfadeDuration_description": "define a duração do efeito crossfade", "crossfadeDuration_description": "Define a duração do efeito crossfade",
"crossfadeStyle_description": "seleciona qual estilo de crossfade usado no player de áudio", "crossfadeStyle_description": "Seleciona qual estilo de crossfade usado no player de áudio",
"customCssEnable": "ativar css customizado", "customCssEnable": "Ativar CSS customizado",
"customCssEnable_description": "permite escrever css customizado", "customCssEnable_description": "Permite escrever CSS customizado",
"customCssNotice": "Aviso: apesar de existir alguma higienização (url() e content: não são permitidas), o uso de css personalizado ainda pode representar riscos ao alterar a interface", "customCssNotice": "Aviso: apesar de existir alguma higienização (URL() e content: não são permitidas), o uso de CSS personalizado ainda pode representar riscos ao alterar a interface",
"customCss": "css customizado", "customCss": "Css customizado",
"disableAutomaticUpdates": "desativar atualizações automáticas", "disableLibraryUpdateOnStartup": "Desativar a verificação de novas versões na inicialização",
"disableLibraryUpdateOnStartup": "desativar a verificação de novas versões na inicialização",
"discordApplicationId": "{{discord}} ID da aplicação", "discordApplicationId": "{{discord}} ID da aplicação",
"discordIdleStatus_description": "quando ativado, atualiza o estado enquanto o player está ocioso", "discordIdleStatus_description": "Quando ativado, atualiza o estado enquanto o player está ocioso",
"discordUpdateInterval_description": "o tempo em segundos entre cada atualização (mínimo 15 segundos)", "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" "playButtonBehavior_description": "Define o comportamento padrão do botão play ao adicionar músicas à fila"
}, },
"table": { "table": {
"column": { "column": {
"discNumber": "disco", "discNumber": "Disco",
"size": "$t(common.size)", "size": "$t(common.size)",
"title": "titulo" "title": "Titulo"
}, },
"config": { "config": {
"label": { "label": {
"discNumber": "numero do disco", "discNumber": "Numero do disco",
"titleCombined": "$t(common.title) (combinado)" "titleCombined": "$t(common.title) (combinado)"
} }
} }
+13 -13
View File
@@ -1,19 +1,19 @@
{ {
"common": { "common": {
"confirm": "confirmă", "confirm": "Confirmă",
"create": "creează", "create": "Creează",
"biography": "biografie", "biography": "Biografie",
"areYouSure": "ești sigur?", "areYouSure": "Ești sigur?",
"no": "nu", "no": "Nu",
"name": "nume", "name": "Nume",
"ok": "ok", "ok": "Ok",
"note": "notă", "note": "Notă",
"yes": "da", "yes": "Da",
"explicit": "explicit", "explicit": "Explicit",
"year": "an", "year": "An",
"menu": "meniu" "menu": "Meniu"
}, },
"filter": { "filter": {
"biography": "biografie" "biography": "Biografie"
} }
} }
+885 -669
View File
File diff suppressed because it is too large Load Diff
+618 -617
View File
File diff suppressed because it is too large Load Diff
+466 -464
View File
File diff suppressed because it is too large Load Diff
+456 -455
View File
File diff suppressed because it is too large Load Diff
+355 -354
View File
@@ -1,322 +1,323 @@
{ {
"action": { "action": {
"editPlaylist": "redigera $t(entity.playlist_one)", "editPlaylist": "Redigera $t(entity.playlist, {\"count\": 1})",
"goToPage": "gå till sida", "goToPage": "Gå till sida",
"moveToTop": "flytta till toppen", "moveToTop": "Flytta till toppen",
"clearQueue": "rensa kö", "clearQueue": "Rensa kö",
"addToFavorites": "lägg till $t(entity.favorite_other)", "addToFavorites": "Lägg till $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "lägg till $t(entity.playlist_one)", "addToPlaylist": "Lägg till $t(entity.playlist, {\"count\": 1})",
"createPlaylist": "skapa $t(entity.playlist_one)", "createPlaylist": "Skapa $t(entity.playlist, {\"count\": 1})",
"removeFromPlaylist": "ta bort från $t(entity.playlist_one)", "removeFromPlaylist": "Ta bort från $t(entity.playlist, {\"count\": 1})",
"viewPlaylists": "visa $t(entity.playlist_other)", "viewPlaylists": "Visa $t(entity.playlist, {\"count\": 2})",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"deletePlaylist": "ta bort $t(entity.playlist_one)", "deletePlaylist": "Ta bort $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "ta bort från kö", "removeFromQueue": "Ta bort från kö",
"deselectAll": "avmarkera alla", "deselectAll": "Avmarkera alla",
"moveToBottom": "flytta till botten", "moveToBottom": "Flytta till botten",
"setRating": "sätt betyg", "setRating": "Sätt betyg",
"toggleSmartPlaylistEditor": "växla $t(entity.smartPlaylist) redigerare", "toggleSmartPlaylistEditor": "Växla $t(entity.smartPlaylist) redigerare",
"removeFromFavorites": "ta bort från $t(entity.favorite_other)", "removeFromFavorites": "Ta bort från $t(entity.favorite, {\"count\": 2})",
"downloadStarted": "startade nedladdning av {{count}} objekt", "downloadStarted": "Startade nedladdning av {{count}} objekt",
"moveToNext": "flytta till nästa", "moveToNext": "Flytta till nästa",
"moveUp": "flytta upp", "moveUp": "Flytta upp",
"moveDown": "flytta ner", "moveDown": "Flytta ner",
"holdToMoveToTop": "håll för att flytta till toppen", "holdToMoveToTop": "Håll för att flytta till toppen",
"holdToMoveToBottom": "håll för att flytta till botten", "holdToMoveToBottom": "Håll för att flytta till botten",
"moveItems": "flytta objekt", "moveItems": "Flytta objekt",
"shuffle": "slumpa", "shuffle": "Slumpa",
"shuffleAll": "slumpa alla", "shuffleAll": "Slumpa alla",
"shuffleSelected": "slumpa valda", "shuffleSelected": "Slumpa valda",
"viewMore": "visa mer", "viewMore": "Visa mer",
"openIn": { "openIn": {
"lastfm": "Öppna i Last.fm", "lastfm": "Öppna i Last.fm",
"musicbrainz": "Öppna i MusicBrainz" "musicbrainz": "Öppna i MusicBrainz"
}, },
"createRadioStation": "skapa $t(entity.radioStation_one)", "createRadioStation": "Skapa $t(entity.radioStation, {\"count\": 1})",
"deleteRadioStation": "ta bort $t(entity.radioStation_one)", "deleteRadioStation": "Ta bort $t(entity.radioStation, {\"count\": 1})",
"addOrRemoveFromSelection": "lägg till eller ta bort från markerade", "addOrRemoveFromSelection": "Lägg till eller ta bort från markerade",
"selectRangeOfItems": "välj en mängd objekt", "selectRangeOfItems": "Välj en mängd objekt",
"selectAll": "markera alla", "selectAll": "Markera alla",
"openApplicationDirectory": "öppna applikationskatalog" "openApplicationDirectory": "Öppna applikationskatalog"
}, },
"common": { "common": {
"backward": "bakåt", "backward": "Bakåt",
"increase": "öka", "increase": "Öka",
"rating": "betyg", "rating": "Betyg",
"bpm": "bpm", "bpm": "Bpm",
"refresh": "laddaom", "refresh": "Laddaom",
"unknown": "okänd", "unknown": "Okänd",
"areYouSure": "är du säker?", "areYouSure": "Är du säker?",
"edit": "redigera", "edit": "Redigera",
"favorite": "favorit", "favorite": "Favorit",
"left": "vänster", "left": "Vänster",
"save": "spara", "save": "Spara",
"right": "höger", "right": "Höger",
"currentSong": "aktuell $t(entity.track_one)", "currentSong": "Aktuell $t(entity.track, {\"count\": 1})",
"collapse": "kollaps", "collapse": "Kollaps",
"trackNumber": "spår", "trackNumber": "Spår",
"descending": "fallande", "descending": "Fallande",
"add": "lägg till", "add": "Lägg till",
"gap": "avstånd", "gap": "Avstånd",
"ascending": "stigande", "ascending": "Stigande",
"dismiss": "avskeda", "dismiss": "Avskeda",
"year": "år", "year": "År",
"manage": "hantera", "manage": "Hantera",
"limit": "gräns", "limit": "Gräns",
"minimize": "minimera", "minimize": "Minimera",
"modified": "modifierad", "modified": "Modifierad",
"duration": "längd", "duration": "Längd",
"name": "namn", "name": "Namn",
"maximize": "maximera", "maximize": "Maximera",
"decrease": "minska", "decrease": "Minska",
"ok": "ok", "ok": "Ok",
"description": "beskrivning", "description": "Beskrivning",
"configure": "konfigurera", "configure": "Konfigurera",
"path": "sökväg", "path": "Sökväg",
"no": "nej", "no": "Nej",
"owner": "ägare", "owner": "Ägare",
"enable": "aktivera", "enable": "Aktivera",
"clear": "töm", "clear": "Töm",
"forward": "framåt", "forward": "Framåt",
"delete": "ta bort", "delete": "Ta bort",
"cancel": "avbryt", "cancel": "Avbryt",
"forceRestartRequired": "starta om för att tillämpa ändringar... Stäng meddelandet för att starta om", "forceRestartRequired": "Starta om för att tillämpa ändringar... Stäng meddelandet för att starta om",
"setting": "inställning", "setting_one": "Inställning",
"version": "version", "setting_other": "",
"title": "titel", "version": "Version",
"filter_one": "filter", "title": "Titel",
"filter_other": "filter", "filter_one": "Filter",
"filters": "filter", "filter_other": "Filter",
"create": "skapa", "filters": "Filter",
"bitrate": "bithastighet", "create": "Skapa",
"saveAndReplace": "spara och skrivöver", "bitrate": "Bithastighet",
"action_one": "handling", "saveAndReplace": "Spara och skrivöver",
"action_other": "handlingar", "action_one": "Handling",
"playerMustBePaused": "spelaren måste pausas", "action_other": "Handlingar",
"confirm": "bekräfta", "playerMustBePaused": "Spelaren måste pausas",
"resetToDefault": "återställ till standard", "confirm": "Bekräfta",
"home": "hem", "resetToDefault": "Återställ till standard",
"comingSoon": "kommer snart…", "home": "Hem",
"reset": "nollställ", "comingSoon": "Kommer snart…",
"channel_one": "kanal", "reset": "Nollställ",
"channel_other": "kanaler", "channel_one": "Kanal",
"disable": "inaktivera", "channel_other": "Kanaler",
"sortOrder": "ordning", "disable": "Inaktivera",
"none": "ingen", "sortOrder": "Ordning",
"menu": "meny", "none": "Ingen",
"restartRequired": "omstart krävs", "menu": "Meny",
"previousSong": "föregående $t(entity.track_one)", "restartRequired": "Omstart krävs",
"noResultsFromQuery": "frågan returnerade inga resultat", "previousSong": "Föregående $t(entity.track, {\"count\": 1})",
"quit": "avsluta", "noResultsFromQuery": "Frågan returnerade inga resultat",
"expand": "expandera", "quit": "Avsluta",
"search": "sök", "expand": "Expandera",
"saveAs": "spara som", "search": "Sök",
"disc": "skiva", "saveAs": "Spara som",
"yes": "ja", "disc": "Skiva",
"random": "slumpmässig", "yes": "Ja",
"size": "storlek", "random": "Slumpmässig",
"biography": "biografi", "size": "Storlek",
"note": "anteckning", "biography": "Biografi",
"center": "center", "note": "Anteckning",
"explicitStatus": "olämplig status", "center": "Center",
"additionalParticipants": "ytterligare medverkare", "explicitStatus": "Olämplig status",
"newVersion": "en ny version har installerats {{version}}", "additionalParticipants": "Ytterligare medverkare",
"viewReleaseNotes": "se utgåveinformation", "newVersion": "En ny version har installerats {{version}}",
"bitDepth": "bitdjup", "viewReleaseNotes": "Se utgåveinformation",
"close": "stäng", "bitDepth": "Bitdjup",
"codec": "kodek", "close": "Stäng",
"doNotShowAgain": "visa inte detta igen", "codec": "Kodek",
"view": "visa", "doNotShowAgain": "Visa inte detta igen",
"externalLinks": "externa länkar", "view": "Visa",
"faster": "snabbare", "externalLinks": "Externa länkar",
"faster": "Snabbare",
"mbid": "MusicBrainz ID", "mbid": "MusicBrainz ID",
"noFilters": "inga filter konfigurerade", "noFilters": "Inga filter konfigurerade",
"preview": "förhandsvisa", "preview": "Förhandsvisa",
"private": "privat", "private": "Privat",
"public": "allmän", "public": "Allmän",
"recordLabel": "skivbolag", "recordLabel": "Skivbolag",
"releaseType": "utgåvetyp", "releaseType": "Utgåvetyp",
"reload": "ladda om", "reload": "Ladda om",
"sampleRate": "samplingstakt", "sampleRate": "Samplingstakt",
"slower": "långsammare", "slower": "Långsammare",
"share": "dela", "share": "Dela",
"sort": "sortera", "sort": "Sortera",
"tags": "taggar", "tags": "Taggar",
"translation": "översättning", "translation": "Översättning",
"explicit": "olämplig", "explicit": "Olämplig",
"clean": "städad", "clean": "Städad",
"gridRows": "rutnätsrader", "gridRows": "Rutnätsrader",
"tableColumns": "tabellkolumner", "tableColumns": "Tabellkolumner",
"itemsMore": "{{count}} fler", "itemsMore": "{{count}} fler",
"countSelected": "{{count}} markerade" "countSelected": "{{count}} markerade"
}, },
"error": { "error": {
"remotePortWarning": "starta om servern för att tillämpa den nya porten", "remotePortWarning": "Starta om servern för att tillämpa den nya porten",
"systemFontError": "ett fel uppstod vid försök att hämta systemteckensnitt", "systemFontError": "Ett fel uppstod vid försök att hämta systemteckensnitt",
"playbackError": "ett fel uppstod vid försök att spela upp media", "playbackError": "Ett fel uppstod vid försök att spela upp media",
"endpointNotImplementedError": "endpoint {{endpoint}} är inte implementerad för {{serverType}}", "endpointNotImplementedError": "Endpoint {{endpoint}} är inte implementerad för {{serverType}}",
"remotePortError": "ett fel uppstod vid försök att ange serverporten", "remotePortError": "Ett fel uppstod vid försök att ange serverporten",
"serverRequired": "server krävs", "serverRequired": "Server krävs",
"authenticationFailed": "autentiseringen misslyckades", "authenticationFailed": "Autentiseringen misslyckades",
"apiRouteError": "det går inte att dirigera begäran", "apiRouteError": "Det går inte att dirigera begäran",
"genericError": "ett fel uppstod", "genericError": "Ett fel uppstod",
"credentialsRequired": "autentiseringsuppgifter som krävs", "credentialsRequired": "Autentiseringsuppgifter som krävs",
"sessionExpiredError": "din session har löpt ut", "sessionExpiredError": "Din session har löpt ut",
"remoteEnableError": "Ett fel uppstod vid försök att $t(common.enable) servern", "remoteEnableError": "Ett fel uppstod vid försök att $t(common.enable) servern",
"localFontAccessDenied": "åtkomst nekad till lokala teckensnitt", "localFontAccessDenied": "Åtkomst nekad till lokala teckensnitt",
"serverNotSelectedError": "ingen server vald", "serverNotSelectedError": "Ingen server vald",
"remoteDisableError": "ett fel uppstod vid försök av $t(common.disable) servern", "remoteDisableError": "Ett fel uppstod vid försök av $t(common.disable) servern",
"mpvRequired": "MPV krävs", "mpvRequired": "MPV krävs",
"audioDeviceFetchError": "ett fel uppstod vid hämtning av ljudenheter", "audioDeviceFetchError": "Ett fel uppstod vid hämtning av ljudenheter",
"invalidServer": "ogiltig server", "invalidServer": "Ogiltig server",
"loginRateError": "för många inloggningsförsök, försök igen om några sekunder", "loginRateError": "För många inloggningsförsök, försök igen om några sekunder",
"badAlbum": "du ser denna sidan eftersom denna låten inte är en del av ett album. du ser troligtvis detta problemet för att du har en låt på toppnivån i din musikmapp. Jellyfin grupperar bara låtar om de finns i en mapp", "badAlbum": "Du ser denna sidan eftersom denna låten inte är en del av ett album. du ser troligtvis detta problemet för att du har en låt på toppnivån i din musikmapp. Jellyfin grupperar bara låtar om de finns i en mapp",
"badValue": "felaktigt alternativ \"{{value}}\". detta värde existerar inte längre", "badValue": "Felaktigt alternativ \"{{value}}\". detta värde existerar inte längre",
"multipleServerSaveQueueError": "spelningskön har en eller flera låtar som inte är från den nuvarande valda servern. detta är inte stöttat", "multipleServerSaveQueueError": "Spelningskön har en eller flera låtar som inte är från den nuvarande valda servern. detta är inte stöttat",
"networkError": "en nätverksfel uppstod", "networkError": "En nätverksfel uppstod",
"notificationDenied": "åtkomst till notifieringarna var nekad. inställningen har ingen verkan", "notificationDenied": "Åtkomst till notifieringarna var nekad. inställningen har ingen verkan",
"openError": "kunde inte öppna filen", "openError": "Kunde inte öppna filen",
"settingsSyncError": "diskrepans hittades mellan inställningarna för renderingsprocessen och huvudprocessen. starta om applikationen för att ändringarna ska tillämpas" "settingsSyncError": "Diskrepans hittades mellan inställningarna för renderingsprocessen och huvudprocessen. starta om applikationen för att ändringarna ska tillämpas"
}, },
"filter": { "filter": {
"mostPlayed": "mest spelade", "mostPlayed": "Mest spelade",
"comment": "kommentar", "comment": "Kommentar",
"playCount": "antal spelningar", "playCount": "Antal spelningar",
"recentlyUpdated": "nyligen uppdaterad", "recentlyUpdated": "Nyligen uppdaterad",
"channels": "$t(common.channel_other)", "channels": "$t(common.channel_other)",
"isCompilation": "är kompilering", "isCompilation": "Är kompilering",
"recentlyPlayed": "nyligen spelad", "recentlyPlayed": "Nyligen spelad",
"isRated": "är betygsatt", "isRated": "Är betygsatt",
"owner": "$t(common.owner)", "owner": "$t(common.owner)",
"title": "titel", "title": "Titel",
"rating": "betyg", "rating": "Betyg",
"search": "sök", "search": "Sök",
"bitrate": "bithastighet", "bitrate": "Bithastighet",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"recentlyAdded": "nyligen tillagda", "recentlyAdded": "Nyligen tillagda",
"note": "anteckning", "note": "Anteckning",
"name": "namn", "name": "Namn",
"dateAdded": "datum tillagt", "dateAdded": "Datum tillagt",
"releaseDate": "utgivningsdag", "releaseDate": "Utgivningsdag",
"communityRating": "betyg från communityn", "communityRating": "Betyg från communityn",
"path": "sökväg", "path": "Sökväg",
"favorited": "favoritmärkt", "favorited": "Favoritmärkt",
"albumArtist": "$t(entity.albumArtist_one)", "albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"isRecentlyPlayed": "spelas nyligen", "isRecentlyPlayed": "Spelas nyligen",
"isFavorited": "är favoritmärkt", "isFavorited": "Är favoritmärkt",
"bpm": "bpm", "bpm": "Bpm",
"releaseYear": "utgivningsår", "releaseYear": "Utgivningsår",
"id": "id", "id": "Id",
"disc": "skiva", "disc": "Skiva",
"biography": "biografi", "biography": "Biografi",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"duration": "längd", "duration": "Längd",
"isPublic": "är offentlig", "isPublic": "Är offentlig",
"random": "slumpmässig", "random": "Slumpmässig",
"lastPlayed": "senast spelad", "lastPlayed": "Senast spelad",
"toYear": "till år", "toYear": "Till år",
"fromYear": "från år", "fromYear": "Från år",
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"trackNumber": "spår", "trackNumber": "Spår",
"songCount": "sångräkning", "songCount": "Sångräkning",
"criticRating": "kritikerbetyg", "criticRating": "Kritikerbetyg",
"albumCount": "$t(entity.album_other) antal", "albumCount": "$t(entity.album, {\"count\": 2}) antal",
"explicitStatus": "$t(common.explicitStatus)" "explicitStatus": "$t(common.explicitStatus)"
}, },
"form": { "form": {
"deletePlaylist": { "deletePlaylist": {
"title": "ta bort $t(entity.playlist_one)", "title": "Ta bort $t(entity.playlist, {\"count\": 1})",
"success": "$t(entity.playlist_one) har tagits bort", "success": "$t(entity.playlist, {\"count\": 1}) har tagits bort",
"input_confirm": "Skriv namnet på $t(entity.playlist_one) för att bekräfta" "input_confirm": "Skriv namnet på $t(entity.playlist, {\"count\": 1}) för att bekräfta"
}, },
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
"title": "skapa $t(entity.playlist_one)", "title": "Skapa $t(entity.playlist, {\"count\": 1})",
"input_public": "offentlig", "input_public": "Offentlig",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"success": "$t(entity.playlist_one) skapad", "success": "$t(entity.playlist, {\"count\": 1}) skapad",
"input_owner": "$t(common.owner)" "input_owner": "$t(common.owner)"
}, },
"addServer": { "addServer": {
"title": "lägg till server", "title": "Lägg till server",
"input_username": "användarnamn", "input_username": "Användarnamn",
"input_url": "länk", "input_url": "Länk",
"input_password": "lösenord", "input_password": "Lösenord",
"input_legacyAuthentication": "aktivera äldre autentisering", "input_legacyAuthentication": "Aktivera äldre autentisering",
"input_name": "server namn", "input_name": "Server namn",
"success": "servern har lagts till", "success": "Servern har lagts till",
"input_savePassword": "spara lösenord", "input_savePassword": "Spara lösenord",
"ignoreSsl": "ignorera ssl ($t(common.restartRequired))", "ignoreSsl": "Ignorera ssl ($t(common.restartRequired))",
"ignoreCors": "ignorera cors ($t(common.restartRequired))", "ignoreCors": "Ignorera cors ($t(common.restartRequired))",
"error_savePassword": "ett fel uppstod när lösenordet skulle sparas", "error_savePassword": "Ett fel uppstod när lösenordet skulle sparas",
"input_preferInstantMix": "föredra instant mixning", "input_preferInstantMix": "Föredra instant mixning",
"input_preferInstantMixDescription": "använd bara instant mixning för att få liknande låtar. användbar om du har plugin för att förändra detta beteendet" "input_preferInstantMixDescription": "Använd bara instant mixning för att få liknande låtar. användbar om du har plugin för att förändra detta beteendet"
}, },
"addToPlaylist": { "addToPlaylist": {
"success": "lade till $t(entity.trackWithCount, {\"count\": {{message}} }) till $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })", "success": "Lade till $t(entity.trackWithCount, {\"count\": {{message}} }) till $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"title": "lägg till i $t(entity.playlist_one)", "title": "Lägg till i $t(entity.playlist, {\"count\": 1})",
"input_skipDuplicates": "hoppa över dubbletter", "input_skipDuplicates": "Hoppa över dubbletter",
"input_playlists": "$t(entity.playlist_other)", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "skapa $t(entity.playlist_one) {{playlist}}", "create": "Skapa $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "sök $t(entity.playlist_other) eller skriv för att skapa en ny" "searchOrCreate": "Sök $t(entity.playlist, {\"count\": 2}) eller skriv för att skapa en ny"
}, },
"updateServer": { "updateServer": {
"title": "uppdatera server", "title": "Uppdatera server",
"success": "servern har uppdaterats" "success": "Servern har uppdaterats"
}, },
"queryEditor": { "queryEditor": {
"input_optionMatchAll": "matcha alla", "input_optionMatchAll": "Matcha alla",
"input_optionMatchAny": "matcha något" "input_optionMatchAny": "Matcha något"
}, },
"lyricSearch": { "lyricSearch": {
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)", "input_artist": "$t(entity.artist, {\"count\": 1})",
"title": "sångtext sök" "title": "Sångtext sök"
}, },
"editPlaylist": { "editPlaylist": {
"title": "redigera $t(entity.playlist_one)", "title": "Redigera $t(entity.playlist, {\"count\": 1})",
"publicJellyfinNote": "Jellyfin visar av någon anledning inte om en spellista är publik eller inte. Om du önskar att denna ska förbli publik, så får du ha följande indata markerade" "publicJellyfinNote": "Jellyfin visar av någon anledning inte om en spellista är publik eller inte. Om du önskar att denna ska förbli publik, så får du ha följande indata markerade"
}, },
"largeFetchConfirmation": { "largeFetchConfirmation": {
"title": "lägg till objekt till kön", "title": "Lägg till objekt till kön",
"description": "Åtgärden kommer att lägga till alla objekt till den nuvarande filtrerade vyn" "description": "Åtgärden kommer att lägga till alla objekt till den nuvarande filtrerade vyn"
}, },
"createRadioStation": { "createRadioStation": {
"success": "radiostation skapades", "success": "Radiostation skapades",
"title": "skapa radiostation", "title": "Skapa radiostation",
"input_homepageUrl": "hemside-URL", "input_homepageUrl": "Hemside-URL",
"input_name": "namn", "input_name": "Namn",
"input_streamUrl": "stream url" "input_streamUrl": "Stream url"
} }
}, },
"page": { "page": {
"fullscreenPlayer": { "fullscreenPlayer": {
"config": { "config": {
"showLyricMatch": "Visa låttext matchning", "showLyricMatch": "Visa låttext matchning",
"dynamicBackground": "dynamisk bakgrund", "dynamicBackground": "Dynamisk bakgrund",
"followCurrentLyric": "följ aktuell låttext", "followCurrentLyric": "Följ aktuell låttext",
"opacity": "ogenomskinlighet", "opacity": "Ogenomskinlighet",
"lyricSize": "låttext storlek", "lyricSize": "Låttext storlek",
"lyricAlignment": "låttext justering", "lyricAlignment": "Låttext justering",
"lyricGap": "låttext mellanrum", "lyricGap": "Låttext mellanrum",
"synchronized": "synkroniserad", "synchronized": "Synkroniserad",
"showLyricProvider": "visa sångtextleverantör", "showLyricProvider": "Visa sångtextleverantör",
"unsynchronized": "osynkroniserad" "unsynchronized": "Osynkroniserad"
}, },
"lyrics": "sångtext", "lyrics": "Sångtext",
"related": "relaterad" "related": "Relaterad"
}, },
"appMenu": { "appMenu": {
"selectServer": "välj server", "selectServer": "Välj server",
"version": "version {{version}}", "version": "Version {{version}}",
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"manageServers": "hantera servrar", "manageServers": "Hantera servrar",
"expandSidebar": "expandera sidofältet", "expandSidebar": "Expandera sidofältet",
"openBrowserDevtools": "öppna webbläsarens utvecklingsverktyg", "openBrowserDevtools": "Öppna webbläsarens utvecklingsverktyg",
"quit": "$t(common.quit)", "quit": "$t(common.quit)",
"goBack": "gå tillbaka", "goBack": "Gå tillbaka",
"goForward": "gå framåt", "goForward": "Gå framåt",
"collapseSidebar": "växla sidofältet" "collapseSidebar": "Växla sidofältet"
}, },
"contextMenu": { "contextMenu": {
"addToPlaylist": "$t(action.addToPlaylist)", "addToPlaylist": "$t(action.addToPlaylist)",
@@ -335,146 +336,146 @@
"play": "$t(player.play)", "play": "$t(player.play)",
"numberSelected": "{{count}} vald", "numberSelected": "{{count}} vald",
"removeFromQueue": "$t(action.removeFromQueue)", "removeFromQueue": "$t(action.removeFromQueue)",
"download": "ladda ner", "download": "Ladda ner",
"moveItems": "$t(action.moveItems)", "moveItems": "$t(action.moveItems)",
"moveToNext": "$t(action.moveToNext)", "moveToNext": "$t(action.moveToNext)",
"playSimilarSongs": "$t(player.playSimilarSongs)", "playSimilarSongs": "$t(player.playSimilarSongs)",
"playShuffled": "$t(player.shuffle)", "playShuffled": "$t(player.shuffle)",
"shareItem": "dela objekt", "shareItem": "Dela objekt",
"goTo": "gå till", "goTo": "Gå till",
"goToAlbum": "gå till $t(entity.album_one)", "goToAlbum": "Gå till $t(entity.album, {\"count\": 1})",
"goToAlbumArtist": "gå till $t(entity.albumArtist_one)", "goToAlbumArtist": "Gå till $t(entity.albumArtist, {\"count\": 1})",
"showDetails": "hämta information" "showDetails": "Hämta information"
}, },
"albumDetail": { "albumDetail": {
"moreFromArtist": "mer från $t(entity.artist_one)", "moreFromArtist": "Mer från $t(entity.artist, {\"count\": 1})",
"moreFromGeneric": "mer från {{item}}" "moreFromGeneric": "Mer från {{item}}"
}, },
"albumArtistList": { "albumArtistList": {
"title": "$t(entity.albumArtist_other)" "title": "$t(entity.albumArtist, {\"count\": 2})"
}, },
"albumList": { "albumList": {
"title": "$t(entity.album_other)" "title": "$t(entity.album, {\"count\": 2})"
}, },
"sidebar": { "sidebar": {
"nowPlaying": "nu spelas" "nowPlaying": "Nu spelas"
}, },
"home": { "home": {
"mostPlayed": "mest spelade", "mostPlayed": "Mest spelade",
"newlyAdded": "nytillkomna utgåvor", "newlyAdded": "Nytillkomna utgåvor",
"explore": "utforska från ditt bibliotek", "explore": "Utforska från ditt bibliotek",
"recentlyPlayed": "nyligen spelat" "recentlyPlayed": "Nyligen spelat"
}, },
"setting": { "setting": {
"playbackTab": "uppspelning", "playbackTab": "Uppspelning",
"generalTab": "allmänt", "generalTab": "Allmänt",
"hotkeysTab": "snabbtangenter", "hotkeysTab": "Snabbtangenter",
"windowTab": "fönster" "windowTab": "Fönster"
}, },
"globalSearch": { "globalSearch": {
"commands": { "commands": {
"serverCommands": "serverkommandon", "serverCommands": "Serverkommandon",
"goToPage": "gå till sidan", "goToPage": "Gå till sidan",
"searchFor": "sök efter {{query}}" "searchFor": "Sök efter {{query}}"
}, },
"title": "kommandon" "title": "Kommandon"
}, },
"manageServers": { "manageServers": {
"url": "URL", "url": "URL",
"username": "användarnamn", "username": "Användarnamn",
"editServerDetailsTooltip": "redigera serverinställningar", "editServerDetailsTooltip": "Redigera serverinställningar",
"removeServer": "ta bort server" "removeServer": "Ta bort server"
} }
}, },
"entity": { "entity": {
"playlist_one": "spellista", "playlist_one": "Spellista",
"playlist_other": "spellistor", "playlist_other": "Spellistor",
"artist_one": "artist", "artist_one": "Artist",
"artist_other": "artister", "artist_other": "Artister",
"albumArtist_one": "albumartist", "albumArtist_one": "Albumartist",
"albumArtist_other": "albumartister", "albumArtist_other": "Albumartister",
"albumArtistCount_one": "{{count}} Albumartist", "albumArtistCount_one": "{{count}} albumartist",
"albumArtistCount_other": "{{count}} Albumartister", "albumArtistCount_other": "{{count}} albumartister",
"albumWithCount_one": "{{count}} album", "albumWithCount_one": "{{count}} album",
"albumWithCount_other": "{{count}} album", "albumWithCount_other": "{{count}} album",
"favorite_one": "favorit", "favorite_one": "Favorit",
"favorite_other": "favoriter", "favorite_other": "Favoriter",
"folder_one": "mapp", "folder_one": "Mapp",
"folder_other": "mappar", "folder_other": "Mappar",
"album_one": "album", "album_one": "Album",
"album_other": "album", "album_other": "Album",
"playlistWithCount_one": "{{count}} spellista", "playlistWithCount_one": "{{count}} spellista",
"playlistWithCount_other": "{{count}} spellistor", "playlistWithCount_other": "{{count}} spellistor",
"folderWithCount_one": "{{count}} mapp", "folderWithCount_one": "{{count}} mapp",
"folderWithCount_other": "{{count}} mappar", "folderWithCount_other": "{{count}} mappar",
"track_one": "spår", "track_one": "Spår",
"track_other": "spår", "track_other": "Spår",
"trackWithCount_one": "{{count}} spår", "trackWithCount_one": "{{count}} spår",
"trackWithCount_other": "{{count}} spår", "trackWithCount_other": "{{count}} spår",
"artistWithCount_one": "{{count}} artist", "artistWithCount_one": "{{count}} artist",
"artistWithCount_other": "{{count}} artister", "artistWithCount_other": "{{count}} artister",
"genre_one": "genre", "genre_one": "Genre",
"genre_other": "genrer", "genre_other": "Genrer",
"genreWithCount_one": "{{count}} genre", "genreWithCount_one": "{{count}} genre",
"genreWithCount_other": "{{count}} genrer", "genreWithCount_other": "{{count}} genrer",
"play_one": "{{count}} spelning", "play_one": "{{count}} spelning",
"play_other": "{{count}} spelningar", "play_other": "{{count}} spelningar",
"smartPlaylist": "smart $t(entity.playlist_one)", "smartPlaylist": "Smart $t(entity.playlist, {\"count\": 1})",
"song_one": "låt", "song_one": "Låt",
"song_other": "låtar", "song_other": "Låtar",
"radioStation_one": "radiostation", "radioStation_one": "Radiostation",
"radioStation_other": "radiostationer", "radioStation_other": "Radiostationer",
"radioStationWithCount_one": "{{count}} radiostation", "radioStationWithCount_one": "{{count}} radiostation",
"radioStationWithCount_other": "{{count}} radiostationer" "radioStationWithCount_other": "{{count}} radiostationer"
}, },
"player": { "player": {
"repeat_all": "repetera alla", "repeat_all": "Repetera alla",
"repeat": "repetera", "repeat": "Repetera",
"queue_remove": "ta bort markerad", "queue_remove": "Ta bort markerad",
"playRandom": "spela slumpmässigt", "playRandom": "Spela slumpmässigt",
"previous": "föregående", "previous": "Föregående",
"favorite": "favorit", "favorite": "Favorit",
"next": "nästa", "next": "Nästa",
"shuffle": "blanda", "shuffle": "Blanda",
"playbackFetchNoResults": "inga låtar hittades", "playbackFetchNoResults": "Inga låtar hittades",
"playbackFetchInProgress": "laddar låtar…", "playbackFetchInProgress": "Laddar låtar…",
"addNext": "lägg till nästa", "addNext": "Lägg till nästa",
"playbackSpeed": "uppspelningshastighet", "playbackSpeed": "Uppspelningshastighet",
"playbackFetchCancel": "det här tar ett tag... stäng aviseringen för att avbryta", "playbackFetchCancel": "Det här tar ett tag... stäng aviseringen för att avbryta",
"play": "spela", "play": "Spela",
"repeat_off": "repetera inaktiverad", "repeat_off": "Repetera inaktiverad",
"queue_clear": "rensa kö", "queue_clear": "Rensa kö",
"muted": "mutad", "muted": "Mutad",
"queue_moveToTop": "flytta markerad till botten", "queue_moveToTop": "Flytta markerad till toppen",
"queue_moveToBottom": "flytta markerad till toppen", "queue_moveToBottom": "Flytta markerad till botten",
"addLast": "lägg till sist", "addLast": "Lägg till sist",
"mute": "muta" "mute": "Muta"
}, },
"datetime": { "datetime": {
"minuteShort": "min", "minuteShort": "Min",
"secondShort": "sek", "secondShort": "Sek",
"hourShort": "h", "hourShort": "H",
"dayShort": "dag" "dayShort": "Dag"
}, },
"filterOperator": { "filterOperator": {
"after": "är efter", "after": "Är efter",
"afterDate": "är efter (datum)", "afterDate": "Är efter (datum)",
"before": "är före", "before": "Är före",
"beforeDate": "är före (datum)", "beforeDate": "Är före (datum)",
"contains": "innehåller", "contains": "Innehåller",
"endsWith": "slutar med", "endsWith": "Slutar med",
"inPlaylist": "är inom", "inPlaylist": "Är inom",
"inTheLast": "är i den sista", "inTheLast": "Är i den sista",
"inTheRange": "är i spannet", "inTheRange": "Är i spannet",
"inTheRangeDate": "är i spannet (datum)", "inTheRangeDate": "Är i spannet (datum)",
"is": "är", "is": "Är",
"isNot": "är inte", "isNot": "Är inte",
"isGreaterThan": "är större än", "isGreaterThan": "Är större än",
"isLessThan": "är mindre än", "isLessThan": "Är mindre än",
"matchesRegex": "matchar regex", "matchesRegex": "Matchar regex",
"notContains": "innehåller inte", "notContains": "Innehåller inte",
"notInPlaylist": "är inte inom", "notInPlaylist": "Är inte inom",
"notInTheLast": "är inte inom den sista", "notInTheLast": "Är inte inom den sista",
"startsWith": "startar med" "startsWith": "Startar med"
} }
} }
+654 -103
View File
File diff suppressed because it is too large Load Diff
+614 -611
View File
File diff suppressed because it is too large Load Diff
+600 -1
View File
@@ -1,5 +1,604 @@
{ {
"action": { "action": {
"addToFavorites": "додати до $t(entity.favorite_other)" "addToFavorites": "Додати до $t(entity.favorite, {\"count\": 2})",
"addOrRemoveFromSelection": "Додати або видалити з вибору",
"selectRangeOfItems": "Вибрати діапазон елементів",
"addToPlaylist": "Додати до $t(entity.playlist, {\"count\": 1})",
"clearQueue": "Очистити чергу",
"createPlaylist": "Створити $t(entity.playlist, {\"count\": 1})",
"createRadioStation": "Створити $t(entity.radioStation, {\"count\": 1})",
"deletePlaylist": "Видалити $t(entity.playlist, {\"count\": 1})",
"deleteRadioStation": "Видалити $t(entity.radioStation, {\"count\": 1})",
"selectAll": "Вибрати все",
"deselectAll": "Скасувати вибір усього",
"downloadStarted": "Почато завантаження {{count}} елементів",
"editPlaylist": "Редагувати $t(entity.playlist, {\"count\": 1})",
"goToPage": "Перейти на сторінку",
"moveToNext": "Перейти до наступного",
"moveToBottom": "Перемістити вниз",
"moveToTop": "Перемістити вгору",
"moveUp": "Перемістити вище",
"moveDown": "Перемістити нижче",
"holdToMoveToTop": "Утримуйте, щоб перемістити вгору",
"holdToMoveToBottom": "Утримувати, щоб перемістити вниз",
"moveItems": "Перемістити елементи",
"shuffle": "Перемішати",
"shuffleAll": "Все випадково",
"shuffleSelected": "Вибране випадково",
"refresh": "$t(common.refresh)",
"removeFromFavorites": "Видалити з $t(entity.favorite, {\"count\": 2})",
"removeFromPlaylist": "Видалити з $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "Видалити з черги",
"setRating": "Встановити рейтинг",
"toggleSmartPlaylistEditor": "Перемикати редактор $t(entity.smartPlaylist)",
"viewPlaylists": "Показати $t(entity.playlist, {\"count\": 2})",
"viewMore": "Переглянути більше",
"openApplicationDirectory": "Відкрити каталог додатків",
"openIn": {
"lastfm": "Відкрити в Last.fm",
"musicbrainz": "Відкрити в MusicBrainz",
"listenbrainz": "Відкрити у ListenBrainz",
"qobuz": "Відкрити у Qobuz",
"spotify": "Відкрити у Spotify"
}
},
"common": {
"countSelected": "Вибрано {{count}}",
"explicitStatus": "Явний статус",
"action_one": "Дія",
"action_few": "дії",
"action_many": "дій",
"add": "Додати",
"additionalParticipants": "Додаткові учасники",
"newVersion": "Встановлено нову версію ({{version}})",
"viewReleaseNotes": "Переглянути список змін",
"albumGain": "Підсилення альбому",
"albumPeak": "Піковий рівень альбому",
"areYouSure": "Ви впевнені?",
"ascending": "Зростаючи",
"backward": "Назад",
"biography": "Біографія",
"bitDepth": "Розрядність",
"bitrate": "Бітрейт",
"bpm": "Уд/хв",
"cancel": "Скасувати",
"center": "Посередині",
"channel_one": "Канал",
"channel_few": "канали",
"channel_many": "каналів",
"clear": "Очистити",
"close": "Закрити",
"codec": "Кодек",
"collapse": "Згорнути",
"comingSoon": "Скоро…",
"configure": "Налаштувати",
"confirm": "Підтвердити",
"create": "Створити",
"currentSong": "Поточний $t(entity.track, {\"count\": 1})",
"decrease": "Знизити",
"delete": "Видалити",
"descending": "За спаданням",
"description": "Опис",
"disable": "Вимкнути",
"disc": "Диск",
"dismiss": "Відхилити",
"doNotShowAgain": "Не показувати це знову",
"duration": "Тривалість",
"view": "Показати",
"edit": "Змінити",
"enable": "Увімкнути",
"expand": "Розширити",
"example": "Приклад",
"externalLinks": "Зовнішні посилання",
"faster": "Швидше",
"favorite": "Улюблений",
"filter_one": "Фільтр",
"filter_few": "фільтри",
"filter_many": "фільтрів",
"filters": "Фільтри",
"filter_single": "Одиночний",
"filter_multiple": "Кілька",
"forceRestartRequired": "Перезапустіть, щоб застосувати зміни… закрийте повідомлення, щоб перезапустити",
"forward": "Уперед",
"gap": "Прогалина",
"grouping": "Групування",
"home": "Додому",
"increase": "Збільшити",
"left": "Ліво",
"limit": "Ліміт",
"manage": "Управління",
"maximize": "Максимізувати",
"menu": "Меню",
"minimize": "Мінімізувати",
"modified": "Відредаговано",
"mbid": "MusicBrainz ID",
"mood": "Настрій",
"name": "Назва",
"no": "Ні",
"none": "Жоден",
"noResultsFromQuery": "Запит не дав результатів",
"noFilters": "Фільтри не налаштовані",
"note": "Примітка",
"ok": "Ок",
"owner": "Власник",
"path": "Шлях",
"playerMustBePaused": "Плеєр повинен бути призупинений",
"preview": "Перегляд",
"previousSong": "Минулий $t(entity.track, {\"count\": 1})",
"private": "Приватний",
"public": "Публічний",
"quit": "Вийти",
"random": "Випадково",
"rating": "Рейтинг",
"retry": "Повторити спробу",
"recordLabel": "Лейбл звукозапису",
"releaseType": "Тип випуску",
"refresh": "Оновити",
"reload": "Перезавантажити",
"rename": "Перейменувати",
"reset": "Скинути",
"resetToDefault": "Скинути до заводських налаштувань",
"restartRequired": "Необхідний перезапуск",
"right": "Право",
"clean": "Чистo",
"sampleRate": "Частота дискретизації",
"save": "Зберегти",
"saveAndReplace": "Зберегти та замінити",
"saveAs": "Зберегти як",
"search": "Пошук",
"setting_one": "Налаштування",
"setting_few": "налаштування",
"setting_many": "налаштувань",
"slower": "Повільніше",
"share": "Поділитися",
"size": "Розмір",
"sort": "Впорядкувати",
"sortOrder": "Порядок",
"tags": "Теги",
"title": "Назва",
"trackNumber": "Трек",
"trackGain": "Підсилення треку",
"trackPeak": "Піковий рівень треку",
"translation": "Переклад",
"unknown": "Невідомий",
"version": "Версія",
"year": "Рік",
"yes": "Так",
"explicit": "Експліцитний зміст",
"gridRows": "Рядки сітки",
"tableColumns": "Стовпці таблиці",
"itemsMore": "{{count}} більше",
"numberOfResults": "{{numberOfResults}} результатів",
"newVersionAvailable": "Доступна нова версія"
},
"entity": {
"album_one": "Альбом",
"album_few": "альбоми",
"album_many": "альбомів",
"albumArtist_one": "Виконавець альбому",
"albumArtist_few": "виконавці альбому",
"albumArtist_many": "виконавців альбому",
"albumArtistCount_one": "{{count}} виконавець альбому",
"albumArtistCount_few": "{{count}} виконавці альбому",
"albumArtistCount_many": "{{count}} виконавців альбому",
"albumWithCount_one": "{{count}} альбом",
"albumWithCount_few": "{{count}} альбоми",
"albumWithCount_many": "{{count}} альбомів",
"radioStation_one": "Радіостанція",
"radioStation_few": "радіостанції",
"radioStation_many": "радіостанцій",
"radioStationWithCount_one": "{{count}} радіостанція",
"radioStationWithCount_few": "{{count}} радіостанції",
"radioStationWithCount_many": "{{count}} радіостанцій",
"artist_one": "Виконавець",
"artist_few": "виконавці",
"artist_many": "виконавців",
"artistWithCount_one": "{{count}} виконавець",
"artistWithCount_few": "{{count}} виконавці",
"artistWithCount_many": "{{count}} виконавців",
"favorite_one": "Улюблений",
"favorite_few": "улюблені",
"favorite_many": "улюблених",
"folder_one": "Папка",
"folder_few": "папки",
"folder_many": "папок",
"folderWithCount_one": "{{count}} папка",
"folderWithCount_few": "{{count}} папки",
"folderWithCount_many": "{{count}} папок",
"genre_one": "Жанр",
"genre_few": "жанри",
"genre_many": "жанрів",
"genreWithCount_one": "{{count}} жанр",
"genreWithCount_few": "{{count}} жанри",
"genreWithCount_many": "{{count}} жанрів",
"playlist_one": "Плейлист",
"playlist_few": "плейлисти",
"playlist_many": "плейлистів",
"play_one": "{{count}} відтворення",
"play_few": "{{count}} відтворення",
"play_many": "{{count}} відтворень",
"playlistWithCount_one": "{{count}} плейлист",
"playlistWithCount_few": "{{count}} плейлисти",
"playlistWithCount_many": "{{count}} плейлистів",
"smartPlaylist": "Розумний $t(entity.playlist, {\"count\": 1})",
"track_one": "Трек",
"track_few": "треки",
"track_many": "треків",
"song_one": "Пісня",
"song_few": "пісні",
"song_many": "пісень",
"trackWithCount_one": "{{count}} трек",
"trackWithCount_few": "{{count}} треки",
"trackWithCount_many": "{{count}} треків"
},
"error": {
"apiRouteError": "Неможливо виконати запит",
"audioDeviceFetchError": "Сталася помилка під час спроби отримати аудіопристрої",
"authenticationFailed": "Аутентифікація не вдалася",
"badAlbum": "Ви бачите цю сторінку, тому що ця пісня не входить до альбому. найімовірніше, ця проблема виникає, якщо у верхньому рівні вашої музичної папки знаходиться пісня. Jellyfin групує треки тільки в тому випадку, якщо вони знаходяться в папці",
"badValue": "Недійсний параметр \"{{value}}\". це значення більше не існує",
"credentialsRequired": "Необхідні дані для входу",
"endpointNotImplementedError": "Кінцева точка {{endpoint}} не реалізована для {{serverType}}",
"genericError": "Сталася помилка",
"invalidServer": "Недійсний сервер",
"localFontAccessDenied": "Відмова в доступі до локальних шрифтів",
"loginRateError": "Занадто багато спроб входу, спробуйте ще раз через кілька секунд",
"mpvRequired": "Необхідний MPV",
"multipleServerSaveQueueError": "У черзі відтворення є одна або кілька пісень, які не належать до поточного сервера. це не підтримується",
"networkError": "Сталася мережева помилка",
"noNetwork": "Сервер недоступний",
"noNetworkDescription": "Не вдалося підключитися до цього сервера",
"notificationDenied": "Дозвіл на сповіщення було відхилено. це налаштування не має впливу",
"openError": "Не вдалося відкрити файл",
"playbackError": "Сталася помилка під час спроби відтворити медіафайл",
"remoteDisableError": "Сталася помилка під час спроби $t(common.disable) віддаленого сервера",
"remoteEnableError": "Сталася помилка під час спроби $t(common.enable) віддаленого сервера",
"remotePortError": "Сталася помилка під час спроби налаштувати порт віддаленого сервера",
"remotePortWarning": "Перезапустіть сервер щоб застосувати новий порт",
"saveQueueFailed": "Не вдалося зберегти чергу",
"serverNotSelectedError": "Не вибрано жодного сервера",
"serverRequired": "Потрібен сервер",
"sessionExpiredError": "Ваша сесія закінчилася",
"systemFontError": "Сталася помилка під час спроби отримати системні шрифти",
"settingsSyncError": "Виявлено розбіжності між налаштуваннями в рендерері та основним процесом. перезапустіть програму, щоб застосувати зміни",
"invalidJson": "Недійсний JSON",
"playbackPausedDueToError": "Відтворення було призупинено через помилку"
},
"filter": {
"album": "$t(entity.album, {\"count\": 1})",
"albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"albumCount": "Кількість $t(entity.album, {\"count\": 2})",
"artist": "$t(entity.artist, {\"count\": 1})",
"biography": "Біографія",
"bitrate": "Бітрейт",
"bpm": "Уд/хв",
"channels": "$t(common.channel, {\"count\": 2})",
"comment": "Коментар",
"communityRating": "Рейтинг спільноти",
"criticRating": "Рейтинг критиків",
"dateAdded": "Дата додавання",
"disc": "Диск",
"duration": "Тривалість",
"favorited": "Улюблене",
"fromYear": "З року",
"genre": "$t(entity.genre, {\"count\": 1})",
"id": "Id",
"isCompilation": "Є компіляцією",
"isFavorited": "Є улюбленим",
"isPublic": "Є публічним",
"isRated": "Є оціненим",
"isRecentlyPlayed": "Нещодавно відтворено",
"lastPlayed": "Останнє відтворене",
"mostPlayed": "Найбільш відтворювані",
"name": "Назва",
"note": "Примітка",
"owner": "$t(common.owner)",
"path": "Шлях",
"playCount": "Кількість відтворень",
"random": "Випадково",
"rating": "Рейтинг",
"recentlyAdded": "Нещодавно додано",
"recentlyPlayed": "Нещодавно відтворено",
"recentlyUpdated": "Нещодавно оновлено",
"releaseDate": "Дата випуску",
"releaseYear": "Рік випуску",
"search": "Шукати",
"songCount": "Кількість пісень",
"sortName": "Сортування за назвою",
"title": "Назва",
"toYear": "До року",
"trackNumber": "Трек",
"explicitStatus": "$t(common.explicitStatus)",
"matchAnd": "І",
"matchOr": "Або"
},
"datetime": {
"minuteShort": "Хв.",
"secondShort": "Сек.",
"hourShort": "Год",
"dayShort": "Дн."
},
"filterOperator": {
"after": "Є після",
"afterDate": "Після (дата)",
"before": "Є перед",
"beforeDate": "Є перед (дата)",
"contains": "Містить",
"endsWith": "Закінчується на",
"inPlaylist": "Є в",
"inTheLast": "Є в останньому",
"inTheRange": "Є в межах",
"inTheRangeDate": "Є в межах (дата)",
"is": "Є",
"isNot": "Не є",
"isGreaterThan": "Більше ніж",
"isLessThan": "Менше ніж",
"matchesRegex": "Відповідає регулярному виразу",
"notContains": "Не містить",
"notInPlaylist": "Немає в",
"notInTheLast": "Не є в останньому",
"startsWith": "Починається з"
},
"form": {
"addServer": {
"error_savePassword": "Сталася помилка під час спроби зберегти пароль",
"ignoreCors": "Ігнорувати cors ($t(common.restartRequired))",
"ignoreSsl": "Ігнорувати ssl ($t(common.restartRequired)}",
"input_legacyAuthentication": "Увімкнути застарілу автентифікацію",
"input_name": "Назва сервера",
"input_password": "Пароль",
"input_preferInstantMix": "Віддавати перевагу миттєвому міксу",
"input_preferInstantMixDescription": "Використовувати тільки миттєвий мікс щоб отримати подібні пісні. корисно, коли у вас є плагіни, які змінюють цю поведінку",
"input_preferRemoteUrl": "Віддавати перевагу публічній URL-адресі",
"input_remoteUrl": "Публічна URL-адреса",
"input_remoteUrlPlaceholder": "Опціонально: публічна URL-адреса для зовнішніх функцій",
"input_savePassword": "Зберегти пароль",
"input_url": "URL-адреса",
"input_username": "Ім'я користувача",
"success": "Сервер додано успішно",
"title": "Додати сервер"
},
"largeFetchConfirmation": {
"title": "Додати елементи до черги",
"description": "Ця дія додасть усі елементи в поточний відфільтрований перегляд"
},
"addToPlaylist": {
"create": "Створити $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
"input_skipDuplicates": "Пропустити дублікати",
"searchOrCreate": "Шукайте $t(entity.playlist, {\"count\": 2}) або пишіть, щоб створити новий",
"success": "Додано $t(entity.trackWithCount, {\"count\": {{message}} }) до $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"title": "Додати до $t(entity.playlist, {\"count\": 1})"
},
"createPlaylist": {
"input_description": "$t(common.description)",
"input_name": "$t(common.name)",
"input_owner": "$t(common.owner)",
"input_public": "Публічний",
"success": "$t(entity.playlist, {\"count\": 1}) стрворено успішно",
"title": "Створити $t(entity.playlist, {\"count\": 1})"
},
"createRadioStation": {
"success": "Радіостанція створена успішно",
"title": "Створити радіостанцію",
"input_homepageUrl": "Адреса домашньої сторінки",
"input_name": "Назва",
"input_streamUrl": "URL-адреса потоку"
},
"deletePlaylist": {
"input_confirm": "Введіть ім'я $t(entity.playlist, {\"count\": 1}) для підтвердження",
"success": "$t(entity.playlist, {\"count\": 1}) успішно видалено",
"title": "Видалити $t(entity.playlist, {\"count\": 1})"
},
"editPlaylist": {
"publicJellyfinNote": "Jellyfin з якоїсь причини не показує, чи є плейлист публічним чи ні. Якщо ви хочете, щоб він залишався публічним, виберіть варіант нижче",
"success": "$t(entity.playlist, {\"count\": 1}) успішно оновлено",
"title": "Змінити $t(entity.playlist, {\"count\": 1})"
},
"lyricsExport": {
"export": "Експортувати тексти пісень",
"input_synced": "Експортувати синхронізовані тексти пісень",
"input_offset": "$t(setting.lyricOffset)"
},
"lyricSearch": {
"input_artist": "$t(entity.artist, {\"count\": 1})",
"input_name": "$t(common.name)",
"title": "Шукати тексти пісень"
},
"queryEditor": {
"title": "Редактор запитів",
"input_optionMatchAll": "Збіг за всіма",
"input_optionMatchAny": "Збіг за будь-яким",
"addRuleGroup": "Додати групу правил",
"removeRuleGroup": "Видалити групу правил",
"resetToDefault": "Скинути до заводських налаштувань",
"clearFilters": "Очистити фільтри"
},
"saveQueue": {
"success": "Черга відтворення збережена на сервері"
},
"shareItem": {
"allowDownloading": "Дозволити завантаження",
"description": "Опис",
"setExpiration": "Встановити термін дії",
"success": "Посилання для спільного використання скопійовано в буфер обміну (натисніть тут, щоб відкрити)",
"expireInvalid": "Термін дії повинен бути в майбутньому",
"createFailed": "Не вдалося створити спільний доступ (чи ввімкнено спільний доступ?)",
"copyToClipboard": "Скопіювати до буфера обміну: Ctrl+C, enter",
"successMustClick": "Посилання успішно створено, натисніть сюди, щоб відкрити"
},
"shuffleAll": {
"title": "Відтворити випадково",
"input_genre": "$t(entity.genre, {\"count\": 1})",
"input_limit": "Скільки пісень?",
"input_minYear": "Від року",
"input_maxYear": "До року",
"input_played": "Відтворити фільтр",
"input_played_optionAll": "Всі треки",
"input_played_optionUnplayed": "Тільки не відтворені треки",
"input_played_optionPlayed": "Тільки відтворені треки"
},
"updateServer": {
"success": "Сервер успішно оновлено",
"title": "Оновити сервер"
},
"privateMode": {
"enabled": "Приватний режим увімкнено, стан відтворення тепер приховано від зовнішніх інтеграцій",
"disabled": "Приватний режим вимкнено, стан відтворення тепер видно для увімкнених зовнішніх інтеграцій",
"title": "Приватний режим"
},
"editRadioStation": {
"success": "Радіо станція успішно оновлена"
}
},
"player": {
"skip": "Пропустити"
},
"page": {
"albumArtistDetail": {
"about": "Про {{artist}}",
"appearsOn": "З'являється на",
"favoriteSongs": "Улюблені пісні",
"groupingTypeAll": "Всі типи випуску",
"groupingTypePrimary": "Основні типи випуску",
"recentReleases": "Останні випуски",
"viewDiscography": "Переглянути дискографію",
"relatedArtists": "Подібні $t(entity.artist, {\"count\": 2})",
"topSongs": "Найкращі пісні",
"topSongsCommunity": "Спільнота",
"topSongsFrom": "Найкращі пісні від {{title}}",
"topSongsPersonal": "Особисте",
"favoriteSongsFrom": "Улюблені пісні від {{title}}",
"viewAll": "Показати все",
"viewAllTracks": "Показати усі $t(entity.track, {\"count\": 2})"
},
"albumArtistList": {
"title": "$t(entity.albumArtist, {\"count\": 2})"
},
"albumDetail": {
"moreFromArtist": "Більше від цього $t(entity.artist, {\"count\": 1})",
"moreFromGeneric": "Більше від {{item}}",
"released": "Видано"
},
"albumList": {
"artistAlbums": "Альбоми виконавця {{artist}}",
"genreAlbums": "\"{{genre}}\" $t(entity.album, {\"count\": 2})",
"title": "$t(entity.album, {\"count\": 2})"
},
"radioList": {
"title": "Радіостанції"
},
"releasenotes": {
"commitsSinceStable": "Комміти від {{stable}}",
"noNewCommits": "Немає нових коммітів у цьому періоді",
"noStableReleaseToCompare": "Немає доступної стабільної версії для порівняння"
},
"favorites": {
"title": "$t(entity.favorite, {\"count\": 2})"
},
"windowBar": {
"paused": "(Призупинено) ",
"privateMode": "(Приватний режим)"
},
"appMenu": {
"collapseSidebar": "Згорнути бічну панель",
"commandPalette": "Відкрити палітру команд",
"expandSidebar": "Розгорнути бічну панель",
"goBack": "Повернутися назад",
"goForward": "Перейти вперед",
"manageServers": "Управління серверами",
"privateModeOff": "Вимкнути приватний режим",
"privateModeOn": "Увімкнути приватний режим",
"openBrowserDevtools": "Відкрити інструменти розробника",
"quit": "$t(common.quit)",
"selectServer": "Вибрати сервер",
"selectMusicFolder": "Вибрати папку з музикою",
"noMusicFolder": "Не вибрано папку з музикою",
"multipleMusicFolders": "Вибрано {{count}} папок з музикою",
"settings": "$t(common.setting, {\"count\": 2})",
"version": "Версія {{version}}"
},
"manageServers": {
"title": "Управління серверами",
"serverDetails": "Інформація про сервер",
"url": "URL-адреса",
"username": "Ім'я користувача",
"editServerDetailsTooltip": "Редагувати дані сервера",
"removeServer": "Видалити сервер"
},
"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)",
"download": "Завантажити",
"moveItems": "$t(action.moveItems)",
"moveToNext": "$t(action.moveToNext)",
"moveToBottom": "$t(action.moveToBottom)",
"moveToTop": "$t(action.moveToTop)",
"numberSelected": "{{count}} вибрано",
"play": "$t(player.play)",
"playSimilarSongs": "$t(player.playSimilarSongs)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromQueue": "$t(action.removeFromQueue)",
"setRating": "$t(action.setRating)",
"playShuffled": "$t(player.shuffle)",
"shareItem": "Поділитися елементом",
"goTo": "Перейти до",
"goToAlbum": "Перейти до $t(entity.album, {\"count\": 1})",
"goToAlbumArtist": "Перейти до $t(entity.albumArtist, {\"count\": 1})",
"showDetails": "Отримати інформацію"
},
"fullscreenPlayer": {
"config": {
"dynamicBackground": "Динамічний фон",
"dynamicImageBlur": "Розмір розмиття зображення",
"dynamicIsImage": "Включити фонове зображення",
"followCurrentLyric": "Слідкувати за поточним рядком",
"lyricAlignment": "Вирівнювання тексту",
"lyricOffset": "Затримка тексту (мс)",
"lyricGap": "Розмір між рядками",
"lyricSize": "Розмір тексту",
"opacity": "Непрозорість",
"showLyricMatch": "Показувати збіг тексту пісень",
"showLyricProvider": "Показувати джерело тексту пісень",
"synchronized": "Синхронізовано",
"unsynchronized": "Несинхронізовано",
"useImageAspectRatio": "Використовувати співвідношення сторін зображення"
},
"lyrics": "Текст пісні",
"related": "Пов'язані",
"upNext": "Далі",
"visualizer": "Візуалізатор",
"noLyrics": "Текст пісні не знайдено"
},
"genreList": {
"showAlbums": "Показати $t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})",
"showTracks": "Показати $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})",
"title": "$t(entity.genre, {\"count\": 2})"
},
"folderList": {
"title": "$t(entity.folder, {\"count\": 2})"
},
"globalSearch": {
"commands": {
"goToPage": "Перейти до сторінки",
"searchFor": "Шукати на {{query}}",
"serverCommands": "Команди сервера"
},
"title": "Команди"
},
"home": {
"explore": "Дослідити з вашої бібліотеки",
"genres": "$t(entity.genre, {\"count\": 2})",
"mostPlayed": "Найбільш відтворені",
"newlyAdded": "Нещодавно додані релізи",
"recentlyPlayed": "Нещодавно відтворені"
}
} }
} }
+389 -139
View File
@@ -1,25 +1,28 @@
{ {
"action": { "action": {
"editPlaylist": "编辑 $t(entity.playlist_one)", "editPlaylist": "编辑 $t(entity.playlist, {\"count\": 1})",
"moveToTop": "移至顶部", "moveToTop": "移至顶部",
"clearQueue": "清空播放队列", "clearQueue": "清空播放队列",
"addToFavorites": "添加到 $t(entity.favorite_other)", "addToFavorites": "添加到 $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "添加到 $t(entity.playlist_one)", "addToPlaylist": "添加到 $t(entity.playlist, {\"count\": 1})",
"createPlaylist": "创建 $t(entity.playlist_one)", "createPlaylist": "创建 $t(entity.playlist, {\"count\": 1})",
"removeFromPlaylist": "从 $t(entity.playlist_one) 移除", "removeFromPlaylist": "从 $t(entity.playlist, {\"count\": 1}) 移除",
"viewPlaylists": "查看 $t(entity.playlist_other)", "viewPlaylists": "查看 $t(entity.playlist, {\"count\": 2})",
"refresh": "$t(common.refresh)", "refresh": "$t(common.refresh)",
"deletePlaylist": "删除 $t(entity.playlist_one)", "deletePlaylist": "删除 $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "从播放队列中移除", "removeFromQueue": "从播放队列中移除",
"deselectAll": "取消全选", "deselectAll": "取消全选",
"moveToBottom": "移至底部", "moveToBottom": "移至底部",
"setRating": "评分", "setRating": "评分",
"toggleSmartPlaylistEditor": "切换 $t(entity.smartPlaylist) 编辑器", "toggleSmartPlaylistEditor": "切换 $t(entity.smartPlaylist) 编辑器",
"removeFromFavorites": "从 $t(entity.favorite_other) 移除", "removeFromFavorites": "从 $t(entity.favorite, {\"count\": 2}) 移除",
"goToPage": "前往页面", "goToPage": "前往页面",
"openIn": { "openIn": {
"lastfm": "在 Last.fm 中打开", "lastfm": "在 Last.fm 中打开",
"musicbrainz": "在 MusicBrainz 中打开" "musicbrainz": "在 MusicBrainz 中打开",
"listenbrainz": "在 ListenBrainz 中打开",
"qobuz": "在 Qobuz 中打开",
"spotify": "在 Spotify 中打开"
}, },
"moveToNext": "移至下一首", "moveToNext": "移至下一首",
"downloadStarted": "开始下载 {{count}} 个项目", "downloadStarted": "开始下载 {{count}} 个项目",
@@ -35,14 +38,15 @@
"addOrRemoveFromSelection": "在所选内容中添加或移除", "addOrRemoveFromSelection": "在所选内容中添加或移除",
"selectRangeOfItems": "批量选择", "selectRangeOfItems": "批量选择",
"selectAll": "全选", "selectAll": "全选",
"createRadioStation": "创建$t(entity.radioStation_one)", "createRadioStation": "创建$t(entity.radioStation, {\"count\": 1})",
"deleteRadioStation": "删除$t(entity.radioStation_one)", "deleteRadioStation": "删除$t(entity.radioStation, {\"count\": 1})",
"openApplicationDirectory": "打开应用程序目录" "openApplicationDirectory": "打开应用程序目录",
"goToCurrent": "转到当前项目"
}, },
"common": { "common": {
"increase": "增高", "increase": "增高",
"rating": "评分", "rating": "评分",
"bpm": "bpm", "bpm": "BPM",
"refresh": "刷新", "refresh": "刷新",
"unknown": "未知", "unknown": "未知",
"edit": "编辑", "edit": "编辑",
@@ -50,7 +54,7 @@
"left": "左", "left": "左",
"save": "保存", "save": "保存",
"right": "右", "right": "右",
"currentSong": "当前$t(entity.track_one)", "currentSong": "当前$t(entity.track, {\"count\": 1})",
"collapse": "折叠", "collapse": "折叠",
"trackNumber": "音轨编号", "trackNumber": "音轨编号",
"descending": "降序", "descending": "降序",
@@ -74,8 +78,8 @@
"forward": "前进", "forward": "前进",
"delete": "删除", "delete": "删除",
"cancel": "取消", "cancel": "取消",
"forceRestartRequired": "重启应用使更改生效…关闭通知即可重启", "forceRestartRequired": "重启应用使更改生效…关闭通知即可重启",
"setting": "设置", "setting_other": "设置",
"version": "版本", "version": "版本",
"title": "标题", "title": "标题",
"filter_other": "筛选", "filter_other": "筛选",
@@ -92,7 +96,7 @@
"disable": "禁用", "disable": "禁用",
"menu": "菜单", "menu": "菜单",
"restartRequired": "需要重启应用", "restartRequired": "需要重启应用",
"previousSong": "上一首$t(entity.track_one)", "previousSong": "上一首$t(entity.track, {\"count\": 1})",
"noResultsFromQuery": "未查询到匹配结果", "noResultsFromQuery": "未查询到匹配结果",
"quit": "退出", "quit": "退出",
"expand": "展开", "expand": "展开",
@@ -107,7 +111,7 @@
"duration": "时长", "duration": "时长",
"ok": "好", "ok": "好",
"no": "否", "no": "否",
"playerMustBePaused": "播放器必须暂停", "playerMustBePaused": "播放器必须暂停",
"channel_other": "频道", "channel_other": "频道",
"none": "无", "none": "无",
"disc": "碟片", "disc": "碟片",
@@ -150,7 +154,15 @@
"tableColumns": "表格列", "tableColumns": "表格列",
"itemsMore": "{{count}} 更多", "itemsMore": "{{count}} 更多",
"countSelected": "已选择{{count}}项", "countSelected": "已选择{{count}}项",
"retry": "重试" "retry": "重试",
"example": "示例",
"filter_single": "单项",
"mood": "氛围",
"rename": "重命名",
"filter_multiple": "多项",
"newVersionAvailable": "新版本现已可用",
"numberOfResults": "{{numberOfResults}} 结果",
"grouping": "分组"
}, },
"entity": { "entity": {
"albumArtist_other": "专辑艺术家", "albumArtist_other": "专辑艺术家",
@@ -166,11 +178,13 @@
"favorite_other": "收藏", "favorite_other": "收藏",
"artistWithCount_other": "{{count}} 位艺术家", "artistWithCount_other": "{{count}} 位艺术家",
"folder_other": "文件夹", "folder_other": "文件夹",
"smartPlaylist": "智能$t(entity.playlist_one)", "smartPlaylist": "智能$t(entity.playlist, {\"count\": 1})",
"genreWithCount_other": "{{count}} 种流派", "genreWithCount_other": "{{count}} 种流派",
"trackWithCount_other": "{{count}} 首曲目", "trackWithCount_other": "{{count}} 首曲目",
"play_other": "{{count}} 次播放", "play_other": "{{count}} 次播放",
"song_other": "歌曲" "song_other": "歌曲",
"radioStation_other": "广播电台",
"radioStationWithCount_other": "{{count}} 个广播电台"
}, },
"player": { "player": {
"repeat_all": "循环全部", "repeat_all": "循环全部",
@@ -187,17 +201,17 @@
"shuffle": "播放(随机)", "shuffle": "播放(随机)",
"playbackFetchNoResults": "未找到歌曲", "playbackFetchNoResults": "未找到歌曲",
"playbackFetchInProgress": "正在加载歌曲…", "playbackFetchInProgress": "正在加载歌曲…",
"addNext": "下一首播放", "addNext": "下一",
"playbackFetchCancel": "请稍等…关闭通知以取消操作", "playbackFetchCancel": "请稍等…关闭通知以取消操作",
"play": "播放", "play": "播放",
"repeat_off": "循环关闭", "repeat_off": "循环关闭",
"queue_clear": "清空播放队列", "queue_clear": "清空播放队列",
"muted": "已静音", "muted": "已静音",
"unfavorite": "取消收藏", "unfavorite": "取消收藏",
"queue_moveToTop": "将所选项移至部", "queue_moveToTop": "将所选项移至部",
"queue_moveToBottom": "将所选项移至部", "queue_moveToBottom": "将所选项移至部",
"shuffle_off": "禁用随机播放", "shuffle_off": "禁用随机播放",
"addLast": "上一曲", "addLast": "最后",
"mute": "静音", "mute": "静音",
"skip_forward": "向前跳过", "skip_forward": "向前跳过",
"playbackSpeed": "播放速度", "playbackSpeed": "播放速度",
@@ -206,18 +220,32 @@
"viewQueue": "查看播放队列", "viewQueue": "查看播放队列",
"saveQueueToServer": "将播放队列保存到服务器", "saveQueueToServer": "将播放队列保存到服务器",
"restoreQueueFromServer": "从服务器恢复播放队列", "restoreQueueFromServer": "从服务器恢复播放队列",
"lyrics": "歌词" "lyrics": "歌词",
"addLastShuffled": "最后(随机)",
"addNextShuffled": "下一个(随机)",
"artistRadio": "艺术家电台",
"holdToShuffle": "按住即可随机",
"trackRadio": "追踪广播",
"sleepTimer": "睡眠定时器",
"sleepTimer_endOfSong": "当前歌曲结束时",
"sleepTimer_minutes": "{{count}} 分钟",
"sleepTimer_hours": "{{count}} 小时",
"sleepTimer_custom": "自定义",
"sleepTimer_off": "关闭",
"sleepTimer_timeRemaining": "剩余时间 {{time}}",
"sleepTimer_setCustom": "设置定时器",
"sleepTimer_cancel": "取消定时器",
"albumRadio": "专辑电台"
}, },
"setting": { "setting": {
"crossfadeStyle_description": "选择用于音频播放器的淡入淡出风格", "crossfadeStyle_description": "选择用于音频播放器的淡入淡出风格",
"hotkey_favoriteCurrentSong": "收藏$t(common.currentSong)", "hotkey_favoriteCurrentSong": "收藏$t(common.currentSong)",
"audioExclusiveMode_description": "启用独占输出模式。在此模式下,系统通常被锁定为只有 mpv 能够输出音频", "audioExclusiveMode_description": "启用独占输出模式。在此模式下,系统通常被锁定为只有 MPV 能够输出音频",
"disableLibraryUpdateOnStartup": "禁用启动时查询新版本", "disableLibraryUpdateOnStartup": "禁用启动时查询新版本",
"gaplessAudio": "无缝音频", "gaplessAudio": "无缝音频",
"audioPlayer_description": "选择用于播放的音频播放器", "audioPlayer_description": "选择用于播放的音频播放器",
"globalMediaHotkeys": "全局媒体快捷键", "globalMediaHotkeys": "全局媒体快捷键",
"gaplessAudio_description": "调整 mpv 无缝音频设置", "gaplessAudio_description": "调整 MPV 无缝音频设置",
"disableAutomaticUpdates": "禁用自动更新",
"followLyric_description": "滚动歌词到当前播放位置", "followLyric_description": "滚动歌词到当前播放位置",
"audioExclusiveMode": "音频独占模式", "audioExclusiveMode": "音频独占模式",
"font": "字体", "font": "字体",
@@ -233,12 +261,12 @@
"followLyric": "跟随当前歌词", "followLyric": "跟随当前歌词",
"crossfadeDuration": "淡入淡出持续时间", "crossfadeDuration": "淡入淡出持续时间",
"audioPlayer": "音频播放器", "audioPlayer": "音频播放器",
"discordApplicationId": "{{discord}} 应用 id", "discordApplicationId": "{{discord}} 应用 ID",
"applicationHotkeys_description": "配置应用快捷键。勾选设为全局快捷键(仅桌面端)", "applicationHotkeys_description": "配置应用快捷键。勾选设为全局快捷键(仅桌面端)",
"customFontPath_description": "设置应用使用的自定义字体路径", "customFontPath_description": "设置应用使用的自定义字体路径",
"gaplessAudio_optionWeak": "弱(推荐)", "gaplessAudio_optionWeak": "弱(推荐)",
"font_description": "设置应用使用的字体", "font_description": "设置应用使用的字体",
"audioDevice_description": "选择用于播放的音频设备(仅 web 播放器)", "audioDevice_description": "选择用于播放的音频设备",
"enableRemote_description": "启用远程控制服务器,以允许其他设备控制此应用", "enableRemote_description": "启用远程控制服务器,以允许其他设备控制此应用",
"remotePort_description": "设置远程服务器端口", "remotePort_description": "设置远程服务器端口",
"hotkey_skipBackward": "向后跳过", "hotkey_skipBackward": "向后跳过",
@@ -257,7 +285,7 @@
"scrobble": "记录播放信息", "scrobble": "记录播放信息",
"skipDuration_description": "设置每次按下跳过按钮将会跳过的时长", "skipDuration_description": "设置每次按下跳过按钮将会跳过的时长",
"fontType_optionSystem": "系统字体", "fontType_optionSystem": "系统字体",
"mpvExecutablePath_description": "设置 mpv 可执行文件的路径。如果留空,则使用默认路径", "mpvExecutablePath_description": "设置 MPV 可执行文件的路径。如果留空,则使用默认路径",
"sampleRate": "采样率", "sampleRate": "采样率",
"sidePlayQueueStyle_optionAttached": "吸附", "sidePlayQueueStyle_optionAttached": "吸附",
"sidebarConfiguration": "侧边栏设定", "sidebarConfiguration": "侧边栏设定",
@@ -283,7 +311,7 @@
"remoteUsername_description": "设置远程控制服务器的用户名。如果用户名和密码都为空,则身份验证将被禁用", "remoteUsername_description": "设置远程控制服务器的用户名。如果用户名和密码都为空,则身份验证将被禁用",
"exitToTray_description": "退出应用时最小化到系统托盘", "exitToTray_description": "退出应用时最小化到系统托盘",
"hotkey_favoritePreviousSong": "收藏$t(common.previousSong)", "hotkey_favoritePreviousSong": "收藏$t(common.previousSong)",
"replayGainMode_optionAlbum": "$t(entity.album_one)", "replayGainMode_optionAlbum": "$t(entity.album, {\"count\": 1})",
"lyricOffset": "歌词偏移(毫秒)", "lyricOffset": "歌词偏移(毫秒)",
"fontType_optionCustom": "自定义字体", "fontType_optionCustom": "自定义字体",
"themeDark_description": "应用将使用深色主题", "themeDark_description": "应用将使用深色主题",
@@ -292,7 +320,7 @@
"language_description": "设置应用的语言($t(common.restartRequired)", "language_description": "设置应用的语言($t(common.restartRequired)",
"playbackStyle_optionCrossFade": "淡入淡出", "playbackStyle_optionCrossFade": "淡入淡出",
"hotkey_rate3": "评为 3 星", "hotkey_rate3": "评为 3 星",
"replayGainMode_optionTrack": "$t(entity.track_one)", "replayGainMode_optionTrack": "$t(entity.track, {\"count\": 1})",
"themeLight_description": "应用将使用浅色主题", "themeLight_description": "应用将使用浅色主题",
"hotkey_toggleFullScreenPlayer": "全屏播放", "hotkey_toggleFullScreenPlayer": "全屏播放",
"hotkey_localSearch": "页面内搜索", "hotkey_localSearch": "页面内搜索",
@@ -306,7 +334,7 @@
"hotkey_toggleShuffle": "切换随机", "hotkey_toggleShuffle": "切换随机",
"theme": "主题", "theme": "主题",
"playbackStyle_description": "选择音频播放器的播放风格", "playbackStyle_description": "选择音频播放器的播放风格",
"mpvExecutablePath": "mpv 可执行文件路径", "mpvExecutablePath": "MPV 可执行文件路径",
"hotkey_rate2": "评为 2 星", "hotkey_rate2": "评为 2 星",
"playButtonBehavior_description": "设置将歌曲添加到播放队列时播放按钮的默认行为", "playButtonBehavior_description": "设置将歌曲添加到播放队列时播放按钮的默认行为",
"minimumScrobblePercentage_description": "歌曲被记录为已播放所需的最小播放百分比", "minimumScrobblePercentage_description": "歌曲被记录为已播放所需的最小播放百分比",
@@ -316,7 +344,7 @@
"savePlayQueue": "保存播放队列", "savePlayQueue": "保存播放队列",
"minimumScrobbleSeconds_description": "歌曲被记录为已播放所需的最小播放时间", "minimumScrobbleSeconds_description": "歌曲被记录为已播放所需的最小播放时间",
"skipPlaylistPage_description": "打开歌单时,直接查看歌曲列表而非查看默认页面", "skipPlaylistPage_description": "打开歌单时,直接查看歌曲列表而非查看默认页面",
"fontType_description": "内置字体可以选择 feishin 提供的字体之一。系统字体允许您选择操作系统提供的任何字体。自定义选项允许您使用自己的字体", "fontType_description": "内置字体可以选择 Feishin 提供的字体之一。系统字体允许您选择操作系统提供的任何字体。自定义选项允许您使用自己的字体",
"playButtonBehavior": "播放按钮行为", "playButtonBehavior": "播放按钮行为",
"volumeWheelStep": "音量滚轮分度", "volumeWheelStep": "音量滚轮分度",
"sidebarPlaylistList_description": "显示或隐藏侧边栏歌单列表", "sidebarPlaylistList_description": "显示或隐藏侧边栏歌单列表",
@@ -333,7 +361,7 @@
"useSystemTheme_description": "使用系统定义的浅色或深色主题", "useSystemTheme_description": "使用系统定义的浅色或深色主题",
"playButtonBehavior_optionAddNext": "$t(player.addNext)", "playButtonBehavior_optionAddNext": "$t(player.addNext)",
"lyricFetch_description": "从多个互联网源获取歌词", "lyricFetch_description": "从多个互联网源获取歌词",
"lyricFetchProvider_description": "选择歌词源。 歌词源顺序与查询顺序一致", "lyricFetchProvider_description": "选择要从中获取歌词的提供商",
"sidePlayQueueStyle_optionDetached": "不吸附", "sidePlayQueueStyle_optionDetached": "不吸附",
"hotkey_zoomOut": "缩小", "hotkey_zoomOut": "缩小",
"hotkey_unfavoriteCurrentSong": "取消收藏$t(common.currentSong)", "hotkey_unfavoriteCurrentSong": "取消收藏$t(common.currentSong)",
@@ -357,20 +385,20 @@
"replayGainClipping_description": "自动降低增益以防止{{ReplayGain}}造成削波", "replayGainClipping_description": "自动降低增益以防止{{ReplayGain}}造成削波",
"replayGainPreamp": "{{ReplayGain}}前置放大(分贝)", "replayGainPreamp": "{{ReplayGain}}前置放大(分贝)",
"replayGainClipping": "{{ReplayGain}}削波", "replayGainClipping": "{{ReplayGain}}削波",
"discordUpdateInterval": "{{discord}} rich presence 更新间隔", "discordUpdateInterval": "{{discord}} Rich Presence 更新间隔",
"discordApplicationId_description": "{{discord}} rich presence 应用 id(默认为 {{defaultId}}", "discordApplicationId_description": "{{discord}} Rich Presence 应用 ID(默认为 {{defaultId}}",
"discordUpdateInterval_description": "更新间隔秒数(至少 15 秒)", "discordUpdateInterval_description": "更新间隔秒数(至少 15 秒)",
"discordRichPresence_description": "在 {{discord}} rich presence 中显示播放状态。图片键为:{{icon}}、{{playing}} 和 {{paused}}", "discordRichPresence_description": "在 {{discord}} Rich Presence 中显示播放状态。图片键为:{{icon}}、{{playing}} 和 {{paused}}",
"accentColor": "强调色", "accentColor": "强调色",
"accentColor_description": "设置应用的强调色", "accentColor_description": "设置应用的强调色",
"replayGainPreamp_description": "调整应用在{{ReplayGain}}值上的前置放大增益", "replayGainPreamp_description": "调整应用在{{ReplayGain}}值上的前置放大增益",
"discordIdleStatus": "显示 rich presence 闲置状态", "discordIdleStatus": "显示 Rich Presence 闲置状态",
"clearCache": "清除浏览器缓存", "clearCache": "清除浏览器缓存",
"buttonSize": "播放器栏按钮大小", "buttonSize": "播放器栏按钮大小",
"buttonSize_description": "播放器栏按钮大小", "buttonSize_description": "播放器栏按钮大小",
"clearCache_description": "feishin的“硬清除”。除了清除feishin的缓存,清空浏览器缓存(保存的图像和其他资源)。服务器凭据和设置会被保留", "clearCache_description": "Feishin的“硬清除”。除了清除Feishin的缓存,清空浏览器缓存(保存的图像和其他资源)。服务器凭据和设置会被保留",
"clearQueryCache_description": "feishin的“软清除”。这将会刷新播放列表、元数据并重置保存的歌词。设置、服务器凭据和缓存图像会被保留", "clearQueryCache_description": "Feishin的“软清除”。这将会刷新播放列表、元数据并重置保存的歌词。设置、服务器凭据和缓存图像会被保留",
"clearQueryCache": "清除feishin缓存", "clearQueryCache": "清除Feishin缓存",
"externalLinks": "显示外部链接", "externalLinks": "显示外部链接",
"externalLinks_description": "允许在艺术家/专辑页面上显示外部链接(Last.fm、MusicBrainz", "externalLinks_description": "允许在艺术家/专辑页面上显示外部链接(Last.fm、MusicBrainz",
"mpvExtraParameters_help": "每行一个", "mpvExtraParameters_help": "每行一个",
@@ -392,10 +420,10 @@
"contextMenu_description": "允许您隐藏右键单击项目时显示在菜单中的项目。未选中的项目将被隐藏", "contextMenu_description": "允许您隐藏右键单击项目时显示在菜单中的项目。未选中的项目将被隐藏",
"customCssEnable_description": "允许编写自定义 css", "customCssEnable_description": "允许编写自定义 css",
"customCss": "自定义css", "customCss": "自定义css",
"customCss_description": "自定义css内容。注意:内容和远程url是不允许的属性。内容预览展示如下。出于安全考虑,您未设置的其它字段也会显示", "customCss_description": "自定义css内容。注意:内容和远程URL是不允许的属性。内容预览展示如下。出于安全考虑,您未设置的其它字段也会显示",
"contextMenu": "上下文菜单(右键单击)配置", "contextMenu": "上下文菜单(右键单击)配置",
"customCssEnable": "启用自定义 css", "customCssEnable": "启用自定义 css",
"customCssNotice": "警告:虽然预设了一些安全限制(不允许 url() 和 content:),但使用自定义 css 仍然会因更改界面而带来风险", "customCssNotice": "警告:虽然预设了一些安全限制(不允许 URL() 和 content:),但使用自定义 css 仍然会因更改界面而带来风险",
"transcode_description": "可以转码为不同的格式", "transcode_description": "可以转码为不同的格式",
"transcodeBitrate": "转码比特率", "transcodeBitrate": "转码比特率",
"albumBackground": "专辑背景图片", "albumBackground": "专辑背景图片",
@@ -423,14 +451,14 @@
"lastfmApiKey": "{{lastfm}} API 密钥", "lastfmApiKey": "{{lastfm}} API 密钥",
"lastfmApiKey_description": "{{lastfm}} 的 API 密钥。封面艺术图所需", "lastfmApiKey_description": "{{lastfm}} 的 API 密钥。封面艺术图所需",
"discordServeImage": "从服务器提供 {{discord}} 图像", "discordServeImage": "从服务器提供 {{discord}} 图像",
"discordServeImage_description": "从服务器本身分享 {{discord}} rich presence 的封面艺术,仅适用于 Jellyfin 和 Navidrome。 {{discord}} 使用机器人来获取图像,因此您的服务器必须可通过公共互联网访问", "discordServeImage_description": "从服务器本身分享 {{discord}} Rich Presence 的封面艺术,仅适用于 Jellyfin 和 Navidrome。 {{discord}} 使用机器人来获取图像,因此您的服务器必须可通过公共互联网访问",
"musicbrainz": "显示 MusicBrainz 链接", "musicbrainz": "显示 MusicBrainz 链接",
"musicbrainz_description": "在存在 MusicBrainz ID 的艺术家/专辑页面上显示 MusicBrainz 链接", "musicbrainz_description": "在艺术家/专辑页面上显示 MusicBrainz 链接(如果存在 MusicBrainz ID",
"lastfm": "显示 last.fm 链接", "lastfm": "显示 Last.fm 链接",
"lastfm_description": "在艺术家/专辑页面上显示 Last.fm 的链接", "lastfm_description": "在艺术家/专辑页面上显示 Last.fm 的链接",
"preferLocalLyrics_description": "优先选择本地歌词(如有),而不是远程歌词", "preferLocalLyrics_description": "优先选择本地歌词(如有),而不是远程歌词",
"preferLocalLyrics": "首选本地歌词", "preferLocalLyrics": "首选本地歌词",
"discordPausedStatus": "暂停时显示rich presence", "discordPausedStatus": "暂停时显示Rich Presence",
"discordPausedStatus_description": "启用后将在播放器暂停时显示状态", "discordPausedStatus_description": "启用后将在播放器暂停时显示状态",
"preservePitch": "保持音高", "preservePitch": "保持音高",
"preservePitch_description": "在调整播放速度时保持音高", "preservePitch_description": "在调整播放速度时保持音高",
@@ -452,16 +480,16 @@
"releaseChannel_optionLatest": "最新的", "releaseChannel_optionLatest": "最新的",
"releaseChannel_optionBeta": "测试版", "releaseChannel_optionBeta": "测试版",
"releaseChannel": "发布通道", "releaseChannel": "发布通道",
"releaseChannel_description": "选择稳定版本或测试版以进行自动更新", "releaseChannel_description": "选择稳定版测试版或 Alpha(夜间构建版)以启用自动更新",
"mediaSession": "启用媒体会话", "mediaSession": "启用媒体会话",
"mediaSession_description": "启用 Windows 媒体会话集成,在系统音量覆盖和锁定屏幕中显示媒体控件和元数据(仅限 Windows", "mediaSession_description": "启用媒体会话集成,在系统音量叠加层和锁屏界面显示媒体控件和元数据",
"exportImportSettings_control_description": "通过 JSON 导出和导入设置", "exportImportSettings_control_description": "通过 JSON 导出和导入设置",
"exportImportSettings_control_exportText": "导出设置", "exportImportSettings_control_exportText": "导出设置",
"exportImportSettings_control_importText": "导入设置", "exportImportSettings_control_importText": "导入设置",
"exportImportSettings_control_title": "导入/导出设置", "exportImportSettings_control_title": "导入/导出设置",
"exportImportSettings_destructiveWarning": "导入设置会破坏现有设置,请在点击下方“导入”按钮前仔细阅读以上内容!", "exportImportSettings_destructiveWarning": "导入设置会破坏现有设置,请在点击下方“导入”按钮前仔细阅读以上内容!",
"exportImportSettings_importBtn": "导入设置", "exportImportSettings_importBtn": "导入设置",
"exportImportSettings_importModalTitle": "导入 feishin 设置", "exportImportSettings_importModalTitle": "导入 Feishin 设置",
"exportImportSettings_importSuccess": "设置已成功导入!", "exportImportSettings_importSuccess": "设置已成功导入!",
"exportImportSettings_notValidJSON": "传递的文件不是有效的 JSON 文件", "exportImportSettings_notValidJSON": "传递的文件不是有效的 JSON 文件",
"exportImportSettings_offendingKeyError": "\"{{offendingKey}}\" 不正确 - {{reason}}", "exportImportSettings_offendingKeyError": "\"{{offendingKey}}\" 不正确 - {{reason}}",
@@ -482,14 +510,14 @@
"combinedLyricsAndVisualizer": "在播放器侧边栏合并歌词和可视化界面", "combinedLyricsAndVisualizer": "在播放器侧边栏合并歌词和可视化界面",
"autoDJ_description": "自动添加相似歌曲到队列中", "autoDJ_description": "自动添加相似歌曲到队列中",
"notify_description": "歌曲变更时显示通知", "notify_description": "歌曲变更时显示通知",
"mpvExtraParameters_description": "向mpv传递额外参数", "mpvExtraParameters_description": "向MPV传递额外参数",
"audioFadeOnStatusChange": "音频改变时淡入淡出", "audioFadeOnStatusChange": "音频改变时淡入淡出",
"showVisualizerInSidebar": "在播放器侧边栏显示可视化效果", "showVisualizerInSidebar": "在播放器侧边栏显示可视化效果",
"showLyricsInSidebar": "在播放器侧边栏显示歌词", "showLyricsInSidebar": "在播放器侧边栏显示歌词",
"analyticsDisable": "退出使用情况的分析", "analyticsDisable": "退出使用情况的分析",
"artistReleaseTypeConfiguration": "艺术家发行类型设置", "artistReleaseTypeConfiguration": "艺术家发行类型设置",
"useThemeAccentColor": "使用主题强调色", "useThemeAccentColor": "使用主题强调色",
"mpvExtraParameters": "mpv额外参数", "mpvExtraParameters": "MPV额外参数",
"showRatings": "显示星级评分", "showRatings": "显示星级评分",
"followCurrentSong": "跟随当前歌曲", "followCurrentSong": "跟随当前歌曲",
"logLevel": "日志等级", "logLevel": "日志等级",
@@ -515,7 +543,76 @@
"playerbarWaveformAlign": "波形对齐方式", "playerbarWaveformAlign": "波形对齐方式",
"playerbarWaveformBarWidth": "波形宽度", "playerbarWaveformBarWidth": "波形宽度",
"playerbarWaveformGap": "波形间距", "playerbarWaveformGap": "波形间距",
"transcode": "启用转码功能" "transcode": "启用转码功能",
"useThemeAccentColor_description": "使用所选主题中定义的主色,而不是自定义强调色",
"homeFeatureStyle_optionSingle": "单项",
"autoDJ": "自动DJ",
"autoDJ_itemCount": "项目数量",
"autoDJ_itemCount_description": "启用自动 DJ 功能后,尝试添加到队列中的项目数",
"autoDJ_timing": "定时",
"autoDJ_timing_description": "自动 DJ 触发前队列中剩余的歌曲数量",
"crossfadeStyle": "交叉渐变风格",
"discordRichPresence": "{{discord}} Rich Presence",
"homeFeatureStyle_description": "控制首页特色轮播图的样式",
"homeFeatureStyle": "首页特色旋转样式",
"homeFeatureStyle_optionMultiple": "多样",
"hotkey_listNavigateToPage": "列表导航至项目页面",
"hotkey_listPlayDefault": "播放列表",
"hotkey_listPlayLast": "播放列表最后",
"hotkey_listPlayNext": "播放列表下一个",
"hotkey_listPlayNow": "播放列表现在",
"pathReplace": "文件路径替换",
"pathReplace_description": "替换服务器的默认文件路径",
"pathReplace_optionRemovePrefix": "移除前缀",
"pathReplace_optionAddPrefix": "添加前缀",
"playerFilters": "从队列中筛选歌曲",
"playerFilters_description": "根据以下条件,忽略添加到队列中的歌曲",
"artistRadioCount_description": "设置艺术家电台和曲目电台要获取的歌曲数量",
"artistRadioCount": "艺术家/曲目电台数量",
"imageResolution_optionItemCard": "项目卡",
"playerbarWaveformRadius": "波形半径",
"enableGridMultiSelect": "启用网格多选",
"enableGridMultiSelect_description": "启用后,允许在网格视图中选择多个项目。禁用后,点击网格项目图像将跳转到项目页面",
"sidebarPlaylistSorting_description": "允许在侧边栏中使用拖放操作手动对播放列表进行排序,而不是使用默认的服务器顺序",
"sidebarPlaylistSorting": "侧边栏播放列表排序",
"sidebarPlaylistListFilterRegex_description": "隐藏侧边栏中与此正则表达式匹配的播放列表",
"sidebarPlaylistListFilterRegex_placeholder": "例如:^每日精选*",
"sidebarPlaylistListFilterRegex": "播放列表筛选正则表达式",
"queryBuilder": "查询构建器",
"queryBuilderCustomFields": "自定义字段",
"analyticsEnable": "发送基于使用情况的分析",
"analyticsEnable_description": "发送匿名使用数据帮助开发者改进应用程序",
"automaticUpdates": "自动更新",
"automaticUpdates_description": "自动检查并安装更新",
"releaseChannel_optionAlpha": "alpha(每日构建版)",
"discordStateIcon": "显示播放图标",
"discordStateIcon_description": "在 Rich Presence 状态中显示一个小的播放图标。启用“暂停时显示 Rich Presence 在线状态”后,暂停图标始终显示",
"blurExplicitImages": "模糊显式图片",
"blurExplicitImages_description": "专辑和歌曲封面若被标记为不雅内容,将会进行模糊处理",
"autosave": "自动保存播放队列",
"autosave_description": "启用自动将播放队列保存到服务器的功能。此功能仅在使用 Navidrome/Subsonic 时可用,且不能使用混合播放队列。",
"autosaveCount": "自动播放队列保存频率",
"autosaveCount_description": "队列保存前需要更改多少首歌曲?1(最少)表示每次歌曲更改",
"useThemePrimaryShade": "使用主题主色调",
"useThemePrimaryShade_description": "对于主要颜色变体,请使用所选主题中定义的主色调",
"primaryShade": "主色调",
"primaryShade_description": "覆盖按钮、链接和其他主色元素使用的主色调(0-9)",
"playerItemConfiguration_description": "配置全屏播放器上显示的项目及其显示顺序",
"playerItemConfiguration": "播放器项目配置",
"listenbrainz_description": "在艺术家/专辑页面上显示 ListenBrainz 链接",
"listenbrainz": "显示 ListenBrainz 链接",
"qobuz_description": "在艺术家/专辑页面上显示 Qobuz 链接",
"qobuz": "显示 Qobuz 链接",
"spotify_description": "在艺术家/专辑页面上显示 Spotify 链接",
"spotify": "显示 Spotify 链接",
"nativeSpotify_description": "在 Spotify 应用中打开,而不是在浏览器中打开",
"nativeSpotify": "使用 Spotify 应用",
"sidePlayQueueLayout": "侧边播放队列布局",
"sidePlayQueueLayout_description": "设置附加侧边播放队列的布局",
"sidePlayQueueLayout_optionHorizontal": "水平",
"sidePlayQueueLayout_optionVertical": "垂直",
"waveformLoadingDelay": "波形加载延迟",
"waveformLoadingDelay_description": "加载波形前的延迟时间(秒)。如果在使用网页播放器时遇到卡顿现象,请增加此值。"
}, },
"error": { "error": {
"remotePortWarning": "重启服务器使新端口生效", "remotePortWarning": "重启服务器使新端口生效",
@@ -546,7 +643,10 @@
"noNetwork": "服务器不可用", "noNetwork": "服务器不可用",
"noNetworkDescription": "无法连接到该服务器", "noNetworkDescription": "无法连接到该服务器",
"saveQueueFailed": "播放列表保存失败", "saveQueueFailed": "播放列表保存失败",
"settingsSyncError": "渲染器设置与主进程中存在差异,请重启程序以应用更改" "settingsSyncError": "渲染器设置与主进程中存在差异,请重启程序以应用更改",
"invalidJson": "无效的 JSON",
"serverLockSingleServer": "服务器锁定时,只允许一台服务器运行",
"playbackPausedDueToError": "发生错误,播放已暂停"
}, },
"filter": { "filter": {
"mostPlayed": "最多播放过", "mostPlayed": "最多播放过",
@@ -563,18 +663,18 @@
"communityRating": "社区评分", "communityRating": "社区评分",
"path": "路径", "path": "路径",
"favorited": "已收藏", "favorited": "已收藏",
"albumArtist": "$t(entity.albumArtist_one)", "albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"releaseYear": "发布年份", "releaseYear": "发布年份",
"biography": "个人简介", "biography": "个人简介",
"songCount": "歌曲数量", "songCount": "歌曲数量",
"random": "随机", "random": "随机",
"lastPlayed": "上次播放", "lastPlayed": "最后播放",
"toYear": "截止年份", "toYear": "截止年份",
"fromYear": "起始年份", "fromYear": "起始年份",
"criticRating": "评论家评分", "criticRating": "评论家评分",
"trackNumber": "曲目", "trackNumber": "曲目",
"bpm": "bpm", "bpm": "BPM",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"comment": "评论", "comment": "评论",
"isCompilation": "为合辑", "isCompilation": "为合辑",
"isFavorited": "已收藏", "isFavorited": "已收藏",
@@ -582,34 +682,38 @@
"recentlyUpdated": "最近更新", "recentlyUpdated": "最近更新",
"isRated": "已评分", "isRated": "已评分",
"isRecentlyPlayed": "最近播放过", "isRecentlyPlayed": "最近播放过",
"channels": "$t(common.channel_other)", "channels": "$t(common.channel, {\"count\": 2})",
"owner": "$t(common.owner)", "owner": "$t(common.owner)",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"note": "注释", "note": "注释",
"albumCount": "$t(entity.album_other)数", "albumCount": "$t(entity.album, {\"count\": 2})数",
"id": "id", "id": "ID",
"disc": "碟片", "disc": "碟片",
"duration": "时长", "duration": "时长",
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"explicitStatus": "$t(common.explicitStatus)" "explicitStatus": "$t(common.explicitStatus)",
"sortName": "排序名称",
"matchAnd": "和",
"matchOr": "或"
}, },
"page": { "page": {
"sidebar": { "sidebar": {
"nowPlaying": "正在播放", "nowPlaying": "正在播放",
"playlists": "$t(entity.playlist_other)", "playlists": "$t(entity.playlist, {\"count\": 2})",
"search": "$t(common.search)", "search": "$t(common.search)",
"tracks": "$t(entity.track_other)", "tracks": "$t(entity.track, {\"count\": 2})",
"albums": "$t(entity.album_other)", "albums": "$t(entity.album, {\"count\": 2})",
"genres": "$t(entity.genre_other)", "genres": "$t(entity.genre, {\"count\": 2})",
"folders": "$t(entity.folder_other)", "folders": "$t(entity.folder, {\"count\": 2})",
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"home": "$t(common.home)", "home": "$t(common.home)",
"artists": "$t(entity.artist_other)", "artists": "$t(entity.artist, {\"count\": 2})",
"albumArtists": "$t(entity.albumArtist_other)", "albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"shared": "共享$t(entity.playlist_other)", "shared": "共享$t(entity.playlist, {\"count\": 2})",
"myLibrary": "我的媒体库", "myLibrary": "我的媒体库",
"favorites": "$t(entity.favorite_other)", "favorites": "$t(entity.favorite, {\"count\": 2})",
"radio": "$t(entity.radioStation_other)" "radio": "$t(entity.radioStation, {\"count\": 2})",
"collections": "合集"
}, },
"fullscreenPlayer": { "fullscreenPlayer": {
"config": { "config": {
@@ -643,13 +747,14 @@
"openBrowserDevtools": "打开浏览器开发者工具", "openBrowserDevtools": "打开浏览器开发者工具",
"goBack": "返回", "goBack": "返回",
"goForward": "前进", "goForward": "前进",
"settings": "$t(common.setting_other)", "settings": "$t(common.setting, {\"count\": 2})",
"quit": "$t(common.quit)", "quit": "$t(common.quit)",
"privateModeOff": "关闭私人模式", "privateModeOff": "关闭私人模式",
"privateModeOn": "开启私人模式", "privateModeOn": "开启私人模式",
"multipleMusicFolders": "已选择{{count}}个媒体库", "multipleMusicFolders": "已选择{{count}}个媒体库",
"noMusicFolder": "未选择任何音乐库", "noMusicFolder": "未选择任何音乐库",
"selectMusicFolder": "选择媒体库" "selectMusicFolder": "选择媒体库",
"commandPalette": "打开命令面板"
}, },
"home": { "home": {
"mostPlayed": "最多播放", "mostPlayed": "最多播放",
@@ -658,10 +763,10 @@
"recentlyPlayed": "最近播放", "recentlyPlayed": "最近播放",
"title": "$t(common.home)", "title": "$t(common.home)",
"recentlyReleased": "最近发布", "recentlyReleased": "最近发布",
"genres": "$t(entity.genre_other)" "genres": "$t(entity.genre, {\"count\": 2})"
}, },
"albumDetail": { "albumDetail": {
"moreFromArtist": "更多该$t(entity.artist_one)作品", "moreFromArtist": "更多该$t(entity.artist, {\"count\": 1})作品",
"moreFromGeneric": "更多{{item}}作品", "moreFromGeneric": "更多{{item}}作品",
"released": "已发布" "released": "已发布"
}, },
@@ -687,7 +792,8 @@
"discord": "Discord", "discord": "Discord",
"logger": "日志记录器", "logger": "日志记录器",
"queryBuilder": "查询构建器", "queryBuilder": "查询构建器",
"lyricsDisplay": "歌词显示" "lyricsDisplay": "歌词显示",
"playerFilters": "播放筛选器"
}, },
"globalSearch": { "globalSearch": {
"commands": { "commands": {
@@ -720,43 +826,48 @@
"download": "下载", "download": "下载",
"playShuffled": "$t(player.shuffle)", "playShuffled": "$t(player.shuffle)",
"moveToNext": "$t(action.moveToNext)", "moveToNext": "$t(action.moveToNext)",
"goToAlbum": "转到 $t(entity.album_one)", "goToAlbum": "转到 $t(entity.album, {\"count\": 1})",
"goToAlbumArtist": "转到 $t(entity.albumArtist_one)", "goToAlbumArtist": "转到 $t(entity.albumArtist, {\"count\": 1})",
"moveItems": "$t(action.moveItems)", "moveItems": "$t(action.moveItems)",
"goTo": "前往" "goTo": "前往"
}, },
"trackList": { "trackList": {
"title": "$t(entity.track_other)", "title": "$t(entity.track, {\"count\": 2})",
"genreTracks": "\"{{genre}}\" $t(entity.track_other)", "genreTracks": "\"{{genre}}\" $t(entity.track, {\"count\": 2})",
"artistTracks": "{{artist}} 的曲目" "artistTracks": "{{artist}} 的曲目"
}, },
"albumArtistList": { "albumArtistList": {
"title": "$t(entity.albumArtist_other)" "title": "$t(entity.albumArtist, {\"count\": 2})"
}, },
"albumList": { "albumList": {
"title": "$t(entity.album_other)", "title": "$t(entity.album, {\"count\": 2})",
"artistAlbums": "{{artist}}的专辑", "artistAlbums": "{{artist}}的专辑",
"genreAlbums": "\"{{genre}}\" $t(entity.album_other)" "genreAlbums": "\"{{genre}}\" $t(entity.album, {\"count\": 2})"
}, },
"genreList": { "genreList": {
"title": "$t(entity.genre_other)", "title": "$t(entity.genre, {\"count\": 2})",
"showAlbums": "显示$t(entity.genre_one) $t(entity.album_other)", "showAlbums": "显示$t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})",
"showTracks": "显示$t(entity.genre_one) $t(entity.track_other)" "showTracks": "显示$t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})"
}, },
"playlistList": { "playlistList": {
"title": "$t(entity.playlist_other)" "title": "$t(entity.playlist, {\"count\": 2})"
}, },
"albumArtistDetail": { "albumArtistDetail": {
"recentReleases": "最近发布", "recentReleases": "最近发布",
"viewDiscography": "查看唱片目录", "viewDiscography": "查看唱片目录",
"relatedArtists": "相关$t(entity.artist_other)", "relatedArtists": "相关$t(entity.artist, {\"count\": 2})",
"topSongs": "热门歌曲", "topSongs": "热门歌曲",
"topSongsFrom": "{{title}}的热门歌曲", "topSongsFrom": "{{title}}的热门歌曲",
"viewAllTracks": "查看所有$t(entity.track_other)", "viewAllTracks": "查看所有$t(entity.track, {\"count\": 2})",
"about": "关于{{artist}}", "about": "关于{{artist}}",
"appearsOn": "出现在", "appearsOn": "出现在",
"viewAll": "查看全部", "viewAll": "查看全部",
"groupingTypeAll": "所有发行类型" "groupingTypeAll": "所有发行类型",
"groupingTypePrimary": "首选发布类型",
"favoriteSongs": "收藏的歌曲",
"favoriteSongsFrom": "来自 {{title}} 收藏的歌曲",
"topSongsCommunity": "社区",
"topSongsPersonal": "个人"
}, },
"itemDetail": { "itemDetail": {
"copyPath": "将路径复制到剪贴板", "copyPath": "将路径复制到剪贴板",
@@ -775,17 +886,33 @@
"removeServer": "移除服务器" "removeServer": "移除服务器"
}, },
"favorites": { "favorites": {
"title": "$t(entity.favorite_other)" "title": "$t(entity.favorite, {\"count\": 2})"
}, },
"folderList": { "folderList": {
"title": "$t(entity.folder_other)" "title": "$t(entity.folder, {\"count\": 2})"
},
"radioList": {
"title": "广播电台"
},
"windowBar": {
"paused": "(暂停) ",
"privateMode": "(私人模式)"
},
"collections": {
"overrideExisting": "覆盖现有",
"saveAsCollection": "保存为集合"
},
"releasenotes": {
"commitsSinceStable": "自 {{stable}} 以来的提交",
"noNewCommits": "此范围内没有新的提交",
"noStableReleaseToCompare": "目前没有稳定版本可供比较"
} }
}, },
"form": { "form": {
"deletePlaylist": { "deletePlaylist": {
"title": "删除$t(entity.playlist_one)", "title": "删除$t(entity.playlist, {\"count\": 1})",
"success": "$t(entity.playlist_one)已成功删除", "success": "$t(entity.playlist, {\"count\": 1})已成功删除",
"input_confirm": "输入$t(entity.playlist_one)的名称进行确认" "input_confirm": "输入$t(entity.playlist, {\"count\": 1})的名称进行确认"
}, },
"addServer": { "addServer": {
"title": "添加服务器", "title": "添加服务器",
@@ -798,7 +925,7 @@
"ignoreSsl": "忽略 ssl $t(common.restartRequired)", "ignoreSsl": "忽略 ssl $t(common.restartRequired)",
"ignoreCors": "忽略 cors $t(common.restartRequired)", "ignoreCors": "忽略 cors $t(common.restartRequired)",
"error_savePassword": "保存密码时出现错误", "error_savePassword": "保存密码时出现错误",
"input_url": "url", "input_url": "URL",
"input_preferInstantMixDescription": "仅使用即时混音来获取类似的歌曲。如果您有修改此行为的插件,则很有用", "input_preferInstantMixDescription": "仅使用即时混音来获取类似的歌曲。如果您有修改此行为的插件,则很有用",
"input_preferInstantMix": "首选即时混音", "input_preferInstantMix": "首选即时混音",
"input_preferRemoteUrl": "首选公共 URL", "input_preferRemoteUrl": "首选公共 URL",
@@ -807,16 +934,16 @@
}, },
"addToPlaylist": { "addToPlaylist": {
"success": "添加$t(entity.trackWithCount, {\"count\": {{message}} })到$t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })", "success": "添加$t(entity.trackWithCount, {\"count\": {{message}} })到$t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"title": "添加到$t(entity.playlist_one)", "title": "添加到$t(entity.playlist, {\"count\": 1})",
"input_skipDuplicates": "跳过重复", "input_skipDuplicates": "跳过重复",
"input_playlists": "$t(entity.playlist_other)", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "创建 $t(entity.playlist_one) {{playlist}}", "create": "创建 $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "搜索 $t(entity.playlist_other) 或键入以创建一个新的" "searchOrCreate": "搜索 $t(entity.playlist, {\"count\": 2}) 或键入以创建一个新的"
}, },
"createPlaylist": { "createPlaylist": {
"title": "创建$t(entity.playlist_one)", "title": "创建$t(entity.playlist, {\"count\": 1})",
"input_public": "公开", "input_public": "公开",
"success": "已成功创建 $t(entity.playlist_one)", "success": "已成功创建 $t(entity.playlist, {\"count\": 1})",
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"input_owner": "$t(common.owner)" "input_owner": "$t(common.owner)"
@@ -835,15 +962,14 @@
"removeRuleGroup": "移除规则组" "removeRuleGroup": "移除规则组"
}, },
"editPlaylist": { "editPlaylist": {
"title": "编辑$t(entity.playlist_one)", "title": "编辑$t(entity.playlist, {\"count\": 1})",
"publicJellyfinNote": "Jellyfin 出于某种原因不会显示播放列表是否公开。如果您希望保持公开,请选择以下输入", "publicJellyfinNote": "Jellyfin 出于某种原因不会显示播放列表是否公开。如果您希望保持公开,请选择以下输入",
"success": "$t(entity.playlist_one)更新成功", "success": "$t(entity.playlist, {\"count\": 1})更新成功"
"editNote": "不建议对大型播放列表进行手动编辑,你确定接受新播放列表覆盖已有播放列表可能导致的数据丢失风险吗?"
}, },
"lyricSearch": { "lyricSearch": {
"title": "搜索歌词", "title": "搜索歌词",
"input_name": "$t(common.name)", "input_name": "$t(common.name)",
"input_artist": "$t(entity.artist_one)" "input_artist": "$t(entity.artist, {\"count\": 1})"
}, },
"shareItem": { "shareItem": {
"expireInvalid": "过期时间必须是将来的时间", "expireInvalid": "过期时间必须是将来的时间",
@@ -851,7 +977,9 @@
"allowDownloading": "允许下载", "allowDownloading": "允许下载",
"description": "描述", "description": "描述",
"setExpiration": "设置过期时间", "setExpiration": "设置过期时间",
"success": "共享链接已复制到剪贴板(或单击此处打开)" "success": "共享链接已复制到剪贴板(或单击此处打开)",
"copyToClipboard": "复制到剪贴板:Ctrl+CEnter",
"successMustClick": "分享创建成功。点击此处打开"
}, },
"privateMode": { "privateMode": {
"enabled": "启用私人模式,播放状态现在对外部集成隐藏", "enabled": "启用私人模式,播放状态现在对外部集成隐藏",
@@ -865,7 +993,9 @@
"createRadioStation": { "createRadioStation": {
"input_homepageUrl": "首页地址", "input_homepageUrl": "首页地址",
"input_name": "名称", "input_name": "名称",
"input_streamUrl": "串流地址" "input_streamUrl": "串流地址",
"success": "电台创建成功",
"title": "创建广播电台"
}, },
"lyricsExport": { "lyricsExport": {
"export": "导出歌词", "export": "导出歌词",
@@ -877,12 +1007,17 @@
}, },
"shuffleAll": { "shuffleAll": {
"title": "随机播放", "title": "随机播放",
"input_genre": "$t(entity.genre_one)", "input_genre": "$t(entity.genre, {\"count\": 1})",
"input_played_optionAll": "所有曲目", "input_played_optionAll": "所有曲目",
"input_maxYear": "截止年份", "input_maxYear": "截止年份",
"input_minYear": "起始年份", "input_minYear": "起始年份",
"input_played_optionUnplayed": "仅未播放的曲目", "input_played_optionUnplayed": "仅未播放的曲目",
"input_played_optionPlayed": "仅已播放的曲目" "input_played_optionPlayed": "仅已播放的曲目",
"input_limit": "有多少首歌?",
"input_played": "播放筛选器"
},
"editRadioStation": {
"success": "电台更新成功"
} }
}, },
"table": { "table": {
@@ -916,12 +1051,14 @@
"advancedSettings": "高级设置", "advancedSettings": "高级设置",
"autosize": "自动调整大小", "autosize": "自动调整大小",
"horizontalBorders": "行边框", "horizontalBorders": "行边框",
"verticalBorders": "列边框" "verticalBorders": "列边框",
"showHeader": "显示标题"
}, },
"view": { "view": {
"table": "表格", "table": "表格",
"grid": "网格", "grid": "网格",
"list": "列表" "list": "列表",
"detail": "详情"
}, },
"label": { "label": {
"releaseDate": "发布日期", "releaseDate": "发布日期",
@@ -929,34 +1066,37 @@
"duration": "$t(common.duration)", "duration": "$t(common.duration)",
"dateAdded": "添加日期", "dateAdded": "添加日期",
"size": "$t(common.size)", "size": "$t(common.size)",
"bpm": "$t(common.bpm)", "bpm": "$t(common.BPM)",
"lastPlayed": "最后播放", "lastPlayed": "最后播放",
"trackNumber": "音轨编号", "trackNumber": "音轨编号",
"rowIndex": "行索引", "rowIndex": "行索引",
"rating": "$t(common.rating)", "rating": "$t(common.rating)",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"note": "$t(common.note)", "note": "$t(common.note)",
"biography": "$t(common.biography)", "biography": "$t(common.biography)",
"owner": "$t(common.owner)", "owner": "$t(common.owner)",
"path": "$t(common.path)", "path": "$t(common.path)",
"channels": "$t(common.channel_other)", "channels": "$t(common.channel, {\"count\": 2})",
"playCount": "播放次数", "playCount": "播放次数",
"bitrate": "$t(common.bitrate)", "bitrate": "$t(common.bitrate)",
"actions": "$t(common.action_other)", "actions": "$t(common.action, {\"count\": 2})",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"discNumber": "碟片编号", "discNumber": "碟片编号",
"favorite": "$t(common.favorite)", "favorite": "$t(common.favorite)",
"year": "$t(common.year)", "year": "$t(common.year)",
"albumArtist": "$t(entity.albumArtist_one)", "albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"titleCombined": "$t(common.title)(合并)", "titleCombined": "$t(common.title)(合并)",
"codec": "$t(common.codec)", "codec": "$t(common.codec)",
"songCount": "$t(entity.track_other)", "songCount": "$t(entity.track, {\"count\": 2})",
"albumCount": "$t(entity.album_other)", "albumCount": "$t(entity.album, {\"count\": 2})",
"image": "图片", "image": "图片",
"bitDepth": "$t(common.bitDepth)", "bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)", "sampleRate": "$t(common.sampleRate)",
"genreBadge": "$t(entity.genre_one)(徽章)" "genreBadge": "$t(entity.genre, {\"count\": 1})(徽章)",
"composer": "作曲家",
"titleArtist": "$t(common.title) (艺术家)",
"albumGroup": "专辑分组"
} }
}, },
"column": { "column": {
@@ -965,22 +1105,22 @@
"rating": "评分", "rating": "评分",
"favorite": "收藏", "favorite": "收藏",
"playCount": "播放次数", "playCount": "播放次数",
"albumCount": "$t(entity.album_other)", "albumCount": "$t(entity.album, {\"count\": 2})",
"releaseYear": "年份", "releaseYear": "年份",
"lastPlayed": "最后播放", "lastPlayed": "最后播放",
"biography": "简介", "biography": "简介",
"releaseDate": "发布日期", "releaseDate": "发布日期",
"bitrate": "比特率", "bitrate": "比特率",
"title": "标题", "title": "标题",
"bpm": "bpm", "bpm": "BPM",
"dateAdded": "添加日期", "dateAdded": "添加日期",
"artist": "$t(entity.artist_one)", "artist": "$t(entity.artist, {\"count\": 1})",
"songCount": "$t(entity.track_other)", "songCount": "$t(entity.track, {\"count\": 2})",
"trackNumber": "音轨编号", "trackNumber": "音轨编号",
"genre": "$t(entity.genre_one)", "genre": "$t(entity.genre, {\"count\": 1})",
"albumArtist": "专辑艺术家", "albumArtist": "专辑艺术家",
"path": "路径", "path": "路径",
"channels": "$t(common.channel_other)", "channels": "$t(common.channel, {\"count\": 2})",
"discNumber": "碟片", "discNumber": "碟片",
"size": "$t(common.size)", "size": "$t(common.size)",
"codec": "$t(common.codec)", "codec": "$t(common.codec)",
@@ -996,7 +1136,7 @@
}, },
"releaseType": { "releaseType": {
"primary": { "primary": {
"album": "$t(entity.album_one)", "album": "$t(entity.album, {\"count\": 1})",
"broadcast": "播送", "broadcast": "播送",
"ep": "迷你专辑(EP", "ep": "迷你专辑(EP",
"single": "单曲", "single": "单曲",
@@ -1011,7 +1151,10 @@
"mixtape": "混音专辑", "mixtape": "混音专辑",
"remix": "再混音(Remix", "remix": "再混音(Remix",
"soundtrack": "原声带", "soundtrack": "原声带",
"audioDrama": "广播剧" "audioDrama": "广播剧",
"djMix": "DJ混音",
"fieldRecording": "现场录制",
"spokenWord": "访谈"
} }
}, },
"filterOperator": { "filterOperator": {
@@ -1032,7 +1175,8 @@
"notContains": "不包含", "notContains": "不包含",
"startsWith": "以…开头", "startsWith": "以…开头",
"inTheRangeDate": "在(日期)范围内", "inTheRangeDate": "在(日期)范围内",
"notInPlaylist": "不在…中" "notInPlaylist": "不在…中",
"notInTheLast": "不在最后"
}, },
"datetime": { "datetime": {
"minuteShort": "分", "minuteShort": "分",
@@ -1085,6 +1229,112 @@
"builtIn": "内置", "builtIn": "内置",
"colors": "颜色", "colors": "颜色",
"gradient": "渐变", "gradient": "渐变",
"miscellaneousSettings": "杂项设置" "miscellaneousSettings": "杂项设置",
"options": {
"channelLayout": {
"single": "单项",
"dualCombined": "双重组合",
"dualHorizontal": "双水平",
"dualVertical": "双垂直"
},
"mode": {
"0": "[0] 离散频率",
"1": "[1] 1/24倍频程 / 240频段",
"2": "[2] 1/12 倍频程 / 120 频段",
"3": "[3] 1/8倍频程 / 80频段",
"4": "[4] 1/6倍频程 / 60频段",
"5": "[5] 1/4倍频程 / 40频段",
"6": "[6] 1/3倍频程 / 30频段",
"7": "[7] 半倍频程 / 20 频段",
"8": "[8] 全倍频程 / 10 频段",
"10": "[10] 折线图 / 面积图"
},
"colorMode": {
"gradient": "渐变",
"barIndex": "Bar-Index",
"barLevel": "Bar-Level"
},
"gradient": {
"classic": "经典",
"prism": "棱镜",
"rainbow": "彩虹",
"steelblue": "钢蓝色",
"orangered": "橙红色"
},
"frequencyScale": {
"none": "无",
"bark": "树皮鳞片",
"linear": "线性刻度",
"log": "对数刻度",
"mel": "梅尔刻度"
},
"weightingFilter": {
"none": "无",
"a": "A",
"b": "B",
"c": "C",
"d": "D",
"z": "Z"
}
},
"cyclePresets": "循环预设",
"includeAllPresets": "包含所有预设",
"ignoredPresets": "忽略预设",
"selectedPresets": "已选预设",
"randomizeNextPreset": "随机化下一个预设",
"blendTime": "混合时间",
"barSpace": "住间距",
"colorStops": "颜色停止",
"level": "等级",
"colorMode": "颜色模式",
"gradientLeft": "左侧渐变",
"gradientRight": "右侧渐变",
"fft": "FFT",
"fftSize": "FFT 大小",
"smoothing": "平滑",
"frequencyRangeAndScaling": "频率范围和缩放",
"minimumFrequency": "最低频率",
"maximumFrequency": "最大频率",
"frequencyScale": "频率尺度",
"sensitivity": "灵敏度",
"weightingFilter": "加权滤波器",
"minimumDecibels": "最低分贝",
"maximumDecibels": "最大分贝",
"linearAmplitude": "线性振幅",
"linearBoost": "线性增强",
"peakBehavior": "峰值行为",
"showPeaks": "显示峰值",
"fadePeaks": "峰值淡出",
"peakLine": "峰值线条",
"gravity": "重力",
"peakFadeTime": "峰值淡出时间(毫秒)",
"peakHoldTime": "峰值保持时间(毫秒)",
"radialSpectrum": "圆形频谱",
"radial": "径向",
"radialInvert": "径向反转",
"spinSpeed": "旋转速度",
"radius": "半径",
"reflexMirror": "反射镜",
"reflexFit": "反射贴合",
"reflexRatio": "反射比率",
"reflexAlpha": "反射Alpha",
"reflexBrightness": "反射亮度",
"mirror": "镜像",
"lowResolution": "低分辨率",
"splitGradient": "渐变分割",
"showScaleX": "显示比例尺 X",
"noteLabels": "笔记标签",
"showScaleY": "显示比例尺 Y",
"alphaBars": "Alpha 条",
"ansiBands": "ANSI 频段",
"ledBars": "LED 灯条",
"trueLeds": "真正的LED",
"lumiBars": "Lumi 条",
"outlineBars": "轮廓栏",
"roundBars": "圆条"
},
"queryBuilder": {
"standardTags": "标准标签",
"customTags": "自定义标签"
} }
} }
File diff suppressed because it is too large Load Diff
+36 -23
View File
@@ -5,6 +5,10 @@ import { getLyricsBySongId as getGenius, getSearchResults as searchGenius } from
import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib'; import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib';
import { getLyricsBySongId as getNetease, getSearchResults as searchNetease } from './netease'; import { getLyricsBySongId as getNetease, getSearchResults as searchNetease } from './netease';
import { orderSearchResults } from './shared'; import { orderSearchResults } from './shared';
import {
getLyricsBySongId as getSimpMusic,
getSearchResults as searchSimpMusic,
} from './simpmusic';
import { Song } from '/@/shared/types/domain-types'; import { Song } from '/@/shared/types/domain-types';
@@ -12,6 +16,7 @@ export enum LyricSource {
GENIUS = 'Genius', GENIUS = 'Genius',
LRCLIB = 'lrclib.net', LRCLIB = 'lrclib.net',
NETEASE = 'NetEase', NETEASE = 'NetEase',
SIMPMUSIC = 'SimpMusic',
} }
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & { export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
@@ -66,18 +71,44 @@ const SEARCH_FETCHERS: Record<LyricSource, SearchFetcher> = {
[LyricSource.GENIUS]: searchGenius, [LyricSource.GENIUS]: searchGenius,
[LyricSource.LRCLIB]: searchLrcLib, [LyricSource.LRCLIB]: searchLrcLib,
[LyricSource.NETEASE]: searchNetease, [LyricSource.NETEASE]: searchNetease,
[LyricSource.SIMPMUSIC]: searchSimpMusic,
}; };
const GET_FETCHERS: Record<LyricSource, GetFetcher> = { const GET_FETCHERS: Record<LyricSource, GetFetcher> = {
[LyricSource.GENIUS]: getGenius, [LyricSource.GENIUS]: getGenius,
[LyricSource.LRCLIB]: getLrcLib, [LyricSource.LRCLIB]: getLrcLib,
[LyricSource.NETEASE]: getNetease, [LyricSource.NETEASE]: getNetease,
[LyricSource.SIMPMUSIC]: getSimpMusic,
}; };
const MAX_CACHED_ITEMS = 10; const MAX_CACHED_ITEMS = 10;
const lyricCache = new Map<string, CachedLyrics>(); const lyricCache = new Map<string, CachedLyrics>();
const searchAllSources = async (
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[]> => {
const sources = store.get('lyrics', []) as LyricSource[];
const searchPromises = sources.map((source) =>
SEARCH_FETCHERS[source](params).then((searchResults) => ({ searchResults, source })),
);
const settled = await Promise.allSettled(searchPromises);
const allSearchResults: InternetProviderLyricSearchResponse[] = [];
for (const result of settled) {
if (result.status === 'fulfilled' && result.value.searchResults) {
allSearchResults.push(...result.value.searchResults);
} else if (result.status === 'rejected') {
const index = settled.indexOf(result);
console.error(`Error searching ${sources[index]} for lyrics:`, result.reason);
}
}
return allSearchResults;
};
const getRemoteLyrics = async (song: Song) => { const getRemoteLyrics = async (song: Song) => {
const sources = store.get('lyrics', []) as LyricSource[]; const sources = store.get('lyrics', []) as LyricSource[];
@@ -97,18 +128,7 @@ const getRemoteLyrics = async (song: Song) => {
name: song.name, name: song.name,
}; };
const allSearchResults: InternetProviderLyricSearchResponse[] = []; const allSearchResults = await searchAllSources(params);
for (const source of sources) {
try {
const searchResults = await SEARCH_FETCHERS[source](params);
if (searchResults) {
allSearchResults.push(...searchResults);
}
} catch (error) {
console.error(`Error searching ${source} for lyrics:`, error);
}
}
if (allSearchResults.length === 0) { if (allSearchResults.length === 0) {
return null; return null;
@@ -172,24 +192,17 @@ const getRemoteLyrics = async (song: Song) => {
}; };
const searchRemoteLyrics = async (params: LyricSearchQuery) => { const searchRemoteLyrics = async (params: LyricSearchQuery) => {
const sources = store.get('lyrics', []) as LyricSource[]; const allSearchResults = await searchAllSources(params);
const results: Record<LyricSource, InternetProviderLyricSearchResponse[]> = { const results: Record<LyricSource, InternetProviderLyricSearchResponse[]> = {
[LyricSource.GENIUS]: [], [LyricSource.GENIUS]: [],
[LyricSource.LRCLIB]: [], [LyricSource.LRCLIB]: [],
[LyricSource.NETEASE]: [], [LyricSource.NETEASE]: [],
[LyricSource.SIMPMUSIC]: [],
}; };
for (const item of allSearchResults) {
for (const source of sources) { results[item.source].push(item);
const response = await SEARCH_FETCHERS[source](params);
if (response) {
response.forEach((result) => {
results[source].push(result);
});
}
} }
return results; return results;
}; };
+4 -2
View File
@@ -58,14 +58,16 @@ export async function getSearchResults(
): Promise<InternetProviderLyricSearchResponse[] | null> { ): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<LrcLibSearchResponse[]>; let result: AxiosResponse<LrcLibSearchResponse[]>;
if (!params.name) { if (!params.name && !params.artist) {
return null; return null;
} }
const searchQuery = [params.name, params.artist].join(' ');
try { try {
result = await axios.get<LrcLibSearchResponse[]>(SEEARCH_URL, { result = await axios.get<LrcLibSearchResponse[]>(SEEARCH_URL, {
params: { params: {
q: params.name, q: searchQuery,
}, },
}); });
} catch (e) { } catch (e) {
+126
View File
@@ -0,0 +1,126 @@
import axios, { AxiosResponse } from 'axios';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '.';
import { orderSearchResults } from './shared';
const API_URL = 'https://api-lyrics.simpmusic.org/v1';
const TIMEOUT_MS = 5000;
export interface SimpMusicLyric {
albumName?: string;
artistName: string;
durationSeconds?: number;
id: string;
plainLyric?: string;
richSyncLyrics?: string;
songTitle: string;
syncedLyrics?: string;
videoId: string;
vote?: number;
}
export interface SimpMusicSearchResponse {
data: SimpMusicLyric[];
success: boolean;
}
export async function getLyricsBySongId(songId: string): Promise<null | string> {
let result: AxiosResponse;
try {
result = await axios.get(`${API_URL}/${songId}`, {
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic lyrics request errored:', (e as Error)?.message);
return null;
}
const firstLyric = (result.data.data?.[0] ?? null) as null | SimpMusicLyric;
if (!firstLyric) return null;
return firstLyric.syncedLyrics || firstLyric.plainLyric || null;
}
export async function getSearchResults(
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<SimpMusicSearchResponse>;
if (!params.name) return null;
try {
result = await axios.get<SimpMusicSearchResponse>(`${API_URL}/search`, {
params: {
q: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic search errored:', (e as Error)?.message);
return null;
}
if (!result.data?.data) return null;
const songResults: InternetProviderLyricSearchResponse[] = result.data.data.map((song) => ({
artist: song.artistName,
id: song.videoId,
isSync: song.syncedLyrics ? true : false,
name: song.songTitle,
source: LyricSource.SIMPMUSIC,
}));
return orderSearchResults({ params, results: songResults });
}
export async function query(
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
if (!params.name) return null;
let search: AxiosResponse<SimpMusicSearchResponse>;
try {
search = await axios.get<SimpMusicSearchResponse>(`${API_URL}/search`, {
params: {
q: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic search errored:', (e as Error).message);
return null;
}
const first = search.data?.data?.[0];
if (!first) return null;
let lyric: AxiosResponse<SimpMusicLyric>;
try {
lyric = await axios.get<SimpMusicLyric>(`${API_URL}/${first.videoId}`, {
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic lyrics fetch errored:', (e as Error).message);
return null;
}
const lyrics = lyric.data.syncedLyrics || lyric.data.plainLyric || null;
if (!lyrics) return null;
return {
artist: lyric.data.artistName,
id: lyric.data.videoId,
lyrics,
name: lyric.data.songTitle,
source: LyricSource.SIMPMUSIC,
};
}
+45 -6
View File
@@ -1,13 +1,13 @@
import console from 'console'; import console from 'console';
import { app, ipcMain } from 'electron'; import { app, ipcMain } from 'electron';
import { rm } from 'fs/promises'; import { access, rm } from 'fs/promises';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import MpvAPI from 'node-mpv'; import MpvAPI from 'node-mpv';
import { pid } from 'node:process'; import { pid } from 'node:process';
import process from 'process'; import process from 'process';
import { getMainWindow, sendToastToRenderer } from '../../../index'; import { getMainWindow, sendToastToRenderer } from '../../../index';
import { createLog, isWindows } from '../../../utils'; import { createLog, isMacOS, isWindows } from '../../../utils';
import { store } from '../settings'; import { store } from '../settings';
import { PlayerData } from '/@/shared/types/domain-types'; import { PlayerData } from '/@/shared/types/domain-types';
@@ -69,6 +69,7 @@ const mpvLog = (
}; };
const MPV_BINARY_PATH = store.get('mpv_path') as string | undefined; const MPV_BINARY_PATH = store.get('mpv_path') as string | undefined;
const MACOS_MPV_BINARY_PATHS = ['/opt/homebrew/bin/mpv', '/usr/local/bin/mpv'];
const prefetchPlaylistParams = [ const prefetchPlaylistParams = [
'--prefetch-playlist=no', '--prefetch-playlist=no',
@@ -86,12 +87,38 @@ const DEFAULT_MPV_PARAMETERS = (extraParameters?: string[]) => {
return parameters; return parameters;
}; };
const resolveMpvBinaryPath = async (binaryPath?: string) => {
if (binaryPath) {
return binaryPath;
}
if (MPV_BINARY_PATH) {
return MPV_BINARY_PATH;
}
if (!isMacOS()) {
return undefined;
}
for (const candidate of MACOS_MPV_BINARY_PATHS) {
try {
await access(candidate);
return candidate;
} catch {
// Try the next common Homebrew location.
}
}
return undefined;
};
const createMpv = async (data: { const createMpv = async (data: {
binaryPath?: string; binaryPath?: string;
extraParameters?: string[]; extraParameters?: string[];
properties?: Record<string, any>; properties?: Record<string, any>;
}): Promise<MpvAPI> => { }): Promise<MpvAPI> => {
const { binaryPath, extraParameters, properties } = data; const { binaryPath, extraParameters, properties } = data;
const resolvedBinaryPath = await resolveMpvBinaryPath(binaryPath);
const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]); const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]);
@@ -99,7 +126,7 @@ const createMpv = async (data: {
{ {
audio_only: true, audio_only: true,
auto_restart: false, auto_restart: false,
binary: binaryPath || MPV_BINARY_PATH || undefined, binary: resolvedBinaryPath,
socket: socketPath, socket: socketPath,
time_update: 1, time_update: 1,
}, },
@@ -116,11 +143,15 @@ const createMpv = async (data: {
mpv.on('status', (status) => { mpv.on('status', (status) => {
if (status.property === 'playlist-pos') { if (status.property === 'playlist-pos') {
// mpv uses playlist-pos = -1 when nothing is playing (ended, cleared, load failure, etc).
if (status.value === -1) { if (status.value === -1) {
mpv?.pause(); mpv?.pause();
return;
} }
if (status.value !== 0) { // In our 2-item queue model, playlist-pos should normally be 0.
// When mpv auto-advances to the next track it becomes > 0 (typically 1).
if (typeof status.value === 'number' && status.value > 0) {
getMainWindow()?.webContents.send('renderer-player-auto-next'); getMainWindow()?.webContents.send('renderer-player-auto-next');
} }
} }
@@ -433,10 +464,18 @@ ipcMain.on('player-mute', async (_event, mute: boolean) => {
ipcMain.handle('player-get-time', async (): Promise<number | undefined> => { ipcMain.handle('player-get-time', async (): Promise<number | undefined> => {
try { try {
return getMpvInstance()?.getTimePosition(); const mpv = getMpvInstance();
if (!mpv) {
return undefined;
}
return await mpv.getTimePosition();
} catch (err: any | NodeMpvError) { } catch (err: any | NodeMpvError) {
// Err 3: IPC command invalid — e.g. time-pos unavailable when idle / between tracks
if (err?.errcode === 3) {
return undefined;
}
mpvLog({ action: `Failed to get current time` }, err); mpvLog({ action: `Failed to get current time` }, err);
return 0; return undefined;
} }
}); });
+1
View File
@@ -40,6 +40,7 @@ export const store = new Store<any>({
playbackType: 'web', playbackType: 'web',
should_prompt_accessibility: true, should_prompt_accessibility: true,
shown_accessibility_warning: false, shown_accessibility_warning: false,
visualizer_system_audio_consent_granted: false,
window_enable_tray: true, window_enable_tray: true,
window_exit_to_tray: false, window_exit_to_tray: false,
window_minimize_to_tray: false, window_minimize_to_tray: false,
+57
View File
@@ -0,0 +1,57 @@
import { app, ipcMain, Menu } from 'electron';
import { getMainWindow } from '/@/main/index';
import { PlayerStatus } from '/@/shared/types/types';
let currentStatus: PlayerStatus = PlayerStatus.PAUSED;
const updateDockMenu = () => {
if (!app.dock) return;
const isPlaying = currentStatus === PlayerStatus.PLAYING;
const dockMenu = Menu.buildFromTemplate([
{
click: () => {
getMainWindow()?.webContents.send('renderer-player-play-pause');
},
label: isPlaying ? 'Pause' : 'Play',
},
{
type: 'separator',
},
{
click: () => {
getMainWindow()?.webContents.send('renderer-player-next');
},
label: 'Next',
},
{
click: () => {
getMainWindow()?.webContents.send('renderer-player-previous');
},
label: 'Previous',
},
{
type: 'separator',
},
{
click: () => {
getMainWindow()?.webContents.send('renderer-player-stop');
},
label: 'Stop',
},
]);
app.dock.setMenu(dockMenu);
};
ipcMain.on('update-playback', (_event, status: PlayerStatus) => {
currentStatus = status;
updateDockMenu();
});
// Initialize dock menu after app is ready
app.whenReady().then(() => {
updateDockMenu();
});
+1
View File
@@ -0,0 +1 @@
import './dock-menu';
+8 -1
View File
@@ -1,2 +1,9 @@
import './core'; import './core';
import(`./${process.platform}`);
if (process.platform === 'linux') {
import('./linux');
} else if (process.platform === 'darwin') {
import('./darwin');
} else if (process.platform === 'win32') {
import('./win32');
}
+17
View File
@@ -150,6 +150,23 @@ ipcMain.on(
return; return;
} }
// If the served id is an empty string, this is a radio
// Use a limited subset of the fields
if (song._serverId === '') {
// The id as passed in from use-mpris is radio- plus the radio ID
// If there are spaces or some other characters, this causes MPRIS to error and
// disconnect the bus. To prevent this, just use a fake track/radio
mprisPlayer.metadata = {
'mpris:trackid': mprisPlayer.objectPath(`track/radio`),
'xesam:album': song.album || null,
'xesam:artist': song.artists?.length
? song.artists.map((artist) => artist.name)
: null,
'xesam:title': song.name || null,
};
return;
}
mprisPlayer.metadata = { mprisPlayer.metadata = {
'mpris:artUrl': imageUrl || null, 'mpris:artUrl': imageUrl || null,
'mpris:length': song.duration ? Math.round((song.duration || 0) * 1e3) : null, 'mpris:length': song.duration ? Math.round((song.duration || 0) * 1e3) : null,

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