Skip to content

Commit

Permalink
feat: moving to api controller client plugin for controlling player api
Browse files Browse the repository at this point in the history
  • Loading branch information
Venipa committed Nov 25, 2024
1 parent 63cc43e commit ce691cb
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv;
}

type StringLiteral<KnownValues extends string> = (string & {}) | KnownValues;
22 changes: 5 additions & 17 deletions src/main/plugins/apiProvider.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,7 @@ export default class ApiProvider extends BaseProvider implements AfterInit, OnDe
}
@IpcHandle(API_ROUTES.TRACK_CONTROL_NEXT)
async nextTrack() {
await this.views.youtubeView.webContents.executeJavaScript(
`(el => el && el.click())(document.querySelector(".ytmusic-player-bar.next-button"))`,
);
return await this.windowContext.sendTrackControl("next")
}
@IpcHandle(API_ROUTES.TRACK_CONTROL_FORWARD)
async forwardTrack(_ev, data) {
Expand Down Expand Up @@ -152,28 +150,18 @@ export default class ApiProvider extends BaseProvider implements AfterInit, OnDe
}
@IpcHandle(API_ROUTES.TRACK_CONTROL_PREV)
async prevTrack() {
await this.views.youtubeView.webContents.executeJavaScript(
`(el => el && el.click())(document.querySelector(".ytmusic-player-bar.previous-button"))`,
);
return await this.windowContext.sendTrackControl("prev")
}
@IpcHandle(API_ROUTES.TRACK_CONTROL_PLAY)
async playTrack() {
if (this.trackProvider.playState === "paused")
await this.views.youtubeView.webContents.executeJavaScript(
`(el => el && el.click())(document.querySelector(".ytmusic-player-bar#play-pause-button"))`,
);
return await this.windowContext.sendTrackControl("play")
}
@IpcHandle(API_ROUTES.TRACK_CONTROL_PAUSE)
async pauseTrack() {
if (this.trackProvider.playState === "playing")
await this.views.youtubeView.webContents.executeJavaScript(
`(el => el && el.click())(document.querySelector(".ytmusic-player-bar#play-pause-button"))`,
);
return await this.windowContext.sendTrackControl("pause")
}
@IpcHandle(API_ROUTES.TRACK_CONTROL_TOGGLE_PLAY)
async toggleTrackPlayback() {
if (this.trackProvider.playState === "playing") return this.pauseTrack();
else if (this.trackProvider.playState === "paused") return this.playTrack();
return Promise.resolve(null);
return await this.windowContext.sendTrackControl("toggle")
}
}
15 changes: 11 additions & 4 deletions src/main/plugins/client/track-api-controls.plugin.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
export const meta = {
name: "Api Control Handler",
};

// todo
const trackControls = {
toggle: () =>
((el) => el && el.click())(document.querySelector(".ytmusic-player-bar#play-pause-button")),
toggle: player => {
const state = player.getPlayerStateObject();
if (!state) return;
return (state.isPlaying || state.isOrWillBePlaying) ? player.stopVideo() : player.playVideo()
},
play: playerApi => playerApi.playVideo(),
pause: playerApi => playerApi.stopVideo(),
next: playerApi => playerApi.nextVideo(),
prev: playerApi => playerApi.previousVideo()
};
export const afterInit = () => {
window.domUtils.ensureDomLoaded(() => {
Expand All @@ -27,7 +33,8 @@ export const afterInit = () => {
const { type } = data;
const handler = trackControls[type];
if (!handler) return;
handler();
const playerApi = window.domUtils.playerApi();
window.api.sendToHost("track:control/response", type, handler(playerApi))
});
});
};
4 changes: 2 additions & 2 deletions src/main/plugins/windowProvider.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export default class WindowUtilsProvider
return null;
}
}
@IpcOn("set-ignore-mouse-events")
private _ignoreMouseEventsFromWebContents(event: IpcMainEvent, ignore: boolean, options: IgnoreMouseEventsOptions) {
@IpcOn("toolbar/set-ignore-mouse-events")
private _toolbarMouseEvent(event: IpcMainEvent, ignore: boolean, options: IgnoreMouseEventsOptions) {
const win = BrowserWindow.fromWebContents(event.sender)
win.setIgnoreMouseEvents(ignore, options)
}
Expand Down
1 change: 1 addition & 0 deletions src/main/utils/eventNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const IPC_EVENT_NAMES = {
TRACK_TITLE_CHANGE: "track:title",
TRACK_CHANGE: "track:change",
TRACK_PLAYSTATE: "track:play-state",
TRACK_CONTROL: "track:control",
APP_UPDATE: "app.update",
APP_UPDATE_CHECKING: "app.updateChecking",
APP_UPDATE_PROGRESS: "app.updateProgress",
Expand Down
10 changes: 10 additions & 0 deletions src/main/utils/mappedWindow.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { ipcPromise } from "@shared/utils/promises";
import { BrowserWindow, WebContentsView } from "electron";
import IPC_EVENT_NAMES from "./eventNames";

type TrackControlTypes = StringLiteral<("play" | "pause" | "next" | "prev" | "toggle")>
type TrackControlFn = <T = any>(type: TrackControlTypes) => Promise<T>;
export interface BrowserWindowViews<T, TView extends WebContentsView = WebContentsView> {
main: BrowserWindow;
views: { [key: string]: TView } & T;
sendToAllViews(ev: string, ...args: any[]): void;
sendTrackControl: TrackControlFn
}

export function getViewObject(bwv: { [key: string]: WebContentsView }) {
Expand All @@ -20,6 +25,11 @@ export function createWindowContext<T, TView extends WebContentsView = WebConten
return new (class implements BrowserWindowViews<T, TView> {
main: BrowserWindow = _data.main;
views: { [key: string]: TView } & T = _data.views || ({} as any);
async sendTrackControl<T = any>(type: TrackControlTypes) {
const view = this.views.youtubeView;
if (!view) return Promise.reject(new Error("View not found"))
return await ipcPromise<T>(view, IPC_EVENT_NAMES.TRACK_CONTROL, {type})
};
sendToAllViews(ev: string, ...args: any[]): void {
return (Object.values(this.views) as TView[])
.filter(
Expand Down
26 changes: 26 additions & 0 deletions src/shared/utils/promises.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
import { ipcMain, WebContentsView } from "electron";

export async function waitMs(ms?: number) {
return await new Promise<void>((resolve) => setTimeout(resolve, ms));
}
/**
* custom ipc promise handler
* @param view view of ipc promise
* @param channel channel name to send the request to, response channel will be created by suffix "/response"
* @param args
* @returns
*/
export async function ipcPromise<T = any, R = T>(view: WebContentsView, channel: string, ...args: any[]) {
let _timeout: any;
view.webContents.send(channel, ...args);
return await new Promise<T>((resolve, reject) => {
const responseChannelName = channel + "/response";
const handler: any = (_ev: any, data: any) => {
ipcMain.off(responseChannelName, handler);
if (_timeout) clearTimeout(_timeout)
resolve(data as T)
}
ipcMain.on(responseChannelName, handler)
_timeout = setTimeout(() => {
ipcMain.off(responseChannelName, handler);
reject(new Error("IPC Promise timeout."))
}, 10000);
})
}

0 comments on commit ce691cb

Please sign in to comment.