import * as ws from "ws"; import * as http from "http"; import * as https from "https"; import * as url from "url"; import * as fs from "fs"; import * as path from "path"; import * as electron from "electron"; const WebSocket = ws; const WebSocketServer = ws.Server; interface standardResponse { status?: Number, message?: String, data?: any, type?: string, } export class wsapi { static clients: any; port: any = 26369 wss: any = null clients: any = [] private _win : any; constructor(win : any) { this._win = win; } createId() { // create random guid return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } public async InitWebSockets () { electron.ipcMain.on('wsapi-updatePlaybackState', (event :any, arg :any) => { this.updatePlaybackState(arg); }) electron.ipcMain.on('wsapi-returnQueue', (event :any, arg :any) => { this.returnQueue(JSON.parse(arg)); }); electron.ipcMain.on('wsapi-returnSearch', (event :any, arg :any) => { console.log("SEARCH") this.returnSearch(JSON.parse(arg)); }); electron.ipcMain.on('wsapi-returnSearchLibrary', (event :any, arg :any) => { this.returnSearchLibrary(JSON.parse(arg)); }); electron.ipcMain.on('wsapi-returnDynamic', (event :any, arg :any, type :any) => { this.returnDynamic(JSON.parse(arg), type); }); electron.ipcMain.on('wsapi-returnMusicKitApi', (event :any, arg :any, method :any) => { this.returnMusicKitApi(JSON.parse(arg), method); }); electron.ipcMain.on('wsapi-returnLyrics', (event :any, arg :any) => { this.returnLyrics(JSON.parse(arg)); }); electron.ipcMain.on('wsapi-returnvolumeMax', (_event: any, arg: any) => { this.returnmaxVolume(JSON.parse(arg)); }); this.wss = new WebSocketServer({ port: this.port, perMessageDeflate: { zlibDeflateOptions: { // See zlib defaults. chunkSize: 1024, memLevel: 7, level: 3 }, zlibInflateOptions: { chunkSize: 10 * 1024 }, // Other options settable: clientNoContextTakeover: true, // Defaults to negotiated value. serverNoContextTakeover: true, // Defaults to negotiated value. serverMaxWindowBits: 10, // Defaults to negotiated value. // Below options specified as default values. concurrencyLimit: 10, // Limits zlib concurrency for perf. threshold: 1024 // Size (in bytes) below which messages // should not be compressed if context takeover is disabled. } }) console.log(`WebSocketServer started on port: ${this.port}`); const defaultResponse :standardResponse = {status :0, data:{}, message:"OK", type:"generic"}; this.wss.on('connection', (ws : any) => { ws.id = this.createId(); console.log(`Client ${ws.id} connected`) this.clients.push(ws); ws.on('message', function incoming(message : any) { }); // ws on message ws.on('message', (message : any) => { let data = JSON.parse(message); let response :standardResponse = {status :0, data:{}, message:"OK", type:"generic"}; if (data.action) { data.action.toLowerCase(); } switch (data.action) { default: response.message = "Action not found"; break; case "identify": response.message = "Thanks for identifying!" response.data = { id: ws.id } ws.identity = { name: data.name, author: data.author, description: data.description, version: data.version } break; case "play-next": this._win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`); response.message = "Play Next"; break; case "play-later": this._win.webContents.executeJavaScript(`wsapi.playLater(\`${data.type}\`,\`${data.id}\`)`); response.message = "Play Later"; break; case "quick-play": this._win.webContents.executeJavaScript(`wsapi.quickPlay(\`${data.term}\`)`); response.message = "Quick Play"; break; case "get-lyrics": this._win.webContents.executeJavaScript(`wsapi.getLyrics()`); break; case "shuffle": this._win.webContents.executeJavaScript(`wsapi.toggleShuffle()`); break; case "set-shuffle": if(data.shuffle == true) { this._win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 1`); }else{ this._win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 0`); } break; case "repeat": this._win.webContents.executeJavaScript(`wsapi.toggleRepeat()`); break; case "seek": this._win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(data.time)})`); response.message = "Seek"; break; case "pause": this._win.webContents.executeJavaScript(`MusicKit.getInstance().pause()`); response.message = "Paused"; break; case "play": this._win.webContents.executeJavaScript(`MusicKit.getInstance().play()`); response.message = "Playing"; break; case "stop": this._win.webContents.executeJavaScript(`MusicKit.getInstance().stop()`); response.message = "Stopped"; break; case "volumeMax": this._win.webContents.executeJavaScript(`wsapi.getmaxVolume()`); response.message = "maxVolume"; break; case "volume": this._win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(data.volume)}`); response.message = "Volume"; break; case "mute": this._win.webContents.executeJavaScript(`MusicKit.getInstance().mute()`); response.message = "Muted"; break; case "unmute": this._win.webContents.executeJavaScript(`MusicKit.getInstance().unmute()`); response.message = "Unmuted"; break; case "next": this._win.webContents.executeJavaScript(`if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) { try { app.prevButtonBackIndicator = false; } catch (e) { } MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex);}`); response.message = "Next"; break; case "previous": this._win.webContents.executeJavaScript(`if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex)}`); response.message = "Previous"; break; case "musickit-api": this._win.webContents.executeJavaScript(`wsapi.musickitApi(\`${data.method}\`, \`${data.id}\`, ${JSON.stringify(data.params)} , ${data.library})`); break; case "musickit-library-api": break; case "set-autoplay": this._win.webContents.executeJavaScript(`wsapi.setAutoplay(${data.autoplay})`); break; case "queue-move": this._win.webContents.executeJavaScript(`wsapi.moveQueueItem(${data.from},${data.to})`); break; case "get-queue": this._win.webContents.executeJavaScript(`wsapi.getQueue()`); break; case "search": if (!data.limit) { data.limit = 10; } this._win.webContents.executeJavaScript(`wsapi.search(\`${data.term}\`, \`${data.limit}\`)`); break; case "library-search": if (!data.limit) { data.limit = 10; } this._win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`); break; case "show-window": this._win.show() break; case "hide-window": this._win.hide() break; case "play-mediaitem": this._win.webContents.executeJavaScript(`wsapi.playTrackById("${data.id}", \`${data.kind}\`)`); response.message = "Playing track"; break; case "get-status": response.data = { isAuthorized: true }; response.message = "Status"; break; case "get-currentmediaitem": this._win.webContents.executeJavaScript(`wsapi.getPlaybackState()`); break; case "quit": electron.app.quit(); break; } ws.send(JSON.stringify(response)); }); ws.on('close', () => { // remove client from list this.clients.splice(wsapi.clients.indexOf(ws), 1); console.log(`Client ${ws.id} disconnected`); }); ws.send(JSON.stringify(defaultResponse)); }); } sendToClient(id : any) { // replace the clients.forEach with a filter to find the client that requested } updatePlaybackState(attr : any) { const response : standardResponse = {status: 0, data: attr, message: "OK", type:"playbackStateUpdate"}; this.clients.forEach(function each(client: any) { client.send(JSON.stringify(response)); }); } returnMusicKitApi(results :any, method :any) { const response : standardResponse = {status :0, data: results, message:"OK", type:`musickitapi.${method}`}; this.clients.forEach(function each(client :any) { client.send(JSON.stringify(response)); }); } returnDynamic(results :any, type :any) { const response : standardResponse = {status :0, data: results, message: "OK", type: type}; this.clients.forEach(function each(client :any) { client.send(JSON.stringify(response)); }); } returnLyrics(results :any) { const response : standardResponse = {status :0, data: results, message: "OK", type: "lyrics"}; this.clients.forEach(function each(client :any) { client.send(JSON.stringify(response)); }); } returnSearch(results :any) { const response : standardResponse = {status :0, data: results, message: "OK", type: "searchResults"}; this.clients.forEach(function each(client :any) { client.send(JSON.stringify(response)); }); } returnSearchLibrary(results :any) { const response: standardResponse = {status :0, data :results, message:"OK", type:"searchResultsLibrary"}; this.clients.forEach(function each(client :any) { client.send(JSON.stringify(response)); }); } returnQueue(queue :any) { const response : standardResponse = {status :0,data :queue, message:"OK", type:"queue"}; this.clients.forEach(function each(client :any) { client.send(JSON.stringify(response)); }); } returnmaxVolume(vol: any) { const response: standardResponse = {status: 0, data: vol, message: "OK", type: "maxVolume"}; this.clients.forEach(function each(client: any) { client.send(JSON.stringify(response)); }); } }