const Lang = imports.lang; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Signals = imports.signals; const Soup = imports.gi.Soup; const DialManager = imports.protocols.dialManager; const WebSocket = imports.protocols.webSocket; const CLIENT_ORIGIN = 'chrome-extension://boadgeojelhgndaghljhdicfkmllpafd'; const SERVICE_VERSION = 'release-3df2f7159e87f36d38115f377f5ca88335dd3649'; const REFRESH_INTERVAL = 1000; // milliseconds const ChromeCast = new Lang.Class({ Name: 'ChromeCast', _init: function() { this._dialManager = new DialManager.DialManager({ appName: 'ChromeCast' }); this._senderId = this._generateSenderId(); this._dialManager.connect('device-found', Lang.bind(this, function(manager, dialDevice) { if (dialDevice.name == 'ChromeCast') { print('device found'); this.emit('device-found', dialDevice); } })); this._dialManager.discover(); }, _generateSenderId: function() { let nonce = ""; for (let i = 0; i < 4; i++) nonce += String.fromCharCode(GLib.random_int_range(0, 255)); return GLib.base64_encode(nonce, nonce.length); }, _openCastSession: function(session, uri) { print('opening cast session'); let message = new Soup.Message({ method: 'POST', uri: uri }); let formData = JSON.stringify({ channel: 0, senderId: { appName: 'ChromeCast', senderId: this._senderId }}); print('formdata', formData); message.set_request('application/json', Soup.MemoryUse.COPY, formData, formData.length); message.request_headers.replace('Origin', CLIENT_ORIGIN); session.queue_message(message, Lang.bind(this, function() { print('response: ', message.status_code, message.reason_phrase); let buffer = message.response_body.flatten(); let castSession = JSON.parse(buffer.get_as_bytes().get_data()); print(JSON.stringify(castSession)); if (castSession.URL) { let webSocket = new WebSocket.WebSocket({ origin: CLIENT_ORIGIN, pingInterval: castSession.pingInterval }); webSocket.open(castSession.URL, Lang.bind(this, function() { print('websocket opened'); })); } })); }, _castWhenReady: function(session, dialDevice, connectionUri) { print('waiting to cast until app is ready'); dialDevice.refresh(Lang.bind(this, function(uri, app) { if (!dialDevice.serviceURL) GLib.timeout_add(GLib.PRIORITY_DEFAULT, REFRESH_INTERVAL, Lang.bind(this, function() { this._castWhenReady(session, dialDevice, connectionUri); })); else this._openCastSession(session, new Soup.URI(dialDevice.serviceURL)); })); }, castToDevice: function(dialDevice) { print('starting ChromeCast app'); let session = new Soup.Session(); let message = new Soup.Message({ method: 'POST', uri: dialDevice.appResourceUri }); print('about to cast', dialDevice.appResourceUri.to_string(false)); let formData = Soup.form_encode_hash({ v: SERVICE_VERSION, id: 'local:0', idle: 'windowclose' }); message.set_request(Soup.FORM_MIME_TYPE_URLENCODED, Soup.MemoryUse.COPY, formData, formData.length); session.queue_message(message, Lang.bind(this, function() { let headers = message.response_headers; let connectionUri = headers.get('Location'); print('response: ', message.status_code, message.reason_phrase); if (connectionUri) { print('Connection URI', connectionUri); this._castWhenReady(session, dialDevice, connectionUri); } })); }, }); Signals.addSignalMethods(ChromeCast.prototype);