pifi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/INSTALL.md +63 -0
  3. data/LICENSE +674 -0
  4. data/README.md +140 -0
  5. data/bin/pifi +24 -0
  6. data/config.ru +4 -0
  7. data/docs/icon/license.pdf +737 -1
  8. data/lib/pifi.rb +2 -0
  9. data/lib/pifi/controllers/application_controller.rb +16 -0
  10. data/lib/pifi/controllers/index_controller.rb +33 -0
  11. data/lib/pifi/controllers/player_controller.rb +40 -0
  12. data/lib/pifi/lib/config_getter.rb +44 -0
  13. data/lib/pifi/lib/lang_chooser.rb +27 -0
  14. data/lib/pifi/lib/player.rb +156 -0
  15. data/lib/pifi/lib/streams_getter.rb +27 -0
  16. data/lib/pifi/lib/utils.rb +8 -0
  17. data/lib/pifi/public/android-chrome-192x192.png +0 -0
  18. data/lib/pifi/public/android-chrome-384x384.png +0 -0
  19. data/lib/pifi/public/apple-touch-icon.png +0 -0
  20. data/lib/pifi/public/browserconfig.xml +9 -0
  21. data/lib/pifi/public/css/app.css +143 -0
  22. data/lib/pifi/public/favicon-16x16.png +0 -0
  23. data/lib/pifi/public/favicon-32x32.png +0 -0
  24. data/lib/pifi/public/favicon.ico +0 -0
  25. data/lib/pifi/public/js/app.js +298 -0
  26. data/lib/pifi/public/js/lang/en-us.js +19 -0
  27. data/lib/pifi/public/js/lang/fr-fr.js +19 -0
  28. data/lib/pifi/public/js/lang/nl-nl.js +19 -0
  29. data/lib/pifi/public/js/lang/pt-br.js +19 -0
  30. data/lib/pifi/public/mstile-144x144.png +0 -0
  31. data/lib/pifi/public/mstile-150x150.png +0 -0
  32. data/lib/pifi/public/mstile-310x150.png +0 -0
  33. data/lib/pifi/public/mstile-310x310.png +0 -0
  34. data/lib/pifi/public/mstile-70x70.png +0 -0
  35. data/lib/pifi/public/safari-pinned-tab.svg +61 -0
  36. data/lib/pifi/public/site.webmanifest +19 -0
  37. data/lib/pifi/public/vendor/css/bootstrap-theme.css +587 -0
  38. data/lib/pifi/public/vendor/css/bootstrap-theme.css.map +1 -0
  39. data/lib/pifi/public/vendor/css/bootstrap-theme.min.css +6 -0
  40. data/lib/pifi/public/vendor/css/bootstrap-theme.min.css.map +1 -0
  41. data/lib/pifi/public/vendor/css/bootstrap.css +6757 -0
  42. data/lib/pifi/public/vendor/css/bootstrap.css.map +1 -0
  43. data/lib/pifi/public/vendor/css/bootstrap.min.css +6 -0
  44. data/lib/pifi/public/vendor/css/bootstrap.min.css.map +1 -0
  45. data/lib/pifi/public/vendor/fonts/glyphicons-halflings-regular.eot +0 -0
  46. data/lib/pifi/public/vendor/fonts/glyphicons-halflings-regular.svg +288 -0
  47. data/lib/pifi/public/vendor/fonts/glyphicons-halflings-regular.ttf +0 -0
  48. data/lib/pifi/public/vendor/fonts/glyphicons-halflings-regular.woff +0 -0
  49. data/lib/pifi/public/vendor/fonts/glyphicons-halflings-regular.woff2 +0 -0
  50. data/lib/pifi/public/vendor/js/bootstrap.js +2377 -0
  51. data/lib/pifi/public/vendor/js/bootstrap.min.js +7 -0
  52. data/lib/pifi/public/vendor/js/jquery.min.js +4 -0
  53. data/lib/pifi/public/vendor/js/npm.js +13 -0
  54. data/lib/pifi/views/index.erb +91 -0
  55. data/lib/pifi/views/layout.erb +35 -0
  56. metadata +169 -0
Binary file
Binary file
Binary file
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+
3
+ var state = null;
4
+
5
+ var view = {
6
+ init: function() {
7
+ this.volTimeout = null;
8
+
9
+ // Set initial "loading" screen
10
+ this.hide(lang.loading);
11
+
12
+ this.setStaticStr();
13
+ this.addListeners();
14
+ },
15
+
16
+ render: function() {
17
+ this.renderPlaying();
18
+ this.renderTitle();
19
+ this.renderProgress();
20
+ this.renderProgressBar();
21
+ this.renderVolButtons();
22
+ this.setView();
23
+ },
24
+
25
+ renderPlaying: function() {
26
+ if (state.playing) {
27
+ $("#playing").text(lang.playing);
28
+ $("#span-ps")
29
+ .addClass("glyphicon-stop")
30
+ .removeClass("glyphicon-play");
31
+ $("#btn-ps")
32
+ .addClass("btn-danger")
33
+ .removeClass("btn-default");
34
+ } else {
35
+ $("#playing").text(lang.notPlaying);
36
+ $("#span-ps")
37
+ .addClass("glyphicon-play")
38
+ .removeClass("glyphicon-stop");
39
+ $("#btn-ps")
40
+ .addClass("btn-default")
41
+ .removeClass("btn-danger");
42
+ }
43
+ },
44
+
45
+ renderTitle: function() {
46
+ $("#title").text(state.title);
47
+ $("#artist").text(state.artist);
48
+ if (state.local) {
49
+ $("#title").addClass("text-uppercase");
50
+ } else {
51
+ $("#title").removeClass("text-uppercase");
52
+ }
53
+ },
54
+
55
+ renderProgress: function() {
56
+ if (state.local && state.playing) {
57
+ $("#elapsed").text(this.toMinSec(state.elapsed));
58
+ $("#length").text(this.toMinSec(state.length));
59
+ $("#progress").show();
60
+ } else {
61
+ $("#progress").hide();
62
+ }
63
+ },
64
+
65
+ renderProgressBar: function() {
66
+ if (state.local && state.playing) {
67
+ var progress = state.elapsed / state.length;
68
+ $("#progress-bar").css("width", progress * 100 + "%");
69
+ } else {
70
+ $("#progress-bar").css("width", "0%");
71
+ }
72
+ },
73
+
74
+ renderVolButtons: function() {
75
+ // Only show buttons if volume is available
76
+ if (state.vol < 0) {
77
+ $("#btn-vdown, #btn-vup").attr("disabled", true);
78
+ } else {
79
+ $("#btn-vdown, #btn-vup").attr("disabled", false);
80
+ }
81
+ },
82
+
83
+ setView() {
84
+ if (state.con_mpd && this.hidden) {
85
+ this.unhide(state.playing);
86
+ } else if (!state.con_mpd) {
87
+ this.hide(lang.disconnectedMpd);
88
+ }
89
+ },
90
+
91
+ setStaticStr: function() {
92
+ $("#lbl-random").text(lang.sBtnRandom);
93
+ $("#lbl-radios").text(lang.sBtnRadios);
94
+ $("#lbl-player").text(lang.sBtnPlayer);
95
+ $("#radios-welcome").text(lang.sRadiosWelcome);
96
+ $("#insert").text(lang.sInsert);
97
+ },
98
+
99
+ showPlayer: function() {
100
+ $(".view").hide();
101
+ $("#player").show();
102
+ },
103
+
104
+ showRadios: function() {
105
+ $(".view").hide();
106
+ $("#radios").show();
107
+ },
108
+
109
+ showAlert: function(text, textMore) {
110
+ if (textMore === undefined) {
111
+ textMore = "";
112
+ }
113
+
114
+ $("#alert-text-main").text(text);
115
+ $("#alert-text-more").text(textMore);
116
+
117
+ $(".view").hide();
118
+ $("#alert").show();
119
+ },
120
+
121
+ hide: function(text) {
122
+ this.showAlert(text);
123
+ this.hidden = true;
124
+ },
125
+
126
+ unhide: function(playing) {
127
+ if (playing) {
128
+ view.showPlayer();
129
+ } else {
130
+ view.showRadios();
131
+ }
132
+ this.hidden = false;
133
+ },
134
+
135
+ osdVol: function(vol) {
136
+ $("#osd-text").text(vol);
137
+ $("#player-bottom").hide();
138
+ $("#osd").show();
139
+
140
+ clearTimeout(this.volTimeout);
141
+ this.volTimeout = setTimeout(function() {
142
+ $("#osd").hide();
143
+ $("#player-bottom").show();
144
+ }, timeConst.volOsd);
145
+ },
146
+
147
+ addListeners: function() {
148
+ // Prevent href="#" to be executed
149
+ $('a[href="#"]').click(function(event) {
150
+ return false;
151
+ });
152
+
153
+ $("#btn-vup").click(function(event) {
154
+ controller.clickVol("+5");
155
+ });
156
+
157
+ $("#btn-vdown").click(function(event) {
158
+ controller.clickVol("-5");
159
+ });
160
+
161
+ $("#btn-ps").click(function(event) {
162
+ controller.clickPs();
163
+ });
164
+
165
+ $("#btn-random").click(function(event) {
166
+ controller.clickRandom();
167
+ });
168
+
169
+ $("#btn-radios").click(function(event) {
170
+ view.showRadios();
171
+ });
172
+
173
+ $("#btn-player").click(function(event) {
174
+ view.showPlayer();
175
+ });
176
+
177
+ $(".radio-name").click(function(event) {
178
+ var name = $(this).text();
179
+ controller.clickRadio(name);
180
+ });
181
+
182
+ $("#insert").click(function(event) {
183
+ var url = prompt(lang.urlInsert);
184
+ controller.clickInsert(url);
185
+ });
186
+ },
187
+
188
+ toMinSec: function(sec) {
189
+ return new Date(sec * 1000).toISOString().substr(14, 5);
190
+ }
191
+ };
192
+
193
+ var controller = {
194
+ PLAYER_API: "/api/player",
195
+
196
+ init: function() {
197
+ view.init();
198
+ this.updateState(true);
199
+ },
200
+
201
+ updateState: function(repeat) {
202
+ var self = controller;
203
+ var fetchState = $.get(self.PLAYER_API)
204
+ .done(function(response) {
205
+ state = response;
206
+ view.render();
207
+ })
208
+ .fail(function() {
209
+ view.hide(lang.disconnectedNet);
210
+ });
211
+
212
+ if (repeat) {
213
+ fetchState.always(function() {
214
+ setTimeout(controller.updateState, timeConst.update, true);
215
+ });
216
+ }
217
+ },
218
+
219
+ clickRandom: function() {
220
+ var text;
221
+ var waitTime;
222
+
223
+ // Define which alert to show based on player state
224
+ if (state.playing && state.local) {
225
+ text = lang.randomNext;
226
+ waitTime = timeConst.randomNext;
227
+ } else {
228
+ text = lang.randomFirst;
229
+ waitTime = timeConst.randomFirst;
230
+ }
231
+ view.showAlert(text);
232
+
233
+ $.post(this.PLAYER_API, { method: "play_random" }).always(function() {
234
+ setTimeout(view.showPlayer, waitTime);
235
+ });
236
+ },
237
+
238
+ clickVol: function(delta) {
239
+ $.post(this.PLAYER_API, { method: "change_vol", params: delta }).done(
240
+ function(response) {
241
+ view.osdVol(response + "%");
242
+ }
243
+ );
244
+ },
245
+
246
+ clickPs: function() {
247
+ var method = state.playing ? "stop" : "play";
248
+ $.post(this.PLAYER_API, { method: method }).done(function() {
249
+ controller.updateState(false);
250
+ });
251
+ },
252
+
253
+ clickRadio: function(name) {
254
+ if (name === state.title && !state.local && state.playing) {
255
+ view.showPlayer();
256
+ } else {
257
+ this.playStream(true, name);
258
+ }
259
+ },
260
+
261
+ clickInsert: function(url) {
262
+ if (url !== null && url !== "") {
263
+ this.playStream(false, url.trim());
264
+ }
265
+ },
266
+
267
+ playStream: function(isName, value) {
268
+ view.showAlert(lang.streamTrying, value);
269
+
270
+ if (isName) {
271
+ var data = { method: "play_radios", params: value };
272
+ } else {
273
+ var data = { method: "play_urls", params: value };
274
+ }
275
+
276
+ $.post(this.PLAYER_API, data)
277
+ .done(function() {
278
+ setTimeout(view.showPlayer, timeConst.playStream);
279
+ })
280
+ .fail(function(response) {
281
+ view.showAlert(lang.streamError, response.responseText);
282
+ setTimeout(view.showRadios, timeConst.error);
283
+ });
284
+ }
285
+ };
286
+
287
+ var timeConst = {
288
+ playStream: 2000,
289
+ error: 3000,
290
+ randomNext: 1500,
291
+ randomFirst: 5000,
292
+ volOsd: 1300,
293
+ update: 1000
294
+ };
295
+
296
+ $(document).ready(function() {
297
+ controller.init();
298
+ });
@@ -0,0 +1,19 @@
1
+ var lang = {
2
+ playing: "Playing",
3
+ notPlaying: "Stopped",
4
+ streamError: "Failed to play this stream",
5
+ streamTrying: "Tunning...",
6
+ urlInsert: "Type the URL",
7
+ randomNext: "Next song",
8
+ randomFirst: "Starting library playback...",
9
+ disconnectedMpd: "Disconnected from MPD",
10
+ disconnectedNet: "No connection",
11
+ loading: "Loading...",
12
+
13
+ // Static
14
+ sBtnRandom: "Random",
15
+ sBtnRadios: "Radios",
16
+ sBtnPlayer: "Player",
17
+ sRadiosWelcome: "Choose a radio station",
18
+ sInsert: "Type a URL..."
19
+ };
@@ -0,0 +1,19 @@
1
+ var lang = {
2
+ playing: "Lecture",
3
+ notPlaying: "Arrêté",
4
+ streamError: "Échec à l'ajout du flux",
5
+ streamTrying: "Mise en tampon…",
6
+ urlInsert: "Entrez l'URL",
7
+ randomNext: "Titre suivant",
8
+ randomFirst: "Ajout des titres…",
9
+ disconnectedMpd: "Déconnecté de MPD",
10
+ disconnectedNet: "Pas de connexion",
11
+ loading: "Chargement",
12
+
13
+ // Static
14
+ sBtnRandom: "Aléatoire",
15
+ sBtnRadios: "Radios",
16
+ sBtnPlayer: "Lecteur",
17
+ sRadiosWelcome: "Choisir une station de radio",
18
+ sInsert: "Entrez l'URL…"
19
+ };
@@ -0,0 +1,19 @@
1
+ var lang = {
2
+ playing: "Aan het afspelen",
3
+ notPlaying: "Gestopt",
4
+ streamError: "Kan deze stream niet afspelen",
5
+ streamTrying: "Bezig met verbinden...",
6
+ urlInsert: "Voer een url in",
7
+ randomNext: "Volgend nummer",
8
+ randomFirst: "Bezig met voorbereiden van verzameling...",
9
+ disconnectedMpd: "Niet verbonden met MPD",
10
+ disconnectedNet: "Geen verbinding",
11
+ loading: "Bezig met laden...",
12
+
13
+ // Static
14
+ sBtnRandom: "Willekeurig",
15
+ sBtnRadios: "Radio's",
16
+ sBtnPlayer: "Speler",
17
+ sRadiosWelcome: "Kies een radiostation",
18
+ sInsert: "Voer een url in..."
19
+ };
@@ -0,0 +1,19 @@
1
+ var lang = {
2
+ playing: "Tocando",
3
+ notPlaying: "Parado",
4
+ streamError: "Não foi possível tocar essa rádio",
5
+ streamTrying: "Sintonizando...",
6
+ urlInsert: "Insira a URL",
7
+ randomNext: "Próxima música",
8
+ randomFirst: "Iniciando reprodução da biblioteca...",
9
+ disconnectedMpd: "Desconectado do MPD",
10
+ disconnectedNet: "Sem conexão",
11
+ loading: "Carregando...",
12
+
13
+ // Static
14
+ sBtnRandom: "Aleatório",
15
+ sBtnRadios: "Rádios",
16
+ sBtnPlayer: "Controles",
17
+ sRadiosWelcome: "Escolha uma emissora",
18
+ sInsert: "Tocar URL..."
19
+ };
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,61 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+ <metadata>
8
+ Created by potrace 1.11, written by Peter Selinger 2001-2013
9
+ </metadata>
10
+ <g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
11
+ fill="#000000" stroke="none">
12
+ <path d="M1582 6765 c-12 -9 -29 -29 -37 -45 -34 -66 -13 -106 89 -173 45 -28
13
+ 89 -56 99 -62 136 -77 3670 -2321 3661 -2325 -10 -4 -2604 -5 -2649 -1 -6 1
14
+ -10 68 -11 180 -1 167 -2 180 -23 209 -12 17 -32 34 -44 38 -29 10 -1128 11
15
+ -1165 2 -15 -4 -39 -19 -53 -34 -23 -25 -24 -30 -27 -207 -2 -100 -5 -184 -7
16
+ -186 -3 -2 -123 -5 -267 -6 -257 -2 -265 -2 -337 -28 -97 -35 -158 -75 -227
17
+ -150 -65 -70 -118 -173 -135 -262 -13 -65 -17 -2956 -5 -3029 22 -130 75 -231
18
+ 170 -319 55 -52 106 -84 185 -115 l66 -27 2635 0 2635 0 69 28 c136 54 255
19
+ 165 308 287 53 122 51 48 51 1640 0 1061 -4 1492 -12 1535 -27 142 -116 273
20
+ -243 356 -91 59 -176 81 -336 84 -119 3 -148 7 -175 23 -17 10 -167 104 -332
21
+ 209 -1179 746 -1978 1250 -1997 1261 -13 7 -127 79 -255 160 -128 81 -371 234
22
+ -540 341 -169 107 -337 213 -373 236 -614 395 -662 420 -718 380z m381 -2394
23
+ c11 -2 9 -211 -3 -212 -5 0 -79 -1 -162 -2 l-153 0 -3 98 c-1 54 -1 103 1 109
24
+ 3 8 55 11 158 10 85 -1 157 -2 162 -3z m549 -11 c2 -8 4 -57 3 -109 l0 -94
25
+ -162 -1 -162 -1 -3 75 c-2 41 -2 91 0 110 l3 35 159 0 c134 0 159 -2 162 -15z
26
+ m1639 -429 c1 -3 2 -790 3 -1748 l2 -1743 -26 -1 c-14 0 -736 0 -1605 0 -1183
27
+ -1 -1592 2 -1625 11 -99 26 -184 103 -223 203 -20 51 -20 67 -21 1507 -1 800
28
+ 3 1477 7 1504 22 131 130 242 257 266 44 8 3230 10 3231 1z m1984 -16 c34 -12
29
+ 70 -38 111 -79 107 -108 98 42 98 -1621 1 -792 -2 -1464 -6 -1493 -15 -121
30
+ -101 -225 -220 -268 -38 -14 -149 -16 -885 -16 -463 0 -845 2 -850 5 -6 4 -10
31
+ 3428 -4 3483 1 9 219 11 854 10 803 -2 855 -3 902 -21z"/>
32
+ <path d="M2289 3716 c-2 -2 -33 -6 -69 -10 -53 -5 -175 -30 -250 -50 -115 -32
33
+ -313 -126 -432 -208 -130 -88 -315 -275 -402 -406 -347 -525 -347 -1185 0
34
+ -1708 147 -222 362 -407 608 -525 104 -49 254 -103 319 -113 20 -3 56 -10 79
35
+ -15 153 -34 402 -30 579 9 512 111 927 465 1114 949 104 268 128 578 69 866
36
+ -68 326 -247 622 -523 863 -89 78 -370 242 -414 242 -9 0 -18 4 -21 9 -3 5
37
+ -16 11 -28 14 -13 3 -54 15 -93 26 -118 36 -233 53 -386 56 -81 2 -148 3 -150
38
+ 1z m386 -244 c461 -105 813 -414 969 -850 68 -190 90 -448 56 -647 -107 -623
39
+ -632 -1081 -1259 -1098 -551 -16 -1055 321 -1261 842 -29 74 -68 232 -76 311
40
+ -13 118 -8 301 11 390 3 14 7 37 10 52 25 137 131 368 229 498 208 277 501
41
+ 454 856 515 111 19 352 12 465 -13z"/>
42
+ <path d="M2335 2728 c-260 -33 -455 -243 -472 -508 -20 -324 237 -587 562
43
+ -575 300 10 525 244 525 545 0 327 -289 580 -615 538z m206 -245 c212 -93 257
44
+ -377 85 -537 -90 -84 -245 -107 -357 -55 -189 89 -248 334 -119 498 94 119
45
+ 250 156 391 94z"/>
46
+ <path d="M4765 3706 c-63 -29 -60 27 -61 -961 -1 -528 3 -910 8 -925 5 -14 21
47
+ -35 36 -47 l27 -22 571 -1 c382 0 581 4 602 11 17 6 39 23 49 37 16 25 17 80
48
+ 18 922 1 744 -1 901 -13 932 -26 70 -12 68 -644 68 -449 -1 -569 -4 -593 -14z
49
+ m1031 -971 l0 -765 -437 0 -437 0 1 110 0 109 289 0 c248 -1 293 2 313 15 50
50
+ 33 65 104 33 155 -28 45 -52 48 -351 48 l-282 0 -2 547 -1 546 437 0 437 0 0
51
+ -765z"/>
52
+ <path d="M5255 3282 c-55 -5 -102 -43 -111 -88 -9 -49 16 -101 59 -119 41 -17
53
+ 292 -15 323 3 31 16 57 77 49 112 -10 43 -42 79 -78 85 -40 7 -189 11 -242 7z"/>
54
+ <path d="M5221 2838 c-37 -10 -81 -61 -81 -95 0 -38 28 -88 57 -104 18 -10 65
55
+ -14 167 -13 125 0 145 2 169 19 65 48 55 148 -18 188 -23 12 -250 16 -294 5z"/>
56
+ <path d="M5257 1519 c-149 -35 -290 -175 -322 -319 -34 -151 9 -305 115 -411
57
+ 88 -89 188 -131 310 -131 327 1 535 346 386 640 -51 100 -174 195 -287 221
58
+ -53 13 -150 12 -202 0z m180 -221 c51 -22 83 -49 108 -91 65 -112 24 -252 -91
59
+ -308 -146 -70 -314 38 -314 203 0 37 40 126 68 151 60 52 164 73 229 45z"/>
60
+ </g>
61
+ </svg>