UNPKG

renderer-finder

Version:
190 lines (165 loc) 5 kB
var dgram = require('dgram'); var util = require('util'); var debug = require('debug')('renderer-finder'); var events = require("events"); var http = require('http'); var concat = require('concat-stream'); var xml2js = require('xml2js'); function getDiscoverMessage(ST) { var buf = new Buffer( "M-SEARCH * HTTP/1.1\r\n" + "HOST:239.255.255.250:1900\r\n" + "MAN:\"ssdp:discover\"\r\n" + "ST: " + (ST || "urn:schemas-upnp-org:device:MediaRenderer:1") + "\r\n" + "MX:2\r\n" + "\r\n" ); debug('Discover message: ' + buf.toString()); return buf; } function parseDiscoverResponse(buffer){ debug('parsing discover buffer response'); var response = {}; var parts = buffer.toString().split('\r\n'); for (var i = 0; i < parts.length; i++){ if (i > 0){ response[parts[i].split(': ')[0].toLowerCase()] = parts[i].split(': ')[1]; }else{ response.Status = parts[i]; } } return response; } function parseDescriptionResponse(xml, cb){ xml2js.parseString(xml, function(err, json){ if (err){ cb(err); } var newJson = { device: {}, service: [] }; var device = json.root.device[0]; for (var porp in device) { if (device.hasOwnProperty(porp) && porp !== 'serviceList') { newJson.device[porp] = device[porp][0]; } } var services = device.serviceList[0].service; for (var serv in services) { if (services.hasOwnProperty(serv)) { newJson.service.push({ SCPDURL: services[serv].SCPDURL[0], controlURL: services[serv].controlURL[0], eventSubURL: services[serv].eventSubURL[0], serviceId: services[serv].serviceId[0], serviceType: services[serv].serviceType[0] }); } } cb(err, newJson); }); } function getDeviceDescription(url, cb){ debug('getting device description'); fetch(url, function(err, body) { if(err) return cb(err); debug('converting XML to JSON'); parseDescriptionResponse(body, cb); }); } function fetch(url, cb) { debug('making HTTP request'); var req = http.get(url, function(res) { if(res.statusCode !== 200) { var err = new Error('Request failed'); err.statusCode = res.statusCode; return cb(err); } debug('piping resonse'); res.pipe(concat(function(buf) { debug('response piped'); cb(null, buf.toString()); })); }); req.on('error', cb); req.end(); } function getSocket(ST, cb) { var client = dgram.createSocket("udp4"); var server = dgram.createSocket("udp4"); client.bind(); // So that we get a port so we can listen before sending var msg = getDiscoverMessage(ST); debug('Client: sending message'); client.send(msg, 0, msg.length, 1900, "239.255.255.250", function(err){ if (err){ cb(err); } debug('Client: message sended'); var port = client.address().port; client.close(); cb(undefined, server, port); }); return server; } function RendererFinder(ST){ //inherit all the event stuff events.EventEmitter.call(this); var that = this; var sST = ST; var _socket = null; that.start = function(gatherInfo){ debug('Finder: getting socket'); _socket = getSocket(sST, function(err, socket, port){ if (err){ that.emit('error', err); } debug('Finder: socket obtained'); socket.on("message", function (msg, rinfo) { var parsedMsg = parseDiscoverResponse(msg); debug('Finder: device found %o', rinfo); debug(' %o', parsedMsg); if (gatherInfo){ getDeviceDescription(parsedMsg.location, function(err, desc){ if (err){ that.emit('error', err); } that.emit('found', rinfo, parsedMsg, desc); }); }else{ that.emit('found', rinfo, parsedMsg); } }); socket.on("error", function (buf) { debug('Finder: device founder error %s', buf.toString()); that.emit('err', buf.toString()); }); socket.bind(port); }); return this; }; that.stop = function(){ debug('Finder: stopping'); if (_socket && _socket._handle) _socket.close(); }; that.findOne = function(getInfo, cb){ //check for the optional parameter cb = getInfo instanceof Function ? getInfo : cb; getInfo = getInfo instanceof Function ? false : getInfo; that.once('found', function(info, msg, desc){ that.stop(); cb(undefined, info, msg, desc); }); that.once('error', function(err){ that.stop(); cb(err); }); that.start(getInfo); }; return that; } //inherit all the event stuff util.inherits(RendererFinder, events.EventEmitter); module.exports = RendererFinder;