[enhancement]: support viewing current/setting current time in remote

This commit is contained in:
Kendall Garner 2024-08-24 13:26:45 -07:00
parent b347b794b9
commit 5b2977e5e8
No known key found for this signature in database
GPG key ID: 18D2767419676C87
9 changed files with 61 additions and 13 deletions

View file

@ -34,13 +34,14 @@ interface MimeType {
js: string; js: string;
} }
interface StatefulWebSocket extends WebSocket { declare class StatefulWebSocket extends WebSocket {
alive: boolean; alive: boolean;
auth: boolean; auth: boolean;
} }
let server: Server | undefined; let server: Server | undefined;
let wsServer: WsServer<StatefulWebSocket> | undefined; let wsServer: WsServer<typeof StatefulWebSocket> | undefined;
const settings: RemoteConfig = { const settings: RemoteConfig = {
enabled: false, enabled: false,
@ -327,9 +328,9 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
}); });
server.listen(config.port, resolve); server.listen(config.port, resolve);
wsServer = new WebSocketServer({ server }); wsServer = new WebSocketServer<typeof StatefulWebSocket>({ server });
wsServer.on('connection', (ws) => { wsServer!.on('connection', (ws: StatefulWebSocket) => {
let authFail: number | undefined; let authFail: number | undefined;
ws.alive = true; ws.alive = true;
@ -471,6 +472,15 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
} }
break; break;
} }
case 'position': {
const { position } = json;
if (mprisPlayer) {
mprisPlayer.getPosition = () => position * 1e6;
}
getMainWindow()?.webContents.send('request-position', {
position,
});
}
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -496,7 +506,7 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
}); });
}, PING_TIMEOUT_MS); }, PING_TIMEOUT_MS);
wsServer.on('close', () => { wsServer!.on('close', () => {
clearInterval(heartBeat); clearInterval(heartBeat);
}); });
@ -649,3 +659,8 @@ if (mprisPlayer) {
broadcast({ data: volume, event: 'volume' }); broadcast({ data: volume, event: 'volume' });
}); });
} }
ipcMain.on('update-position', (_event, position: number) => {
currentState.position = position;
broadcast({ data: position, event: 'position' });
});

View file

@ -106,7 +106,7 @@ mprisPlayer.on('seek', (event: number) => {
}); });
}); });
ipcMain.on('mpris-update-position', (_event, arg) => { ipcMain.on('update-position', (_event, arg: number) => {
mprisPlayer.getPosition = () => arg * 1e6; mprisPlayer.getPosition = () => arg * 1e6;
}); });

View file

@ -84,6 +84,10 @@ const updateVolume = (volume: number) => {
ipcRenderer.send('update-volume', volume); ipcRenderer.send('update-volume', volume);
}; };
const updatePosition = (timeSec: number) => {
ipcRenderer.send('update-position', timeSec);
};
export const remote = { export const remote = {
requestFavorite, requestFavorite,
requestPosition, requestPosition,
@ -95,6 +99,7 @@ export const remote = {
updateFavorite, updateFavorite,
updatePassword, updatePassword,
updatePlayback, updatePlayback,
updatePosition,
updateRating, updateRating,
updateRepeat, updateRepeat,
updateSetting, updateSetting,

View file

@ -21,7 +21,7 @@ import { Tooltip } from '/@/renderer/components/tooltip';
import { Rating } from '/@/renderer/components/rating'; import { Rating } from '/@/renderer/components/rating';
export const RemoteContainer = () => { export const RemoteContainer = () => {
const { repeat, shuffle, song, status, volume } = useInfo(); const { position, repeat, shuffle, song, status, volume } = useInfo();
const send = useSend(); const send = useSend();
const showImage = useShowImage(); const showImage = useShowImage();
@ -154,6 +154,16 @@ export const RemoteContainer = () => {
</div> </div>
)} )}
</Group> </Group>
{id && position !== undefined && (
<WrapperSlider
label={(value) => formatDuration(value * 1e3)}
leftLabel={formatDuration(position * 1e3)}
max={song.duration / 1e3}
rightLabel={formatDuration(song.duration)}
value={position}
onChangeEnd={(e) => send({ event: 'position', position: e })}
/>
)}
<WrapperSlider <WrapperSlider
leftLabel={<RiVolumeUpFill size={20} />} leftLabel={<RiVolumeUpFill size={20} />}
max={100} max={100}

View file

@ -139,6 +139,12 @@ export const useRemoteStore = create<SettingsSlice>()(
}); });
break; break;
} }
case 'position': {
set((state) => {
state.info.position = data;
});
break;
}
case 'proxy': { case 'proxy': {
set((state) => { set((state) => {
if (state.info.song) { if (state.info.song) {

View file

@ -2,6 +2,7 @@ import type { QueueSong } from '/@/renderer/api/types';
import type { PlayerRepeat, PlayerStatus, SongState } from '/@/renderer/types'; import type { PlayerRepeat, PlayerStatus, SongState } from '/@/renderer/types';
export interface SongUpdateSocket extends Omit<SongState, 'song'> { export interface SongUpdateSocket extends Omit<SongState, 'song'> {
position?: number;
song?: QueueSong | null; song?: QueueSong | null;
} }
@ -20,6 +21,10 @@ export interface ServerPlayStatus {
event: 'playback'; event: 'playback';
} }
export interface ServerPosition {
data: number;
event: 'position';
}
export interface ServerProxy { export interface ServerProxy {
data: string; data: string;
event: 'proxy'; event: 'proxy';
@ -59,6 +64,7 @@ export type ServerEvent =
| ServerError | ServerError
| ServerFavorite | ServerFavorite
| ServerPlayStatus | ServerPlayStatus
| ServerPosition
| ServerRating | ServerRating
| ServerRepeat | ServerRepeat
| ServerShuffle | ServerShuffle
@ -93,8 +99,14 @@ export interface ClientAuth {
header: string; header: string;
} }
export interface ClientPosition {
event: 'position';
position: number;
}
export type ClientEvent = export type ClientEvent =
| ClientAuth | ClientAuth
| ClientPosition
| ClientSimpleEvent | ClientSimpleEvent
| ClientFavorite | ClientFavorite
| ClientRating | ClientRating

View file

@ -131,16 +131,15 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
const formattedTime = formatDuration(currentTime * 1000 || 0); const formattedTime = formatDuration(currentTime * 1000 || 0);
useEffect(() => { useEffect(() => {
let interval: any; let interval: ReturnType<typeof setInterval>;
if (status === PlayerStatus.PLAYING && !isSeeking) { if (status === PlayerStatus.PLAYING && !isSeeking) {
if (!isElectron() || playbackType === PlaybackType.WEB) { if (!isElectron() || playbackType === PlaybackType.WEB) {
// Update twice a second for slightly better performance
interval = setInterval(() => { interval = setInterval(() => {
setCurrentTime(currentPlayerRef.getCurrentTime()); setCurrentTime(currentPlayerRef.getCurrentTime());
}, 1000); }, 500);
} }
} else {
clearInterval(interval);
} }
return () => clearInterval(interval); return () => clearInterval(interval);

View file

@ -669,11 +669,11 @@ export const useCenterControls = (args: { playersRef: any }) => {
]); ]);
useEffect(() => { useEffect(() => {
if (utils?.isLinux()) { if (remote) {
const unsubCurrentTime = usePlayerStore.subscribe( const unsubCurrentTime = usePlayerStore.subscribe(
(state) => state.current.time, (state) => state.current.time,
(time) => { (time) => {
mpris?.updatePosition(time); remote.updatePosition(time);
}, },
); );

View file

@ -212,6 +212,7 @@ export type GridCardData = {
}; };
export type SongState = { export type SongState = {
position?: number;
repeat?: PlayerRepeat; repeat?: PlayerRepeat;
shuffle?: boolean; shuffle?: boolean;
song?: QueueSong; song?: QueueSong;