Compare commits

...

13 Commits

Author SHA1 Message Date
Hosted Weblate e49e488b4c Translated using Weblate
Currently translated at 100.0% (1208 of 1208 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-05-08 09:59:37 +02:00
Felix Prillwitz 5d4547080d Always use remote url for discord rpc image (#2009)
* add option to force remote url for api calls

* force remote url for discord rpc image
2026-05-07 21:13:34 -07:00
jeffvli cc8910cfd6 add artistrating smart playlist field (#2015) 2026-05-07 21:08:57 -07:00
jeffvli 9fb241dca2 remove server selector from collapsed sidebar 2026-05-07 20:54:42 -07:00
Maximilian Leith e3a0879301 fix: only show Jellyfin audio playlist (#2000) 2026-05-07 20:48:43 -07:00
Norman 953494e9d0 Fixed bad smart playlist field s (#2011) 2026-05-07 20:46:48 -07:00
jeffvli f190626c8c fix playcounts on jellyfin album detail songs (#2005) 2026-05-07 20:42:51 -07:00
Hosted Weblate 324936e0c8 Translated using Weblate
Currently translated at 100.0% (1208 of 1208 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% (1208 of 1208 strings) (Catalan)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/

Co-authored-by: Ondo <SparkyOndo@proton.me>
Co-authored-by: York <goog10216922@gmail.com>
2026-05-05 05:09:50 +00:00
Hosted Weblate 868ec15b16 Added translation using Weblate (Thai)
Translated using Weblate

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

Translated using Weblate

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

Translated using Weblate

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

Co-authored-by: Anucha Hlownonkor <anucha.initdz@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: skajmer <skajmer@protonmail.com>
2026-05-03 12:06:39 +02:00
Hosted Weblate 775c4e68fa Translated using Weblate
Currently translated at 81.3% (981 of 1206 strings) (Russian)
Translation: feishin/Translation
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ru/

Translated using Weblate

Currently translated at 100.0% (1206 of 1206 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 85.6% (1033 of 1206 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: York <goog10216922@gmail.com>
Co-authored-by: spoon <monkeyfart907@gmail.com>
2026-05-02 04:34:18 +00:00
jeffvli 34e0c4bd4a prevent first lyric line highlight before timestamp (#1965) 2026-05-01 21:33:40 -07:00
jeffvli 323130a877 add toggle for app-suspension for powersave block (#1992) 2026-05-01 21:24:45 -07:00
jeffvli 3b2aab74ac enforce web player seek by seconds when less than 1 (#1993) 2026-05-01 21:12:20 -07:00
26 changed files with 253 additions and 117 deletions
+5 -2
View File
@@ -409,7 +409,8 @@
"input_skipDuplicates": "salta't els duplicats", "input_skipDuplicates": "salta't els duplicats",
"success": "s'ha afegit $t(entity.trackWithCount, {\"count\": {{message}} }) a $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })", "success": "s'ha afegit $t(entity.trackWithCount, {\"count\": {{message}} }) a $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"create": "crea $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "crea $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "cerca $t(entity.playlist, {\"count\": 2}) o escriu per crear-ne una de nova" "searchOrCreate": "cerca $t(entity.playlist, {\"count\": 2}) o escriu per crear-ne una de nova",
"noneAdded": "no s'han afegit pistes a la $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
}, },
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
@@ -935,7 +936,9 @@
"sidePlayQueueLayout_optionHorizontal": "horitzontal", "sidePlayQueueLayout_optionHorizontal": "horitzontal",
"sidePlayQueueLayout_optionVertical": "vertical", "sidePlayQueueLayout_optionVertical": "vertical",
"waveformLoadingDelay": "demora de càrrega de la forma d'ona", "waveformLoadingDelay": "demora de càrrega de la forma d'ona",
"waveformLoadingDelay_description": "demora en segons abans de carregar la forma d'ona. incrementeu aquest valor si patiu interrupcions en fer servir el reproductor web." "waveformLoadingDelay_description": "demora en segons abans de carregar la forma d'ona. incrementeu aquest valor si patiu interrupcions en fer servir el reproductor web.",
"preventSuspendOnPlayback_description": "evita que l'aplicació quedi suspesa mentre es reprodueix música",
"preventSuspendOnPlayback": "evita la suspensió durant la reproducció"
}, },
"table": { "table": {
"column": { "column": {
+5 -2
View File
@@ -427,7 +427,9 @@
"waveformLoadingDelay": "zpoždění načítání vlnové křivky", "waveformLoadingDelay": "zpoždění načítání vlnové křivky",
"waveformLoadingDelay_description": "zpoždění v sekundách před načtením vlnové křivky. zvyšte, pokud jste během používání webového přehrávače zaznamenali záseky.", "waveformLoadingDelay_description": "zpoždění v sekundách před načtením vlnové křivky. zvyšte, pokud jste během používání webového přehrávače zaznamenali záseky.",
"playerbarWaveformStretch": "natáhnutí vlnové křivky", "playerbarWaveformStretch": "natáhnutí vlnové křivky",
"playerbarWaveformStretch_description": "natáhně vlnovou křivku tak, aby vyplnila dostupný prostor" "playerbarWaveformStretch_description": "natáhně vlnovou křivku tak, aby vyplnila dostupný prostor",
"preventSuspendOnPlayback_description": "zabránit aplikaci v uspání během přehrávání hudby",
"preventSuspendOnPlayback": "zabránit uspání při přehrávání"
}, },
"action": { "action": {
"editPlaylist": "upravit $t(entity.playlist, {\"count\": 1})", "editPlaylist": "upravit $t(entity.playlist, {\"count\": 1})",
@@ -1045,7 +1047,8 @@
"input_skipDuplicates": "přeskočit duplicity", "input_skipDuplicates": "přeskočit duplicity",
"input_playlists": "$t(entity.playlist, {\"count\": 2})", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "vytvořit $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "vytvořit $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "vyhledejte $t(entity.playlist, {\"count\": 2}) nebo pište pro vytvoření nového" "searchOrCreate": "vyhledejte $t(entity.playlist, {\"count\": 2}) nebo pište pro vytvoření nového",
"noneAdded": "do $t(entity.playlist, {\"count\": 1}) '{{playlist}}' nebyly přidány žádné skladby"
}, },
"updateServer": { "updateServer": {
"title": "upravit server", "title": "upravit server",
+2
View File
@@ -1008,6 +1008,8 @@
"audioFadeOnStatusChange_description": "enables fade out and fade in when play/pause status changes", "audioFadeOnStatusChange_description": "enables fade out and fade in when play/pause status changes",
"preventSleepOnPlayback_description": "prevent the display from sleeping while music is playing", "preventSleepOnPlayback_description": "prevent the display from sleeping while music is playing",
"preventSleepOnPlayback": "prevent sleep on playback", "preventSleepOnPlayback": "prevent sleep on playback",
"preventSuspendOnPlayback_description": "prevent the application from suspending while music is playing",
"preventSuspendOnPlayback": "prevent suspend on playback",
"remotePassword_description": "sets the password for the remote control server. These credentials are by default transferred insecurely, so you should use a unique password that you do not care about", "remotePassword_description": "sets the password for the remote control server. These credentials are by default transferred insecurely, so you should use a unique password that you do not care about",
"remotePassword": "remote control server password", "remotePassword": "remote control server password",
"remotePort_description": "sets the port for the remote control server", "remotePort_description": "sets the port for the remote control server",
+5 -2
View File
@@ -427,7 +427,9 @@
"waveformLoadingDelay": "Retraso de carga de la forma de onda", "waveformLoadingDelay": "Retraso de carga de la forma de onda",
"waveformLoadingDelay_description": "Retraso en segundos antes de cargar la forma de onda. Incrementa este valor si estás experimentando tartamudeos al usar el reproductor web.", "waveformLoadingDelay_description": "Retraso en segundos antes de cargar la forma de onda. Incrementa este valor si estás experimentando tartamudeos al usar el reproductor web.",
"playerbarWaveformStretch": "Estiramiento de la forma de onda", "playerbarWaveformStretch": "Estiramiento de la forma de onda",
"playerbarWaveformStretch_description": "Estira la forma de onda para rellenar el espacio disponible" "playerbarWaveformStretch_description": "Estira la forma de onda para rellenar el espacio disponible",
"preventSuspendOnPlayback": "Evitar la suspensión durante la reproducción",
"preventSuspendOnPlayback_description": "Evita que la aplicación se suspenda mientras se reproduce música"
}, },
"action": { "action": {
"editPlaylist": "editar $t(entity.playlist, {\"count\": 1})", "editPlaylist": "editar $t(entity.playlist, {\"count\": 1})",
@@ -936,7 +938,8 @@
"input_skipDuplicates": "saltar duplicados", "input_skipDuplicates": "saltar duplicados",
"input_playlists": "$t(entity.playlist, {\"count\": 2})", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "Crear $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "Crear $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "Buscar $t(entity.playlist, {\"count\": 2}) o escribir para crear uno nuevo" "searchOrCreate": "Buscar $t(entity.playlist, {\"count\": 2}) o escribir para crear uno nuevo",
"noneAdded": "Ninguna pista fue añadida a $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
}, },
"updateServer": { "updateServer": {
"title": "actualizar servidor", "title": "actualizar servidor",
+50 -9
View File
@@ -157,7 +157,8 @@
"doNotShowAgain": "ez erakutsi hau berriro", "doNotShowAgain": "ez erakutsi hau berriro",
"numberOfResults": "{{numberOfResults}} emaitza", "numberOfResults": "{{numberOfResults}} emaitza",
"rename": "berrizendatu", "rename": "berrizendatu",
"newVersionAvailable": "bertsio berri bat eskuragarri dago" "newVersionAvailable": "bertsio berri bat eskuragarri dago",
"gridRows": "sareta-errenkadak"
}, },
"player": { "player": {
"repeat": "errepikatu", "repeat": "errepikatu",
@@ -409,7 +410,8 @@
"fromYear": "urtetik aurrera", "fromYear": "urtetik aurrera",
"explicitStatus": "$t(common.explicitStatus)", "explicitStatus": "$t(common.explicitStatus)",
"matchAnd": "eta", "matchAnd": "eta",
"matchOr": "edo" "matchOr": "edo",
"sortName": "izenaren arabera ordenatu"
}, },
"setting": { "setting": {
"hotkey_playbackPause": "pausatu", "hotkey_playbackPause": "pausatu",
@@ -643,7 +645,7 @@
"passwordStore_description": "zein pasahitz/sekretu biltegi erabili. aldatu hau pasahitzak gordetzeko arazoak badituzu", "passwordStore_description": "zein pasahitz/sekretu biltegi erabili. aldatu hau pasahitzak gordetzeko arazoak badituzu",
"playerFilters": "Iragazi ilarako abestiak", "playerFilters": "Iragazi ilarako abestiak",
"sidePlayQueueStyle_description": "alboko erreprodukzio-ilararen estiloa ezartzen du", "sidePlayQueueStyle_description": "alboko erreprodukzio-ilararen estiloa ezartzen du",
"mediaSession_description": "Windows Media Session integrazioa gaitzen du, multimedia kontrolak eta metadatuak sistemaren bolumenaren gainjartzean eta blokeo pantailan bistaratuz (Windows bakarrik)", "mediaSession_description": "Media Session integrazioa gaitzen du, multimedia kontrolak eta metadatuak sistemaren bolumenaren gainjartzean eta blokeo pantailan bistaratuz",
"sidePlayQueueStyle": "alboko erreprodukzio-ilarako estiloa", "sidePlayQueueStyle": "alboko erreprodukzio-ilarako estiloa",
"skipPlaylistPage": "saltatu erreprodukzio-zerrenda orria", "skipPlaylistPage": "saltatu erreprodukzio-zerrenda orria",
"startMinimized_description": "abiarazi aplikazioa sistemaren erretiluan", "startMinimized_description": "abiarazi aplikazioa sistemaren erretiluan",
@@ -714,7 +716,19 @@
"preservePitch": "mantendu tonua", "preservePitch": "mantendu tonua",
"preventSleepOnPlayback": "erreprodukzioan loa saihestu", "preventSleepOnPlayback": "erreprodukzioan loa saihestu",
"replayGainClipping_description": "Saihestu {{ReplayGain}}-k eragindako mozketa irabazpena automatikoki jaitsiz", "replayGainClipping_description": "Saihestu {{ReplayGain}}-k eragindako mozketa irabazpena automatikoki jaitsiz",
"replayGainMode_description": "doitu bolumenaren irabazia fitxategiaren metadatuetan gordetako {{ReplayGain}} balioen arabera" "replayGainMode_description": "doitu bolumenaren irabazia fitxategiaren metadatuetan gordetako {{ReplayGain}} balioen arabera",
"listenbrainz": "erakutsi ListenBrainz estekak",
"sidebarPlaylistSorting": "alboko barrako erreprodukzio-zerrenda ordenatzea",
"sidebarPlaylistListFilterRegex_description": "ezkutatu alboko barran adierazpen erregular honekin bat datozen erreprodukzio-zerrendak",
"sidebarPlaylistListFilterRegex_placeholder": "adib. ^Eguneroko Nahasketa.*",
"sidebarPlaylistListFilterRegex": "erreprodukzio-zerrenda iragazteko adierazpen erregularra",
"sidePlayQueueLayout": "alboko erreprodukzio-ilararen diseinua",
"sidePlayQueueLayout_description": "erantsitako alboko erreprodukzio-ilararen diseinua ezartzen du",
"sidePlayQueueLayout_optionHorizontal": "horizontala",
"sidePlayQueueLayout_optionVertical": "bertikala",
"skipDuration_description": "erreproduzitzailearen barran saltatzeko botoiak erabiltzean saltatzeko iraupena ezartzen du",
"skipPlaylistPage_description": "erreprodukzio-zerrenda batera nabigatzean, joan erreprodukzio-zerrendako abestien zerrendara lehenetsitako orrialdera joan beharrean",
"translationApiKey_description": "itzulpenerako api gakoa (zerbitzu globalaren amaiera-puntua soilik)"
}, },
"form": { "form": {
"addServer": { "addServer": {
@@ -731,7 +745,9 @@
"success": "zerbitzaria behar bezala gehitu da", "success": "zerbitzaria behar bezala gehitu da",
"input_preferInstantMix": "nahiago izan berehalako nahasketa", "input_preferInstantMix": "nahiago izan berehalako nahasketa",
"input_preferInstantMixDescription": "erabili berehalako nahasketa soilik antzeko abestiak lortzeko. erabilgarria portaera hau aldatzen duten pluginak badituzu", "input_preferInstantMixDescription": "erabili berehalako nahasketa soilik antzeko abestiak lortzeko. erabilgarria portaera hau aldatzen duten pluginak badituzu",
"input_remoteUrl": "URL publikoa" "input_remoteUrl": "URL publikoa",
"input_preferRemoteUrl": "url publikoa nahiago izan",
"input_remoteUrlPlaceholder": "aukerakoa: kanpoko funtzioetarako url publikoa"
}, },
"addToPlaylist": { "addToPlaylist": {
"input_playlists": "$t(entity.playlist, {\"count\": 2})", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
@@ -739,7 +755,8 @@
"input_skipDuplicates": "saltatu bikoiztuak", "input_skipDuplicates": "saltatu bikoiztuak",
"title": "gehitu $t(entity.playlist, {\"count\": 1})-(a)ri", "title": "gehitu $t(entity.playlist, {\"count\": 1})-(a)ri",
"create": "sortu $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "sortu $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "bilatu $t(entity.playlist, {\"count\": 2}) edo idatzi berri bat sortzeko" "searchOrCreate": "bilatu $t(entity.playlist, {\"count\": 2}) edo idatzi berri bat sortzeko",
"noneAdded": "ez da pistarik gehitu honi $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
}, },
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
@@ -760,7 +777,9 @@
"success": "partekatzeko esteka arbelera kopiatu da (edo egin klik hemen irekitzeko)", "success": "partekatzeko esteka arbelera kopiatu da (edo egin klik hemen irekitzeko)",
"expireInvalid": "iraungitze-data etorkizunean izan behar da", "expireInvalid": "iraungitze-data etorkizunean izan behar da",
"allowDownloading": "baimendu deskargatzea", "allowDownloading": "baimendu deskargatzea",
"createFailed": "partekatzea sortzeak huts egin du (partekatzea gaituta al dago?)" "createFailed": "partekatzea sortzeak huts egin du (partekatzea gaituta al dago?)",
"copyToClipboard": "Kopiatu arbelean: Ctrl+C, Enter",
"successMustClick": "partekatzea behar bezala sortu da. egin klik hemen irekitzeko"
}, },
"deletePlaylist": { "deletePlaylist": {
"success": "$t(entity.playlist, {\"count\": 1}) behar bezala ezabatu da", "success": "$t(entity.playlist, {\"count\": 1}) behar bezala ezabatu da",
@@ -988,7 +1007,10 @@
"recentReleases": "azken argitalpenak", "recentReleases": "azken argitalpenak",
"viewDiscography": "ikusi diskografia", "viewDiscography": "ikusi diskografia",
"groupingTypeAll": "argitalpen mota guztiak", "groupingTypeAll": "argitalpen mota guztiak",
"groupingTypePrimary": "argitalpen mota nagusiak" "groupingTypePrimary": "argitalpen mota nagusiak",
"favoriteSongs": "abesti gogokoenak",
"topSongsCommunity": "komunitatea",
"topSongsPersonal": "pertsonala"
}, },
"itemDetail": { "itemDetail": {
"copyPath": "kopiatu bidea arbelean", "copyPath": "kopiatu bidea arbelean",
@@ -1006,6 +1028,13 @@
}, },
"radioList": { "radioList": {
"title": "irrati-kateak" "title": "irrati-kateak"
},
"releasenotes": {
"noStableReleaseToCompare": "ez dago bertsio egonkorrik alderatzeko"
},
"windowBar": {
"paused": "(Pausatuta) ",
"privateMode": "(Modu pribatua)"
} }
}, },
"releaseType": { "releaseType": {
@@ -1038,7 +1067,19 @@
"notContains": "ez dauka", "notContains": "ez dauka",
"startsWith": "honekin hasten da", "startsWith": "honekin hasten da",
"endsWith": "honekin amaitzen da", "endsWith": "honekin amaitzen da",
"isNot": "ez da" "isNot": "ez da",
"afterDate": "(data) ondoren da",
"after": "ondoren da",
"before": "lehenago da",
"beforeDate": "(data) baino lehenagokoa da",
"inTheLast": "azkenengoan dago",
"inPlaylist": "bertan dago",
"inTheRange": "tartean dago",
"inTheRangeDate": "(data) tartean dago",
"isGreaterThan": "hau baino handiagoa da",
"isLessThan": "hau baino gutxiago da",
"notInPlaylist": "ez dago barruan",
"notInTheLast": "ez dago azkenengoan"
}, },
"visualizer": { "visualizer": {
"general": "Orokorra", "general": "Orokorra",
+14 -6
View File
@@ -85,7 +85,7 @@
"hotkey_zoomIn": "拡大", "hotkey_zoomIn": "拡大",
"scrobble_description": "再生した音楽をメディアサーバーから Scrobble します", "scrobble_description": "再生した音楽をメディアサーバーから Scrobble します",
"hotkey_browserForward": "ブラウザ 進む", "hotkey_browserForward": "ブラウザ 進む",
"audioExclusiveMode_description": "排他出力モードを有効にします。このモードでは、システムの他の出力がロックされ、MPV のみがオーディオを出力できるようになります", "audioExclusiveMode_description": "排他出力モードを有効にします。このモードでは、システムの他の出力がロックされ、MPV のみがオーディオを出力できるようになります。このモードが有効になっている間は、ビジュアライザーやシステムオーディオのキャプチャ機能は動作しません",
"discordUpdateInterval": "{{discord}} Rich Presence の更新間隔", "discordUpdateInterval": "{{discord}} Rich Presence の更新間隔",
"themeLight": "テーマ (ライト)", "themeLight": "テーマ (ライト)",
"fontType_optionBuiltIn": "組み込みフォント", "fontType_optionBuiltIn": "組み込みフォント",
@@ -425,7 +425,11 @@
"sidePlayQueueLayout_optionHorizontal": "水平", "sidePlayQueueLayout_optionHorizontal": "水平",
"sidePlayQueueLayout_optionVertical": "垂直", "sidePlayQueueLayout_optionVertical": "垂直",
"waveformLoadingDelay": "波形読み込みの遅延", "waveformLoadingDelay": "波形読み込みの遅延",
"waveformLoadingDelay_description": "波形を読み込むまでの遅延時間(秒単位)を設定します。Web プレーヤー使用時にカクつきが発生する場合は、この値を増やしてください。" "waveformLoadingDelay_description": "波形を読み込むまでの遅延時間(秒単位)を設定します。Web プレーヤー使用時にカクつきが発生する場合は、この値を増やしてください。",
"playerbarWaveformStretch": "波形伸縮",
"playerbarWaveformStretch_description": "波形を伸縮させて、利用可能なスペースを埋めます",
"preventSuspendOnPlayback_description": "音楽再生中にアプリケーションが停止しないようにします",
"preventSuspendOnPlayback": "再生の中断を防止する"
}, },
"action": { "action": {
"editPlaylist": "$t(entity.playlist, {\"count\": 1}) を編集", "editPlaylist": "$t(entity.playlist, {\"count\": 1}) を編集",
@@ -808,7 +812,7 @@
"dynamicBackground": "ダイナミック背景", "dynamicBackground": "ダイナミック背景",
"synchronized": "同期", "synchronized": "同期",
"followCurrentLyric": "歌詞を再生位置に追従", "followCurrentLyric": "歌詞を再生位置に追従",
"opacity": "非透過率", "opacity": "不透明度",
"lyricSize": "歌詞のサイズ", "lyricSize": "歌詞のサイズ",
"showLyricProvider": "歌詞の提供元を表示", "showLyricProvider": "歌詞の提供元を表示",
"unsynchronized": "非同期", "unsynchronized": "非同期",
@@ -817,7 +821,9 @@
"lyricGap": "歌詞の間隔", "lyricGap": "歌詞の間隔",
"dynamicImageBlur": "画像のぼかしサイズ", "dynamicImageBlur": "画像のぼかしサイズ",
"dynamicIsImage": "背景画像を有効にする", "dynamicIsImage": "背景画像を有効にする",
"lyricOffset": "歌詞のオフセット (ms)" "lyricOffset": "歌詞のオフセット (ms)",
"lyricOpacityNonActive": "非アクティブな歌詞の不透明度",
"lyricScaleNonActive": "非アクティブな歌詞のスケール"
}, },
"upNext": "次へ", "upNext": "次へ",
"lyrics": "歌詞", "lyrics": "歌詞",
@@ -1033,7 +1039,8 @@
"input_skipDuplicates": "重複をスキップ", "input_skipDuplicates": "重複をスキップ",
"input_playlists": "$t(entity.playlist, {\"count\": 2})", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "$t(entity.playlist, {\"count\": 1}) {{playlist}} を作成する", "create": "$t(entity.playlist, {\"count\": 1}) {{playlist}} を作成する",
"searchOrCreate": "$t(entity.playlist, {\"count\": 2}) を検索するか、入力して新しいプレイリストを作成してください" "searchOrCreate": "$t(entity.playlist, {\"count\": 2}) を検索するか、入力して新しいプレイリストを作成してください",
"noneAdded": "$t(entity.playlist, {\"count\": 1}) {{playlist}} にトラックが追加されませんでした"
}, },
"updateServer": { "updateServer": {
"title": "サーバーをアップデート", "title": "サーバーをアップデート",
@@ -1341,6 +1348,7 @@
"systemAudioConsentDecline": "拒否", "systemAudioConsentDecline": "拒否",
"systemAudioConsentTitle": "システムオーディオへのアクセスを許可しますか?", "systemAudioConsentTitle": "システムオーディオへのアクセスを許可しますか?",
"systemAudioCaptureFailed": "キャプチャを開始できませんでした: {{message}}", "systemAudioCaptureFailed": "キャプチャを開始できませんでした: {{message}}",
"systemAudioNoAudioTrack": "音声トラックが返されませんでした。プロンプトが表示されたら、音声キャプチャが有効になっていることを確認してください。" "systemAudioNoAudioTrack": "音声トラックが返されませんでした。プロンプトが表示されたら、音声キャプチャが有効になっていることを確認してください。",
"systemAudioExclusiveModeNotSupported": "オーディオ排他モードが有効になっている間は、ビジュアライザーは利用できません。MPV の設定でオーディオ排他モードを無効にしてから、もう一度お試しください。"
} }
} }
+7 -2
View File
@@ -352,7 +352,8 @@
"input_skipDuplicates": "pomiń duplikaty", "input_skipDuplicates": "pomiń duplikaty",
"input_playlists": "$t(entity.playlist, {\"count\": 2})", "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "utwórz $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "utwórz $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "wyszukaj $t(entity.playlist, {\"count\": 2}) lub wpisz, aby utworzyć nową" "searchOrCreate": "wyszukaj $t(entity.playlist, {\"count\": 2}) lub wpisz, aby utworzyć nową",
"noneAdded": "nie dodano utworów do $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
}, },
"updateServer": { "updateServer": {
"title": "uaktualnij serwer", "title": "uaktualnij serwer",
@@ -1066,7 +1067,11 @@
"sidePlayQueueLayout_optionHorizontal": "poziomy", "sidePlayQueueLayout_optionHorizontal": "poziomy",
"sidePlayQueueLayout_optionVertical": "pionowy", "sidePlayQueueLayout_optionVertical": "pionowy",
"waveformLoadingDelay": "opóźnienie załadowania fali", "waveformLoadingDelay": "opóźnienie załadowania fali",
"waveformLoadingDelay_description": "opóźnienie w sekundach przed załadowaniem fali. zwiększ tą wartość jeżeli doświadczasz zawieszania się odtwarzacza przeglądarkowego." "waveformLoadingDelay_description": "opóźnienie w sekundach przed załadowaniem fali. zwiększ tą wartość jeżeli doświadczasz zawieszania się odtwarzacza przeglądarkowego.",
"playerbarWaveformStretch": "rozciąganie przebiegu",
"playerbarWaveformStretch_description": "rozciąga przebieg aby wypełnić dostępną przestrzeń",
"preventSuspendOnPlayback_description": "powstrzymuj wstrzymanie podczas odtwarzania muzyki",
"preventSuspendOnPlayback": "powstrzymuje wstrzymanie przy odtwarzaniu"
}, },
"table": { "table": {
"config": { "config": {
+37 -10
View File
@@ -170,7 +170,8 @@
"explicit": "нецензурная лексика", "explicit": "нецензурная лексика",
"externalLinks": "внешние ссылки", "externalLinks": "внешние ссылки",
"explicitStatus": "признак нецензурного контента", "explicitStatus": "признак нецензурного контента",
"newVersionAvailable": "доступна новая версия" "newVersionAvailable": "доступна новая версия",
"numberOfResults": "{{numberOfResults}} результатов"
}, },
"entity": { "entity": {
"album_one": "альбом", "album_one": "альбом",
@@ -438,7 +439,17 @@
"home": "$t(common.home)", "home": "$t(common.home)",
"myLibrary": "Моя библиотека", "myLibrary": "Моя библиотека",
"shared": "Публичные плейлисты $t(entity.playlist, {\"count\": 2})", "shared": "Публичные плейлисты $t(entity.playlist, {\"count\": 2})",
"collections": "коллекции" "collections": "коллекции",
"albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"albums": "$t(entity.album, {\"count\": 2})",
"artists": "$t(entity.artist, {\"count\": 2})",
"favorites": "$t(entity.favorite, {\"count\": 2})",
"folders": "$t(entity.folder, {\"count\": 2})",
"genres": "$t(entity.genre, {\"count\": 2})",
"radio": "$t(entity.radioStation, {\"count\": 2})",
"playlists": "$t(entity.playlist, {\"count\": 2})",
"settings": "$t(common.setting, {\"count\": 2})",
"tracks": "$t(entity.track, {\"count\": 2})"
}, },
"fullscreenPlayer": { "fullscreenPlayer": {
"config": { "config": {
@@ -455,7 +466,9 @@
"useImageAspectRatio": "использовать соотношение сторон изображения", "useImageAspectRatio": "использовать соотношение сторон изображения",
"lyricGap": "пробел между словами", "lyricGap": "пробел между словами",
"dynamicIsImage": "включить фоновое изображение", "dynamicIsImage": "включить фоновое изображение",
"dynamicImageBlur": "сила размытия изображения" "dynamicImageBlur": "сила размытия изображения",
"lyricOpacityNonActive": "непрозрачность неактивных слов",
"lyricScaleNonActive": "размер неактивных слов"
}, },
"upNext": "играет", "upNext": "играет",
"lyrics": "слова", "lyrics": "слова",
@@ -478,7 +491,8 @@
"selectMusicFolder": "выбрать папку с музыкой", "selectMusicFolder": "выбрать папку с музыкой",
"noMusicFolder": "папка с музыкой не выбрана", "noMusicFolder": "папка с музыкой не выбрана",
"multipleMusicFolders": "{{count}} выбрано музыкальных папок", "multipleMusicFolders": "{{count}} выбрано музыкальных папок",
"commandPalette": "открыть командную строку" "commandPalette": "открыть командную строку",
"settings": "$t(common.setting, {\"count\": 2})"
}, },
"manageServers": { "manageServers": {
"title": "сервера", "title": "сервера",
@@ -522,7 +536,8 @@
"title": "$t(common.home)", "title": "$t(common.home)",
"explore": "откройте новое", "explore": "откройте новое",
"recentlyPlayed": "игралось недавно", "recentlyPlayed": "игралось недавно",
"recentlyReleased": "Новинки" "recentlyReleased": "Новинки",
"genres": "$t(entity.genre, {\"count\": 2})"
}, },
"albumDetail": { "albumDetail": {
"moreFromArtist": "больше от $t(entity.artist, {\"count\": 1})", "moreFromArtist": "больше от $t(entity.artist, {\"count\": 1})",
@@ -556,10 +571,13 @@
}, },
"genreList": { "genreList": {
"showAlbums": "показать $t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})", "showAlbums": "показать $t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})",
"showTracks": "показать $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})" "showTracks": "показать $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})",
"title": "$t(entity.genre, {\"count\": 2})"
}, },
"trackList": { "trackList": {
"artistTracks": "Треки {{artist}}" "artistTracks": "Треки {{artist}}",
"genreTracks": "\"{{genre}}\" $t(entity.track, {\"count\": 2})",
"title": "$t(entity.track, {\"count\": 2})"
}, },
"globalSearch": { "globalSearch": {
"commands": { "commands": {
@@ -620,6 +638,12 @@
}, },
"favorites": { "favorites": {
"title": "$t(entity.favorite, {\"count\": 2})" "title": "$t(entity.favorite, {\"count\": 2})"
},
"folderList": {
"title": "$t(entity.folder, {\"count\": 2})"
},
"playlistList": {
"title": "$t(entity.playlist, {\"count\": 2})"
} }
}, },
"form": { "form": {
@@ -660,7 +684,8 @@
"input_skipDuplicates": "не добавлять дубликаты", "input_skipDuplicates": "не добавлять дубликаты",
"create": "создать $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "создать $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "для создания нового списка выполните поиск по $t(entity.playlist, {\"count\": 2}) или введите соответствующий текст", "searchOrCreate": "для создания нового списка выполните поиск по $t(entity.playlist, {\"count\": 2}) или введите соответствующий текст",
"input_playlists": "$t(entity.playlist, {\"count\": 2})" "input_playlists": "$t(entity.playlist, {\"count\": 2})",
"noneAdded": "в плейлист $t(entity.playlist, {\"count\": 1}) '{{playlist}}' не было добавлено ни одного трека"
}, },
"updateServer": { "updateServer": {
"title": "обновление сервера", "title": "обновление сервера",
@@ -1060,7 +1085,8 @@
"audioFadeOnStatusChange": "плавное изменение звука", "audioFadeOnStatusChange": "плавное изменение звука",
"audioFadeOnStatusChange_description": "включает эффекты затухания и появления звука при изменении статуса (пауза/проигрывание)", "audioFadeOnStatusChange_description": "включает эффекты затухания и появления звука при изменении статуса (пауза/проигрывание)",
"preventSleepOnPlayback_description": "запрещает спящий режим экрана, пока играет музыка", "preventSleepOnPlayback_description": "запрещает спящий режим экрана, пока играет музыка",
"preventSleepOnPlayback": "не переходить в спящий режим" "preventSleepOnPlayback": "не переходить в спящий режим",
"discordLinkType_none": "$t(common.none)"
}, },
"releaseType": { "releaseType": {
"secondary": { "secondary": {
@@ -1081,7 +1107,8 @@
"other": "другие", "other": "другие",
"broadcast": "транслировать", "broadcast": "транслировать",
"ep": "эп", "ep": "эп",
"single": "сингл" "single": "сингл",
"album": "$t(entity.album, {\"count\": 1})"
} }
}, },
"datetime": { "datetime": {
+1
View File
@@ -0,0 +1 @@
{}
+5 -2
View File
@@ -796,7 +796,9 @@
"waveformLoadingDelay": "波形載入延遲", "waveformLoadingDelay": "波形載入延遲",
"waveformLoadingDelay_description": "載入波形前的延遲(以秒為單位)。如果您在使用網頁播放器時遇到卡頓,請增加此值。", "waveformLoadingDelay_description": "載入波形前的延遲(以秒為單位)。如果您在使用網頁播放器時遇到卡頓,請增加此值。",
"playerbarWaveformStretch": "波形拉伸", "playerbarWaveformStretch": "波形拉伸",
"playerbarWaveformStretch_description": "拉伸波形來填補可用空間" "playerbarWaveformStretch_description": "拉伸波形來填補可用空間",
"preventSuspendOnPlayback_description": "音樂播放時防止應用程式進入休眠",
"preventSuspendOnPlayback": "在播放時防止應用程式暫停"
}, },
"table": { "table": {
"config": { "config": {
@@ -1046,7 +1048,8 @@
"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, {\"count\": 1})", "title": "新增到$t(entity.playlist, {\"count\": 1})",
"create": "建立 $t(entity.playlist, {\"count\": 1}) {{playlist}}", "create": "建立 $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "搜尋$t(entity.playlist, {\"count\": 2}) 或輸入內容以建立新項目" "searchOrCreate": "搜尋$t(entity.playlist, {\"count\": 2}) 或輸入內容以建立新項目",
"noneAdded": "沒有曲目新增到 $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
}, },
"createPlaylist": { "createPlaylist": {
"input_description": "$t(common.description)", "input_description": "$t(common.description)",
+4 -2
View File
@@ -931,12 +931,14 @@ ipcMain.on(
}, },
); );
ipcMain.handle('power-save-blocker-start', () => { ipcMain.handle('power-save-blocker-start', (_event, { full }: { full: boolean }) => {
if (powerSaveBlockerId !== null) { if (powerSaveBlockerId !== null) {
return powerSaveBlockerId; return powerSaveBlockerId;
} }
powerSaveBlockerId = powerSaveBlocker.start('prevent-display-sleep'); powerSaveBlockerId = powerSaveBlocker.start(
full ? 'prevent-display-sleep' : 'prevent-app-suspension',
);
return powerSaveBlockerId; return powerSaveBlockerId;
}); });
+3 -2
View File
@@ -404,11 +404,12 @@ export const createAuthHeader = (): string => {
}; };
export const jfApiClient = (args: { export const jfApiClient = (args: {
forceRemoteUrl?: boolean;
server: null | ServerListItemWithCredential; server: null | ServerListItemWithCredential;
signal?: AbortSignal; signal?: AbortSignal;
url?: string; url?: string;
}) => { }) => {
const { server, signal, url } = args; const { forceRemoteUrl, server, signal, url } = args;
return initClient(contract, { return initClient(contract, {
api: async ({ body, headers, method, path }) => { api: async ({ body, headers, method, path }) => {
@@ -418,7 +419,7 @@ export const jfApiClient = (args: {
const { params, path: api } = parsePath(path); const { params, path: api } = parsePath(path);
if (server) { if (server) {
const serverUrl = getServerUrl(server); const serverUrl = getServerUrl(server, forceRemoteUrl);
baseUrl = serverUrl; baseUrl = serverUrl;
token = server?.credential; token = server?.credential;
} else { } else {
@@ -395,10 +395,15 @@ export const JellyfinController: InternalControllerEndpoint = {
userId: apiClientProps.server.userId, userId: apiClientProps.server.userId,
}, },
query: { query: {
AlbumIds: query.id,
EnableUserData: true,
Fields: JF_FIELDS.SONG, Fields: JF_FIELDS.SONG,
IncludeItemTypes: 'Audio', IncludeItemTypes: 'Audio',
ParentId: query.id, Recursive: true,
SortBy: 'ParentIndexNumber,IndexNumber,SortName', SortBy: 'ParentIndexNumber,IndexNumber,SortName',
SortOrder: JFSortOrder.ASC,
StartIndex: 0,
UserId: apiClientProps.server.userId,
}, },
}); });
@@ -928,6 +933,7 @@ export const JellyfinController: InternalControllerEndpoint = {
Fields: JF_FIELDS.PLAYLIST_LIST, Fields: JF_FIELDS.PLAYLIST_LIST,
IncludeItemTypes: 'Playlist', IncludeItemTypes: 'Playlist',
Limit: query.limit, Limit: query.limit,
MediaTypes: 'Audio',
Recursive: true, Recursive: true,
SearchTerm: query.searchTerm, SearchTerm: query.searchTerm,
SortBy: playlistListSortMap.jellyfin[query.sortBy], SortBy: playlistListSortMap.jellyfin[query.sortBy],
+3 -2
View File
@@ -479,11 +479,12 @@ axiosClient.interceptors.response.use(
); );
export const ndApiClient = (args: { export const ndApiClient = (args: {
forceRemoteUrl?: boolean;
server: null | ServerListItemWithCredential; server: null | ServerListItemWithCredential;
signal?: AbortSignal; signal?: AbortSignal;
url?: string; url?: string;
}) => { }) => {
const { server, signal, url } = args; const { forceRemoteUrl, server, signal, url } = args;
return initClient(contract, { return initClient(contract, {
api: async ({ body, headers, method, path }) => { api: async ({ body, headers, method, path }) => {
@@ -493,7 +494,7 @@ export const ndApiClient = (args: {
const { params, path: api } = parsePath(path); const { params, path: api } = parsePath(path);
if (server) { if (server) {
const serverUrl = getServerUrl(server); const serverUrl = getServerUrl(server, forceRemoteUrl);
baseUrl = serverUrl ? `${serverUrl}/api` : undefined; baseUrl = serverUrl ? `${serverUrl}/api` : undefined;
token = server?.ndCredential; token = server?.ndCredential;
} else { } else {
+3 -2
View File
@@ -428,12 +428,13 @@ const silentlyTransformResponse = (data: any) => {
}; };
export const ssApiClient = (args: { export const ssApiClient = (args: {
forceRemoteUrl?: boolean;
server: null | ServerListItemWithCredential; server: null | ServerListItemWithCredential;
signal?: AbortSignal; signal?: AbortSignal;
silent?: boolean; silent?: boolean;
url?: string; url?: string;
}) => { }) => {
const { server, signal, silent, url } = args; const { forceRemoteUrl, server, signal, silent, url } = args;
return initClient(contract, { return initClient(contract, {
api: async ({ body, headers, method, path, rawQuery }) => { api: async ({ body, headers, method, path, rawQuery }) => {
@@ -443,7 +444,7 @@ export const ssApiClient = (args: {
const { params, path: api } = parsePath(path); const { params, path: api } = parsePath(path);
if (server) { if (server) {
const serverUrl = getServerUrl(server); const serverUrl = getServerUrl(server, forceRemoteUrl);
baseUrl = serverUrl ? `${serverUrl}/rest` : undefined; baseUrl = serverUrl ? `${serverUrl}/rest` : undefined;
const token = server.credential; const token = server.credential;
const params = token.split(/&?\w=/gm); const params = token.split(/&?\w=/gm);
@@ -284,7 +284,10 @@ export const useDiscordRpc = () => {
) { ) {
try { try {
const info = await api.controller.getAlbumInfo({ const info = await api.controller.getAlbumInfo({
apiClientProps: { serverId: song._serverId }, apiClientProps: {
forceRemoteUrl: true,
serverId: song._serverId,
},
query: { id: song.albumId }, query: { id: song.albumId },
}); });
@@ -95,18 +95,20 @@ export const SynchronizedLyrics = ({
const programmaticScrollRef = useRef(false); const programmaticScrollRef = useRef(false);
const getCurrentLyric = (timeInMs: number) => { const getCurrentLyric = (timeInMs: number) => {
if (lyricRef.current) {
const activeLyrics = lyricRef.current; const activeLyrics = lyricRef.current;
for (let idx = 0; idx < activeLyrics.length; idx += 1) { if (!activeLyrics?.length) {
if (timeInMs <= activeLyrics[idx][0]) {
return idx === 0 ? idx : idx - 1;
}
}
return activeLyrics.length - 1;
}
return -1; return -1;
}
let index = -1;
for (let idx = 0; idx < activeLyrics.length; idx += 1) {
if (timeInMs < activeLyrics[idx][0]) {
break;
}
index = idx;
}
return index;
}; };
const setCurrentLyricRef = useRef< const setCurrentLyricRef = useRef<
@@ -141,7 +143,20 @@ export const SynchronizedLyrics = ({
.forEach((node) => node.classList.remove('active')); .forEach((node) => node.classList.remove('active'));
if (index === -1) { if (index === -1) {
lyricRef.current = null; const activeLyrics = lyricRef.current;
if (!activeLyrics?.length) {
return;
}
const firstTime = activeLyrics[0][0];
if (timeInMs < firstTime) {
const elapsed = performance.now() - start;
const delay = Math.max(0, firstTime - timeInMs - elapsed);
lyricTimer.current = setTimeout(() => {
setCurrentLyricRef.current(firstTime, nextEpoch, 0);
}, delay);
}
return; return;
} }
@@ -153,7 +168,6 @@ export const SynchronizedLyrics = ({
const offsetTop = currentLyric?.offsetTop - doc?.clientHeight / 2 || 0; const offsetTop = currentLyric?.offsetTop - doc?.clientHeight / 2 || 0;
if (currentLyric === null) { if (currentLyric === null) {
lyricRef.current = null;
return; return;
} }
@@ -140,9 +140,15 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
}; };
}, },
seekTo(seekTo: number) { seekTo(seekTo: number) {
let type: 'fraction' | 'seconds' | undefined = undefined;
if (seekTo < 1) {
type = 'seconds';
}
playerNum === 1 playerNum === 1
? player1Ref.current?.seekTo(seekTo) ? player1Ref.current?.seekTo(seekTo, type)
: player2Ref.current?.seekTo(seekTo); : player2Ref.current?.seekTo(seekTo, type);
}, },
setVolume(volume: number) { setVolume(volume: number) {
setInternalVolume1(volume / 100 || 0); setInternalVolume1(volume / 100 || 0);
@@ -240,10 +240,16 @@ export function WebPlayer() {
} }
} }
let type: 'fraction' | 'seconds' | undefined = undefined;
if (timestamp < 1) {
type = 'seconds';
}
if (num === 1) { if (num === 1) {
playerRef.current?.player1()?.ref?.seekTo(timestamp); playerRef.current?.player1()?.ref?.seekTo(timestamp, type);
} else { } else {
playerRef.current?.player2()?.ref?.seekTo(timestamp); playerRef.current?.player2()?.ref?.seekTo(timestamp, type);
} }
}, },
onPlayerStatus: async (properties) => { onPlayerStatus: async (properties) => {
@@ -8,17 +8,17 @@ const ipc = isElectron() ? window.api.ipc : null;
export const usePowerSaveBlocker = () => { export const usePowerSaveBlocker = () => {
const status = usePlayerStatus(); const status = usePlayerStatus();
const { preventSleepOnPlayback } = useWindowSettings(); const { preventSleepOnPlayback, preventSuspendOnPlayback } = useWindowSettings();
const startPowerSaveBlocker = useCallback(async () => { const startPowerSaveBlocker = useCallback(async () => {
if (!ipc) return; if (!ipc) return;
try { try {
await ipc.invoke('power-save-blocker-start'); await ipc.invoke('power-save-blocker-start', { full: preventSleepOnPlayback });
} catch (error) { } catch (error) {
console.error('Failed to start power save blocker:', error); console.error('Failed to start power save blocker:', error);
} }
}, []); }, [preventSleepOnPlayback]);
const stopPowerSaveBlocker = useCallback(async () => { const stopPowerSaveBlocker = useCallback(async () => {
if (!ipc) return; if (!ipc) return;
@@ -31,16 +31,21 @@ export const usePowerSaveBlocker = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!preventSleepOnPlayback) return; if (!preventSleepOnPlayback || !preventSuspendOnPlayback) return;
if (status === PlayerStatus.PLAYING) { if (status === PlayerStatus.PLAYING) {
startPowerSaveBlocker(); startPowerSaveBlocker();
} else { } else {
stopPowerSaveBlocker(); stopPowerSaveBlocker();
} }
}, [status, preventSleepOnPlayback, startPowerSaveBlocker, stopPowerSaveBlocker]); }, [
status,
preventSleepOnPlayback,
startPowerSaveBlocker,
stopPowerSaveBlocker,
preventSuspendOnPlayback,
]);
// Clean up on unmount
useEffect(() => { useEffect(() => {
return () => { return () => {
stopPowerSaveBlocker(); stopPowerSaveBlocker();
@@ -56,8 +61,11 @@ const PowerSaveBlockerHookInner = () => {
export const PowerSaveBlockerHook = () => { export const PowerSaveBlockerHook = () => {
const isElectronEnv = isElectron(); const isElectronEnv = isElectron();
const preventSleepOnPlayback = useSettingsStore((state) => state.window.preventSleepOnPlayback); const preventSleepOnPlayback = useSettingsStore((state) => state.window.preventSleepOnPlayback);
const preventSuspendOnPlayback = useSettingsStore(
(state) => state.window.preventSuspendOnPlayback,
);
if (!isElectronEnv || !preventSleepOnPlayback) { if (!isElectronEnv || !preventSleepOnPlayback || !preventSuspendOnPlayback) {
return null; return null;
} }
@@ -184,7 +184,7 @@ export const WindowSettings = memo(() => {
<Switch <Switch
aria-label="Toggle prevent sleep on playback" aria-label="Toggle prevent sleep on playback"
defaultChecked={settings.preventSleepOnPlayback} defaultChecked={settings.preventSleepOnPlayback}
disabled={!isElectron()} disabled={!isElectron() || settings.preventSuspendOnPlayback}
onChange={(e) => { onChange={(e) => {
if (!e) return; if (!e) return;
localSettings?.set( localSettings?.set(
@@ -206,6 +206,33 @@ export const WindowSettings = memo(() => {
isHidden: !isElectron(), isHidden: !isElectron(),
title: t('setting.preventSleepOnPlayback', { postProcess: 'sentenceCase' }), title: t('setting.preventSleepOnPlayback', { postProcess: 'sentenceCase' }),
}, },
{
control: (
<Switch
aria-label="Toggle prevent suspend on playback"
defaultChecked={settings.preventSuspendOnPlayback}
disabled={!isElectron() || settings.preventSleepOnPlayback}
onChange={(e) => {
if (!e) return;
localSettings?.set(
'window_prevent_suspend_on_playback',
e.currentTarget.checked,
);
setSettings({
window: {
preventSuspendOnPlayback: e.currentTarget.checked,
},
});
}}
/>
),
description: t('setting.preventSuspendOnPlayback', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: !isElectron(),
title: t('setting.preventSuspendOnPlayback', { postProcess: 'sentenceCase' }),
},
]; ];
return ( return (
@@ -6,12 +6,8 @@ import { Link, NavLink, useNavigate } from 'react-router';
import styles from './collapsed-sidebar.module.css'; import styles from './collapsed-sidebar.module.css';
import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
import { CollapsedSidebarButton } from '/@/renderer/features/sidebar/components/collapsed-sidebar-button'; import { CollapsedSidebarButton } from '/@/renderer/features/sidebar/components/collapsed-sidebar-button';
import { CollapsedSidebarItem } from '/@/renderer/features/sidebar/components/collapsed-sidebar-item'; import { CollapsedSidebarItem } from '/@/renderer/features/sidebar/components/collapsed-sidebar-item';
import { ServerSelectorItems } from '/@/renderer/features/sidebar/components/server-selector-items';
import { getCollectionTo } from '/@/renderer/features/sidebar/components/sidebar-collection-list'; import { getCollectionTo } from '/@/renderer/features/sidebar/components/sidebar-collection-list';
import { SidebarIcon } from '/@/renderer/features/sidebar/components/sidebar-icon'; import { SidebarIcon } from '/@/renderer/features/sidebar/components/sidebar-icon';
import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu'; import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu';
@@ -19,7 +15,6 @@ import { AppRoute } from '/@/renderer/router/routes';
import { import {
SidebarItemType, SidebarItemType,
useCollections, useCollections,
useCurrentServer,
useSidebarCollapsedNavigation, useSidebarCollapsedNavigation,
useSidebarItems, useSidebarItems,
useWindowSettings, useWindowSettings,
@@ -30,7 +25,7 @@ import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon'; import { Icon } from '/@/shared/components/icon/icon';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area'; import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
import { Stack } from '/@/shared/components/stack/stack'; import { Stack } from '/@/shared/components/stack/stack';
import { LibraryItem, ServerType } from '/@/shared/types/domain-types'; import { LibraryItem } from '/@/shared/types/domain-types';
import { Platform } from '/@/shared/types/types'; import { Platform } from '/@/shared/types/types';
export const CollapsedSidebar = () => { export const CollapsedSidebar = () => {
@@ -40,8 +35,6 @@ export const CollapsedSidebar = () => {
const { windowBarStyle } = useWindowSettings(); const { windowBarStyle } = useWindowSettings();
const sidebarCollapsedNavigation = useSidebarCollapsedNavigation(); const sidebarCollapsedNavigation = useSidebarCollapsedNavigation();
const sidebarItems = useSidebarItems(); const sidebarItems = useSidebarItems();
const currentServer = useCurrentServer();
const translatedSidebarItemMap = useMemo( const translatedSidebarItemMap = useMemo(
() => ({ () => ({
Albums: t('page.sidebar.albums', { postProcess: 'titleCase' }), Albums: t('page.sidebar.albums', { postProcess: 'titleCase' }),
@@ -174,38 +167,6 @@ export const CollapsedSidebar = () => {
/> />
), ),
)} )}
{currentServer && (
<DropdownMenu offset={0} position="right-end" width={240}>
<DropdownMenu.Target>
<CollapsedSidebarItem
activeIcon={null}
component={Flex}
icon={
<img
className={styles.serverIcon}
src={
currentServer.type === ServerType.NAVIDROME
? NavidromeLogo
: currentServer.type === ServerType.JELLYFIN
? JellyfinLogo
: OpenSubsonicLogo
}
/>
}
label={''}
py="md"
style={{
cursor: 'pointer',
}}
/>
</DropdownMenu.Target>
<DropdownMenu.Dropdown>
<ScrollArea style={{ maxHeight: '95vh' }}>
<ServerSelectorItems />
</ScrollArea>
</DropdownMenu.Dropdown>
</DropdownMenu>
)}
</ScrollArea> </ScrollArea>
</motion.div> </motion.div>
); );
+2
View File
@@ -638,6 +638,7 @@ const WindowSettingsSchema = z.object({
exitToTray: z.boolean(), exitToTray: z.boolean(),
minimizeToTray: z.boolean(), minimizeToTray: z.boolean(),
preventSleepOnPlayback: z.boolean(), preventSleepOnPlayback: z.boolean(),
preventSuspendOnPlayback: z.boolean(),
releaseChannel: z.enum(['alpha', 'beta', 'latest']), releaseChannel: z.enum(['alpha', 'beta', 'latest']),
startMinimized: z.boolean(), startMinimized: z.boolean(),
tray: z.boolean(), tray: z.boolean(),
@@ -1914,6 +1915,7 @@ const initialState: SettingsState = {
exitToTray: false, exitToTray: false,
minimizeToTray: false, minimizeToTray: false,
preventSleepOnPlayback: false, preventSleepOnPlayback: false,
preventSuspendOnPlayback: false,
releaseChannel: 'latest', releaseChannel: 'latest',
startMinimized: false, startMinimized: false,
tray: true, tray: true,
+8 -7
View File
@@ -87,6 +87,7 @@ export const NDSongQueryFields = [
{ label: 'Artist Date Rated', type: 'date', value: 'artistdaterated' }, { label: 'Artist Date Rated', type: 'date', value: 'artistdaterated' },
{ label: 'Artist Is Favorite', type: 'boolean', value: 'artistloved' }, { label: 'Artist Is Favorite', type: 'boolean', value: 'artistloved' },
{ label: 'Artist Play Count', type: 'number', value: 'artistplaycount' }, { label: 'Artist Play Count', type: 'number', value: 'artistplaycount' },
{ label: 'Artist Rating', type: 'number', value: 'artistrating' },
{ label: 'Artists', type: 'string', value: 'artists' }, { label: 'Artists', type: 'string', value: 'artists' },
{ label: 'ASIN', type: 'string', value: 'asin' }, { label: 'ASIN', type: 'string', value: 'asin' },
{ label: 'Average Rating', type: 'number', value: 'averagerating' }, { label: 'Average Rating', type: 'number', value: 'averagerating' },
@@ -158,7 +159,7 @@ export const NDSongQueryFields = [
{ label: 'MusicBrainz Work Id', type: 'string', value: 'musicbrainz_workid' }, { label: 'MusicBrainz Work Id', type: 'string', value: 'musicbrainz_workid' },
{ label: 'Name', type: 'string', value: 'title' }, { label: 'Name', type: 'string', value: 'title' },
{ label: 'Original Date', type: 'date', value: 'originaldate' }, { label: 'Original Date', type: 'date', value: 'originaldate' },
{ label: 'Original Year', type: 'date', value: 'originalyear' }, { label: 'Original Year', type: 'number', value: 'originalyear' },
{ label: 'Performer', type: 'string', value: 'performer' }, { label: 'Performer', type: 'string', value: 'performer' },
{ label: 'Play Count', type: 'number', value: 'playcount' }, { label: 'Play Count', type: 'number', value: 'playcount' },
{ label: 'Playlist', type: 'playlist', value: 'id' }, { label: 'Playlist', type: 'playlist', value: 'id' },
@@ -178,18 +179,18 @@ export const NDSongQueryFields = [
{ label: 'ReplayGain Track Peak', type: 'number', value: 'replaygain_track_peak' }, { label: 'ReplayGain Track Peak', type: 'number', value: 'replaygain_track_peak' },
{ label: 'Remixer', type: 'string', value: 'remixer' }, { label: 'Remixer', type: 'string', value: 'remixer' },
{ label: 'Script', type: 'string', value: 'script' }, { label: 'Script', type: 'string', value: 'script' },
{ label: 'Sample Rate', type: 'number', value: 'sampleRate' }, { label: 'Sample Rate', type: 'number', value: 'samplerate' },
{ label: 'Size', type: 'number', value: 'size' }, { label: 'Size', type: 'number', value: 'size' },
{ label: 'Sort Album', type: 'string', value: 'albumsort' }, { label: 'Sort Album', type: 'string', value: 'sortalbum' },
{ label: 'Sort Album Artist', type: 'string', value: 'albumartistsort' }, { label: 'Sort Album Artist', type: 'string', value: 'sortalbumartist' },
{ label: 'Sort Album Artists', type: 'string', value: 'albumartistssort' }, { label: 'Sort Album Artists', type: 'string', value: 'albumartistssort' },
{ label: 'Sort Artist', type: 'string', value: 'artistsort' }, { label: 'Sort Artist', type: 'string', value: 'sortartist' },
{ label: 'Sort Artists', type: 'string', value: 'artistssort' }, { label: 'Sort Artists', type: 'string', value: 'artistssort' },
{ label: 'Sort Composer', type: 'string', value: 'composersort' }, { label: 'Sort Composer', type: 'string', value: 'composersort' },
{ label: 'Sort Lyricist', type: 'string', value: 'lyricistsort' }, { label: 'Sort Lyricist', type: 'string', value: 'lyricistsort' },
{ label: 'Sort Name', type: 'string', value: 'titlesort' }, { label: 'Sort Name', type: 'string', value: 'sorttitle' },
{ label: 'Subtitle', type: 'string', value: 'subtitle' }, { label: 'Subtitle', type: 'string', value: 'subtitle' },
{ label: 'Track Number', type: 'number', value: 'track' }, { label: 'Track Number', type: 'number', value: 'tracknumber' },
{ label: 'Track Total', type: 'number', value: 'tracktotal' }, { label: 'Track Total', type: 'number', value: 'tracktotal' },
{ label: 'Website', type: 'string', value: 'website' }, { label: 'Website', type: 'string', value: 'website' },
{ label: 'Work', type: 'string', value: 'work' }, { label: 'Work', type: 'string', value: 'work' },
+1 -1
View File
@@ -318,7 +318,7 @@ export const sortSongList = (songs: Song[], sortBy: SongListSort, sortOrder: Sor
case SongListSort.YEAR: case SongListSort.YEAR:
results = orderBy( results = orderBy(
results, results,
['releaseYear', (v) => v.album?.toLowerCase(), 'discNumber', 'track'], ['releaseYear', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
[order, order, order, order], [order, order, order, order],
); );
break; break;
+1
View File
@@ -421,6 +421,7 @@ type ApiContext = {
type BaseEndpointArgs = { type BaseEndpointArgs = {
apiClientProps: { apiClientProps: {
forceRemoteUrl?: boolean;
server?: null | ServerListItemWithCredential; server?: null | ServerListItemWithCredential;
serverId: string; serverId: string;
signal?: AbortSignal; signal?: AbortSignal;