Add crazy streaming JS at the last minute! holy crap!
[libreplanet-static.git] / 2015 / assets / js / stream.js
CommitLineData
a1288c2a
DT
1/**
2 * @licstart The following is the entire license notice for the JavaScript code in this page.
3 *
4 * IceCast Stream Monitor
5 * Copyright © 2015 David Thompson <davet@gnu.org>
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 *
21 * @licend The above is the entire license notice for the JavaScript code in this page
22 */
23
6c5ea831 24var app = {};
a1288c2a 25
6c5ea831
DT
26app.icecastUrl = "http://live.fsf.org";
27
28app.scheduleEvery = function(duration, thunk) {
a1288c2a
DT
29 thunk();
30 setTimeout(function() {
6c5ea831 31 app.scheduleEvery(duration, thunk);
a1288c2a 32 }, duration);
6c5ea831 33};
a1288c2a 34
6c5ea831
DT
35app.nullStats = {
36 listeners: 0,
37 server_name: null,
38 server_description: null
39};
40
41app.streamStats = function(mount) {
42 var statsUrl = app.icecastUrl.concat('/status-json.xsl');
43
44 return m.request({
45 method: "GET",
46 url: statsUrl
47 }).then(function(data) {
48 // Match the end of the listen URL for the mount point.
49 var regexp = new RegExp(mount.concat('$'));
50
51 // Due to <https://trac.xiph.org/ticket/2174>, we must
52 // explicitly test if icestats.source is an array.
53 if(!(data.icestats.source instanceof Array)) {
54 data.icestats.source = [data.icestats.source];
55 }
56
57 var stats = data.icestats.source.find(function(source) {
58 return regexp.test(source.listenurl);
59 });
60
61 return stats || app.nullStats;
62 });
63};
64
65app.validStreamInfo = function(stats) {
66 var name = stats.server_name;
67 var desc = stats.server_description;
68
69 return name && desc && name !== "Unspecified name" &&
70 desc !== "Unspecified description";
71};
72
73app.mountToStreamUrl = function(mount) {
74 return app.icecastUrl.concat(mount);
75};
76
77app.changeVideoMount = function(video, mount) {
78 console.log(mount);
79 video.src = app.mountToStreamUrl(mount);
80 video.load();
81 video.play();
82};
83
84app.streams = [
85 {
86 name: "Room 123",
87 speakerMount: "/room123.ogv",
88 desktopMount: "/room123-desktop.ogv",
89 ircChannel: "#libreplanet_room123"
90 }, {
91 name: "Room 141",
92 speakerMount: "/room141.ogv",
93 desktopMount: "/room141-desktop.ogv",
94 ircChannel: "#libreplanet_room141"
95 }, {
96 name: "Room 155",
97 speakerMount: "/room155.ogv",
98 desktopMount: "/room155-desktop.ogv",
99 ircChannel: "#libreplanet_room155"
a1288c2a 100 }
6c5ea831 101];
a1288c2a 102
6c5ea831
DT
103app.controller = function() {
104 var self = this;
a1288c2a 105
6c5ea831
DT
106 this.stream = m.prop(app.streams[0]);
107 this.stats = m.prop(app.nullStats);
108
109 this.speakerPosition = m.prop({
110 x: 0,
111 y: 0
112 });
113
114 this.showDesktop = m.prop(false);
115
116 this.updateStats = function() {
117 self.stats = app.streamStats(self.stream().speakerMount);
118 };
119
120 app.scheduleEvery(10000, function() {
121 self.updateStats();
a1288c2a 122 });
6c5ea831
DT
123};
124
125app.view = function(ctrl) {
126 var stream = ctrl.stream();
127 var pos = ctrl.speakerPosition();
128 var showDesktop = ctrl.showDesktop();
129 var stats = ctrl.stats();
130 console.log(stats);
131
132 function renderDesktopStream() {
133 return m("video.lp-video", {
134 id: "desktop-video",
135 autoplay: true
136 }, [
137 m("source", { src: app.mountToStreamUrl(stream.desktopMount) }),
138 m("p",
139 m("em", [
140 "Your browser does not support the HTML5 video tag, ",
141 m("a", { href: "TODO" }, "[ please download ]"),
142 "the video instead"
143 ]))
144 ]);
145 }
146
147 function renderToggleDesktopStream() {
148 var action = showDesktop ? "Hide desktop stream" : "Show desktop stream";
149
150 return m(".row", [
151 m(".col-sm-offset-4.col-sm-4",
152 m("button.btn.btn-block.btn-default", {
153 onclick: function() {
154 ctrl.showDesktop(!showDesktop);
155 }
156 }, action)
157 )
158 ]);
159 }
160
161 function renderRoomSelector() {
162 return m(".row",
163 m(".col-sm-offset-1.col-sm-10",
164 m("ol.breadcrumb.text-center", app.streams.map(function(s) {
165 return m("li", {
166 class: s === stream ? "active" : null,
167 onclick: function() {
168 var speakerVideo = document.getElementById("speaker-video");
169 var desktopVideo = document.getElementById("desktop-video");
170
171 console.log(s);
172
173 app.changeVideoMount(speakerVideo, s.speakerMount);
174
175 // Video element doesn't exist when the user
176 // hasn't elected to show it.
177 if(desktopVideo) {
178 app.changeVideoMount(desktopVideo, s.desktopMount);
179 }
180
181 ctrl.stream(s);
182 }
183 }, m("a.alt-a", { href: "#" }, s.name));
184 }))));
185 }
186
187 function renderStats() {
188 var info = app.validStreamInfo(stats) ? [
189 m("strong", stats.server_name),
190 " — ",
191 m("i", stats.server_description)
192 ] : null;
193
194 return m(".row", [
195 m(".col-sm-8", info),
196 m(".col-sm-4.text-right", [
197 m("strong", stats.listeners),
198 " watching"
199 ])
200 ]);
201 }
202
203 return [
204 renderRoomSelector(),
205 m("h2", stream.name),
206 renderStats(),
207 m("video.lp-video", {
208 id: "speaker-video",
209 controls: true,
210 autoplay: true
211 }, [
212 m("source", {
213 src: app.mountToStreamUrl(stream.speakerMount)
214 }),
215 m("p",
216 m("em", [
217 "Your browser does not support the HTML5 video tag, ",
218 m("a", { href: "TODO" }, "[ please download ]"),
219 "the video instead"
220 ]))
221 ]),
222 showDesktop ? renderDesktopStream() : null,
223 renderToggleDesktopStream(),
224 m("h2", "IRC"),
225 m("p", "Join the discussion online!"),
226 m("ul", [
227 m("li", [
228 "Conference-wide Freenode IRC channel: ",
229 m("strong", "#libreplanet")
230 ]),
231 m("li", [
232 "Freenode IRC channel for ",
233 stream.name,
234 ": ",
235 m("strong", stream.ircChannel)
236 ]),
237 m("li", [
238 "Conference hashtag for ",
239 m("a", { href: "https://fsf.org/twitter" }, "microblogging"),
240 ": ",
241 m("strong", "#lp2015")
242 ])
243 ])
244 ];
245};
246
247m.module(document.getElementById("stream"), app);