ignore some files when building
[playVideoOnDreambox.git] / index.js
1 var buttons = require('sdk/ui/button/action');
2 var tabs = require("sdk/tabs");
3 require("sdk/simple-prefs").on("", reloadPrefs);
4
5 //IP or hostname of dreambox
6 var dreamboxHost;
7 //Full file path to "youtube-dl" binary
8 var youtubedlPath;
9 //dreambox web interface access token
10 var dbToken;
11 //number of failures fetching the access token
12 var dbTokenFails = 0;
13
14 reloadPrefs();
15
16 function reloadPrefs()
17 {
18     var prefs = require("sdk/simple-prefs").prefs;
19     youtubedlPath = prefs.youtubedlPath;
20     dreamboxHost  = prefs.dreamboxHost;
21 }
22
23 //toolbar button
24 var button = buttons.ActionButton({
25   id: "dreambox-play-link",
26   label: "Play video on Dreambox",
27   icon: "./play-32.png",
28   onClick: playCurrentTab
29 });
30 //context menu for links
31 var contextMenu = require("sdk/context-menu");
32 var menuItem = contextMenu.Item({
33     label:   'Play linked video on Dreambox',
34     context: contextMenu.SelectorContext('a[href]'),
35     contentScript: 'self.on("click", function(node) {' +
36                  '    self.postMessage(node.href);' +
37                  '});',
38     accesskey: 'x',
39     onMessage: function (linkUrl) {
40         playPageUrl(linkUrl);
41     }
42 });
43
44 function playCurrentTab(state)
45 {
46     console.log('active tab', tabs.activeTab.url);
47     var pageUrl = tabs.activeTab.url;
48     playPageUrl(pageUrl);
49 }
50
51 function playPageUrl(pageUrl)
52 {
53     var child_process = require("sdk/system/child_process");
54     var ytdl = child_process.spawn(youtubedlPath, ['--get-url', pageUrl]);
55
56     var videoUrl = null;
57     var errors = '';
58     ytdl.stdout.on('data', function (data) {
59         videoUrl = data.trim();
60         console.debug('youtube-dl URL: ' + data.trim());
61     });
62
63     ytdl.stderr.on('data', function (data) {
64         console.debug('youtube-dl stderr: ' + data.trim());
65         if (data.substr(0, 7) != 'WARNING') {
66             errors += "\n" + data.trim();
67         }
68     });
69
70     ytdl.on('close', function (code) {
71         if (code == 0) {
72             //we have a url. run dreambox
73             playVideoOnDreambox(videoUrl);
74         } else if (code == -1) {
75             showError('youtube-dl not found');
76         } else {
77             console.log('youtube-dl exit code ' + code);
78             showError(
79                 'Failed to extract video URL with youtube-dl'
80                 + errors
81             );
82         }
83     });
84 }
85
86 function playVideoOnDreambox(videoUrl)
87 {
88     var dreamboxUrl = 'http://' + dreamboxHost
89         + '/web/mediaplayerplay?file=4097:0:1:0:0:0:0:0:0:0:'
90         + encodeURIComponent(videoUrl).replace('%3A', '%253A');
91     console.log('dreambox url: ' + dreamboxUrl);
92
93     var Request = require("sdk/request").Request;
94     Request({
95         url: dreamboxUrl,
96         content: {
97             sessionid: dbToken
98         },
99         onComplete: function (response) {
100             // https://developer.mozilla.org/en-US/docs/Web/HTTP/Response_codes
101             if (response.status == 412) {
102                 //web interface requires an access token
103                 // fetch it and try to play the video again
104                 fetchAccessToken(
105                     function() {
106                         playVideoOnDreambox(videoUrl);
107                     }
108                 );
109             } else if (response.status == 0) {
110                 showError('Failed to connect to dreambox. Maybe wrong IP?');
111             } else if (response.status > 399) {
112                 showError('Dreambox failed to play the movie');
113                 console.error('Dreambox play response status: ' + response.status);
114             } else {
115                 //all fine
116                 console.log('Dreambox play response status: ' + response.status);
117             }
118         }
119     }).post();
120 }
121
122 /**
123  * Obtain an access token from the dreambox and store it
124  * in the global dbToken variable.
125  *
126  * @param function replayFunc Function to call when the token has
127  *                            been acquired successfully
128  */
129 function fetchAccessToken(replayFunc)
130 {
131     if (dbTokenFails > 0) {
132         return;
133     }
134
135     dbTokenFails++;
136     var Request = require("sdk/request").Request;
137     Request({
138         url: 'http://' + dreamboxHost + '/web/session',
139         onComplete: function (response) {
140             if (response.status != 200) {
141                 return;
142             }
143             var nStart = response.text.indexOf('<e2sessionid>') + 13;
144             var nEnd   = response.text.indexOf('</e2sessionid>');
145             if (nStart == -1 || nEnd == -1) {
146                 showError('Could not parse web interface token XML');
147                 return;
148             }
149
150             dbTokenFails = 0;
151             dbToken = response.text.substring(nStart, nEnd);
152             console.log('Acquired access token: ' + dbToken);
153             replayFunc();
154         }
155     }).post();
156 }
157
158 /**
159  * Show the error message in a notification popup window
160  * @link https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/notifications
161  */
162 function showError(msg)
163 {
164     require("sdk/notifications").notify({
165         title: "Error - Play video on Dreambox",
166         text: encodeXmlEntities(msg.trim()),
167         iconURL: "./play-32.png"
168     });
169     console.error('showError: ' + msg.trim());
170 }
171
172 /**
173  * Encoding XML entities is necessary on Ubuntu 14.04 with Mate 1.8.2
174  * If I don't do it, the message is empty.
175  */
176 function encodeXmlEntities(str)
177 {
178     return str.replace('&', '&amp;')
179         .replace('<', '&lt;')
180         .replace('>', '&gt;')
181         .replace('"', '&quot;')
182         .replace("'", '&apos;');
183 };