import isElectron from 'is-electron'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ListConfigTable } from '/@/renderer/features/shared/components/list-config-menu'; import { usePlayerActions, usePlayerData, usePlayerProperties, usePlayerQueueType, usePlayerSpeed, usePlayerStatus, } from '/@/renderer/store'; import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Popover } from '/@/shared/components/popover/popover'; import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control'; import { Select } from '/@/shared/components/select/select'; import { Slider } from '/@/shared/components/slider/slider'; import { toast } from '/@/shared/components/toast/toast'; import { CrossfadeStyle, PlayerQueueType, PlayerStatus, PlayerStyle, PlayerType, } from '/@/shared/types/types'; const ipc = isElectron() ? window.api.ipc : null; const getAudioDevice = async () => { const devices = await navigator.mediaDevices.enumerateDevices(); return (devices || []).filter((dev: MediaDeviceInfo) => dev.kind === 'audiooutput'); }; export const PlayerConfig = () => { const { t } = useTranslation(); const { currentSong } = usePlayerData(); const speed = usePlayerSpeed(); const queueType = usePlayerQueueType(); const status = usePlayerStatus(); const { crossfadeDuration, crossfadeStyle, transitionType } = usePlayerProperties(); const { setCrossfadeDuration, setCrossfadeStyle, setQueueType, setSpeed, setTransitionType } = usePlayerActions(); const playbackSettings = usePlaybackSettings(); const { setSettings } = useSettingsStoreActions(); const [audioDevices, setAudioDevices] = useState<{ label: string; value: string }[]>([]); useEffect(() => { const fetchAudioDevices = () => { getAudioDevice() .then((dev) => setAudioDevices(dev.map((d) => ({ label: d.label, value: d.deviceId }))), ) .catch(() => toast.error({ message: t('error.audioDeviceFetchError', { postProcess: 'sentenceCase' }), }), ); }; if (playbackSettings.type === PlayerType.WEB) { fetchAudioDevices(); } }, [playbackSettings.type, t]); const options = useMemo(() => { const formatPlaybackSpeedSliderLabel = (value: number) => { const bpm = Number(currentSong?.bpm); if (bpm > 0) { return `${value} x / ${(bpm * value).toFixed(1)} BPM`; } return `${value} x`; }; const allOptions = [ { component: ( setQueueType(value as PlayerQueueType)} size="sm" value={queueType} w="100%" /> ), id: 'queueType', label: t('player.queueType', { postProcess: 'titleCase' }), }, { component: null, id: 'divider-0', isDivider: true, label: '', }, { component: ( setSettings({ playback: { ...playbackSettings, audioDeviceId: e, }, }) } width="100%" /> ), id: 'audioDevice', label: t('setting.audioDevice', { postProcess: 'titleCase' }), }, { component: null, id: 'divider-1', isDivider: true, label: '', }, { component: ( setTransitionType(value as PlayerStyle)} size="sm" value={transitionType} w="100%" /> ), id: 'transitionType', label: t('setting.playbackStyle', { postProcess: 'titleCase', }), }, { component: (