Integrate WMS music widget
This commit is contained in:
parent
29bd7822a3
commit
347e5348b4
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@ dist
|
||||
node_modules
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
|
||||
config/local*
|
@ -10,5 +10,11 @@ export default {
|
||||
midi: {
|
||||
controller: 'Launchkey Mini MIDI 2',
|
||||
output: 'Launchkey Mini MIDI 2',
|
||||
}
|
||||
},
|
||||
wms: {
|
||||
music_widget: {
|
||||
ws: 'wss://watch-my.stream/widgets/music/stream',
|
||||
token: 'default',
|
||||
},
|
||||
},
|
||||
};
|
@ -10,6 +10,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.0.23",
|
||||
"@types/ws": "^7.2.6",
|
||||
"typescript": "^3.9.7"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -17,6 +18,7 @@
|
||||
"config": "^3.3.1",
|
||||
"jzz": "^1.0.8",
|
||||
"obs-websocket-js": "^4.0.1",
|
||||
"ts-node": "^8.10.2"
|
||||
"ts-node": "^8.10.2",
|
||||
"ws": "^7.3.1"
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import jzz from "jzz";
|
||||
import ObsWebSocket from "obs-websocket-js";
|
||||
import LedState from "./LedState";
|
||||
import ObsStateTracker from "./obs/ObsStateTracker";
|
||||
import WMSMusicWidget from "./wms/WMSMusicWidget";
|
||||
|
||||
export default class App {
|
||||
private obs: ObsWebSocket = new ObsWebSocket();
|
||||
@ -12,6 +13,7 @@ export default class App {
|
||||
private controls: MidiControl[] = [];
|
||||
private midiIn: any;
|
||||
private pendingStates: LedState[] = [];
|
||||
private readonly wmsMusicWidget: WMSMusicWidget = new WMSMusicWidget();
|
||||
|
||||
public constructor() {
|
||||
|
||||
@ -25,6 +27,7 @@ export default class App {
|
||||
await this.initObs();
|
||||
await this.obsStateTracker.init(this);
|
||||
await this.initMidi();
|
||||
await this.wmsMusicWidget.start();
|
||||
}
|
||||
|
||||
public async stop(): Promise<void> {
|
||||
|
107
src/wms/WMSMusicWidget.ts
Normal file
107
src/wms/WMSMusicWidget.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import config from "config";
|
||||
import WebSocket from "ws";
|
||||
import child_process from "child_process";
|
||||
|
||||
const metadataSeparator = '\n';
|
||||
/**
|
||||
* Watch My Stream (watch-my.stream) music widget
|
||||
*/
|
||||
export default class WMSMusicWidget {
|
||||
private readonly webSocketAddress: string = config.get<string>('wms.music_widget.ws');
|
||||
private readonly token: string = config.get<string>('wms.music_widget.token');
|
||||
private activeSocket?: WebSocket;
|
||||
private checkInterval?: NodeJS.Timeout;
|
||||
private lastSentData?: string;
|
||||
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
|
||||
public async start(): Promise<void> {
|
||||
if (!this.token || this.token === 'default') {
|
||||
console.warn('WMS music widget not started due to missing token.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.startWebSocket();
|
||||
|
||||
this.checkInterval = setInterval(async () => {
|
||||
try {
|
||||
if (this.activeSocket) {
|
||||
const metadata = (await this.getInfo()).split(metadataSeparator);
|
||||
const data = JSON.stringify({
|
||||
playing: metadata[0] === 'Playing',
|
||||
author: metadata[1],
|
||||
title: metadata[2],
|
||||
album: metadata[3],
|
||||
artUrl: metadata[4],
|
||||
});
|
||||
if (this.lastSentData !== data) {
|
||||
console.log('WMS music widget out:', data);
|
||||
this.activeSocket.send(data);
|
||||
this.lastSentData = data;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
public async stop(): Promise<void> {
|
||||
if (typeof this.checkInterval !== 'undefined') {
|
||||
clearInterval(this.checkInterval);
|
||||
this.checkInterval = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private startWebSocket() {
|
||||
const socket = new WebSocket(this.webSocketAddress, {});
|
||||
|
||||
socket.on('error', (event) => {
|
||||
console.error(event);
|
||||
});
|
||||
socket.on('close', (code, reason) => {
|
||||
this.activeSocket = undefined;
|
||||
console.log(`WMS music widget WS closed ${code} ${reason}. Retrying in 2s...`);
|
||||
setTimeout(() => this.startWebSocket(), 2000);
|
||||
});
|
||||
socket.on('open', () => {
|
||||
socket.send(JSON.stringify({
|
||||
token: this.token,
|
||||
type: 'emitter',
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on('message', data => {
|
||||
console.log('WMS music widget WebSocket ready!');
|
||||
this.activeSocket = socket;
|
||||
});
|
||||
}
|
||||
|
||||
private async getInfo(): Promise<string> {
|
||||
const format = [
|
||||
'status',
|
||||
'artist',
|
||||
'title',
|
||||
'album',
|
||||
'mpris:artUrl',
|
||||
].map(s => '{{' + s + '}}').join(metadataSeparator);
|
||||
return await this.runCommand(`playerctl metadata -f "${format}" | sed "s/open\\.spotify\\.com/i.scdn.co/"`);
|
||||
}
|
||||
|
||||
private async runCommand(command: string): Promise<string> {
|
||||
// console.info(`> ${command}`);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
child_process.exec(command, {}, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.error(stderr);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user