@darrellvs/node-wave-link-sdk
Version:
An unofficial SDK for Elgato's Wave Link
810 lines (799 loc) • 22.3 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
WaveLinkController: () => WaveLinkController,
WaveLinkInputController: () => WaveLinkInputController,
WaveLinkOutputController: () => WaveLinkOutputController
});
module.exports = __toCommonJS(src_exports);
// src/Helpers/EventEmitterHelpers.ts
var import_events = require("events");
var TypedEventEmitter = class {
eventEmitter = new import_events.EventEmitter();
constructor() {
}
emit(event, ...args) {
this.eventEmitter.emit(event, ...args);
}
on(event, listener) {
this.eventEmitter.on(event, listener);
}
off(event, listener) {
this.eventEmitter.off(event, listener);
}
removeAllListeners(event) {
this.eventEmitter.removeAllListeners(event);
}
setMaxListeners(n) {
this.eventEmitter.setMaxListeners(n);
}
};
// src/Base/BaseController.ts
var BaseController = class extends TypedEventEmitter {
constructor() {
super();
}
};
// src/Base/BaseWaveLinkController.ts
var import_ws = require("ws");
// src/Utils/constants.ts
var mixerIDSelectedOutputAndValueTransformer = ({
mixerID,
value,
selectedOutput
}) => ({
mixerID,
value,
selectedOutput
});
var identifierAndFilterIDTransformer = ({ identifier, filterID }) => ({
identifier,
filterID
});
var identifierAndFilterTransformer = ({ identifier, filter }) => ({
identifier,
filter
});
var identifierMixerIDAndValueTransformer = ({
identifier,
mixerID,
value
}) => ({
identifier,
mixerID,
value
});
var identifierFilterIDAndValueTransformer = ({
identifier,
filterID,
value
}) => ({
identifier,
filterID,
value
});
var WaveLinkEventMap = {
outputMuteChanged: mixerIDSelectedOutputAndValueTransformer,
outputVolumeChanged: mixerIDSelectedOutputAndValueTransformer,
inputNameChanged: identifierMixerIDAndValueTransformer,
inputMuteChanged: identifierMixerIDAndValueTransformer,
inputVolumeChanged: identifierMixerIDAndValueTransformer,
filterBypassStateChanged: identifierMixerIDAndValueTransformer,
filterAdded: identifierAndFilterTransformer,
filterRemoved: identifierAndFilterIDTransformer,
filterChanged: identifierFilterIDAndValueTransformer
};
var getWaveLinkEventData = (method, params, selectedOutput) => {
if (WaveLinkEventMap[method]) {
return WaveLinkEventMap[method]({
...params,
selectedOutput
});
}
return null;
};
var waveLinkInternalEventsToBeRemoved = [
"websocketClose",
"outputMuteChanged",
"outputVolumeChanged",
"selectedOutputChanged",
"inputNameChanged",
"inputMuteChanged",
"inputVolumeChanged",
"filterBypassStateChanged",
"filterAdded",
"filterRemoved",
"filterChanged",
"inputsChanged"
];
var waveLinkOutputEvents = [
"localVolumeChanged",
"streamVolumeChanged",
"volumeChanged",
"localMuteChanged",
"streamMuteChanged",
"muteChanged",
"selectedOutputChanged"
];
var waveLinkFilterEvents = [
"muteChanged",
"mute",
"unmute"
];
// src/Base/BaseWaveLinkController.ts
var BaseWaveLinkController = class extends BaseController {
constructor(rpc, host) {
super();
this.rpc = rpc;
this.host = host;
}
websocket;
minPort = 1824;
currentPort = this.minPort;
isConnecting = false;
lastId = 0;
emittedIdsMap = /* @__PURE__ */ new Map();
selectedOutput;
connect(isAutoTry = false) {
return new Promise((resolve, reject) => {
if (!this.currentPort || this.isConnecting) {
reject("Already connecting");
return;
}
this.isConnecting = true;
setTimeout(async () => {
await this.tryToConnect(isAutoTry, () => resolve("Initialised"));
}, 1e3);
});
}
getWaveLinkEmmiter() {
return this;
}
async tryToConnect(isAutoTry = false, callback) {
if (this.websocket)
this.websocket.close();
this.websocket = new import_ws.WebSocket(
`ws://${this.host}:${this.currentPort}`
);
this.websocket.rpc = this.rpc;
this.websocket.onopen = async () => {
this.isConnecting = false;
this.initRPC();
this.emit("websocketOpen");
const { selectedOutput } = await this.sendRPC(
"getOutputs",
{
mixerID: "com.elgato.mix.local"
}
);
this.selectedOutput = selectedOutput;
const { localMixer, streamMixer } = await this.sendRPC("getOutputConfig");
const inputs = await this.sendRPC(
"getInputConfigs"
);
this.emit("initialiseChannels", {
inputs,
outputs: { localMixer, streamMixer },
selectedOutput
});
callback();
};
this.websocket.onclose = () => {
this.isConnecting = false;
this.emit("websocketClose");
setTimeout(async () => {
try {
await this.connect(true);
} catch (err) {
}
}, 5e3);
};
this.websocket.onerror = (evt) => {
setTimeout(async () => {
try {
await this.connect(true);
} catch (err) {
}
}, 5e3);
};
this.websocket.onmessage = async (evt) => {
if (typeof evt.data === "string") {
const data = JSON.parse(evt.data);
switch (data.method) {
case "inputsChanged":
const newInputs = await this.sendRPC(
"getInputConfigs"
);
this.emit("inputsChanged", newInputs);
break;
case "selectedOutputChanged":
this.selectedOutput = data.params.value;
this.emit("selectedOutputChanged", data.params.value);
break;
default:
if (data.method === "realTimeChanges")
return;
const eventData = getWaveLinkEventData(
data.method,
data.params,
this.selectedOutput
);
if (eventData) {
this.emit(data.method, eventData);
}
break;
}
if (data.id) {
const callback2 = this.emittedIdsMap.get(data.id);
if (callback2) {
callback2(data.result);
}
}
}
};
}
initRPC() {
this.rpc.toStream = (msg) => {
try {
const { id } = JSON.parse(msg);
this.lastId = id;
this.websocket.send(msg);
} catch (error) {
console.log("ERROR:", error);
}
};
}
async sendRPC(method, params = {}) {
return new Promise((resolve) => {
this.sendRPCProxy(method, params, resolve);
});
}
sendRPCProxy(method, params, callback) {
const id = ++this.lastId;
this.emittedIdsMap.set(id, callback);
this.rpc.call(method, params, id);
}
};
// src/index.ts
var import_simple_jsonrpc_js = __toESM(require("simple-jsonrpc-js"));
// src/Base/WaveLinkFilterController.ts
var WaveLinkFilterController = class extends BaseController {
constructor(rpc, waveLinkEmitter, filter, channelIdentifier) {
super();
this.rpc = rpc;
this.#id = filter.filterID;
this.#name = filter.name;
this.#muted = !filter.isActive;
this.#pluginID = filter.pluginID;
this.#channelIdentifier = channelIdentifier;
waveLinkEmitter.on("filterChanged", ({ identifier, filterID, value }) => {
if (identifier === this.#channelIdentifier && filterID === this.#id) {
this.#muted = !value;
this.emit("muteChanged", this.#muted);
this.emit(value ? "unmute" : "mute");
}
});
}
#id = "";
#name = "";
#muted = true;
#pluginID = "";
#channelIdentifier = "";
get id() {
return this.#id;
}
get name() {
return this.#name;
}
get muted() {
return this.#muted;
}
get pluginID() {
return this.#pluginID;
}
set muted(shouldMute) {
shouldMute ? this.mute() : this.unmute();
}
mute() {
this.rpc.call("setFilter", {
filterID: this.id,
identifier: this.#channelIdentifier,
value: false
});
}
unmute() {
this.rpc.call("setFilter", {
filterID: this.id,
identifier: this.#channelIdentifier,
value: true
});
}
};
// src/Base/WaveLinkInputController.ts
var WaveLinkInputController = class extends BaseController {
#rpc;
#waveLinkEmitter;
#FILTERS = [];
#name = "";
#bgColor = "";
#iconData = "";
#inputType = 0;
#identifier = "";
#isAvailable = false;
#localVolume = 0;
#localMute = false;
#streamVolume = 0;
#streamMute = false;
#localFiltersMuted = false;
#streamFiltersMuted = false;
constructor(rpc, waveLinkEmitter, input) {
super();
input.filters?.forEach((filter) => {
this.#FILTERS.push(
new WaveLinkFilterController(
rpc,
waveLinkEmitter,
filter,
input.identifier
)
);
});
this.#rpc = rpc;
this.#identifier = input.identifier;
this.#bgColor = input.bgColor;
this.#iconData = input.iconData;
this.#inputType = input.inputType;
this.#isAvailable = input.isAvailable;
this.#name = input.name;
this.#waveLinkEmitter = waveLinkEmitter;
this.#localMute = input.localMixer[0];
this.#localVolume = input.localMixer[1];
this.#streamMute = input.streamMixer[0];
this.#streamVolume = input.streamMixer[1];
this.#waveLinkEmitter.on(
"filterBypassStateChanged",
({ identifier, mixerID, value }) => {
if (identifier === this.#identifier) {
if (mixerID === "com.elgato.mix.local") {
this.#localFiltersMuted = value;
this.emit("localFiltersMuteChanged", value);
} else if (mixerID === "com.elgato.mix.stream") {
this.#streamFiltersMuted = value;
this.emit("streamFiltersMuteChanged", value);
}
}
}
);
this.#waveLinkEmitter.on(
"inputVolumeChanged",
({ identifier, mixerID, value }) => {
if (identifier === this.#identifier) {
if (mixerID === "com.elgato.mix.local") {
this.#localVolume = value;
this.emit("localVolumeChanged", value);
this.emit("volumeChanged", {
localVolume: this.#localVolume,
streamVolume: this.#streamVolume,
localMute: this.#localMute,
streamMute: this.#streamMute
});
} else if (mixerID === "com.elgato.mix.stream") {
this.#streamVolume = value;
this.emit("streamVolumeChanged", value);
this.emit("volumeChanged", {
localVolume: this.#localVolume,
streamVolume: this.#streamVolume,
localMute: this.#localMute,
streamMute: this.#streamMute
});
}
}
}
);
this.#waveLinkEmitter.on(
"inputMuteChanged",
({ identifier, mixerID, value }) => {
if (identifier === this.#identifier) {
if (mixerID === "com.elgato.mix.local") {
this.#localMute = value;
this.emit("localMuteChanged", value);
this.emit("muteChanged", {
localMute: this.#localMute,
streamMute: this.#streamMute,
streamVolume: this.#streamVolume,
localVolume: this.#localVolume
});
} else if (mixerID === "com.elgato.mix.stream") {
this.#streamMute = value;
this.emit("streamMuteChanged", value);
this.emit("muteChanged", {
localMute: this.#localMute,
streamMute: this.#streamMute,
streamVolume: this.#streamVolume,
localVolume: this.#localVolume
});
}
}
}
);
this.#waveLinkEmitter.on(
"inputsChanged",
(inputs) => {
const newInput = inputs.find(
(input2) => input2.identifier === this.#identifier
);
if (!newInput) {
return;
}
this.#name = newInput.name;
this.#bgColor = newInput.bgColor;
this.#iconData = newInput.iconData;
this.#inputType = newInput.inputType;
this.#isAvailable = newInput.isAvailable;
}
);
this.#waveLinkEmitter.on(
"inputNameChanged",
({ identifier, value }) => {
if (identifier === this.#identifier) {
this.#name = value;
this.emit("nameChanged", value);
}
}
);
this.#waveLinkEmitter.on("websocketClose", () => {
this.#FILTERS.forEach((filter) => {
for (const filterEvent of waveLinkFilterEvents) {
filter.removeAllListeners(filterEvent);
}
filter = null;
});
this.#FILTERS = [];
});
}
getFilter({ name, filterID }) {
return this.#FILTERS.find(
(filter) => name && filter.name === name || filterID && filter.id === filterID
);
}
get identifier() {
return this.#identifier;
}
get filters() {
return this.#FILTERS;
}
get bgColor() {
return this.#bgColor;
}
get iconData() {
return this.#iconData;
}
get inputType() {
return this.#inputType;
}
get isAvailable() {
return this.#isAvailable;
}
get name() {
return this.#name;
}
get localFiltersMute() {
return this.#localFiltersMuted;
}
get streamFiltersMute() {
return this.#streamFiltersMuted;
}
get localVolume() {
return this.#localVolume;
}
get streamVolume() {
return this.#streamVolume;
}
get localMute() {
return this.#localMute;
}
get streamMute() {
return this.#streamMute;
}
set localVolume(volume) {
this.#rpc.call("setInputConfig", {
identifier: this.#identifier,
mixerID: "com.elgato.mix.local",
property: "Volume",
value: volume
});
}
set streamVolume(volume) {
this.#rpc.call("setInputConfig", {
identifier: this.#identifier,
mixerID: "com.elgato.mix.stream",
property: "Volume",
value: volume
});
}
set localMute(isMuted) {
this.#rpc.call("setInputConfig", {
identifier: this.#identifier,
mixerID: "com.elgato.mix.local",
property: "Mute",
value: isMuted
});
}
set streamMute(isMuted) {
this.#rpc.call("setInputConfig", {
identifier: this.#identifier,
mixerID: "com.elgato.mix.stream",
property: "Mute",
value: isMuted
});
}
set localFiltersMute(isMuted) {
this.#rpc.call("setFilterBypass", {
identifier: this.#identifier,
mixerID: "com.elgato.mix.local",
value: isMuted
});
}
set streamFiltersMute(isMuted) {
this.#rpc.call("setFilterBypass", {
identifier: this.#identifier,
mixerID: "com.elgato.mix.stream",
value: isMuted
});
}
muteLocal() {
this.localMute = true;
}
unmuteLocal() {
this.localMute = false;
}
muteStream() {
this.streamMute = true;
}
unmuteStream() {
this.streamMute = false;
}
};
// src/Base/WaveLinkOutputController.ts
var WaveLinkOutputController = class extends BaseController {
#rpc;
#waveLinkEmitter;
#identifier = "";
#localVolume = 0;
#localMute = false;
#streamVolume = 0;
#streamMute = false;
constructor(rpc, waveLinkEmitter, identifier, localMixer, streamMixer) {
super();
this.#rpc = rpc;
this.#waveLinkEmitter = waveLinkEmitter;
this.#identifier = identifier;
this.#localMute = localMixer[0];
this.#localVolume = localMixer[1];
this.#streamMute = streamMixer[0];
this.#streamVolume = streamMixer[1];
this.#waveLinkEmitter.on(
"outputVolumeChanged",
async ({ selectedOutput, mixerID, value }) => {
if (this.#identifier === selectedOutput) {
if (mixerID === "com.elgato.mix.local") {
this.#localVolume = value;
this.emit("localVolumeChanged", value);
this.emit("volumeChanged", {
localVolume: this.#localVolume,
streamVolume: this.#streamVolume,
localMute: this.#localMute,
streamMute: this.#streamMute
});
} else if (mixerID === "com.elgato.mix.stream") {
this.#streamVolume = value;
this.emit("streamVolumeChanged", value);
this.emit("volumeChanged", {
localVolume: this.#localVolume,
streamVolume: this.#streamVolume,
localMute: this.#localMute,
streamMute: this.#streamMute
});
}
}
}
);
this.#waveLinkEmitter.on(
"outputMuteChanged",
({ selectedOutput, mixerID, value }) => {
if (this.#identifier === selectedOutput) {
if (mixerID === "com.elgato.mix.local") {
this.#localMute = value;
this.emit("localMuteChanged", value);
this.emit("muteChanged", {
localMute: this.#localMute,
streamMute: this.#streamMute,
streamVolume: this.#streamVolume,
localVolume: this.#localVolume
});
} else if (mixerID === "com.elgato.mix.stream") {
this.#streamMute = value;
this.emit("streamMuteChanged", value);
this.emit("muteChanged", {
localMute: this.#localMute,
streamMute: this.#streamMute,
streamVolume: this.#streamVolume,
localVolume: this.#localVolume
});
}
}
}
);
this.#waveLinkEmitter.on("selectedOutputChanged", (selectedOutput) => {
this.#identifier = selectedOutput;
this.emit("selectedOutputChanged", selectedOutput);
});
waveLinkEmitter.on("websocketClose", () => {
for (const eventName of waveLinkOutputEvents) {
this.removeAllListeners(eventName);
}
});
}
get identifier() {
return this.#identifier;
}
get localVolume() {
return this.#localVolume;
}
get streamVolume() {
return this.#streamVolume;
}
get localMute() {
return this.#localMute;
}
get streamMute() {
return this.#streamMute;
}
set localVolume(volume) {
this.#rpc.call("setOutputConfig", {
mixerID: "com.elgato.mix.local",
property: "Output Level",
value: volume
});
}
set streamVolume(volume) {
this.#rpc.call("setOutputConfig", {
mixerID: "com.elgato.mix.stream",
property: "Output Level",
value: volume
});
}
set localMute(isMuted) {
this.#rpc.call("setOutputConfig", {
mixerID: "com.elgato.mix.local",
property: "Mute",
value: isMuted
});
}
set streamMute(isMuted) {
this.#rpc.call("setOutputConfig", {
mixerID: "com.elgato.mix.stream",
property: "Mute",
value: isMuted
});
}
muteLocal() {
this.localMute = true;
}
unmuteLocal() {
this.localMute = false;
}
muteStream() {
this.streamMute = true;
}
unmuteStream() {
this.streamMute = false;
}
};
// src/index.ts
var WaveLinkController = class extends BaseController {
waveLinkController = null;
INPUTS = [];
OUTPUT;
rpc = new import_simple_jsonrpc_js.default();
constructor(host = "127.0.0.1") {
super();
this.waveLinkController = new BaseWaveLinkController(this.rpc, host);
this.waveLinkController.setMaxListeners(16);
this.waveLinkController.on(
"initialiseChannels",
({ inputs, outputs, selectedOutput }) => {
this.initialiseInputs(inputs);
this.initialiseOutputs(outputs, selectedOutput);
this.emit("ready");
}
);
this.waveLinkController.on("websocketOpen", () => {
this.attachCloseListener();
this.emit("websocketOpen");
});
}
initialiseInputs(inputs) {
inputs.forEach((input) => {
this.INPUTS.push(
new WaveLinkInputController(
this.rpc,
this.waveLinkController.getWaveLinkEmmiter(),
input
)
);
});
}
initialiseOutputs(outputs, selectedOutput) {
this.OUTPUT = new WaveLinkOutputController(
this.rpc,
this.waveLinkController.getWaveLinkEmmiter(),
selectedOutput,
[...outputs.localMixer, false],
[...outputs.streamMixer, false]
);
}
attachCloseListener() {
this.waveLinkController.on("websocketClose", () => {
setTimeout(() => {
waveLinkInternalEventsToBeRemoved.forEach((eventName) => {
this.waveLinkController.removeAllListeners(eventName);
});
this.INPUTS = [];
this.OUTPUT = null;
this.emit("websocketClose");
}, 100);
});
}
async connect() {
await this.waveLinkController.connect();
}
getOutput() {
return this.OUTPUT;
}
getInputs() {
return this.INPUTS;
}
getInput({
name,
identifier
}) {
return this.INPUTS.find(
(input) => input.identifier === identifier || input.name === name
);
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
WaveLinkController,
WaveLinkInputController,
WaveLinkOutputController
});
//# sourceMappingURL=index.js.map