node-red-contrib-castv2
Version:
A Node-Red node that provides basic Google Cast functionality based on the node-castv2-client package.
242 lines (187 loc) • 7.24 kB
JavaScript
;
const util = require('util');
const castv2Cli = require('castv2-client');
const RequestResponseController = castv2Cli.RequestResponseController;
function MediaController(client, sourceId, destinationId) {
RequestResponseController.call(this, client, sourceId, destinationId, 'urn:x-cast:com.google.cast.media');
this.currentSession = null;
this.on('message', onmessage);
this.once('close', onclose);
var self = this;
function onmessage(data, broadcast) {
if (data.type === 'MEDIA_STATUS' && broadcast) {
var status = data.status[0];
// Sometimes an empty status array can come through; if so don't emit it
if (!status) return;
self.currentSession = status;
self.emit('status', status);
}
}
function onclose() {
self.removeListener('message', onmessage);
}
}
util.inherits(MediaController, RequestResponseController);
MediaController.prototype.getStatus = function(callback) {
var self = this;
this.request({ type: 'GET_STATUS' }, function(err, response) {
if (err) return callback(err);
var status = response.status[0];
self.currentSession = status;
callback(null, status);
});
};
MediaController.prototype.load = function(media, options, callback) {
if (typeof options === 'function' || typeof options === 'undefined') {
callback = options;
options = {};
}
var data = { type: 'LOAD' };
data.autoplay = (typeof options.autoplay !== 'undefined')
? options.autoplay
: false;
data.currentTime = (typeof options.currentTime !== 'undefined')
? options.currentTime
: 0;
data.activeTrackIds = (typeof options.activeTrackIds !== 'undefined')
? options.activeTrackIds
: [];
data.repeatMode = (typeof options.repeatMode === "string" &&
typeof options.repeatMode !== 'undefined')
? options.repeatMode
: "REPEAT_OFF";
data.media = media;
this.request(data, function(err, response) {
if (err) return callback(err);
if (response.type === 'LOAD_FAILED') {
return callback(new Error('Load failed'));
}
if (response.type === 'LOAD_CANCELLED') {
return callback(new Error('Load cancelled'));
}
var status = response.status[0];
callback(null, status);
});
};
function noop() {}
MediaController.prototype.sessionRequest = function(data, callback) {
data.mediaSessionId = this.currentSession.mediaSessionId;
callback = callback || noop;
this.request(data, function(err, response) {
if (err) return callback(err);
var status = response.status[0];
callback(null, status);
});
};
MediaController.prototype.play = function(callback) {
this.sessionRequest({ type: 'PLAY' }, callback);
};
MediaController.prototype.pause = function(callback) {
this.sessionRequest({ type: 'PAUSE' }, callback);
};
MediaController.prototype.stop = function(callback) {
this.sessionRequest({ type: 'STOP' }, callback);
};
MediaController.prototype.seek = function(currentTime, callback) {
var data = {
type: 'SEEK',
currentTime: currentTime
};
this.sessionRequest(data, callback);
};
//Load a queue of items to play (playlist)
//See https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.QueueLoadRequest
MediaController.prototype.queueLoad = function(items, options, callback) {
if (typeof options === 'function' || typeof options === 'undefined') {
callback = options;
options = {};
}
var data = { type: 'QUEUE_LOAD' };
//REPEAT_OFF, REPEAT_ALL, REPEAT_SINGLE, REPEAT_ALL_AND_SHUFFLE
data.repeatMode = (typeof options.repeatMode === "string" &&
typeof options.repeatMode !== 'undefined')
? options.repeatMode
: "REPEAT_OFF";
data.currentTime = (typeof options.currentTime !== 'undefined')
? options.currentTime
: 0;
data.startIndex = (typeof options.startIndex !== 'undefined')
? options.startIndex
: 0;
data.items = items;
this.request(data, function(err, response) {
if (err) return callback(err);
if (response.type === 'LOAD_FAILED') {
return callback(new Error('queueLoad failed'));
}
if (response.type === 'LOAD_CANCELLED') {
return callback(new Error('queueLoad cancelled'));
}
var status = response.status[0];
callback(null, status);
});
};
MediaController.prototype.queueInsert = function(items, options, callback) {
if (typeof options === 'function' || typeof options === 'undefined') {
callback = options;
options = {};
}
var data = {
type: 'QUEUE_INSERT',
currentItemId: options.currentItemId, //Item ID to play after this request or keep same item if undefined
currentItemIndex: options.currentItemIndex, //Item Index to play after this request or keep same item if undefined
currentTime: options.currentTime, //Seek in seconds for current stream
insertBefore: options.insertBefore, //ID or append if undefined
items: items
};
this.sessionRequest(data, callback);
};
MediaController.prototype.queueRemove = function(itemIds, options, callback) {
if (typeof options === 'function' || typeof options === 'undefined') {
callback = options;
options = {};
}
var data = {
type: 'QUEUE_REMOVE',
currentItemId: options.currentItemId,
currentTime: options.currentTime,
itemIds: itemIds
};
this.sessionRequest(data, callback);
};
MediaController.prototype.queueReorder = function(itemIds, options, callback) {
if (typeof options === 'function' || typeof options === 'undefined') {
callback = options;
options = {};
}
var data = {
type: 'QUEUE_REORDER',
currentItemId: options.currentItemId,
currentTime: options.currentTime,
insertBefore: options.insertBefore,
itemIds: itemIds
};
this.sessionRequest(data, callback);
};
MediaController.prototype.queueUpdate = function(items, options, callback) {
if (typeof options === 'function' || typeof options === 'undefined') {
callback = options;
options = {};
}
var data = {
type: 'QUEUE_UPDATE',
currentItemId: options.currentItemId,
currentTime: options.currentTime,
jump: options.jump, //Skip or go back (if negative) number of items
repeatMode: options.repeatMode,
items: items
};
this.sessionRequest(data, callback);
};
MediaController.prototype.queueNext = function(callback) {
this.sessionRequest({ type: 'QUEUE_UPDATE', jump: 1 }, callback);
};
MediaController.prototype.queuePrev = function(callback) {
this.sessionRequest({ type: 'QUEUE_UPDATE', jump: -1 }, callback);
};
module.exports = MediaController;