Compare commits

..

5 Commits

Author SHA1 Message Date
jeffvli 1f7d510110 update to v0.21.1 2025-10-13 04:48:10 -07:00
Hosted Weblate 44fae06143 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.8% (715 of 716 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: linger <linger0517@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/zh_Hant/
Translation: feishin/Translation
2025-10-13 11:33:21 +00:00
Hosted Weblate 1e24e12a55 Translated using Weblate (Catalan)
Currently translated at 100.0% (716 of 716 strings)

Co-authored-by: Ondo <SparkyOndo@proton.me>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/ca/
Translation: feishin/Translation
2025-10-13 11:33:20 +00:00
Hosted Weblate 358bdec2b6 Translated using Weblate (Basque)
Currently translated at 81.9% (587 of 716 strings)

Translated using Weblate (Basque)

Currently translated at 73.1% (524 of 716 strings)

Co-authored-by: Aitor Astorga <a.astorga.sdv@protonmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/feishin/translation/eu/
Translation: feishin/Translation
2025-10-13 11:33:20 +00:00
jeffvli 1b8661d566 fix playback controls being called multiple times on media key input 2025-10-13 04:33:11 -07:00
6 changed files with 164 additions and 40 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "feishin",
"version": "0.21.0",
"version": "0.21.1",
"description": "A modern self-hosted music player.",
"keywords": [
"subsonic",
+12 -2
View File
@@ -483,7 +483,7 @@
"discordRichPresence": "estat d'activitat de {{discord}}",
"discordRichPresence_description": "activa l'estat de reproducció a l'activitat de {{discord}}. Les tecles d'imatge són: {{icon}}, {{playing}} i {{paused}}",
"discordServeImage": "serveix imatges de {{discord}} des del servidor",
"discordServeImage_description": "comparteix la caràtula per l'estat d'activitat de {{discord}} des del servidor; només disponible per jellyfin i navidrome",
"discordServeImage_description": "comparteix la caràtula per l'estat d'activitat de {{discord}} des del servidor; només disponible per jellyfin i navidrome. {{discord}} fa ser un bot per trobar les imatges, de manera que el vostre servidor ha de ser visible per l'internet públic.",
"discordUpdateInterval": "interval d'actualització de l'estat d'activitat de {{discord}}",
"doubleClickBehavior": "posa en cua totes les pistes cercades en fer doble clic",
"doubleClickBehavior_description": "si està actiu, totes les pistes coincidents en una cerca de pistes es posaran a la cua. altrament, només la que seleccioneu s'afegirà a la cua",
@@ -653,7 +653,17 @@
"discordLinkType": "enllaços d'estat de {{discord}}",
"discordLinkType_description": "afegeix enllaços externs a {{lastfm}} o {{musicbrainz}} als camps de cançó i artista a l'estat d'activitat de {{discord}}. {{musicbrainz}} és el més precís, però requereix etiquetes i no proporciona enllaços d'artista, mentre que {{lastfm}} hauria de propocionar un enllaç sempre. no fa sol·licituds de xarxa addicionals",
"discordLinkType_none": "$t(common.none)",
"discordLinkType_mbz_lastfm": "{{musicbrainz}} amb {{lastfm}} com a alternativa"
"discordLinkType_mbz_lastfm": "{{musicbrainz}} amb {{lastfm}} com a alternativa",
"artistBackground": "imatge de fons de l'artista",
"artistBackground_description": "afegeix una imatge de fons per les pàgines d'artista amb l'art de l'artista",
"artistBackgroundBlur": "mida del desenfocament de la imatge de fons de l'artista",
"artistBackgroundBlur_description": "ajusta la quantitat de desenfocament aplicat a la imatge de fons de l'artista",
"releaseChannel_optionLatest": "estable",
"releaseChannel_optionBeta": "beta",
"releaseChannel": "canal de versions",
"releaseChannel_description": "tria entre versions estables i versions beta per les actualitzacions automàtiques",
"mediaSession": "activa Media Session",
"mediaSession_description": "Activa la integració amb Windows Media Session per mostrar els controls multimèdia i les metadades a l'indicador de volum del sistema i la pantalla de bloqueig (només per Windows)"
},
"table": {
"column": {
+76 -10
View File
@@ -116,7 +116,8 @@
"saveAs": "gorde honela",
"trackGain": "pista irabazpena",
"comingSoon": "laster…",
"trackPeak": "pistaren gailurra"
"trackPeak": "pistaren gailurra",
"albumPeak": "albumaren gailurra"
},
"player": {
"repeat": "errepikatu",
@@ -161,7 +162,10 @@
},
"general": {
"gap": "$t(common.gap)",
"size": "$t(common.size)"
"size": "$t(common.size)",
"tableColumns": "taula zutabeak",
"itemSize": "elementuaren tamaina (px)",
"followCurrentSong": "jarraitu uneko abestia"
},
"label": {
"actions": "$t(common.action_other)",
@@ -183,7 +187,13 @@
"size": "$t(common.size)",
"songCount": "$t(entity.track_other)",
"title": "$t(common.title)",
"year": "$t(common.year)"
"year": "$t(common.year)",
"titleCombined": "$t(common.title) (batuta)",
"releaseDate": "argitalpen data",
"playCount": "erreprodukzio kopurua",
"lastPlayed": "azken aldiz entzundakoa",
"discNumber": "disko zenbakia",
"dateAdded": "gehitze data"
}
},
"column": {
@@ -206,7 +216,11 @@
"trackNumber": "pista",
"bpm": "bpm",
"comment": "iruzkina",
"playCount": "erreprodukzioak"
"playCount": "erreprodukzioak",
"releaseDate": "argitalpen data",
"lastPlayed": "azken aldiz entzundakoa",
"dateAdded": "gehitutako data",
"albumArtist": "albumeko artista"
}
},
"entity": {
@@ -397,7 +411,7 @@
"discordRichPresence": "{{discord}} jarduera-egoera",
"discordRichPresence_description": "gaitu erreprodukzioa egoera {{discord}}-en jarduera-egoeran. Irudi gakoak hauek dira: {{icon}}, {{playing}}, eta {{paused}}",
"discordServeImage": "zerbitzatu {{discord}} irudiak zerbitzaritik",
"discordServeImage_description": "partekatu {{discord}} jarduera-egoerarentzako azala artea zerbitzaritik bertatik, Jellyfin eta Navidrome-rentzat bakarrik eskuragarri",
"discordServeImage_description": "partekatu {{discord}} jarduera-egoerarentzako azala artea zerbitzaritik bertatik, Jellyfin eta Navidrome-rentzat bakarrik eskuragarri. {{discord}}-(e)k bot bat erabiltzen du irudiak eskuratzeko, beraz, zure zerbitzaria internet publikotik eskuragarri egon behar da.",
"discordUpdateInterval": "{{discord}} jarduera-egoera eguneraketa tartea",
"discordLinkType_none": "$t(common.none)",
"albumBackground": "albumaren atzeko planoaren irudia",
@@ -413,7 +427,55 @@
"discordDisplayType_songname": "abesti izena",
"discordDisplayType_artistname": "artista izena(k)",
"fontType_optionBuiltIn": "barneko letra-tipoa",
"hotkey_globalSearch": "bilaketa globala"
"hotkey_globalSearch": "bilaketa globala",
"albumBackgroundBlur_description": "albumaren atzeko planoaren irudiari aplikatzen zaion lausotze-kopurua doitzen du",
"artistBackground": "artistaren atzeko planoaren irudia",
"artistBackgroundBlur": "artistaren atzeko planoko irudiaren lausotze-tamaina",
"artistBackgroundBlur_description": "artistaren atzeko planoaren irudiari aplikatzen zaion lausotze-kopurua doitzen du",
"artistConfiguration": "albumaren artistaren konfigurazio orria",
"artistConfiguration_description": "konfiguratu zein elementu erakusten diren eta zein ordenatan albumaren artistaren orrian",
"audioExclusiveMode": "audio esklusiboko modua",
"releaseChannel_optionLatest": "egonkorra",
"releaseChannel_optionBeta": "beta",
"releaseChannel": "argitalpen kanala",
"releaseChannel_description": "aukeratu argitalpen egonkorren edo beta artean eguneratze automatikoak lortzeko",
"discordUpdateInterval_description": "eguneratze bakoitzaren arteko denbora segundotan (gutxienez 15 segundo)",
"discordDisplayType": "{{discord}} jarduera-pantailaren mota",
"discordDisplayType_description": "zure egoeran entzuten ari zarena aldatzen du",
"discordLinkType": "{{discord}} egoera estekak",
"fontType_description": "barneko letra-tipoa Feishinek eskaintzen dituen letra-tipoetako bat aukeratzen du. sistemaren letra-tipoa zure sistema eragileak eskaintzen duen edozein letra-tipo hautatzeko aukera ematen dizu. pertsonalizatua zure letra-tipoa eskaintzeko aukera ematen dizu",
"genreBehavior": "genero orriaren portaera lehenetsia",
"homeConfiguration_description": "konfiguratu zein elementu erakusten diren hasierako orrian eta zein ordenatan",
"homeFeature": "etxeko karrusela nabarmendua",
"homeFeature_description": "hasierako orrian karrusel nabarmen handia erakutsi behar den ala ez kontrolatzen du",
"hotkey_localSearch": "orrian bilatu",
"hotkey_rate0": "garbitu balorazioa",
"hotkey_rate1": "1 izarretako balorazioa",
"hotkey_rate2": "2 izarretako balorazioa",
"hotkey_rate3": "3 izarretako balorazioa",
"hotkey_rate4": "4 izarretako balorazioa",
"hotkey_rate5": "5 izarretako balorazioa",
"zoom_description": "aplikazioaren zoom ehunekoa ezartzen du",
"zoom": "zoom ehunekoa",
"windowBarStyle_description": "aukeratu leiho-barraren estiloa",
"windowBarStyle": "leiho-barra estiloa",
"webAudio": "erabili web audioa",
"useSystemTheme_description": "jarraitu sistemak definitutako argi edo iluntasun lehentasuna",
"useSystemTheme": "erabili sistemaren gaia",
"translationTargetLanguage_description": "itzulpenerako helburu-hizkuntza",
"translationTargetLanguage": "itzulpenerako helburu-hizkuntza",
"translationApiKey": "itzulpen api gakoa",
"translationApiProvider_description": "itzulpenerako api hornitzailea",
"translationApiProvider": "itzulpen api hornitzailea",
"mediaSession": "gaitu multimedia saioa",
"themeLight_description": "aplikaziorako erabiliko den gaia argia ezartzen du",
"themeLight": "gaia (argia)",
"themeDark_description": "aplikaziorako erabiliko den gai iluna ezartzen du",
"themeDark": "gaia (iluna)",
"theme_description": "aplikaziorako erabiliko den gaia ezartzen du",
"externalLinks": "kanpoko estekak erakutsi",
"externalLinks_description": "kanpoko estekak (Last.fm, MusicBrainz) artista/album orrietan erakustea gaitzen du",
"exitToTray": "irten erretilura"
},
"form": {
"addServer": {
@@ -426,7 +488,8 @@
"title": "zerbitzaria gehitu",
"ignoreCors": "alde batera utzi cors $t(common.restartRequired)",
"ignoreSsl": "alde batera utzi ssl $t(common.restartRequired)",
"input_legacyAuthentication": "gaitu zaharkitutako autentifikazioa"
"input_legacyAuthentication": "gaitu zaharkitutako autentifikazioa",
"success": "zerbitzaria behar bezala gehitu da"
},
"addToPlaylist": {
"input_playlists": "$t(entity.playlist_other)",
@@ -462,7 +525,8 @@
},
"editPlaylist": {
"success": "$t(entity.playlist_one) behar bezala eguneratu da",
"title": "$t(entity.playlist_one) editatu"
"title": "$t(entity.playlist_one) editatu",
"publicJellyfinNote": "Arrazoiren batengatik, Jellyfin ez du erakusten erreprodukzio-zerrendak publikoak diren edo ez. Hau publiko izaten jarraitzea nahi baduzu, hautatu sarrera hau"
},
"queryEditor": {
"title": "kontsulta editorea",
@@ -504,7 +568,8 @@
"privateModeOff": "itzali modu pribatua",
"privateModeOn": "aktibatu modu pribatua",
"selectServer": "aukeratu zerbitzaria",
"version": "bertsioa {{version}}"
"version": "bertsioa {{version}}",
"openBrowserDevtools": "ireki nabigatzailearen garapen tresnak"
},
"manageServers": {
"url": "URLa",
@@ -552,7 +617,8 @@
"dynamicImageBlur": "irudiaren lausotze tamaina",
"lyricAlignment": "letraren lerrokatzea",
"showLyricMatch": "erakutsi letren bat-etortzea",
"showLyricProvider": "erakutsi letra hornitzailea"
"showLyricProvider": "erakutsi letra hornitzailea",
"lyricOffset": "letra-desplazamendua (ms)"
},
"lyrics": "letrak",
"related": "erlazionatuta",
+38 -6
View File
@@ -142,7 +142,9 @@
"playSimilarSongs": "$t(player.playSimilarSongs)",
"playShuffled": "$t(player.shuffle)",
"shareItem": "分享項目",
"showDetails": "取得資訊"
"showDetails": "取得資訊",
"goToAlbum": "前往 $t(entity.album_one)",
"goToAlbumArtist": "前往 $t(entity.albumArtist_one)"
},
"globalSearch": {
"title": "指令",
@@ -157,7 +159,8 @@
"recentlyPlayed": "最近播放",
"title": "$t(common.home)",
"mostPlayed": "最多播放",
"newlyAdded": "最近新增的發行"
"newlyAdded": "最近新增的發行",
"recentlyReleased": "最近發佈"
},
"appMenu": {
"openBrowserDevtools": "打開瀏覽器開發者工具",
@@ -169,7 +172,9 @@
"selectServer": "選擇伺服器",
"settings": "$t(common.setting_other)",
"version": "版本 {{version}}",
"manageServers": "管理伺服器"
"manageServers": "管理伺服器",
"privateModeOff": "關閉私人模式",
"privateModeOn": "開啟私人模式"
},
"fullscreenPlayer": {
"config": {
@@ -489,7 +494,7 @@
"discordListening": "將狀態設為\"正在聽\"",
"discordListening_description": "將狀態顯示為\"正在聽\"而不是\"正在玩\"",
"discordServeImage": "從伺服器提供{{discord}}圖片",
"discordServeImage_description": "從伺服器本身 {{discord}} rich presence 分享封面圖片,只在jellyfin和navidrome可用",
"discordServeImage_description": "從伺服器本身分享 {{discord}} Rich Presence封面圖片,僅支援 Jellyfin 與 Navidrome。{{discord}} 會透過機器人擷取圖片,因此您的伺服器必須能從公開網路連線。",
"doubleClickBehavior": "雙擊時將所有搜尋到的曲目加入佇列",
"doubleClickBehavior_description": "如果為 true,則歌曲搜尋中所有符合的歌曲都會被加入佇列。否則,只有被點擊的歌曲才會被加入佇列",
"externalLinks": "顯示外部連結",
@@ -544,7 +549,27 @@
"webAudio": "使用網頁音訊",
"webAudio_description": "使用網頁音訊。這將啟用重播增益等進階功能。如果您遇到其他問題,請停用",
"preservePitch": "保持音高",
"preservePitch_description": "修改播放速度時保留音調"
"preservePitch_description": "修改播放速度時保留音調",
"artistBackground": "藝人背景圖片",
"artistBackground_description": "為藝人頁面新增含藝人圖片的背景圖像",
"artistBackgroundBlur": "藝人背景圖片模糊程度",
"artistBackgroundBlur_description": "調整套用至藝人背景圖片的模糊程度",
"releaseChannel_optionLatest": "穩定版",
"releaseChannel_optionBeta": "測試版",
"releaseChannel_description": "選擇自動更新時要使用穩定版本或是測試版本",
"discordDisplayType": "{{discord}} presence 顯示類型",
"discordDisplayType_description": "變更您在狀態中正在聆聽的內容",
"discordDisplayType_songname": "歌曲名稱",
"discordDisplayType_artistname": "藝人名稱",
"discordLinkType": "{{discord}} presence 連結",
"discordLinkType_description": "在 {{discord}} Rich Presence中,為歌曲和藝人欄位新增 {{lastfm}} 或 {{musicbrainz}} 的外部連結。{{musicbrainz}} 的準確度最高,但需要標籤且不提供藝人連結;而 {{lastfm}} 通常都能提供連結。此功能不會產生額外的網路請求",
"discordLinkType_none": "$t(common.none)",
"discordLinkType_mbz_lastfm": "{{musicbrainz}} 並以 {{lastfm}} 備用",
"hotkey_navigateHome": "導航至首頁",
"preventSleepOnPlayback": "防止播放時進入睡眠狀態",
"preventSleepOnPlayback_description": "在音樂播放時防止螢幕進入睡眠狀態",
"mediaSession": "啟用Media Session",
"mediaSession_description": "啟用 Windows Media Session 整合功能,於系統音量Overlay和鎖定畫面中顯示媒體資料與控制面板(僅限 Windows)"
},
"table": {
"config": {
@@ -723,7 +748,9 @@
"title": "新增伺服器",
"error_savePassword": "儲存密碼時出現錯誤",
"ignoreCors": "忽略 cors $t(common.restartRequired)",
"ignoreSsl": "忽略 ssl $t(common.restartRequired)"
"ignoreSsl": "忽略 ssl $t(common.restartRequired)",
"input_preferInstantMix": "偏好即時混音",
"input_preferInstantMixDescription": "僅使用即時混音功能來獲取相似歌曲。若您擁有能修改此行為的外掛,此功能將相當實用"
},
"addToPlaylist": {
"input_playlists": "$t(entity.playlist_other)",
@@ -770,6 +797,11 @@
"success": "分享連結已複製到剪貼簿(或點擊此處開啟)",
"expireInvalid": "到期日必須是未來",
"createFailed": "無法建立分享(分享是否啟用?)"
},
"privateMode": {
"enabled": "已啟用私人模式,播放狀態將對外部整合隱藏",
"disabled": "已停用私人模式,播放狀態現對已啟用的外部整合可見",
"title": "私人模式"
}
}
}
@@ -57,6 +57,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
handlePlay,
handlePlayPause,
handlePrevTrack,
handleSeekSlider,
handleSkipBackward,
handleSkipForward,
handleStop,
@@ -84,7 +85,16 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
],
]);
useMediaSession(playersRef);
useMediaSession({
handleNextTrack,
handlePause,
handlePlay,
handlePrevTrack,
handleSeekSlider,
handleSkipBackward,
handleSkipForward,
handleStop,
});
return (
<>
@@ -227,22 +237,24 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
/>
</div>
</div>
<PlayerSeekSlider player1={player1} player2={player2} playersRef={playersRef} />
<PlayerSeekSlider
handleSeekSlider={handleSeekSlider}
player1={player1}
player2={player2}
/>
</>
);
};
const PlayerSeekSlider = ({
handleSeekSlider,
player1,
player2,
playersRef,
}: {
handleSeekSlider: (e: any | number) => void;
player1: any;
player2: any;
playersRef: any;
}) => {
const { handleSeekSlider } = useCenterControls({ playersRef });
const player = useCurrentPlayer();
const playbackType = usePlaybackType();
const setCurrentTime = useSetCurrentTime();
@@ -1,6 +1,5 @@
import { useEffect } from 'react';
import { useCenterControls } from '/@/renderer/features/player/hooks/use-center-controls';
import {
useCurrentSong,
useCurrentStatus,
@@ -9,26 +8,31 @@ import {
} from '/@/renderer/store';
import { PlayerStatus } from '/@/shared/types/types';
export const useMediaSession = (playersRef: { player1: any; player2: any }) => {
export const useMediaSession = ({
handleNextTrack,
handlePause,
handlePlay,
handlePrevTrack,
handleSeekSlider,
handleSkipBackward,
handleSkipForward,
handleStop,
}: {
handleNextTrack: () => void;
handlePause: () => void;
handlePlay: () => void;
handlePrevTrack: () => void;
handleSeekSlider: (e: any | number) => void;
handleSkipBackward: (seconds: number) => void;
handleSkipForward: (seconds: number) => void;
handleStop: () => void;
}) => {
const { mediaSession: mediaSessionEnabled } = usePlaybackSettings();
const playerStatus = useCurrentStatus();
const currentSong = useCurrentSong();
const mediaSession = navigator.mediaSession;
const skip = useSettingsStore((state) => state.general.skipButtons);
const {
handleNextTrack,
handlePause,
handlePlay,
handlePrevTrack,
handleSeekSlider,
handleSkipBackward,
handleSkipForward,
handleStop,
} = useCenterControls({
playersRef,
});
useEffect(() => {
if (!mediaSessionEnabled || !mediaSession) {
return;