<commit_before>'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('meshblu-chromecast')
var Client = require('castv2').Client;
var mdns = require('mdns');
var io = require('socket.io-client')
var getYouTubeId = require('get-youtube-id');
var chromecastFound;
var _ = require('lodash');

var MESSAGE_SCHEMA = {
  type: 'object',
  properties: {
    CastingApplication: {
      type: 'string',
      "enum" : ['youtube', 'DisplayText', 'Url' , 'Media', 'CustomApp' ] ,
      required: true
    },
    youtubeUrl: {
      type: 'string',
      required: true
    },
    Message: {
      type: 'string',
      required: true
    },
    Url: {
      type: 'string',
      required: true
    },
    MediaURL: {
      type: 'string',
      required: true
    },
    AppID: {
      type: 'string',
      required: true
    },
    urn: {
      type: 'string',
      required: true
    },
    payload: {
      type: 'string',
      required: true
    }
  }
};

var OPTIONS_SCHEMA = {
  type: 'object',
  properties: {
    AutoDiscovery: {
      type: 'boolean',
      required: true
    },
    ChromecastName: {
      type: 'string',
      required: true
    }
  }
};

var doesMatchPluginSerivce = function(pluginName, serviceName){
  if(!pluginName || !serviceName) return;
  return pluginName.toLowerCase() === serviceName.toLowerCase;
};

function Plugin(){
  var self = this;
  self.options = {};
  self.messageSchema = MESSAGE_SCHEMA;
  self.optionsSchema = OPTIONS_SCHEMA;
  self.requestId = 1443;
  return self;
}

util.inherits(Plugin, EventEmitter);

Plugin.prototype.onMessage = function (message) {
  debug('onMessage', message);

  if (!message.payload) return;

  this.detectChromecast(message.payload);
};

Plugin.prototype.onConfig = function(device){
  debug('onConfig');

  this.setOptions(device.options || {});
  this.setupChromecast();
};

Plugin.prototype.setOptions = function (options){
  this.options = options || {};
};

Plugin.prototype.setupChromecast = function() {
  debug('Setting up chromecast....');
};

Plugin.prototype.detectChromecast = function (message) {
  var self = this,
    pluginName = self.options.ChromecastName,
    autodiscovery = self.options.AutoDiscovery;

  if(!autodiscovery && !pluginName) return;

  var browser = mdns.createBrowser(mdns.tcp('googlecast')).on('serviceUp', function (chromecast) {
    self.chromecast = chromecast;
    if (autodiscovery || doesMatchPluginSerivce(pluginName, self.chromecast.name)) {
      return self.sendMessageToDevice(message, self.chromecast);
    }
  });

  browser.start();
};

 Plugin.prototype.sendMessageToDevice = function (message, service) {
  debug('sendMessageToDevice', 'Casting...');

  var hostIP = service.addresses[0];

  // this.emit('message', { devices: ['*'], topic: 'echo', payload: service });
  this.onDeviceUp(hostIP, message);
}

Plugin.prototype.onDeviceUp = function (host, message) {
  debug('onDeviceUp', message);
  var self = this;
  self.client = new Client();

  self.client.connect(host, function () {
    self.sendMessageToClient(message);
  });
}

Plugin.prototype.sendMessageToClient = function(message){
  var self = this;
  var APPID = this.getChromecastApplicationID(message);
  // Google Chromecast various namespace handlers for initializing connection.
  var connection = self.client.createChannel('sender-0', 'receiver-0', 'urn:x-cast:com.google.cast.tp.connection', 'JSON');
  var heartbeat = self.client.createChannel('sender-0', 'receiver-0', 'urn:x-cast:com.google.cast.tp.heartbeat', 'JSON');
  var receiver = self.client.createChannel('sender-0', 'receiver-0', 'urn:x-cast:com.google.cast.receiver', 'JSON');

  var launchRequestId;

  // establish virtual connection to the receiver
  connection.send({ type: 'CONNECT' });

  // Check first if the app is avaliable.
  receiver.send({ type: 'GET_APP_AVAILABILITY', appId: [APPID], requestId: self.requestId });

  // start heartbeating
  setInterval(function () {
    heartbeat.send({ type: 'PING' });
  }, 5000);

  receiver.on('message', function (data, broadcast) {
    debug('chromecast on message', data);
    if (data.requestId === self.requestId) {
      if ('APP_AVAILABLE' === data.availability[APPID]) {
        debug('app is available');
        // console.log(data);
        launchRequestId = self.requestId;
        debug('request id', self.requestId);
        receiver.send({ type: 'LAUNCH', appId: APPID, requestId: self.requestId++ });
      }
    }else if (data.requestId == launchRequestId) {
      debug('handling launch response...');
      var app = _.find(data.status.applications, {appId: APPID})
      if(_.isEmpty(app)) return;
      var mySenderConnection = self.client.createChannel('client-13243', app.transportId, 'urn:x-cast:com.google.cast.tp.connection', 'JSON');
      mySenderConnection.send({ type: 'CONNECT' });<commit_msg>Debounced detectChromecast<commit_after>'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('meshblu-chromecast')
var Client = require('castv2').Client;
var mdns = require('mdns');
var io = require('socket.io-client')
var getYouTubeId = require('get-youtube-id');
var chromecastFound;
var _ = require('lodash');

var MESSAGE_SCHEMA = {
  type: 'object',
  properties: {
    CastingApplication: {
      type: 'string',
      "enum" : ['youtube', 'DisplayText', 'Url' , 'Media', 'CustomApp' ] ,
      required: true
    },
    youtubeUrl: {
      type: 'string',
      required: true
    },
    Message: {
      type: 'string',
      required: true
    },
    Url: {
      type: 'string',
      required: true
    },
    MediaURL: {
      type: 'string',
      required: true
    },
    AppID: {
      type: 'string',
      required: true
    },
    urn: {
      type: 'string',
      required: true
    },
    payload: {
      type: 'string',
      required: true
    }
  }
};

var OPTIONS_SCHEMA = {
  type: 'object',
  properties: {
    AutoDiscovery: {
      type: 'boolean',
      required: true
    },
    ChromecastName: {
      type: 'string',
      required: true
    }
  }
};

var doesMatchPluginSerivce = function(pluginName, serviceName){
  if(!pluginName || !serviceName) return;
  return pluginName.toLowerCase() === serviceName.toLowerCase;
};

function Plugin(){
  var self = this;
  self.options = {};
  self.messageSchema = MESSAGE_SCHEMA;
  self.optionsSchema = OPTIONS_SCHEMA;
  self.requestId = 1443;
  return self;
}

util.inherits(Plugin, EventEmitter);

Plugin.prototype.onMessage = function (message) {
  debug('onMessage', message);

  if (!message.payload) return;

  this.detectChromecast(message.payload);
};

Plugin.prototype.onConfig = function(device){
  debug('onConfig');

  this.setOptions(device.options || {});
  this.setupChromecast();
};

Plugin.prototype.setOptions = function (options){
  this.options = options || {};
};

Plugin.prototype.setupChromecast = function() {
  debug('Setting up chromecast....');
};

Plugin.prototype.detectChromecastImmediately = function (message) {
  var self = this,
    pluginName = self.options.ChromecastName,
    autodiscovery = self.options.AutoDiscovery;

  if(!autodiscovery && !pluginName) return;

  var browser = mdns.createBrowser(mdns.tcp('googlecast')).on('serviceUp', function (chromecast) {
    self.chromecast = chromecast;
    if (autodiscovery || doesMatchPluginSerivce(pluginName, self.chromecast.name)) {
      return self.sendMessageToDevice(message, self.chromecast);
    }
  });

  browser.start();
};

Plugin.prototype.detectChromecast = _.debounce(Plugin.prototype.detectChromecastImmediately, 1000);

Plugin.prototype.sendMessageToDevice = function (message, service) {
  debug('sendMessageToDevice', 'Casting...');

  var hostIP = service.addresses[0];

  // this.emit('message', { devices: ['*'], topic: 'echo', payload: service });
  this.onDeviceUp(hostIP, message);
}

Plugin.prototype.onDeviceUp = function (host, message) {
  debug('onDeviceUp', message);
  var self = this;
  self.client = new Client();

  self.client.connect(host, function () {
    self.sendMessageToClient(message);
  });
}

Plugin.prototype.sendMessageToClient = function(message){
  var self = this;
  var APPID = this.getChromecastApplicationID(message);
  // Google Chromecast various namespace handlers for initializing connection.
  var connection = self.client.createChannel('sender-0', 'receiver-0', 'urn:x-cast:com.google.cast.tp.connection', 'JSON');
  var heartbeat = self.client.createChannel('sender-0', 'receiver-0', 'urn:x-cast:com.google.cast.tp.heartbeat', 'JSON');
  var receiver = self.client.createChannel('sender-0', 'receiver-0', 'urn:x-cast:com.google.cast.receiver', 'JSON');

  var launchRequestId;

  // establish virtual connection to the receiver
  connection.send({ type: 'CONNECT' });

  // Check first if the app is avaliable.
  receiver.send({ type: 'GET_APP_AVAILABILITY', appId: [APPID], requestId: self.requestId });

  // start heartbeating
  setInterval(function () {
    heartbeat.send({ type: 'PING' });
  }, 5000);

  receiver.on('message', function (data, broadcast) {
    debug('chromecast ReceiverMessage', JSON.stringify(data));
    if (data.requestId === self.requestId) {
      debug('data requestId');
      debug('self requestId');
      if ('APP_AVAILABLE' === data.availability[APPID]) {
        debug('app is available', data.availability[APPID]);
        launchRequestId = self.requestId;
        debug('request id', self.requestId);
        receiver.send({ type: 'LAUNCH', appId: APPID, requestId: self.requestId++ });
      }
    }else if (data.requestId == launchRequestId) {
      // data requestId and self requestId are diff
      debug('handling launch response...');
      debug('launchRequestId', launchRequestId);
      var app = _.find(data.status.applications, {appId: APPID})
      if(_.isEmpty(app)) return;
      var mySenderConnection = self.client.createChannel('client-13243', app.transportId, 'urn:x-cast:com.google.cast.tp.connection', 'JSON');
      mySenderConnection.send({ type: 'CONNECT' });
{'fraction_non_alphanumeric': 0.15280521532990912, 'fraction_numerical': 0.004543658632951403, 'mean_word_length': 3.7313084112149535, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6897058', 'n_tokens_mistral': 3265, 'n_tokens_neox': 3184, 'n_words': 660}