From 42a86b2edf59cba8f67b99e6474e27caeb3cf48d Mon Sep 17 00:00:00 2001 From: jeffvli Date: Tue, 23 Nov 2021 12:05:20 -0800 Subject: [PATCH] Add system audio device selector (#96) --- src/components/player/Player.tsx | 7 ++++ .../settings/ConfigPanels/PlayerConfig.tsx | 42 +++++++++++++++++-- src/components/shared/setDefaultSettings.ts | 4 ++ src/redux/configSlice.ts | 7 ++++ src/shared/mockSettings.ts | 1 + 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index 426dfbe..57c0992 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -590,6 +590,13 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { [config.serverType, currentEntryList, playQueue, player.currentSeek] ); + useEffect(() => { + if (config.playback.audioDeviceId) { + player1Ref.current.audioEl.current.setSinkId(config.playback.audioDeviceId); + player2Ref.current.audioEl.current.setSinkId(config.playback.audioDeviceId); + } + }, [config.playback.audioDeviceId]); + return ( <> diff --git a/src/components/settings/ConfigPanels/PlayerConfig.tsx b/src/components/settings/ConfigPanels/PlayerConfig.tsx index bccaf68..f608e1e 100644 --- a/src/components/settings/ConfigPanels/PlayerConfig.tsx +++ b/src/components/settings/ConfigPanels/PlayerConfig.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { shell } from 'electron'; import settings from 'electron-settings'; import { ControlLabel, Divider, Form } from 'rsuite'; @@ -9,16 +9,24 @@ import { StyledInput, StyledInputGroup, StyledInputNumber, + StyledInputPicker, + StyledInputPickerContainer, StyledLink, StyledPanel, } from '../../shared/styled'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import { setPlaybackSetting } from '../../../redux/playQueueSlice'; import ListViewTable from '../../viewtypes/ListViewTable'; -import { appendPlaybackFilter } from '../../../redux/configSlice'; +import { appendPlaybackFilter, setAudioDeviceId } from '../../../redux/configSlice'; +import { notifyToast } from '../../shared/toast'; const isMacOS = process.platform === 'darwin'; +const getAudioDevice = async () => { + const devices = await navigator.mediaDevices.enumerateDevices(); + return (devices || []).filter((dev: MediaDeviceInfo) => dev.kind === 'audiooutput'); +}; + const playbackFilterColumns = [ { id: '#', @@ -64,18 +72,44 @@ const PlayerConfig = () => { Boolean(settings.getSync('globalMediaHotkeys')) ); const [scrobble, setScrobble] = useState(Boolean(settings.getSync('scrobble'))); + const [audioDevices, setAudioDevices] = useState(); + const audioDevicePickerContainerRef = useRef(null); useEffect(() => { settings.setSync('playbackFilters', config.playback.filters); }, [config.playback.filters]); + useEffect(() => { + const getAudioDevices = () => { + getAudioDevice() + .then((dev) => setAudioDevices(dev)) + .catch(() => notifyToast('error', 'Error fetching audio devices')); + }; + + getAudioDevices(); + }, []); + return (

- Configure the number of seconds to skip forwards/backwards by when clicking the seek - forward/backward buttons. + Set your desired audio device. Leaving this blank will use the system default audio device.


+ + audioDevicePickerContainerRef.current} + data={audioDevices} + defaultValue={config.playback.audioDeviceId} + value={config.playback.audioDeviceId} + labelKey="label" + valueKey="deviceId" + onChange={(e: string) => { + dispatch(setAudioDeviceId(e)); + settings.setSync('audioDeviceId', e); + }} + /> + +
Seek forward (s) { settings.setSync('volume', 0.3); } + if (force || !settings.hasSync('audioDeviceId')) { + settings.setSync('audioDeviceId', null); + } + if (force || !settings.hasSync('seekForwardInterval')) { settings.setSync('seekForwardInterval', 5); } diff --git a/src/redux/configSlice.ts b/src/redux/configSlice.ts index bf59548..6d93cc6 100644 --- a/src/redux/configSlice.ts +++ b/src/redux/configSlice.ts @@ -14,6 +14,7 @@ export interface ConfigPage { }; playback: { filters: PlaybackFilter[]; + audioDeviceId?: string; }; sort: { albumListPage?: SortColumn; @@ -64,6 +65,7 @@ const initialState: ConfigPage = { }, playback: { filters: parsedSettings.playbackFilters, + audioDeviceId: parsedSettings.audioDeviceId || undefined, }, sort: { albumListPage: undefined, @@ -176,6 +178,10 @@ const configSlice = createSlice({ state.playback.filters[selectedFilterIndex] = action.payload.newFilter; }, + setAudioDeviceId: (state, action: PayloadAction) => { + state.playback.audioDeviceId = action.payload; + }, + removePlaybackFilter: (state, action: PayloadAction<{ filterName: string }>) => { state.playback.filters = state.playback.filters.filter( (f: PlaybackFilter) => f.filter !== action.payload.filterName @@ -237,6 +243,7 @@ export const { removePlaybackFilter, setPlaybackFilter, setPlaybackFilters, + setAudioDeviceId, setColumnList, setRowHeight, setFontSize, diff --git a/src/shared/mockSettings.ts b/src/shared/mockSettings.ts index 00c8dde..ede200e 100644 --- a/src/shared/mockSettings.ts +++ b/src/shared/mockSettings.ts @@ -5,6 +5,7 @@ export const mockSettings = { cachePath: 'C:\\Users\\jli\\AppData\\Roaming\\Electron', legacyAuth: false, volume: 0.93, + audioDeviceId: null, seekForwardInterval: 5, seekBackwardInterval: 5, volumeFade: true,