Skip to content

Commit

Permalink
feat(api-server): add endpoint to get shuffle state (#2792)
Browse files Browse the repository at this point in the history
  • Loading branch information
franz-dc authored Jan 18, 2025
1 parent 3339f99 commit 368b251
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 6 deletions.
38 changes: 37 additions & 1 deletion src/plugins/api-server/backend/routes/control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,24 @@ const routes = {
},
},
}),

getShuffleState: createRoute({
method: 'get',
path: `/api/${API_VERSION}/shuffle`,
summary: 'get shuffle state',
description: 'Get the current shuffle state',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({
state: z.boolean().nullable(),
}),
},
},
},
},
}),
shuffle: createRoute({
method: 'post',
path: `/api/${API_VERSION}/shuffle`,
Expand Down Expand Up @@ -581,6 +598,25 @@ export const register = (
ctx.status(204);
return ctx.body(null);
});

app.openapi(routes.getShuffleState, async (ctx) => {
const stateResponsePromise = new Promise<boolean>((resolve) => {
ipcMain.once(
'ytmd:get-shuffle-response',
(_, isShuffled: boolean | undefined) => {
return resolve(!!isShuffled);
},
);

controller.requestShuffleInformation();
});

const isShuffled = await stateResponsePromise;

ctx.status(200);
return ctx.json({ state: isShuffled });
});

app.openapi(routes.shuffle, (ctx) => {
controller.shuffle();

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/shortcuts/mpris-service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ declare module '@jellybrick/mpris-service' {
supportedMimeTypes: string[];
canQuit: boolean;
canRaise: boolean;
canSetFullscreen?: boolean;
canUsePlayerControls?: boolean;
desktopEntry?: string;
hasTrackList: boolean;

Expand Down
25 changes: 21 additions & 4 deletions src/plugins/shortcuts/mpris.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function setupMPRIS() {

instance.canRaise = true;
instance.canQuit = false;
instance.canSetFullscreen = true;
instance.canUsePlayerControls = true;
instance.supportedUriSchemes = ['http', 'https'];
instance.desktopEntry = 'youtube-music';
return instance;
Expand All @@ -93,6 +93,7 @@ function registerMPRIS(win: BrowserWindow) {
shuffle,
switchRepeat,
setFullscreen,
requestShuffleInformation,
requestFullscreenInformation,
requestQueueInformation,
} = songControls;
Expand Down Expand Up @@ -126,8 +127,10 @@ function registerMPRIS(win: BrowserWindow) {
win.webContents.send('ytmd:setup-time-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-repeat-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-volume-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-shuffle-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-fullscreen-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-autoplay-changed-listener', 'mpris');
requestShuffleInformation();
requestFullscreenInformation();
requestQueueInformation();
});
Expand Down Expand Up @@ -156,8 +159,16 @@ function registerMPRIS(win: BrowserWindow) {
requestQueueInformation();
});

ipcMain.on('ytmd:shuffle-changed', (_, shuffleEnabled: boolean) => {
if (player.shuffle === undefined || !player.canUsePlayerControls) {
return;
}

player.shuffle = shuffleEnabled ?? !player.shuffle;
});

ipcMain.on('ytmd:fullscreen-changed', (_, changedTo: boolean) => {
if (player.fullscreen === undefined || !player.canSetFullscreen) {
if (player.fullscreen === undefined || !player.canUsePlayerControls) {
return;
}

Expand All @@ -168,7 +179,7 @@ function registerMPRIS(win: BrowserWindow) {
ipcMain.on(
'ytmd:set-fullscreen',
(_, isFullscreen: boolean | undefined) => {
if (!player.canSetFullscreen || isFullscreen === undefined) {
if (!player.canUsePlayerControls || isFullscreen === undefined) {
return;
}

Expand All @@ -179,7 +190,7 @@ function registerMPRIS(win: BrowserWindow) {
ipcMain.on(
'ytmd:fullscreen-changed-supported',
(_, isFullscreenSupported: boolean) => {
player.canSetFullscreen = isFullscreenSupported;
player.canUsePlayerControls = isFullscreenSupported;
},
);
ipcMain.on('ytmd:autoplay-changed', (_) => {
Expand Down Expand Up @@ -272,6 +283,12 @@ function registerMPRIS(win: BrowserWindow) {
player.on('position', seekTo);

player.on('shuffle', (enableShuffle) => {
if (!player.canUsePlayerControls || enableShuffle === undefined) {
return;
}

player.shuffle = enableShuffle;

if (enableShuffle) {
shuffle();
requestQueueInformation();
Expand Down
3 changes: 3 additions & 0 deletions src/providers/song-controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export default (win: BrowserWindow) => {
win.webContents.send('ytmd:seek-by', seconds);
}
},
requestShuffleInformation: () => {
win.webContents.send('ytmd:get-shuffle');
},
shuffle: () => win.webContents.send('ytmd:shuffle'),
switchRepeat: (n: ArgsType<number> = 1) => {
const repeat = parseNumberFromArgsType(n);
Expand Down
26 changes: 26 additions & 0 deletions src/providers/song-info-front.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ export const setupVolumeChangedListener = singleton((api: YoutubePlayer) => {
window.ipcRenderer.send('ytmd:volume-changed', api.getVolume());
});

export const setupShuffleChangedListener = singleton(() => {
const playerBar = document.querySelector('ytmusic-player-bar');

if (!playerBar) {
window.ipcRenderer.send('ytmd:shuffle-changed-supported', false);
return;
}

const observer = new MutationObserver(() => {
window.ipcRenderer.send(
'ytmd:shuffle-changed',
(playerBar?.attributes.getNamedItem('shuffle-on') ?? null) !== null,
);
});

observer.observe(playerBar, {
attributes: true,
childList: false,
subtree: false,
});
});

export const setupFullScreenChangedListener = singleton(() => {
const playerBar = document.querySelector('ytmusic-player-bar');

Expand Down Expand Up @@ -139,6 +161,10 @@ export default (api: YoutubePlayer) => {
setupVolumeChangedListener(api);
});

window.ipcRenderer.on('ytmd:setup-shuffle-changed-listener', () => {
setupShuffleChangedListener();
});

window.ipcRenderer.on('ytmd:setup-fullscreen-changed-listener', () => {
setupFullScreenChangedListener();
});
Expand Down
14 changes: 14 additions & 0 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ async function onApiLoaded() {
>('ytmusic-player-bar')
?.queue.shuffle();
});

const isShuffled = () => {
const isShuffled =
document
.querySelector<HTMLElement>('ytmusic-player-bar')
?.attributes.getNamedItem('shuffle-on') ?? null;

return isShuffled !== null;
};

window.ipcRenderer.on('ytmd:get-shuffle', () => {
window.ipcRenderer.send('ytmd:get-shuffle-response', isShuffled());
});

window.ipcRenderer.on(
'ytmd:update-like',
(_, status: 'LIKE' | 'DISLIKE' = 'LIKE') => {
Expand Down

0 comments on commit 368b251

Please sign in to comment.