mythtv-event-emitter
Version:
Event Emitter for MythTV system events.
259 lines • 9.25 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.disconnectMythProto = exports.monitorEvents = void 0;
const net_1 = require("net");
const lodash_1 = require("lodash");
const events_1 = require("./events");
const events_2 = require("events");
const messages_1 = require("./messages");
const moment = require("moment");
class DoneMessage {
constructMessage() {
return 'DONE';
}
}
class AbstractMessageConstructor {
constructMessage() {
return this.constructMessageParts().join(' ');
}
}
class MythProtoVersionMessage extends AbstractMessageConstructor {
constructor(version) {
super();
this.version = version ? version : MythProtoVersionMessage.protocolVersions.entries().next().value[0];
}
static get protocolVersions() {
if (this._protocolVersions.size == 0) {
this._protocolVersions.set(91, 'BuzzOff');
this._protocolVersions.set(90, 'BuzzCut');
this._protocolVersions.set(89, 'BuzzKill');
this._protocolVersions.set(88, 'XmasGift');
this._protocolVersions.set(85, 'BluePool');
}
return this._protocolVersions;
}
constructMessageParts() {
const token = MythProtoVersionMessage.protocolVersions.get(this.version);
if (token != undefined) {
return ['MYTH_PROTO_VERSION', this.version, token];
}
else {
throw new Error('Unsupported proto version ' + this.version);
}
}
}
MythProtoVersionMessage._protocolVersions = new Map();
var EventMode;
(function (EventMode) {
EventMode[EventMode["NO_EVENT"] = 0] = "NO_EVENT";
EventMode[EventMode["ALL_EVENTS"] = 1] = "ALL_EVENTS";
EventMode[EventMode["NO_SYSTEM_EVENT"] = 2] = "NO_SYSTEM_EVENT";
EventMode[EventMode["ONLY_SYSTEM_EVENT"] = 3] = "ONLY_SYSTEM_EVENT";
})(EventMode || (EventMode = {}));
class AnnMonitorMessage extends AbstractMessageConstructor {
constructor(hostname, eventMode) {
super();
this.hostname = hostname;
this.eventMode = eventMode;
}
constructMessageParts() {
return ['ANN', 'Monitor', this.hostname, this.eventMode];
}
}
class RejectMessageHandler {
constructor() {
this.messageType = 'REJECT';
}
process(client, message) {
client.connectEmitter.emit('REJECT', Number(message[1]));
}
}
class AcceptMessageHandler {
constructor() {
this.messageType = 'ACCEPT';
}
process(client, message) {
client.connectEmitter.emit('ACCEPT', Number(message[1]));
}
}
class BackendMessageHandler {
constructor() {
this.messageType = "BACKEND_MESSAGE";
this.dataSuffixes = ['UTC', 'ISO', 'ISOUTC'];
}
addDateFields(fieldName, notificationMessage) {
const dateValue = new Date(notificationMessage[fieldName]);
this.dataSuffixes.forEach(suffix => {
notificationMessage[fieldName + suffix] = dateValue;
});
notificationMessage[fieldName] = dateValue;
}
process(client, message) {
const eventMessage = message[1].split(' ');
const notificationMessage = {};
for (let i = 0; i < eventMessage.length; i += 2) {
const key = eventMessage[i];
const value = eventMessage[i + 1];
notificationMessage[key] = value;
if (messages_1.dateFields.indexOf(key) >= 0) {
this.addDateFields(key, notificationMessage);
}
else if (key == 'ORIGINALAIRDATE') {
notificationMessage[key] = moment(value, 'YYYY-MM-DD').toDate();
}
}
const eventType = notificationMessage['SYSTEM_EVENT'];
const sender = notificationMessage['SENDER'];
notificationMessage['EVENTNAME'] = eventType;
events_1.notifyEvent(eventType, sender, notificationMessage);
}
}
class OkMessageHandler {
constructor() {
this.messageType = "OK";
}
process(client, message) {
client.connectEmitter.emit('OK');
}
}
class MythProtocolClient {
constructor(host, port) {
this.messageHandlers = new Map();
this.connectEmitter = new events_2.EventEmitter();
this.messageHandlers.set('ACCEPT', new AcceptMessageHandler());
this.messageHandlers.set('BACKEND_MESSAGE', new BackendMessageHandler());
this.messageHandlers.set('OK', new OkMessageHandler());
this.messageHandlers.set('REJECT', new RejectMessageHandler());
if (!port) {
port = 6543;
}
this.options = {
port: port,
host: host,
timeout: 3
};
}
get connection() {
if (!this._connection) {
const lConnection = net_1.createConnection(this.options, () => {
lConnection.on('data', (data) => {
let msgEnd = 0;
while (msgEnd < data.length) {
msgEnd = this.readMessage(data, msgEnd);
}
});
lConnection.on('close', (had_error) => {
console.log('close backend connection');
this._connection = undefined;
if (had_error) {
this.connectEmitter.emit('error', new Error('Connection Error'));
}
else {
this.connectEmitter.emit('close');
}
});
});
this._connection = lConnection;
}
return this._connection;
}
createBackendMessage(message) {
const msgLen = message.length;
const msgPrefix = lodash_1.padEnd(msgLen.toString(), 8);
return msgPrefix + message;
}
readMessage(data, start) {
const msgLenEnd = start + 8;
const msgLen = Number(data.toString(MythProtocolClient.LOCALE, start, msgLenEnd));
const msgEnd = msgLenEnd + msgLen;
const message = data.toString(MythProtocolClient.LOCALE, msgLenEnd, msgEnd);
const messageParts = message.split(MythProtocolClient.LIST_DELIMITER);
const messageHandler = this.messageHandlers.get(messageParts[0]);
if (messageHandler) {
try {
messageHandler.process(this, messageParts);
}
catch (err) {
this.connectEmitter.emit('error', err);
}
}
else {
console.log('No handler found for message %s', message);
}
return msgEnd;
}
sendMessage(message) {
try {
const backendMessage = this.createBackendMessage(message.constructMessage());
this.connection.write(backendMessage);
}
catch (err) {
this.connectEmitter.emit('error', err);
}
}
disconnect() {
if (this._connection) {
return new Promise((resolve, reject) => {
this.connectEmitter.once('close', () => {
resolve();
});
this.sendMessage(new DoneMessage());
});
}
else {
return Promise.resolve();
}
}
createList(args) {
return args.join(MythProtocolClient.LIST_DELIMITER);
}
connectClient() {
return new Promise((resolve, reject) => {
this.connectEmitter.once('ACCEPT', (version) => {
this.connectEmitter.removeAllListeners();
resolve(version);
});
this.connectEmitter.once('REJECT', (version) => {
this.connectEmitter.once('close', () => {
this.sendMessage(new MythProtoVersionMessage(version));
});
});
this.connectEmitter.once('error', (err) => {
reject(err);
});
this.sendMessage(new MythProtoVersionMessage());
});
}
async monitor(monitorHostName, eventMode) {
await this.connectClient();
return new Promise((resolve, reject) => {
this.connectEmitter.on('OK', () => {
console.log('Monitor mythbacked using host %s', monitorHostName);
resolve();
});
this.connectEmitter.once('error', (err) => {
reject(err);
});
this.sendMessage(new AnnMonitorMessage(monitorHostName, eventMode));
});
}
}
MythProtocolClient.LOCALE = 'utf-8';
MythProtocolClient.LIST_DELIMITER = '[]:[]';
let client;
async function monitorEvents(monitorHostName, backendHostName, host, port) {
client = new MythProtocolClient(host, port);
await client.monitor(monitorHostName, EventMode.ONLY_SYSTEM_EVENT);
const mythEventEmitter = events_1.mythNotifier.hostEmitter(backendHostName);
mythEventEmitter.on('MASTER_STARTED', async (message) => {
await client.monitor(monitorHostName, EventMode.ONLY_SYSTEM_EVENT);
});
}
exports.monitorEvents = monitorEvents;
async function disconnectMythProto() {
if (client) {
await client.disconnect();
}
}
exports.disconnectMythProto = disconnectMythProto;
//# sourceMappingURL=mythProtocol.js.map