web remote loads and connects but is non functional

This commit is contained in:
booploops 2022-01-17 22:12:23 -08:00
parent 904dc87728
commit fcaa891648
7 changed files with 407 additions and 1 deletions

View file

@ -45,7 +45,7 @@
"run-script-os": "^1.1.6",
"source-map-support": "^0.5.21",
"v8-compile-cache": "^2.3.0",
"ws": "^8.4.0",
"ws": "^8.4.2",
"xml2js": "^0.4.23",
"youtube-search-without-api-key": "^1.0.7"
},

View file

@ -9,6 +9,8 @@ import * as fs from "fs";
import { Stream } from "stream";
import * as qrcode from "qrcode-terminal";
import * as os from "os";
import {wsapi} from "./wsapi";
export class Win {
win: any | undefined = null;
app: any | undefined = null;
@ -189,6 +191,8 @@ export class Win {
* TODO: Broadcast the remote so that /web-remote/ can connect
* https://github.com/ciderapp/Apple-Music-Electron/blob/818ed18940ff600d76eb59d22016723a75885cd5/resources/functions/handler.js#L1173
*/
const ws = new wsapi()
ws.InitWebSockets()
const remote = express();
remote.use(express.static(path.join(this.paths.srcPath, "./web-remote/")))
remote.listen(this.remotePort, () => {

291
src/main/base/wsapi.ts Normal file
View file

@ -0,0 +1,291 @@
// @ts-nocheck
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;
private class standardResponse {
status: number;
message: string;
data: any;
type: string;
}
export class wsapi {
private standa2rdResponse (status, data, message, type: string = "generic") {
this.status = status;
this.message = message;
this.data = data;
this.type = type;
}
port: any = 26369
wss: any = null
clients: []
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, arg) => {
wsapi.updatePlaybackState(arg);
})
electron.ipcMain.on('wsapi-returnQueue', (event, arg) => {
wsapi.returnQueue(JSON.parse(arg));
});
electron.ipcMain.on('wsapi-returnSearch', (event, arg) => {
console.log("SEARCH")
wsapi.returnSearch(JSON.parse(arg));
});
electron.ipcMain.on('wsapi-returnSearchLibrary', (event, arg) => {
wsapi.returnSearchLibrary(JSON.parse(arg));
});
electron.ipcMain.on('wsapi-returnDynamic', (event, arg, type) => {
wsapi.returnDynamic(JSON.parse(arg), type);
});
electron.ipcMain.on('wsapi-returnMusicKitApi', (event, arg, method) => {
wsapi.returnMusicKitApi(JSON.parse(arg), method);
});
electron.ipcMain.on('wsapi-returnLyrics', (event, arg) => {
wsapi.returnLyrics(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 = new standardResponse(0, {}, "OK");
this.wss.on('connection', function connection(ws) {
ws.id = wsapi.createId();
console.log(`Client ${ws.id} connected`)
wsapi.clients.push(ws);
ws.on('message', function incoming(message) {
});
// ws on message
ws.on('message', function incoming(message) {
let data = JSON.parse(message);
let response = new standardResponse(0, {}, "OK");;
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":
electron.app.win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`);
response.message = "Play Next";
break;
case "play-later":
electron.app.win.webContents.executeJavaScript(`wsapi.playLater(\`${data.type}\`,\`${data.id}\`)`);
response.message = "Play Later";
break;
case "quick-play":
electron.app.win.webContents.executeJavaScript(`wsapi.quickPlay(\`${data.term}\`)`);
response.message = "Quick Play";
break;
case "get-lyrics":
electron.app.win.webContents.executeJavaScript(`wsapi.getLyrics()`);
break;
case "shuffle":
electron.app.win.webContents.executeJavaScript(`wsapi.toggleShuffle()`);
break;
case "set-shuffle":
if(data.shuffle == true) {
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 1`);
}else{
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 0`);
}
break;
case "repeat":
electron.app.win.webContents.executeJavaScript(`wsapi.toggleRepeat()`);
break;
case "seek":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(data.time)})`);
response.message = "Seek";
break;
case "pause":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().pause()`);
response.message = "Paused";
break;
case "play":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().play()`);
response.message = "Playing";
break;
case "stop":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().stop()`);
response.message = "Stopped";
break;
case "volume":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(data.volume)}`);
response.message = "Volume";
break;
case "mute":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().mute()`);
response.message = "Muted";
break;
case "unmute":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().unmute()`);
response.message = "Unmuted";
break;
case "next":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().skipToNextItem()`);
response.message = "Next";
break;
case "previous":
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().skipToPreviousItem()`);
response.message = "Previous";
break;
case "musickit-api":
electron.app.win.webContents.executeJavaScript(`wsapi.musickitApi(\`${data.method}\`, \`${data.id}\`, ${JSON.stringify(data.params)})`);
break;
case "musickit-library-api":
break;
case "set-autoplay":
electron.app.win.webContents.executeJavaScript(`wsapi.setAutoplay(${data.autoplay})`);
break;
case "queue-move":
electron.app.win.webContents.executeJavaScript(`wsapi.moveQueueItem(${data.from},${data.to})`);
break;
case "get-queue":
electron.app.win.webContents.executeJavaScript(`wsapi.getQueue()`);
break;
case "search":
if (!data.limit) {
data.limit = 10;
}
electron.app.win.webContents.executeJavaScript(`wsapi.search(\`${data.term}\`, \`${data.limit}\`)`);
break;
case "library-search":
if (!data.limit) {
data.limit = 10;
}
electron.app.win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`);
break;
case "show-window":
electron.app.win.show()
break;
case "hide-window":
electron.app.win.hide()
break;
case "play-mediaitem":
electron.app.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":
electron.app.win.webContents.executeJavaScript(`wsapi.getPlaybackState()`);
break;
}
ws.send(JSON.stringify(response));
});
ws.on('close', function close() {
// remove client from list
wsapi.clients.splice(wsapi.clients.indexOf(ws), 1);
console.log(`Client ${ws.id} disconnected`);
});
ws.send(JSON.stringify(defaultResponse));
});
}
sendToClient(id) {
// replace the clients.forEach with a filter to find the client that requested
}
updatePlaybackState(attr) {
const response = new standardResponse(0, attr, "OK", "playbackStateUpdate");
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
returnMusicKitApi(results, method) {
const response = new standardResponse(0, results, "OK", `musickitapi.${method}`);
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
returnDynamic(results, type) {
const response = new standardResponse(0, results, "OK", type);
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
returnLyrics(results) {
const response = new standardResponse(0, results, "OK", "lyrics");
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
returnSearch(results) {
const response = new standardResponse(0, results, "OK", "searchResults");
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
returnSearchLibrary(results) {
const response = new standardResponse(0, results, "OK", "searchResultsLibrary");
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
returnQueue(queue) {
const response = new standardResponse(0, queue, "OK", "queue");
wsapi.clients.forEach(function each(client) {
client.send(JSON.stringify(response));
});
}
}

View file

@ -0,0 +1,103 @@
const wsapi = {
cache: {playParams: {id: 0}, status: null, remainingTime: 0},
playbackCache: {status: null, time: Date.now()},
search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
})
},
searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
})
},
getAttributes: function () {
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found';
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'};
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found';
attributes.albumName = attributes.albumName ? attributes.albumName : '';
attributes.artistName = attributes.artistName ? attributes.artistName : '';
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.startTime = Date.now();
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
attributes.volume = mk.volume;
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes
},
moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0])
MusicKit.getInstance().queue._reindex()
},
setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value
},
returnDynamic(data, type) {
ipcRenderer.send('wsapi-returnDynamic', JSON.stringify(data), type)
},
musickitApi(method, id, params) {
MusicKit.getInstance().api[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
},
getPlaybackState () {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
},
getLyrics() {
return []
_lyrics.GetLyrics(1, false)
},
getQueue() {
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue))
},
playNext(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playNext(request)
},
playLater(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playLater(request)
},
love() {
},
playTrackById(id, kind = "song") {
MusicKit.getInstance().setQueue({ [kind]: id }).then(function (queue) {
MusicKit.getInstance().play()
})
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"] }).then(function (queue) {
MusicKit.getInstance().play()
})
})
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
},
toggleRepeat() {
if(MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 2
}else if(MusicKit.getInstance().repeatMode == 2){
MusicKit.getInstance().repeatMode = 1
}else{
MusicKit.getInstance().repeatMode = 0
}
}
}

View file

@ -610,10 +610,16 @@ const app = new Vue({
}
})
this.mk.addEventListener(MusicKit.Events.playbackStateDidChange, ()=>{
ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
})
this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => {
self.lyriccurrenttime = self.mk.currentPlaybackTime
this.currentSongInfo = a
self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime)
// wsapi
ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
})
this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {

View file

@ -710,5 +710,6 @@
<script src="index.js?v=1"></script>
<script src="https://cdn.jsdelivr.net/npm/resonance-audio/build/resonance-audio.min.js"></script>
<script src="/audio/audio.js?v=1"></script>
<script src="/WSAPI_Interop.js"></script>
</body>
</html>

View file

@ -504,6 +504,7 @@ var app = new Vue({
}
socket.onmessage = (e) => {
console.log(e.data)
const response = JSON.parse(e.data);
switch (response.type) {
default: console.log(response);