Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pct fixes #27

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 86 additions & 101 deletions device.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,74 @@
var Client = require('castv2-client').Client;
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;

var events = require('events');
var util = require('util');
var debug = require('debug')('Device');
var debug = require('debug')('chromecast-js');

/* Chromecast
* Supported Media: https://developers.google.com/cast/docs/media
* Receiver Apps: https://developers.google.com/cast/docs/receiver_apps
*/

var Device = function(options) {
events.EventEmitter.call(this);
var self = this;
self.config = options;
this.config = options;
this.init();
};

exports.Device = Device;
util.inherits(Device, events.EventEmitter);

Device.prototype.connect = function(callback) {
Device.prototype.init = function() {
this.host = this.config.addresses[0];
this.playing = false;
};

Device.prototype.play = function(resource, n, callback) {
var self = this;

// Always use a fresh client when connecting
if (self.client) self.client.close();
debug('chromecast-js: Connect to host: '+ self.host);
//debug('chromecast-js: Config', self.config);
self.client = new Client();
self.client.connect(self.host, function() {
debug('connected, launching app ...');
self.client.connect(self.host, function(err, status) {
if (err) {
console.error('chromecast-js: Error connecting', err);
return callback(err);
}
debug('chromecast-js: Connected, launching player...');
self.client.launch(DefaultMediaReceiver, function(err, player) {
if (err) {
debug(err);
} else {
self.player = player;
self.emit('connected');
if (callback) callback();
console.error('chromecast-js: Error launching MediaReceiver', err);
return callback(err);
}

self.player = player;
self._privatePlayMedia(resource, n, callback);
player.on('status', function(status) {
if (status){
debug('status broadcast playerState=%s',status.playerState);
} else {
debug('-');
if (status) {
debug('chromecast-js: PlayerState=%s',status.playerState);
// Propagate status event
self.emit('status', status);
}
});

});
});

self.client.on('error', function(err) {
console.log('Error: %s', err.message);
self.connect(self.host);
debug('chromecast-js Error: %s', err.message);
self.client.close();
//TODO: Trigger IDLE state or better yet switch to trigger('device:closed') for these events.
});
};

Device.prototype.init = function() {
var self = this;

self.host = self.config.addresses[0];
self.playing = false;
};

Device.prototype.play = function(resource, n, callback) {
Device.prototype._privatePlayMedia = function(resource, n, callback) {
var self = this;

options = { autoplay: true };
options = { autoplay: true,
currentTime: n || 0 };

if (typeof(resource) === 'string'){
if (typeof(resource) === 'string') {
var media = {
contentId: resource,
contentType: 'video/mp4'
Expand All @@ -71,7 +78,7 @@ Device.prototype.play = function(resource, n, callback) {
contentId: resource.url,
contentType: 'video/mp4'
};
if (resource.subtitles){
if (resource.subtitles) {
var tracks = [];
var i = 0;
for (var each in resource.subtitles ) {
Expand All @@ -91,7 +98,7 @@ Device.prototype.play = function(resource, n, callback) {
media.tracks = tracks;
options['activeTrackIds'] = [0];
}
if (resource.subtitles_style){
if (resource.subtitles_style) {
media.textTrackStyle = resource.subtitles_style;
self.subtitles_style = resource.subtitles_style;
}
Expand All @@ -107,25 +114,24 @@ Device.prototype.play = function(resource, n, callback) {
}
}

options['currentTime'] = n || 0;

debug('chromecast-js: Loading media: %s (%s)', media.contentId, media.contentType);
self.player.load(media, options, function(err, status) {
self.playing = true;
if (callback){
callback(err,status);
if (err) {
console.error('chromecast-js: Error loading media', err);
return callback(err);
}
self.playing = true;
callback(null, status);
});
};

Device.prototype.getStatus = function(callback) {
var self = this;

self.player.getStatus(function(err, status) {
this.player.getStatus(function(err, status) {
if (err) {
console.log("getStatus error: %s", err.message);
} else {
callback(status);
debug("chromecast-js.getStatus error: %s", err.message);
return callback(err);
}
callback(null, status);
});
};

Expand All @@ -139,87 +145,66 @@ Device.prototype.seek = function(seconds, callback) {
var self = this;

// Retrieve updated status just before seek
self.getStatus(function(newStatus) {
self.getStatus(function(err, newStatus) {
if (err) return callback(err);
newCurrentTime = newStatus.currentTime + seconds;
self.seekTo(newCurrentTime, callback);
});
};

Device.prototype.pause = function(callback) {
var self = this;
this.playing = false;
this.player.pause(callback);
};

self.playing = false;
self.player.pause(callback);
Device.prototype.unpause = function(callback) {
this.playing = true;
this.player.play(callback);
};

Device.prototype.setVolume = function(volume, callback) {
var self = this;

self.client.setVolume({ level: volume }, callback);
this.client.setVolume({ level: volume }, callback);
};

Device.prototype.setVolumeMuted = function(muted, callback){
var self = this;

self.client.setVolume({ 'muted': muted }, callback);
};

Device.prototype.unpause = function(callback) {
var self = this;

self.playing = true;
self.player.play(callback);
};

Device.prototype.stop = function(callback) {
var self = this;

self.playing = false;
self.player.stop(callback);
this.client.setVolume({ muted: muted }, callback);
};

Device.prototype.subtitlesOff = function(callback) {
var self = this;

self.player.media.sessionRequest({
this.player.media.sessionRequest({
type: 'EDIT_TRACKS_INFO',
activeTrackIds: [] // turn off subtitles.
}, function(err, status){
if (err) callback(err);
callback(null, status);
});
}, callback);
};

Device.prototype.changeSubtitles = function(num, callback) {
var self = this;

self.player.media.sessionRequest({
Device.prototype.changeSubtitles = function(subIdx, callback) {
this.player.media.sessionRequest({
type: 'EDIT_TRACKS_INFO',
activeTrackIds: [num] // turn off subtitles.
}, function(err, status){
if (err) callback(err);
callback(null, status);
});
activeTrackIds: [subIdx]
}, callback);
};

Device.prototype.changeSubtitlesSize = function(num, callback) {
var self = this;

var newStyle = self.subtitles_style;
newStyle.fontScale = num;
self.player.media.sessionRequest({
Device.prototype.changeSubtitlesSize = function(fontScale, callback) {
var newStyle = this.subtitles_style;
newStyle.fontScale = fontScale;
this.player.media.sessionRequest({
type: 'EDIT_TRACKS_INFO',
textTrackStyle: newStyle
}, function(err, status){
if (err) callback(err);
callback(null, status);
});
}, callback);
};

Device.prototype.close = function(callback) {
if ( this.client ) {
this.client.close();
this.client = null;
}
if (callback) callback();
}
// Stop player, wait 5 sec at stand-by screen, then close client.
Device.prototype.stop = function(callback) {
var self = this;
self.player.stop(function() {
debug('chromecast-js: Player Stopped');
setTimeout(function() {
self.client.stop(self.player, function() {
self.client.close();
self.client = null;
debug('chromecast-js: Disconnected');
if (callback) callback();
});
}, 5000);
});
};
55 changes: 31 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var util = require( 'util' );
var events = require( 'events' );
var os = require( 'os' );
var ssdp = require('node-ssdp').Client;
var http = require('http');
var Device = require('./device').Device;
Expand All @@ -16,35 +17,41 @@ util.inherits(Browser, events.EventEmitter );

exports.Browser = Browser;

Browser.prototype.update = function( device ) {
Browser.prototype.update = function(device) {
var dev_config = {addresses: device.addresses, name: device.name};
this.device = new Device(dev_config);
this.emit('deviceOn', this.device);
}
};

Browser.prototype.init = function( options ) {
Browser.prototype.init = function(options) {
var self = this;
var responseCallback = function (headers, statusCode, rinfo) {
if (statusCode !== 200) return;
if (!headers['LOCATION']) return;

var request = http.get(headers['LOCATION'], function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var match = body.match(/<friendlyName>(.+?)<\/friendlyName>/);
if (!match || match.length != 2)
return;
self.update({addresses: [rinfo.address], name: match[1]});
});
});
};

var ssdpBrowser = new ssdp();
ssdpBrowser.on('response', function (headers, statusCode, rinfo) {
if (statusCode != 200)
return;
if (!headers['LOCATION'])
return;
var request = http.get(headers['LOCATION'], function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
if (body.search('<manufacturer>Google Inc.</manufacturer>') == -1)
return;
var match = body.match(/<friendlyName>(.+?)<\/friendlyName>/);
if (!match || match.length != 2)
return;
self.update({addresses: [rinfo.address], name: match[1]});
});
});
var networkInterfaces = os.networkInterfaces();
Object.keys(networkInterfaces).forEach(function (type) {
networkInterfaces[type].forEach(function (networkInterface) {
if (networkInterface.internal) return;
var ssdpBrowser = new ssdp({
unicastHost: networkInterface.address
});
ssdpBrowser.on('response', responseCallback);
ssdpBrowser.search('urn:dial-multiscreen-org:service:dial:1');
});
});
ssdpBrowser.search('urn:dial-multiscreen-org:service:dial:1');
};