[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;
}
interface StatefulWebSocket extends WebSocket {
declare class StatefulWebSocket extends WebSocket {
alive: boolean;
auth: boolean;
}
let server: Server | undefined;
let wsServer: WsServer<StatefulWebSocket> | undefined;
let wsServer: WsServer<typeof StatefulWebSocket> | undefined;
const settings: RemoteConfig = {
enabled: false,
@ -327,9 +328,9 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
});
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;
ws.alive = true;
@ -471,6 +472,15 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
}
break;
}
case 'position': {
const { position } = json;
if (mprisPlayer) {
mprisPlayer.getPosition = () => position * 1e6;
}
getMainWindow()?.webContents.send('request-position', {
position,
});
}
}
} catch (error) {
console.error(error);
@ -496,7 +506,7 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
});
}, PING_TIMEOUT_MS);
wsServer.on('close', () => {
wsServer!.on('close', () => {
clearInterval(heartBeat);
});
@ -649,3 +659,8 @@ if (mprisPlayer) {
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;
});

View file

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

View file

@ -21,7 +21,7 @@ import { Tooltip } from '/@/renderer/components/tooltip';
import { Rating } from '/@/renderer/components/rating';
export const RemoteContainer = () => {
const { repeat, shuffle, song, status, volume } = useInfo();
const { position, repeat, shuffle, song, status, volume } = useInfo();
const send = useSend();
const showImage = useShowImage();
@ -154,6 +154,16 @@ export const RemoteContainer = () => {
</div>
)}
</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
leftLabel={<RiVolumeUpFill size={20} />}
max={100}

View file

@ -139,6 +139,12 @@ export const useRemoteStore = create<SettingsSlice>()(
});
break;
}
case 'position': {
set((state) => {
state.info.position = data;
});
break;
}
case 'proxy': {
set((state) => {
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';
export interface SongUpdateSocket extends Omit<SongState, 'song'> {
position?: number;
song?: QueueSong | null;
}
@ -20,6 +21,10 @@ export interface ServerPlayStatus {
event: 'playback';
}
export interface ServerPosition {
data: number;
event: 'position';
}
export interface ServerProxy {
data: string;
event: 'proxy';
@ -59,6 +64,7 @@ export type ServerEvent =
| ServerError
| ServerFavorite
| ServerPlayStatus
| ServerPosition
| ServerRating
| ServerRepeat
| ServerShuffle
@ -93,8 +99,14 @@ export interface ClientAuth {
header: string;
}
export interface ClientPosition {
event: 'position';
position: number;
}
export type ClientEvent =
| ClientAuth
| ClientPosition
| ClientSimpleEvent
| ClientFavorite
| ClientRating

View file

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

View file

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

View file

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