summaryrefslogtreecommitdiff
path: root/protocols/chromeCast.js
blob: b0abaef39b5a79187a5671cf6aacab9da7668ece (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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);