Browse Source

Integrate WMS music widget

develop
Alice Gaudon 7 months ago
parent
commit
347e5348b4
5 changed files with 123 additions and 3 deletions
  1. +3
    -1
      .gitignore
  2. +7
    -1
      config/default.ts
  3. +3
    -1
      package.json
  4. +3
    -0
      src/App.ts
  5. +107
    -0
      src/wms/WMSMusicWidget.ts

+ 3
- 1
.gitignore View File

@ -2,4 +2,6 @@
dist
node_modules
yarn.lock
yarn-error.log
yarn-error.log
config/local*

+ 7
- 1
config/default.ts View File

@ -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',
},
},
};

+ 3
- 1
package.json View File

@ -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"
}
}

+ 3
- 0
src/App.ts View File

@ -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
- 0
src/wms/WMSMusicWidget.ts View 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…
Cancel
Save