Delete webapp2. Poor thing never saw the light of day
@ -1,120 +0,0 @@
|
||||
/* ==========================================================================
|
||||
|
||||
LET'S GET STARTED!
|
||||
|
||||
========================================================================== */
|
||||
|
||||
body, input, select, button {
|
||||
font-size: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: 'Montserrat', 'Proxima Nova', sans-serif;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
=BASE
|
||||
========================================================================== */
|
||||
|
||||
.hide {
|
||||
/*display: none;*/
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 1em;
|
||||
background: #222;
|
||||
border: none;
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
transition: all .15s ease;
|
||||
}
|
||||
|
||||
input:hover {
|
||||
background: #252525;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
border-color: #2962FF;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.label--magic {
|
||||
position: relative;
|
||||
margin: 0 0 1.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.label--magic > span {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
color: #555;
|
||||
margin: 1em .625em;
|
||||
padding: 0 .375em;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
transform-origin: 0 50%;
|
||||
transition: all .3s ease;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.label--magic > input {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.label--magic input:focus {
|
||||
background: #252525;
|
||||
}
|
||||
|
||||
.label--magic input:focus + span,
|
||||
.label--magic input:not(:focus):valid + span {
|
||||
transform: scale(.8);
|
||||
background: #252525;
|
||||
top: -2em;
|
||||
}
|
||||
|
||||
|
||||
button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
border: none;
|
||||
color: white;
|
||||
background: #2962FF;
|
||||
cursor: pointer;
|
||||
transition: all .15s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #2979FF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
=LOGIN OVERLAY
|
||||
========================================================================== */
|
||||
|
||||
.login-overlay {
|
||||
position: fixed;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
width: 300px;
|
||||
padding: 2em;
|
||||
margin: 2em auto;
|
||||
background: #111;
|
||||
}
|
||||
|
||||
.login-icon {
|
||||
max-width: 50%;
|
||||
margin: 0 auto 1.5em;
|
||||
display: block;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "App",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/android-icon-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image\/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image\/png",
|
||||
"density": "1.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image\/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image\/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image\/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="enable-background:new 0 0 218.6 220.5;"
|
||||
viewBox="0 0 218.6 220.5"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata15"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs13" /><style
|
||||
id="style2"
|
||||
type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#6785B3;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#26487C;}
|
||||
</style><polygon
|
||||
id="polygon4"
|
||||
points="8.4,211.7 8.4,9.9 68.9,41.5 68.9,211.7 "
|
||||
class="st0" /><polygon
|
||||
id="polygon6"
|
||||
points="77.6,211.7 77.6,44 109.7,61 140.9,44 140.9,211.7 "
|
||||
class="st1" /><polygon
|
||||
id="polygon8"
|
||||
points="149.6,211.7 149.6,41.5 210.1,9.9 210.1,211.7 "
|
||||
class="st0" /></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 612 153" style="enable-background:new 0 0 612 153;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#264679;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#6684B2;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#26477B;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M179.9,45.5c-6.2,0-11.5,1.7-15.9,5c-4.4,3.3-6.5,8.1-6.5,14.4c0,4.9,1.3,9.1,3.8,12.4
|
||||
c2.5,3.4,5.7,5.8,9.3,7.3c3.7,1.5,7.3,2.8,11,3.8c3.7,1,6.8,2.3,9.3,3.9c2.5,1.5,3.8,3.5,3.8,5.8c0,4.8-4.4,7.2-13.1,7.2h-24.1V118
|
||||
h24.1c17.1,0,25.6-6.7,25.6-20.2c0-1.9-0.2-3.8-0.6-5.8c-0.4-2-1.2-4-2.6-6c-1.3-2.1-3.3-3.7-5.8-4.9c-2.5-1.2-6.4-2.7-11.5-4.5
|
||||
l-8.8-3.1c-0.7-0.2-1.7-0.7-2.9-1.3c-1.3-0.7-2.2-1.3-2.8-1.9c-0.6-0.6-1.1-1.4-1.6-2.3c-0.5-0.9-0.7-2-0.7-3.2c0-2,1-3.5,2.9-4.6
|
||||
c1.9-1.1,4.3-1.6,7-1.6h24.6V45.5H179.9z"/>
|
||||
<path class="st0" d="M226.4,58.3v31c0,10.2,2.5,17.6,7.6,22c5.1,4.4,13,6.6,23.7,6.6v-12.8c-2.7,0-4.9-0.2-6.8-0.4
|
||||
c-1.8-0.3-3.7-0.9-5.8-1.9c-2-0.9-3.6-2.6-4.7-4.9c-1.1-2.3-1.6-5.2-1.6-8.7V58.3h18.8V45.5h-18.8V31.6L214,58.3H226.4z"/>
|
||||
<path class="st0" d="M281.1,118V76.8c0-7.2,0.9-12,2.6-14.5c1-1.3,2.2-2.2,3.6-2.8c1.4-0.6,2.6-1,3.6-1.1c1-0.1,2.5-0.1,4.3-0.1
|
||||
H310V45.5h-12.2c-3.6,0-6.5,0.1-8.6,0.3c-2.1,0.2-4.5,0.9-7.3,2c-2.8,1.1-5.1,2.8-7.1,5c-4,4.4-6,12.4-6,24V118H281.1z"/>
|
||||
<path class="st0" d="M326.2,53.8c-6.2,7.4-9.3,17-9.3,28.9c0,10.7,3.2,19.4,9.5,26.2s14.7,10.1,25.3,10.1c8.7,0,16.3-2.7,22.7-8.1
|
||||
L366,102c-3.7,2.1-8.5,3.2-14.3,3.2c-6.5,0-11.8-2.3-15.8-6.9c-4-4.6-6-10.5-6-17.9c0-7,1.9-12.9,5.6-17.9c3.8-5,8.9-7.5,15.5-7.5
|
||||
c3.3,0,6.1,0.8,8.2,2.4c2.1,1.6,3.2,4,3.2,7.2c0,5-1.2,8.5-3.6,10.6c-2.4,2.1-6.7,3.2-12.9,3.2h-6.7v11.7h5.7
|
||||
c20.3,0,30.5-8.5,30.5-25.4c0-13.6-7.9-20.7-23.7-21.5C340.9,43,332.4,46.5,326.2,53.8z"/>
|
||||
<path class="st0" d="M412.3,73.2c-7.4,0-13.6,1.9-18.5,5.7c-4.9,3.8-7.4,9.4-7.4,16.7c0,7.3,2.3,12.9,7,16.7
|
||||
c4.6,3.8,10.9,5.7,18.8,5.7h31V73.6c0-9.1-2.4-16-7.2-20.8c-4.8-4.8-11.7-7.2-20.7-7.2h-22.9v12.8h22.3c10.9,0,16.4,6.1,16.4,18.2
|
||||
v28.7h-18.4c-9.1,0-13.6-3.2-13.6-9.8c0-3.3,1.2-5.9,3.6-7.8c2.4-1.8,5.8-2.7,10.2-2.7c5.1,0,9.4,1.4,12.9,4.3V75.3
|
||||
C420.9,73.9,416.5,73.2,412.3,73.2z"/>
|
||||
<path class="st0" d="M458.8,118H471V58.3h24.4V118h12.2V58.3h5.7c6.8,0,11.3,0.7,13.5,2c4.3,2.5,6.5,7.7,6.5,15.5V118h12.2V75.7
|
||||
c0-6-0.6-11.2-1.9-15.5c-1.2-4.3-3.9-7.8-7.9-10.6c-3.9-2.7-9.1-4.1-15.7-4.1h-61.4V118z"/>
|
||||
<polygon class="st1" points="75,118.5 75,35.5 96,48.5 96,118.5 "/>
|
||||
<polygon class="st2" points="99,118.5 99,49.5 110.5,56.5 121,49.5 121,118.5 "/>
|
||||
<polygon class="st1" points="124,118.5 124,48.5 145,35.5 145,118.5 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@ -1,156 +0,0 @@
|
||||
/*!
|
||||
* JavaScript Cookie v2.1.3
|
||||
* https://github.com/js-cookie/js-cookie
|
||||
*
|
||||
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
|
||||
* Released under the MIT license
|
||||
*/
|
||||
;(function (factory) {
|
||||
var registeredInModuleLoader = false;
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
registeredInModuleLoader = true;
|
||||
}
|
||||
if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
registeredInModuleLoader = true;
|
||||
}
|
||||
if (!registeredInModuleLoader) {
|
||||
var OldCookies = window.Cookies;
|
||||
var api = window.Cookies = factory();
|
||||
api.noConflict = function () {
|
||||
window.Cookies = OldCookies;
|
||||
return api;
|
||||
};
|
||||
}
|
||||
}(function () {
|
||||
function extend () {
|
||||
var i = 0;
|
||||
var result = {};
|
||||
for (; i < arguments.length; i++) {
|
||||
var attributes = arguments[ i ];
|
||||
for (var key in attributes) {
|
||||
result[key] = attributes[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function init (converter) {
|
||||
function api (key, value, attributes) {
|
||||
var result;
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
if (arguments.length > 1) {
|
||||
attributes = extend({
|
||||
path: '/'
|
||||
}, api.defaults, attributes);
|
||||
|
||||
if (typeof attributes.expires === 'number') {
|
||||
var expires = new Date();
|
||||
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
|
||||
attributes.expires = expires;
|
||||
}
|
||||
|
||||
try {
|
||||
result = JSON.stringify(value);
|
||||
if (/^[\{\[]/.test(result)) {
|
||||
value = result;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (!converter.write) {
|
||||
value = encodeURIComponent(String(value))
|
||||
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
|
||||
} else {
|
||||
value = converter.write(value, key);
|
||||
}
|
||||
|
||||
key = encodeURIComponent(String(key));
|
||||
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
|
||||
key = key.replace(/[\(\)]/g, escape);
|
||||
|
||||
return (document.cookie = [
|
||||
key, '=', value,
|
||||
attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
attributes.path ? '; path=' + attributes.path : '',
|
||||
attributes.domain ? '; domain=' + attributes.domain : '',
|
||||
attributes.secure ? '; secure' : ''
|
||||
].join(''));
|
||||
}
|
||||
|
||||
// Read
|
||||
|
||||
if (!key) {
|
||||
result = {};
|
||||
}
|
||||
|
||||
// To prevent the for loop in the first place assign an empty array
|
||||
// in case there are no cookies at all. Also prevents odd result when
|
||||
// calling "get()"
|
||||
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||
var rdecode = /(%[0-9A-Z]{2})+/g;
|
||||
var i = 0;
|
||||
|
||||
for (; i < cookies.length; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var cookie = parts.slice(1).join('=');
|
||||
|
||||
if (cookie.charAt(0) === '"') {
|
||||
cookie = cookie.slice(1, -1);
|
||||
}
|
||||
|
||||
try {
|
||||
var name = parts[0].replace(rdecode, decodeURIComponent);
|
||||
cookie = converter.read ?
|
||||
converter.read(cookie, name) : converter(cookie, name) ||
|
||||
cookie.replace(rdecode, decodeURIComponent);
|
||||
|
||||
if (this.json) {
|
||||
try {
|
||||
cookie = JSON.parse(cookie);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (key === name) {
|
||||
result = cookie;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
api.set = api;
|
||||
api.get = function (key) {
|
||||
return api.call(api, key);
|
||||
};
|
||||
api.getJSON = function () {
|
||||
return api.apply({
|
||||
json: true
|
||||
}, [].slice.call(arguments));
|
||||
};
|
||||
api.defaults = {};
|
||||
|
||||
api.remove = function (key, attributes) {
|
||||
api(key, '', extend(attributes, {
|
||||
expires: -1
|
||||
}));
|
||||
};
|
||||
|
||||
api.withConverter = init;
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
return init(function () {});
|
||||
}));
|
||||
@ -1,799 +0,0 @@
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
exports.FLACDemuxer = require('./src/demuxer');
|
||||
exports.FLACDecoder = require('./src/decoder');
|
||||
require('./src/ogg');
|
||||
|
||||
},{"./src/decoder":2,"./src/demuxer":3,"./src/ogg":4}],2:[function(require,module,exports){
|
||||
(function (global){
|
||||
/*
|
||||
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
|
||||
* Original C version from FFmpeg (c) 2003 Alex Beregszaszi
|
||||
* JavaScript port by Devon Govett and Jens Nockert of Official.fm Labs
|
||||
*
|
||||
* Licensed under the same terms as the original. The original
|
||||
* license follows.
|
||||
*
|
||||
* FLAC.js is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FLAC.js is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null);
|
||||
|
||||
var FLACDecoder = AV.Decoder.extend(function() {
|
||||
AV.Decoder.register('flac', this);
|
||||
|
||||
this.prototype.setCookie = function(cookie) {
|
||||
this.cookie = cookie;
|
||||
|
||||
// initialize arrays
|
||||
this.decoded = [];
|
||||
for (var i = 0; i < this.format.channelsPerFrame; i++) {
|
||||
this.decoded[i] = new Int32Array(cookie.maxBlockSize);
|
||||
}
|
||||
|
||||
// for 24 bit lpc frames, this is used to simulate a 64 bit int
|
||||
this.lpc_total = new Int32Array(2);
|
||||
};
|
||||
|
||||
const BLOCK_SIZES = new Int16Array([
|
||||
0, 192, 576 << 0, 576 << 1, 576 << 2, 576 << 3, 0, 0,
|
||||
256 << 0, 256 << 1, 256 << 2, 256 << 3, 256 << 4, 256 << 5, 256 << 6, 256 << 7
|
||||
]);
|
||||
|
||||
const SAMPLE_RATES = new Int32Array([
|
||||
0, 88200, 176400, 192000,
|
||||
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
|
||||
0, 0, 0, 0
|
||||
]);
|
||||
|
||||
const SAMPLE_SIZES = new Int8Array([
|
||||
0, 8, 12, 0, 16, 20, 24, 0
|
||||
]);
|
||||
|
||||
const MAX_CHANNELS = 8,
|
||||
CHMODE_INDEPENDENT = 0,
|
||||
CHMODE_LEFT_SIDE = 8,
|
||||
CHMODE_RIGHT_SIDE = 9,
|
||||
CHMODE_MID_SIDE = 10;
|
||||
|
||||
this.prototype.readChunk = function() {
|
||||
var stream = this.bitstream;
|
||||
if (!stream.available(32))
|
||||
return;
|
||||
|
||||
// frame sync code
|
||||
if ((stream.read(15) & 0x7FFF) !== 0x7FFC)
|
||||
throw new Error('Invalid sync code');
|
||||
|
||||
var isVarSize = stream.read(1), // variable block size stream code
|
||||
bsCode = stream.read(4), // block size
|
||||
srCode = stream.read(4), // sample rate code
|
||||
chMode = stream.read(4), // channel mode
|
||||
bpsCode = stream.read(3); // bits per sample
|
||||
|
||||
stream.advance(1); // reserved bit
|
||||
|
||||
// channels
|
||||
this.chMode = chMode;
|
||||
var channels;
|
||||
|
||||
if (chMode < MAX_CHANNELS) {
|
||||
channels = chMode + 1;
|
||||
this.chMode = CHMODE_INDEPENDENT;
|
||||
} else if (chMode <= CHMODE_MID_SIDE) {
|
||||
channels = 2;
|
||||
} else {
|
||||
throw new Error('Invalid channel mode');
|
||||
}
|
||||
|
||||
if (channels !== this.format.channelsPerFrame)
|
||||
throw new Error('Switching channel layout mid-stream not supported.');
|
||||
|
||||
// bits per sample
|
||||
if (bpsCode === 3 || bpsCode === 7)
|
||||
throw new Error('Invalid sample size code');
|
||||
|
||||
this.bps = SAMPLE_SIZES[bpsCode];
|
||||
if (this.bps !== this.format.bitsPerChannel)
|
||||
throw new Error('Switching bits per sample mid-stream not supported.');
|
||||
|
||||
// sample number or frame number
|
||||
// see http://www.hydrogenaudio.org/forums/index.php?s=ea7085ffe6d57132c36e6105c0d434c9&showtopic=88390&pid=754269&st=0&#entry754269
|
||||
var ones = 0;
|
||||
while (stream.read(1) === 1)
|
||||
ones++;
|
||||
|
||||
var frame_or_sample_num = stream.read(7 - ones);
|
||||
for (; ones > 1; ones--) {
|
||||
stream.advance(2); // == 2
|
||||
frame_or_sample_num = (frame_or_sample_num << 6) | stream.read(6);
|
||||
}
|
||||
|
||||
// block size
|
||||
if (bsCode === 0)
|
||||
throw new Error('Reserved blocksize code');
|
||||
else if (bsCode === 6)
|
||||
this.blockSize = stream.read(8) + 1;
|
||||
else if (bsCode === 7)
|
||||
this.blockSize = stream.read(16) + 1;
|
||||
else
|
||||
this.blockSize = BLOCK_SIZES[bsCode];
|
||||
|
||||
// sample rate
|
||||
var sampleRate;
|
||||
if (srCode < 12)
|
||||
sampleRate = SAMPLE_RATES[srCode];
|
||||
else if (srCode === 12)
|
||||
sampleRate = stream.read(8) * 1000;
|
||||
else if (srCode === 13)
|
||||
sampleRate = stream.read(16);
|
||||
else if (srCode === 14)
|
||||
sampleRate = stream.read(16) * 10;
|
||||
else
|
||||
throw new Error('Invalid sample rate code');
|
||||
|
||||
stream.advance(8); // skip CRC check
|
||||
|
||||
// subframes
|
||||
for (var i = 0; i < channels; i++)
|
||||
this.decodeSubframe(i);
|
||||
|
||||
stream.align();
|
||||
stream.advance(16); // skip CRC frame footer
|
||||
|
||||
var is32 = this.bps > 16,
|
||||
output = new ArrayBuffer(this.blockSize * channels * (is32 ? 4 : 2)),
|
||||
buf = is32 ? new Int32Array(output) : new Int16Array(output),
|
||||
blockSize = this.blockSize,
|
||||
decoded = this.decoded,
|
||||
j = 0;
|
||||
|
||||
switch (this.chMode) {
|
||||
case CHMODE_INDEPENDENT:
|
||||
for (var k = 0; k < blockSize; k++) {
|
||||
for (var i = 0; i < channels; i++) {
|
||||
buf[j++] = decoded[i][k];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CHMODE_LEFT_SIDE:
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
var left = decoded[0][i],
|
||||
right = decoded[1][i];
|
||||
|
||||
buf[j++] = left;
|
||||
buf[j++] = (left - right);
|
||||
}
|
||||
break;
|
||||
|
||||
case CHMODE_RIGHT_SIDE:
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
var left = decoded[0][i],
|
||||
right = decoded[1][i];
|
||||
|
||||
buf[j++] = (left + right);
|
||||
buf[j++] = right;
|
||||
}
|
||||
break;
|
||||
|
||||
case CHMODE_MID_SIDE:
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
var left = decoded[0][i],
|
||||
right = decoded[1][i];
|
||||
|
||||
left -= right >> 1;
|
||||
buf[j++] = (left + right);
|
||||
buf[j++] = left;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return buf;
|
||||
};
|
||||
|
||||
this.prototype.decodeSubframe = function(channel) {
|
||||
var wasted = 0,
|
||||
stream = this.bitstream,
|
||||
blockSize = this.blockSize,
|
||||
decoded = this.decoded;
|
||||
|
||||
this.curr_bps = this.bps;
|
||||
if (channel === 0) {
|
||||
if (this.chMode === CHMODE_RIGHT_SIDE)
|
||||
this.curr_bps++;
|
||||
} else {
|
||||
if (this.chMode === CHMODE_LEFT_SIDE || this.chMode === CHMODE_MID_SIDE)
|
||||
this.curr_bps++;
|
||||
}
|
||||
|
||||
if (stream.read(1))
|
||||
throw new Error("Invalid subframe padding");
|
||||
|
||||
var type = stream.read(6);
|
||||
|
||||
if (stream.read(1)) {
|
||||
wasted = 1;
|
||||
while (!stream.read(1))
|
||||
wasted++;
|
||||
|
||||
this.curr_bps -= wasted;
|
||||
}
|
||||
|
||||
if (this.curr_bps > 32)
|
||||
throw new Error("decorrelated bit depth > 32 (" + this.curr_bps + ")");
|
||||
|
||||
if (type === 0) {
|
||||
var tmp = stream.read(this.curr_bps, true);
|
||||
for (var i = 0; i < blockSize; i++)
|
||||
decoded[channel][i] = tmp;
|
||||
|
||||
} else if (type === 1) {
|
||||
var bps = this.curr_bps;
|
||||
for (var i = 0; i < blockSize; i++)
|
||||
decoded[channel][i] = stream.read(bps, true);
|
||||
|
||||
} else if ((type >= 8) && (type <= 12)) {
|
||||
this.decode_subframe_fixed(channel, type & ~0x8);
|
||||
|
||||
} else if (type >= 32) {
|
||||
this.decode_subframe_lpc(channel, (type & ~0x20) + 1);
|
||||
|
||||
} else {
|
||||
throw new Error("Invalid coding type");
|
||||
}
|
||||
|
||||
if (wasted) {
|
||||
for (var i = 0; i < blockSize; i++)
|
||||
decoded[channel][i] <<= wasted;
|
||||
}
|
||||
};
|
||||
|
||||
this.prototype.decode_subframe_fixed = function(channel, predictor_order) {
|
||||
var decoded = this.decoded[channel],
|
||||
stream = this.bitstream,
|
||||
bps = this.curr_bps;
|
||||
|
||||
// warm up samples
|
||||
for (var i = 0; i < predictor_order; i++)
|
||||
decoded[i] = stream.read(bps, true);
|
||||
|
||||
this.decode_residuals(channel, predictor_order);
|
||||
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
|
||||
if (predictor_order > 0)
|
||||
a = decoded[predictor_order - 1];
|
||||
|
||||
if (predictor_order > 1)
|
||||
b = a - decoded[predictor_order - 2];
|
||||
|
||||
if (predictor_order > 2)
|
||||
c = b - decoded[predictor_order - 2] + decoded[predictor_order - 3];
|
||||
|
||||
if (predictor_order > 3)
|
||||
d = c - decoded[predictor_order - 2] + 2 * decoded[predictor_order - 3] - decoded[predictor_order - 4];
|
||||
|
||||
switch (predictor_order) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
var abcd = new Int32Array([a, b, c, d]),
|
||||
blockSize = this.blockSize;
|
||||
|
||||
for (var i = predictor_order; i < blockSize; i++) {
|
||||
abcd[predictor_order - 1] += decoded[i];
|
||||
|
||||
for (var j = predictor_order - 2; j >= 0; j--) {
|
||||
abcd[j] += abcd[j + 1];
|
||||
}
|
||||
|
||||
decoded[i] = abcd[0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid Predictor Order " + predictor_order);
|
||||
}
|
||||
};
|
||||
|
||||
this.prototype.decode_subframe_lpc = function(channel, predictor_order) {
|
||||
var stream = this.bitstream,
|
||||
decoded = this.decoded[channel],
|
||||
bps = this.curr_bps,
|
||||
blockSize = this.blockSize;
|
||||
|
||||
// warm up samples
|
||||
for (var i = 0; i < predictor_order; i++) {
|
||||
decoded[i] = stream.read(bps, true);
|
||||
}
|
||||
|
||||
var coeff_prec = stream.read(4) + 1;
|
||||
if (coeff_prec === 16)
|
||||
throw new Error("Invalid coefficient precision");
|
||||
|
||||
var qlevel = stream.read(5, true);
|
||||
if (qlevel < 0)
|
||||
throw new Error("Negative qlevel, maybe buggy stream");
|
||||
|
||||
var coeffs = new Int32Array(32);
|
||||
for (var i = 0; i < predictor_order; i++) {
|
||||
coeffs[i] = stream.read(coeff_prec, true);
|
||||
}
|
||||
|
||||
this.decode_residuals(channel, predictor_order);
|
||||
|
||||
if (this.bps <= 16) {
|
||||
for (var i = predictor_order; i < blockSize - 1; i += 2) {
|
||||
var d = decoded[i - predictor_order],
|
||||
s0 = 0, s1 = 0, c = 0;
|
||||
|
||||
for (var j = predictor_order - 1; j > 0; j--) {
|
||||
c = coeffs[j];
|
||||
s0 += c * d;
|
||||
d = decoded[i - j];
|
||||
s1 += c * d;
|
||||
}
|
||||
|
||||
c = coeffs[0];
|
||||
s0 += c * d;
|
||||
d = decoded[i] += (s0 >> qlevel);
|
||||
s1 += c * d;
|
||||
decoded[i + 1] += (s1 >> qlevel);
|
||||
}
|
||||
|
||||
if (i < blockSize) {
|
||||
var sum = 0;
|
||||
for (var j = 0; j < predictor_order; j++)
|
||||
sum += coeffs[j] * decoded[i - j - 1];
|
||||
|
||||
decoded[i] += (sum >> qlevel);
|
||||
}
|
||||
} else {
|
||||
// simulate 64 bit integer using an array of two 32 bit ints
|
||||
var total = this.lpc_total;
|
||||
for (var i = predictor_order; i < blockSize; i++) {
|
||||
// reset total to 0
|
||||
total[0] = 0;
|
||||
total[1] = 0;
|
||||
|
||||
for (j = 0; j < predictor_order; j++) {
|
||||
// simulate `total += coeffs[j] * decoded[i - j - 1]`
|
||||
multiply_add(total, coeffs[j], decoded[i - j - 1]);
|
||||
}
|
||||
|
||||
// simulate `decoded[i] += total >> qlevel`
|
||||
// we know that qlevel < 32 since it is a 5 bit field (see above)
|
||||
decoded[i] += (total[0] >>> qlevel) | (total[1] << (32 - qlevel));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const TWO_PWR_32_DBL = Math.pow(2, 32);
|
||||
|
||||
// performs `total += a * b` on a simulated 64 bit int
|
||||
// total is an Int32Array(2)
|
||||
// a and b are JS numbers (32 bit ints)
|
||||
function multiply_add(total, a, b) {
|
||||
// multiply a * b (we can use normal JS multiplication for this)
|
||||
var r = a * b;
|
||||
var n = r < 0;
|
||||
if (n)
|
||||
r = -r;
|
||||
|
||||
var r_low = (r % TWO_PWR_32_DBL) | 0;
|
||||
var r_high = (r / TWO_PWR_32_DBL) | 0;
|
||||
if (n) {
|
||||
r_low = ~r_low + 1;
|
||||
r_high = ~r_high;
|
||||
}
|
||||
|
||||
// add result to total
|
||||
var a48 = total[1] >>> 16;
|
||||
var a32 = total[1] & 0xFFFF;
|
||||
var a16 = total[0] >>> 16;
|
||||
var a00 = total[0] & 0xFFFF;
|
||||
|
||||
var b48 = r_high >>> 16;
|
||||
var b32 = r_high & 0xFFFF;
|
||||
var b16 = r_low >>> 16;
|
||||
var b00 = r_low & 0xFFFF;
|
||||
|
||||
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
|
||||
c00 += a00 + b00;
|
||||
c16 += c00 >>> 16;
|
||||
c00 &= 0xFFFF;
|
||||
c16 += a16 + b16;
|
||||
c32 += c16 >>> 16;
|
||||
c16 &= 0xFFFF;
|
||||
c32 += a32 + b32;
|
||||
c48 += c32 >>> 16;
|
||||
c32 &= 0xFFFF;
|
||||
c48 += a48 + b48;
|
||||
c48 &= 0xFFFF;
|
||||
|
||||
// store result back in total
|
||||
total[0] = (c16 << 16) | c00;
|
||||
total[1] = (c48 << 16) | c32;
|
||||
}
|
||||
|
||||
const INT_MAX = 32767;
|
||||
|
||||
this.prototype.decode_residuals = function(channel, predictor_order) {
|
||||
var stream = this.bitstream,
|
||||
method_type = stream.read(2);
|
||||
|
||||
if (method_type > 1)
|
||||
throw new Error('Illegal residual coding method ' + method_type);
|
||||
|
||||
var rice_order = stream.read(4),
|
||||
samples = (this.blockSize >>> rice_order);
|
||||
|
||||
if (predictor_order > samples)
|
||||
throw new Error('Invalid predictor order ' + predictor_order + ' > ' + samples);
|
||||
|
||||
var decoded = this.decoded[channel],
|
||||
sample = predictor_order,
|
||||
i = predictor_order;
|
||||
|
||||
for (var partition = 0; partition < (1 << rice_order); partition++) {
|
||||
var tmp = stream.read(method_type === 0 ? 4 : 5);
|
||||
|
||||
if (tmp === (method_type === 0 ? 15 : 31)) {
|
||||
tmp = stream.read(5);
|
||||
for (; i < samples; i++)
|
||||
decoded[sample++] = stream.read(tmp, true);
|
||||
|
||||
} else {
|
||||
for (; i < samples; i++)
|
||||
decoded[sample++] = this.golomb(tmp, INT_MAX, 0);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const MIN_CACHE_BITS = 25;
|
||||
|
||||
this.prototype.golomb = function(k, limit, esc_len) {
|
||||
var data = this.bitstream,
|
||||
offset = data.bitPosition,
|
||||
buf = data.peek(32 - offset) << offset,
|
||||
v = 0;
|
||||
|
||||
var log = 31 - clz(buf | 1); // log2(buf)
|
||||
|
||||
if (log - k >= 32 - MIN_CACHE_BITS && 32 - log < limit) {
|
||||
buf >>>= log - k;
|
||||
buf += (30 - log) << k;
|
||||
|
||||
data.advance(32 + k - log);
|
||||
v = buf;
|
||||
|
||||
} else {
|
||||
for (var i = 0; data.read(1) === 0; i++)
|
||||
buf = data.peek(32 - offset) << offset;
|
||||
|
||||
if (i < limit - 1) {
|
||||
if (k)
|
||||
buf = data.read(k);
|
||||
else
|
||||
buf = 0;
|
||||
|
||||
v = buf + (i << k);
|
||||
|
||||
} else if (i === limit - 1) {
|
||||
buf = data.read(esc_len);
|
||||
v = buf + 1;
|
||||
|
||||
} else {
|
||||
v = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return (v >> 1) ^ -(v & 1);
|
||||
};
|
||||
|
||||
// Should be in the damned standard library...
|
||||
function clz(input) {
|
||||
var output = 0,
|
||||
curbyte = 0;
|
||||
|
||||
while(true) { // emulate goto in JS using the break statement :D
|
||||
curbyte = input >>> 24;
|
||||
if (curbyte) break;
|
||||
output += 8;
|
||||
|
||||
curbyte = input >>> 16;
|
||||
if (curbyte & 0xff) break;
|
||||
output += 8;
|
||||
|
||||
curbyte = input >>> 8;
|
||||
if (curbyte & 0xff) break;
|
||||
output += 8;
|
||||
|
||||
curbyte = input;
|
||||
if (curbyte & 0xff) break;
|
||||
output += 8;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
if (!(curbyte & 0xf0))
|
||||
output += 4;
|
||||
else
|
||||
curbyte >>>= 4;
|
||||
|
||||
if (curbyte & 0x8)
|
||||
return output;
|
||||
|
||||
if (curbyte & 0x4)
|
||||
return output + 1;
|
||||
|
||||
if (curbyte & 0x2)
|
||||
return output + 2;
|
||||
|
||||
if (curbyte & 0x1)
|
||||
return output + 3;
|
||||
|
||||
// shouldn't get here
|
||||
return output + 4;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FLACDecoder;
|
||||
|
||||
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{}],3:[function(require,module,exports){
|
||||
(function (global){
|
||||
/*
|
||||
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
|
||||
* By Devon Govett and Jens Nockert of Official.fm Labs
|
||||
*
|
||||
* FLAC.js is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FLAC.js is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null);
|
||||
|
||||
var FLACDemuxer = AV.Demuxer.extend(function() {
|
||||
AV.Demuxer.register(this);
|
||||
|
||||
this.probe = function(buffer) {
|
||||
return buffer.peekString(0, 4) === 'fLaC';
|
||||
}
|
||||
|
||||
const STREAMINFO = 0,
|
||||
PADDING = 1,
|
||||
APPLICATION = 2,
|
||||
SEEKTABLE = 3,
|
||||
VORBIS_COMMENT = 4,
|
||||
CUESHEET = 5,
|
||||
PICTURE = 6,
|
||||
INVALID = 127,
|
||||
STREAMINFO_SIZE = 34;
|
||||
|
||||
this.prototype.readChunk = function() {
|
||||
var stream = this.stream;
|
||||
|
||||
if (!this.readHeader && stream.available(4)) {
|
||||
if (stream.readString(4) !== 'fLaC')
|
||||
return this.emit('error', 'Invalid FLAC file.');
|
||||
|
||||
this.readHeader = true;
|
||||
}
|
||||
|
||||
while (stream.available(1) && !this.last) {
|
||||
if (!this.readBlockHeaders) {
|
||||
var tmp = stream.readUInt8();
|
||||
this.last = (tmp & 0x80) === 0x80,
|
||||
this.type = tmp & 0x7F,
|
||||
this.size = stream.readUInt24();
|
||||
}
|
||||
|
||||
if (!this.foundStreamInfo && this.type !== STREAMINFO)
|
||||
return this.emit('error', 'STREAMINFO must be the first block');
|
||||
|
||||
if (!stream.available(this.size))
|
||||
return;
|
||||
|
||||
switch (this.type) {
|
||||
case STREAMINFO:
|
||||
if (this.foundStreamInfo)
|
||||
return this.emit('error', 'STREAMINFO can only occur once.');
|
||||
|
||||
if (this.size !== STREAMINFO_SIZE)
|
||||
return this.emit('error', 'STREAMINFO size is wrong.');
|
||||
|
||||
this.foundStreamInfo = true;
|
||||
var bitstream = new AV.Bitstream(stream);
|
||||
|
||||
var cookie = {
|
||||
minBlockSize: bitstream.read(16),
|
||||
maxBlockSize: bitstream.read(16),
|
||||
minFrameSize: bitstream.read(24),
|
||||
maxFrameSize: bitstream.read(24)
|
||||
};
|
||||
|
||||
this.format = {
|
||||
formatID: 'flac',
|
||||
sampleRate: bitstream.read(20),
|
||||
channelsPerFrame: bitstream.read(3) + 1,
|
||||
bitsPerChannel: bitstream.read(5) + 1
|
||||
};
|
||||
|
||||
this.emit('format', this.format);
|
||||
this.emit('cookie', cookie);
|
||||
|
||||
var sampleCount = bitstream.read(36);
|
||||
this.emit('duration', sampleCount / this.format.sampleRate * 1000 | 0);
|
||||
|
||||
stream.advance(16); // skip MD5 hashes
|
||||
this.readBlockHeaders = false;
|
||||
break;
|
||||
|
||||
/*
|
||||
I am only looking at the least significant 32 bits of sample number and offset data
|
||||
This is more than sufficient for the longest flac file I have (~50 mins 2-channel 16-bit 44.1k which uses about 7.5% of the UInt32 space for the largest offset)
|
||||
Can certainly be improved by storing sample numbers and offests as doubles, but would require additional overriding of the searchTimestamp and seek functions (possibly more?)
|
||||
Also the flac faq suggests it would be possible to find frame lengths and thus create seek points on the fly via decoding but I assume this would be slow
|
||||
I may look into these thigns though as my project progresses
|
||||
*/
|
||||
case SEEKTABLE:
|
||||
for(var s=0; s<this.size/18; s++)
|
||||
{
|
||||
if(stream.peekUInt32(0) == 0xFFFFFFFF && stream.peekUInt32(1) == 0xFFFFFFFF)
|
||||
{
|
||||
//placeholder, ignore
|
||||
stream.advance(18);
|
||||
} else {
|
||||
if(stream.readUInt32() > 0)
|
||||
{
|
||||
this.emit('error', 'Seek points with sample number >UInt32 not supported');
|
||||
}
|
||||
var samplenum = stream.readUInt32();
|
||||
if(stream.readUInt32() > 0)
|
||||
{
|
||||
this.emit('error', 'Seek points with stream offset >UInt32 not supported');
|
||||
}
|
||||
var offset = stream.readUInt32();
|
||||
|
||||
stream.advance(2);
|
||||
|
||||
this.addSeekPoint(offset, samplenum);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VORBIS_COMMENT:
|
||||
// see http://www.xiph.org/vorbis/doc/v-comment.html
|
||||
this.metadata || (this.metadata = {});
|
||||
var len = stream.readUInt32(true);
|
||||
|
||||
this.metadata.vendor = stream.readString(len);
|
||||
var length = stream.readUInt32(true);
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
len = stream.readUInt32(true);
|
||||
var str = stream.readString(len, 'utf8'),
|
||||
idx = str.indexOf('=');
|
||||
|
||||
this.metadata[str.slice(0, idx).toLowerCase()] = str.slice(idx + 1);
|
||||
}
|
||||
|
||||
// TODO: standardize field names across formats
|
||||
break;
|
||||
|
||||
case PICTURE:
|
||||
var type = stream.readUInt32();
|
||||
if (type !== 3) { // make sure this is album art (type 3)
|
||||
stream.advance(this.size - 4);
|
||||
} else {
|
||||
var mimeLen = stream.readUInt32(),
|
||||
mime = stream.readString(mimeLen),
|
||||
descLen = stream.readUInt32(),
|
||||
description = stream.readString(descLen),
|
||||
width = stream.readUInt32(),
|
||||
height = stream.readUInt32(),
|
||||
depth = stream.readUInt32(),
|
||||
colors = stream.readUInt32(),
|
||||
length = stream.readUInt32(),
|
||||
picture = stream.readBuffer(length);
|
||||
|
||||
this.metadata || (this.metadata = {});
|
||||
this.metadata.coverArt = picture;
|
||||
}
|
||||
|
||||
// does anyone want the rest of the info?
|
||||
break;
|
||||
|
||||
default:
|
||||
stream.advance(this.size);
|
||||
this.readBlockHeaders = false;
|
||||
}
|
||||
|
||||
if (this.last && this.metadata)
|
||||
this.emit('metadata', this.metadata);
|
||||
}
|
||||
|
||||
while (stream.available(1) && this.last) {
|
||||
var buffer = stream.readSingleBuffer(stream.remainingBytes());
|
||||
this.emit('data', buffer);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.exports = FLACDemuxer;
|
||||
|
||||
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{}],4:[function(require,module,exports){
|
||||
(function (global){
|
||||
var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null);
|
||||
|
||||
// if ogg.js exists, register a plugin
|
||||
try {
|
||||
var OggDemuxer = (typeof window !== "undefined" ? window['AV']['OggDemuxer'] : typeof global !== "undefined" ? global['AV']['OggDemuxer'] : null);
|
||||
} catch (e) {};
|
||||
if (!OggDemuxer) return;
|
||||
|
||||
OggDemuxer.plugins.push({
|
||||
magic: "\177FLAC",
|
||||
|
||||
init: function() {
|
||||
this.list = new AV.BufferList();
|
||||
this.stream = new AV.Stream(this.list);
|
||||
},
|
||||
|
||||
readHeaders: function(packet) {
|
||||
var stream = this.stream;
|
||||
this.list.append(new AV.Buffer(packet));
|
||||
|
||||
stream.advance(5); // magic
|
||||
if (stream.readUInt8() != 1)
|
||||
throw new Error('Unsupported FLAC version');
|
||||
|
||||
stream.advance(3);
|
||||
if (stream.peekString(0, 4) != 'fLaC')
|
||||
throw new Error('Not flac');
|
||||
|
||||
this.flac = AV.Demuxer.find(stream.peekSingleBuffer(0, stream.remainingBytes()));
|
||||
if (!this.flac)
|
||||
throw new Error('Flac demuxer not found');
|
||||
|
||||
this.flac.prototype.readChunk.call(this);
|
||||
return true;
|
||||
},
|
||||
|
||||
readPacket: function(packet) {
|
||||
this.list.append(new AV.Buffer(packet));
|
||||
this.flac.prototype.readChunk.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{}]},{},[1])
|
||||
|
||||
|
||||
//# sourceMappingURL=flac.js.map
|
||||
2
webapp2/js/lib/howler.core.min.js
vendored
@ -1,326 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function buildDraggable(Sortable) {
|
||||
function removeNode(node) {
|
||||
node.parentElement.removeChild(node);
|
||||
}
|
||||
|
||||
function insertNodeAt(fatherNode, node, position) {
|
||||
if (position < fatherNode.children.length) {
|
||||
fatherNode.insertBefore(node, fatherNode.children[position]);
|
||||
} else {
|
||||
fatherNode.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
function computeVmIndex(vnodes, element) {
|
||||
return vnodes.map(function (elt) {
|
||||
return elt.elm;
|
||||
}).indexOf(element);
|
||||
}
|
||||
|
||||
function _computeIndexes(slots, children) {
|
||||
if (!slots) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var elmFromNodes = slots.map(function (elt) {
|
||||
return elt.elm;
|
||||
});
|
||||
return [].concat(_toConsumableArray(children)).map(function (elt) {
|
||||
return elmFromNodes.indexOf(elt);
|
||||
});
|
||||
}
|
||||
|
||||
function emit(evtName, evtData) {
|
||||
var _this = this;
|
||||
|
||||
this.$nextTick(function () {
|
||||
return _this.$emit(evtName.toLowerCase(), evtData);
|
||||
});
|
||||
}
|
||||
|
||||
function delegateAndEmit(evtName) {
|
||||
var _this2 = this;
|
||||
|
||||
return function (evtData) {
|
||||
if (_this2.realList !== null) {
|
||||
_this2['onDrag' + evtName](evtData);
|
||||
}
|
||||
emit.call(_this2, evtName, evtData);
|
||||
};
|
||||
}
|
||||
|
||||
var eventsListened = ['Start', 'Add', 'Remove', 'Update', 'End'];
|
||||
var eventsToEmit = ['Choose', 'Sort', 'Filter', 'Clone'];
|
||||
var readonlyProperties = ['Move'].concat(eventsListened, eventsToEmit).map(function (evt) {
|
||||
return 'on' + evt;
|
||||
});
|
||||
var draggingElement = null;
|
||||
|
||||
var props = {
|
||||
options: Object,
|
||||
list: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
clone: {
|
||||
type: Function,
|
||||
default: function _default(original) {
|
||||
return original;
|
||||
}
|
||||
},
|
||||
element: {
|
||||
type: String,
|
||||
default: 'div'
|
||||
},
|
||||
move: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
};
|
||||
|
||||
var draggableComponent = {
|
||||
props: props,
|
||||
|
||||
data: function data() {
|
||||
return {
|
||||
transitionMode: false,
|
||||
componentMode: false
|
||||
};
|
||||
},
|
||||
render: function render(h) {
|
||||
if (this.$slots.default && this.$slots.default.length === 1) {
|
||||
var child = this.$slots.default[0];
|
||||
if (child.componentOptions && child.componentOptions.tag === "transition-group") {
|
||||
this.transitionMode = true;
|
||||
}
|
||||
}
|
||||
return h(this.element, null, this.$slots.default);
|
||||
},
|
||||
mounted: function mounted() {
|
||||
var _this3 = this;
|
||||
|
||||
this.componentMode = this.element.toLowerCase() !== this.$el.nodeName.toLowerCase();
|
||||
if (this.componentMode && this.transitionMode) {
|
||||
throw new Error('Transition-group inside component is not suppported. Please alter element value or remove transition-group. Current element value: ' + this.element);
|
||||
}
|
||||
var optionsAdded = {};
|
||||
eventsListened.forEach(function (elt) {
|
||||
optionsAdded['on' + elt] = delegateAndEmit.call(_this3, elt);
|
||||
});
|
||||
|
||||
eventsToEmit.forEach(function (elt) {
|
||||
optionsAdded['on' + elt] = emit.bind(_this3, elt);
|
||||
});
|
||||
|
||||
var options = _extends({}, this.options, optionsAdded, { onMove: function onMove(evt) {
|
||||
return _this3.onDragMove(evt);
|
||||
} });
|
||||
this._sortable = new Sortable(this.rootContainer, options);
|
||||
this.computeIndexes();
|
||||
},
|
||||
beforeDestroy: function beforeDestroy() {
|
||||
this._sortable.destroy();
|
||||
},
|
||||
|
||||
|
||||
computed: {
|
||||
rootContainer: function rootContainer() {
|
||||
return this.transitionMode ? this.$el.children[0] : this.$el;
|
||||
},
|
||||
isCloning: function isCloning() {
|
||||
return !!this.options && !!this.options.group && this.options.group.pull === 'clone';
|
||||
},
|
||||
realList: function realList() {
|
||||
return !!this.list ? this.list : this.value;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
options: function options(newOptionValue) {
|
||||
for (var property in newOptionValue) {
|
||||
if (readonlyProperties.indexOf(property) == -1) {
|
||||
this._sortable.option(property, newOptionValue[property]);
|
||||
}
|
||||
}
|
||||
},
|
||||
realList: function realList() {
|
||||
this.computeIndexes();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getChildrenNodes: function getChildrenNodes() {
|
||||
if (this.componentMode) {
|
||||
return this.$children[0].$slots.default;
|
||||
}
|
||||
var rawNodes = this.$slots.default;
|
||||
return this.transitionMode ? rawNodes[0].child.$slots.default : rawNodes;
|
||||
},
|
||||
computeIndexes: function computeIndexes() {
|
||||
var _this4 = this;
|
||||
|
||||
this.$nextTick(function () {
|
||||
_this4.visibleIndexes = _computeIndexes(_this4.getChildrenNodes(), _this4.rootContainer.children);
|
||||
});
|
||||
},
|
||||
getUnderlyingVm: function getUnderlyingVm(htmlElt) {
|
||||
var index = computeVmIndex(this.getChildrenNodes(), htmlElt);
|
||||
var element = this.realList[index];
|
||||
return { index: index, element: element };
|
||||
},
|
||||
getUnderlyingPotencialDraggableComponent: function getUnderlyingPotencialDraggableComponent(_ref) {
|
||||
var __vue__ = _ref.__vue__;
|
||||
|
||||
if (!__vue__ || !__vue__.$options || __vue__.$options._componentTag !== "transition-group") {
|
||||
return __vue__;
|
||||
}
|
||||
return __vue__.$parent;
|
||||
},
|
||||
emitChanges: function emitChanges(evt) {
|
||||
var _this5 = this;
|
||||
|
||||
this.$nextTick(function () {
|
||||
_this5.$emit('change', evt);
|
||||
});
|
||||
},
|
||||
alterList: function alterList(onList) {
|
||||
if (!!this.list) {
|
||||
onList(this.list);
|
||||
} else {
|
||||
var newList = [].concat(_toConsumableArray(this.value));
|
||||
onList(newList);
|
||||
this.$emit('input', newList);
|
||||
}
|
||||
},
|
||||
spliceList: function spliceList() {
|
||||
var _arguments = arguments;
|
||||
|
||||
var spliceList = function spliceList(list) {
|
||||
return list.splice.apply(list, _arguments);
|
||||
};
|
||||
this.alterList(spliceList);
|
||||
},
|
||||
updatePosition: function updatePosition(oldIndex, newIndex) {
|
||||
var updatePosition = function updatePosition(list) {
|
||||
return list.splice(newIndex, 0, list.splice(oldIndex, 1)[0]);
|
||||
};
|
||||
this.alterList(updatePosition);
|
||||
},
|
||||
getRelatedContextFromMoveEvent: function getRelatedContextFromMoveEvent(_ref2) {
|
||||
var to = _ref2.to,
|
||||
related = _ref2.related;
|
||||
|
||||
var component = this.getUnderlyingPotencialDraggableComponent(to);
|
||||
if (!component) {
|
||||
return { component: component };
|
||||
}
|
||||
var list = component.realList;
|
||||
var context = { list: list, component: component };
|
||||
if (to !== related && list && component.getUnderlyingVm) {
|
||||
var destination = component.getUnderlyingVm(related);
|
||||
return _extends(destination, context);
|
||||
}
|
||||
|
||||
return context;
|
||||
},
|
||||
getVmIndex: function getVmIndex(domIndex) {
|
||||
var indexes = this.visibleIndexes;
|
||||
var numberIndexes = indexes.length;
|
||||
return domIndex > numberIndexes - 1 ? numberIndexes : indexes[domIndex];
|
||||
},
|
||||
onDragStart: function onDragStart(evt) {
|
||||
this.context = this.getUnderlyingVm(evt.item);
|
||||
evt.item._underlying_vm_ = this.clone(this.context.element);
|
||||
draggingElement = evt.item;
|
||||
},
|
||||
onDragAdd: function onDragAdd(evt) {
|
||||
var element = evt.item._underlying_vm_;
|
||||
if (element === undefined) {
|
||||
return;
|
||||
}
|
||||
removeNode(evt.item);
|
||||
var newIndex = this.getVmIndex(evt.newIndex);
|
||||
this.spliceList(newIndex, 0, element);
|
||||
this.computeIndexes();
|
||||
var added = { element: element, newIndex: newIndex };
|
||||
this.emitChanges({ added: added });
|
||||
},
|
||||
onDragRemove: function onDragRemove(evt) {
|
||||
insertNodeAt(this.rootContainer, evt.item, evt.oldIndex);
|
||||
if (this.isCloning) {
|
||||
removeNode(evt.clone);
|
||||
return;
|
||||
}
|
||||
var oldIndex = this.context.index;
|
||||
this.spliceList(oldIndex, 1);
|
||||
var removed = { element: this.context.element, oldIndex: oldIndex };
|
||||
this.emitChanges({ removed: removed });
|
||||
},
|
||||
onDragUpdate: function onDragUpdate(evt) {
|
||||
removeNode(evt.item);
|
||||
insertNodeAt(evt.from, evt.item, evt.oldIndex);
|
||||
var oldIndex = this.context.index;
|
||||
var newIndex = this.getVmIndex(evt.newIndex);
|
||||
this.updatePosition(oldIndex, newIndex);
|
||||
var moved = { element: this.context.element, oldIndex: oldIndex, newIndex: newIndex };
|
||||
this.emitChanges({ moved: moved });
|
||||
},
|
||||
computeFutureIndex: function computeFutureIndex(relatedContext, evt) {
|
||||
if (!relatedContext.element) {
|
||||
return 0;
|
||||
}
|
||||
var domChildren = [].concat(_toConsumableArray(evt.to.children));
|
||||
var currentDOMIndex = domChildren.indexOf(evt.related);
|
||||
var currentIndex = relatedContext.component.getVmIndex(currentDOMIndex);
|
||||
var draggedInList = domChildren.indexOf(draggingElement) != -1;
|
||||
return draggedInList ? currentIndex : currentIndex + 1;
|
||||
},
|
||||
onDragMove: function onDragMove(evt) {
|
||||
var onMove = this.move;
|
||||
if (!onMove || !this.realList) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var relatedContext = this.getRelatedContextFromMoveEvent(evt);
|
||||
var draggedContext = this.context;
|
||||
var futureIndex = this.computeFutureIndex(relatedContext, evt);
|
||||
_extends(draggedContext, { futureIndex: futureIndex });
|
||||
_extends(evt, { relatedContext: relatedContext, draggedContext: draggedContext });
|
||||
return onMove(evt);
|
||||
},
|
||||
onDragEnd: function onDragEnd(evt) {
|
||||
this.computeIndexes();
|
||||
draggingElement = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
return draggableComponent;
|
||||
}
|
||||
|
||||
if (typeof exports == "object") {
|
||||
var Sortable = require("sortablejs");
|
||||
module.exports = buildDraggable(Sortable);
|
||||
} else if (typeof define == "function" && define.amd) {
|
||||
define(['sortablejs'], function (Sortable) {
|
||||
return buildDraggable(Sortable);
|
||||
});
|
||||
} else if (window && window.Vue && window.Sortable) {
|
||||
var draggable = buildDraggable(window.Sortable);
|
||||
Vue.component('draggable', draggable);
|
||||
}
|
||||
})();
|
||||
@ -1,180 +0,0 @@
|
||||
// TODO: MOVE THIS ALL TO PROMISES
|
||||
|
||||
|
||||
var MSTREAMAPI = (function () {
|
||||
let mstreamModule = {};
|
||||
|
||||
mstreamModule.listOfServers = [];
|
||||
mstreamModule.currentServer = {
|
||||
host:"",
|
||||
username:"",
|
||||
token: "",
|
||||
vPath: ""
|
||||
}
|
||||
|
||||
$.ajaxPrefilter(function( options ) {
|
||||
options.beforeSend = function (xhr) {
|
||||
xhr.setRequestHeader('x-access-token', MSTREAMAPI.currentServer.token);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// TODO: Special functions for handling multiple servers
|
||||
// Add Server
|
||||
// Delete Server
|
||||
// Select Server
|
||||
// Edit Server
|
||||
// Test Sever
|
||||
// Login server and save credentials
|
||||
|
||||
|
||||
|
||||
function makeRequest(url, type, dataObject, callback){
|
||||
var request = $.ajax({
|
||||
url: url,
|
||||
type: type,
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
data: JSON.stringify(dataObject)
|
||||
});
|
||||
|
||||
request.done(function( response ) {
|
||||
callback(response, false);
|
||||
});
|
||||
|
||||
// TODO: AHandle errors
|
||||
request.fail(function( jqXHR, textStatus ) {
|
||||
callback(textStatus, jqXHR);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function makePOSTRequest(url, dataObject, callback){
|
||||
makeRequest(url, "POST", dataObject, callback);
|
||||
}
|
||||
|
||||
function makeGETRequest(url, dataObject, callback){
|
||||
makeRequest(url, "GET", dataObject, callback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
mstreamModule.dirparser = function(directory, filetypes, callback){
|
||||
makePOSTRequest('/dirparser', {dir: directory}, callback);
|
||||
}
|
||||
|
||||
mstreamModule.savePlaylist = function(title, songs, callback){
|
||||
makePOSTRequest('/playlist/save', { title: title, songs: songs }, callback);
|
||||
}
|
||||
|
||||
mstreamModule.deletePlaylist = function(playlistname, callback){
|
||||
makePOSTRequest('/playlist/delete', {playlistname: playlistname}, callback);
|
||||
}
|
||||
|
||||
mstreamModule.loadPlaylist = function(playlistname, callback){
|
||||
makePOSTRequest('/playlist/load', {playlistname: playlistname}, callback);
|
||||
}
|
||||
|
||||
mstreamModule.getAllPlaylists = function(callback){
|
||||
makeGETRequest('/playlist/getall', false, callback);
|
||||
}
|
||||
|
||||
mstreamModule.search = function(searchTerm, callback){
|
||||
makePOSTRequest('/db/search', {search: searchTerm}, callback);
|
||||
}
|
||||
|
||||
mstreamModule.artists = function(callback){
|
||||
makeGETRequest('/db/artists', false, callback);
|
||||
}
|
||||
|
||||
mstreamModule.albums = function(callback){
|
||||
makeGETRequest('/db/albums', false, callback);
|
||||
}
|
||||
|
||||
mstreamModule.artistAlbums = function(artist, callback){
|
||||
makePOSTRequest("/db/artists-albums", {artist: artist}, callback);
|
||||
}
|
||||
|
||||
mstreamModule.albumSongs = function(album, callback){
|
||||
makePOSTRequest("/db/album-songs", {album: album}, callback);
|
||||
}
|
||||
|
||||
mstreamModule.dbStatus = function(callback){
|
||||
makeGETRequest("/db/status", false, callback);
|
||||
}
|
||||
|
||||
mstreamModule.dbScan = function(callback){
|
||||
makeGETRequest("/db/recursive-scan", false, callback);
|
||||
}
|
||||
|
||||
mstreamModule.makeShared = function(playlist, shareTimeInDays, callback){
|
||||
makePOSTRequest("/shared/make-shared", { time: shareTimeInDays, playlist: playlist}, callback);
|
||||
}
|
||||
|
||||
|
||||
mstreamModule.lookupMetadata = function(filepath, callback){
|
||||
makePOSTRequest("/db/metadata", {filepath: filepath}, callback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// LOGIN
|
||||
mstreamModule.login = function(username, password, callback){
|
||||
makePOSTRequest("/login", { username: username, password: password}, callback);
|
||||
}
|
||||
mstreamModule.updateCurrentServer = function(username, token, vPath){
|
||||
mstreamModule.currentServer.user = username;
|
||||
mstreamModule.currentServer.token = token;
|
||||
mstreamModule.currentServer.vPath = vPath;
|
||||
}
|
||||
|
||||
mstreamModule.ping = function(callback){
|
||||
makeGETRequest("/ping", false, callback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Special helper function
|
||||
// TODO: handle metadata
|
||||
MSTREAMPLAYER.addSongWizard = function(filepath, metadata, lookupMetadata){
|
||||
var url = mstreamModule.currentServer.host + filepath;
|
||||
|
||||
if(mstreamModule.currentServer.vPath){
|
||||
url = mstreamModule.currentServer.vPath + '/' + url;
|
||||
}
|
||||
|
||||
if(mstreamModule.currentServer.token){
|
||||
url = url + '?token=' + mstreamModule.currentServer.token;
|
||||
}
|
||||
|
||||
var newSong = {
|
||||
url: url,
|
||||
filepath: filepath,
|
||||
metadata: metadata
|
||||
};
|
||||
|
||||
MSTREAMPLAYER.addSong(newSong);
|
||||
|
||||
// perform lookup
|
||||
if(lookupMetadata === true){
|
||||
mstreamModule.lookupMetadata(filepath, function(response, error){
|
||||
if(error !== false){
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(response);
|
||||
|
||||
if(response.metadata){
|
||||
newSong.metadata = Object.create(response.metadata);
|
||||
MSTREAMPLAYER.resetCurrentMetadata();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return mstreamModule;
|
||||
}());
|
||||
@ -1,293 +0,0 @@
|
||||
var MSTREAMGEN = (function () {
|
||||
let mstreamModule = {};
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: Functions to change current server and save servers
|
||||
// Not needed until express port
|
||||
|
||||
|
||||
|
||||
|
||||
mstreamModule.currentProperties = {
|
||||
currentList: false
|
||||
// Can be anything in the title array
|
||||
}
|
||||
|
||||
var currentListTypes = {
|
||||
'filebrowser': {displayName: 'File Browser'},
|
||||
'albums': {displayName: 'Albums'},
|
||||
'artists': {displayName: 'Artists'},
|
||||
'search': {displayName: 'Search'},
|
||||
'playlists': {displayname: 'Playlists'}
|
||||
};
|
||||
|
||||
mstreamModule.dataList = [];
|
||||
// TODO: Modify prototype functions for dataList to verify all items
|
||||
// dataItem = {
|
||||
// type: '',
|
||||
// data:'',
|
||||
//}
|
||||
|
||||
function clearAndSetDataList(type){
|
||||
if(!(type in currentListTypes) || type !== false){
|
||||
// TODO: Throw Error
|
||||
}
|
||||
|
||||
mstreamModule.currentProperties.currentList = type;
|
||||
// Loop through and pop so Vue doesn't throw a fit
|
||||
while(mstreamModule.dataList.length > 0){
|
||||
mstreamModule.dataList.pop();
|
||||
}
|
||||
}
|
||||
|
||||
mstreamModule.clearDataList = function(){
|
||||
clearAndSetDataList(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: TURN THIS INTO MAP
|
||||
mstreamModule.fileExplorerArray = [
|
||||
{name:'/', position:0}
|
||||
];
|
||||
|
||||
function getDirectoryContents(){
|
||||
// Construct the directory string
|
||||
var directoryString = "";
|
||||
for (var i = 0; i < mstreamModule.fileExplorerArray.length; i++) {
|
||||
// Ignore root directory
|
||||
if(mstreamModule.fileExplorerArray[i].name !== '/'){
|
||||
directoryString += mstreamModule.fileExplorerArray[i].name + "/";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MSTREAMAPI.dirparser(directoryString, false, function(response, error){
|
||||
if(error !== false){
|
||||
boilerplateFailure(response, error);
|
||||
}
|
||||
|
||||
clearAndSetDataList('filebrowser');
|
||||
$.each(response.contents, function() {
|
||||
mstreamModule.dataList.push(
|
||||
{
|
||||
type: (this.type === 'directory' ? "directory" : "file"),
|
||||
metadata: {}, // TODO: Move all metadata to here
|
||||
path: response.path + this.name,
|
||||
name: this.name,
|
||||
artist: false, // TODO:
|
||||
title: false // TODO:
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
mstreamModule.getCurrentDirectoryContents = function(){
|
||||
getDirectoryContents();
|
||||
}
|
||||
|
||||
mstreamModule.goToNextDirectory = function(folder, currentScrollPosition = 0){
|
||||
if(currentScrollPosition != 0 ){
|
||||
// TODO: Save Scroll Position
|
||||
}
|
||||
|
||||
mstreamModule.fileExplorerArray.push({name:folder, position:0});
|
||||
getDirectoryContents();
|
||||
}
|
||||
|
||||
mstreamModule.goBackDirectory = function(){
|
||||
// Make sure it's not the root directory
|
||||
// TODO: TEST THAT THIS ALL WORKS
|
||||
if(mstreamModule.dataList[mstreamModule.dataList.length-1].name === '/'){
|
||||
return false;
|
||||
}
|
||||
|
||||
mstreamModule.fileExplorerArray.pop();
|
||||
getDirectoryContents();
|
||||
|
||||
// TODO: Return Current Scroll Position
|
||||
}
|
||||
|
||||
mstreamModule.getCurrentScrollPosition = function(){
|
||||
return mstreamModule.dataList[mstreamModule.dataList.length-1].position;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
mstreamModule.goToExactDirectory = function(directory){
|
||||
// Clear Out fileExplorerArray
|
||||
// loop and pop
|
||||
|
||||
// Setup new fileExplorerArray
|
||||
// splice
|
||||
// loop
|
||||
|
||||
getDirectoryContents();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO Move this to a secondary module that's initiated when it's assured the MSTREAM module is looded
|
||||
mstreamModule.savePlaylist = function(saveThis){
|
||||
// TODO: Verify all data in saveThis
|
||||
|
||||
if(saveThis.length == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
// Get playlist from MSTREAM
|
||||
// var playlist = MSTREAMPLAYER.whatever
|
||||
|
||||
// Get user entered title
|
||||
// var title = '';
|
||||
|
||||
|
||||
|
||||
// Check for special characters
|
||||
if(/^[a-zA-Z0-9-_ ]*$/.test(title) == false) {
|
||||
// TODO: Warn User
|
||||
return false;
|
||||
}
|
||||
|
||||
// loop through array and add each file to the playlist
|
||||
// $.each( playlistArray, function() {
|
||||
// // TODO:
|
||||
// });
|
||||
|
||||
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "saveplaylist",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
data: {
|
||||
title:title,
|
||||
stuff:saveThis // TODO: Change this on server end
|
||||
},
|
||||
})
|
||||
.done(function( msg ) {
|
||||
|
||||
if(msg == 1){
|
||||
// ???
|
||||
}
|
||||
if(msg == 0){
|
||||
// .. ???
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// TODO: error handeling
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
mstreamModule.getAllPlaylists = function(){
|
||||
var request = $.ajax({
|
||||
url: "getallplaylists",
|
||||
type: "GET"
|
||||
});
|
||||
|
||||
request.done(function( msg ) {
|
||||
clearAndSetDataList('playlists');
|
||||
var parsedResponse = $.parseJSON(msg);
|
||||
|
||||
//parse through the json array and make an array of corresponding divs
|
||||
var playlists = [];
|
||||
$.each(parsedResponse, function() {
|
||||
mstreamModule.dataList.push(
|
||||
{
|
||||
type: 'playlist',
|
||||
name: this.name
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
request.fail(function( jqXHR, textStatus ) {
|
||||
// TODO:
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mstreamModule.deletePlaylist = function(playlistNameString){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mstreamModule.getPlaylistContents = function(playlistNameString){
|
||||
|
||||
// Make an AJAX call to get the contents of the playlist
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "loadplaylist",
|
||||
data: {playlistname: playlistNameString},
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(function( msg ) {
|
||||
// Add the playlist name to the modal
|
||||
|
||||
// Clear the playlist
|
||||
|
||||
// Append the playlist items to the playlist
|
||||
$.each( msg, function(i ,item) {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO:
|
||||
function boilerplateFailure(response, error){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mstreamModule.getSharedPlaylist = function(){
|
||||
// Get the URL parameters
|
||||
console.log(window.location.pathname);
|
||||
|
||||
|
||||
// Call the api with the the short token
|
||||
|
||||
// Add songs to MSTREAM
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Return an object that is assigned to Module
|
||||
return mstreamModule;
|
||||
|
||||
}());
|
||||
@ -1,673 +0,0 @@
|
||||
$(document).ready(function(){
|
||||
|
||||
// Auto Focus
|
||||
Vue.directive('focus', {
|
||||
// When the bound element is inserted into the DOM...
|
||||
inserted: function (el) {
|
||||
// Focus the element
|
||||
el.focus()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var loginPanel = new Vue({
|
||||
el: '#login-overlay',
|
||||
data: {
|
||||
needToLogin: false,
|
||||
error: false,
|
||||
errorMessage: 'Login Failed',
|
||||
pending: false
|
||||
},
|
||||
methods: {
|
||||
submitCode: function(e){
|
||||
// Get Code
|
||||
this.pending = true;
|
||||
var that = this;
|
||||
MSTREAMAPI.login($('#login-username').val(), $('#login-password').val(), function(response, error){
|
||||
if(error !== false){
|
||||
// Alert the user
|
||||
that.pending = false;
|
||||
that.error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Eye-candy: change the error color and essage
|
||||
$('#login-alert').toggleClass('alert');
|
||||
$('#login-alert').toggleClass('success');
|
||||
that.errorMessage = "Welcome To mStream!";
|
||||
|
||||
// Add the token to the cookies
|
||||
Cookies.set('token', response.token);
|
||||
|
||||
// Add the token the URL calls
|
||||
MSTREAMAPI.updateCurrentServer($('#login-username').val(), response.token, response.vPath)
|
||||
|
||||
loadFileExplorer();
|
||||
// MSTREAMGEN.getCurrentDirectoryContents();
|
||||
|
||||
// Remove the overlay
|
||||
$('.login-overlay').fadeOut( "slow" );
|
||||
that.pending = false;
|
||||
that.needToLogin = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function testIt(token){
|
||||
if(token){
|
||||
MSTREAMAPI.currentServer.token = token;
|
||||
}
|
||||
|
||||
MSTREAMAPI.ping( function(response, error){
|
||||
if(error !== false){
|
||||
// NOTE: There needs to be a split here
|
||||
// For the webapp we simply display the login panel
|
||||
loginPanel.needToLogin = true;
|
||||
// TODO: Move this transitionstuff to vue
|
||||
$('.login-overlay').fadeIn( "slow" );
|
||||
console.log(loginPanel);
|
||||
// For electron we need to alert the user that user it failed and guide them to the login form
|
||||
|
||||
return;
|
||||
}
|
||||
// set vPath
|
||||
MSTREAMAPI.currentServer.vPath = response.vPath;
|
||||
// Setup the filebrowser
|
||||
loadFileExplorer();
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: There needs to be a split here
|
||||
// For the normal webap we just get the token
|
||||
// var token = Cookies.get('token');
|
||||
testIt(Cookies.get('token'));
|
||||
// For electron we need to pull it from wherever electron stores things
|
||||
|
||||
|
||||
|
||||
////////////////////////////// Global Variables
|
||||
// These vars track your position within the file explorer
|
||||
var fileExplorerArray = [];
|
||||
var fileExplorerScrollPosition = [];
|
||||
// Stores an array of searchable ojects
|
||||
var currentBrowsingList = [];
|
||||
|
||||
//////////////////////////////// Administrative stuff
|
||||
// when you click an mp3, add it to the now playling playlist
|
||||
$("#filelist").on('click', 'div.filez', function() {
|
||||
MSTREAMPLAYER.addSongWizard($(this).data("file_location"), {}, true);
|
||||
});
|
||||
|
||||
// Handle panel stuff
|
||||
function resetPanel(panelName, className){
|
||||
$('#filelist').empty();
|
||||
$('.directoryTitle').hide();
|
||||
$('#filelist').removeClass('scrollBoxHeight1');
|
||||
$('#filelist').removeClass('scrollBoxHeight2');
|
||||
|
||||
$('#filelist').addClass(className);
|
||||
$('.panel_one_name').html(panelName);
|
||||
}
|
||||
|
||||
function boilerplateFailure(response, error){
|
||||
$('#filelist').empty();
|
||||
$('#filelist').html('<p>Call Failed</p>');
|
||||
}
|
||||
|
||||
// clear the playlist
|
||||
$("#clear").on('click', function() {
|
||||
MSTREAMPLAYER.clearPlaylist();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////// File Explorer
|
||||
function loadFileExplorer(){
|
||||
resetPanel('File Explorer', 'scrollBoxHeight1');
|
||||
$('#directory_bar').show();
|
||||
|
||||
// Reset file explorer vars
|
||||
fileExplorerArray = [];
|
||||
fileExplorerScrollPosition = [];
|
||||
|
||||
//send this directory to be parsed and displayed
|
||||
senddir(0);
|
||||
}
|
||||
|
||||
// Load up the file explorer
|
||||
$('.get_file_explorer').on('click', loadFileExplorer);
|
||||
|
||||
// when you click on a directory, go to that directory
|
||||
$("#filelist").on('click', 'div.dirz', function() {
|
||||
//get the id of that class
|
||||
var nextDir = $(this).attr("id");
|
||||
fileExplorerArray.push(nextDir);
|
||||
|
||||
// Save the scroll position
|
||||
var scrollPosition = $('#filelist').scrollTop();
|
||||
fileExplorerScrollPosition.push(scrollPosition);
|
||||
|
||||
// pass this value along
|
||||
senddir(0);
|
||||
});
|
||||
|
||||
// when you click the back directory
|
||||
$(".backButton").on('click', function() {
|
||||
if(fileExplorerArray.length != 0){
|
||||
// remove the last item in the array
|
||||
fileExplorerArray.pop();
|
||||
// Get the scroll postion
|
||||
var scrollPosition = fileExplorerScrollPosition.pop();
|
||||
|
||||
senddir(scrollPosition);
|
||||
}
|
||||
});
|
||||
|
||||
// send a new directory to be parsed.
|
||||
function senddir(scrollPosition){
|
||||
// Construct the directory string
|
||||
var directoryString = "";
|
||||
for (var i = 0; i < fileExplorerArray.length; i++) {
|
||||
directoryString += fileExplorerArray[i] + "/";
|
||||
}
|
||||
|
||||
MSTREAMAPI.dirparser(directoryString, false, function(response, error){
|
||||
if(error !== false){
|
||||
boilerplateFailure(response, error);
|
||||
}
|
||||
// Set any directory views
|
||||
$('.directoryName').html('/' + directoryString);
|
||||
// hand this data off to be printed on the page
|
||||
printdir(response);
|
||||
// Set scroll postion
|
||||
$('#filelist').scrollTop(scrollPosition);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// function that will recieve JSON array of a directory listing. It will then make a list of the directory and tack on classes for functionality
|
||||
function printdir(response){
|
||||
currentBrowsingList = response.contents;
|
||||
|
||||
// clear the list
|
||||
$('#filelist').empty();
|
||||
$('#search_folders').val('');
|
||||
|
||||
// TODO: create an object of everything that the user can easily sort through
|
||||
var searchObject = [];
|
||||
|
||||
//parse through the json array and make an array of corresponding divs
|
||||
var filelist = [];
|
||||
$.each(currentBrowsingList, function() {
|
||||
if(this.type=='directory'){
|
||||
filelist.push('<div id="'+this.name+'" class="dirz">'+this.name+'</div>');
|
||||
}else{
|
||||
if(this.artist!=null || this.title!=null){
|
||||
filelist.push('<div data-filetype="'+this.type+'" data-file_location="'+response.path+this.name+'" class="filez"><span class="pre-char">♬</span> <span class="title">'+this.artist+' - '+this.title+'</span></div>');
|
||||
}else{
|
||||
filelist.push('<div data-filetype="'+this.type+'" data-file_location="'+response.path+this.name+'" class="filez"><span class="pre-char">♫</span> <span class="title">'+this.name+'</span></div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Post the html to the filelist div
|
||||
$('#filelist').html(filelist);
|
||||
}
|
||||
|
||||
// when you click 'add directory', add entire directory to the playlist
|
||||
$("#addall").on('click', function() {
|
||||
//make an array of all the mp3 files in the curent directory
|
||||
var elems = document.getElementsByClassName('filez');
|
||||
var arr = jQuery.makeArray(elems);
|
||||
|
||||
//loop through array and add each file to the playlist
|
||||
$.each( arr, function() {
|
||||
MSTREAMPLAYER.addSongWizard($(this).data("file_location"), {}, true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Search Files
|
||||
$('#search_folders').on('keyup', function(){
|
||||
if($(this).val().length>1){
|
||||
var searchVal = $(this).val();
|
||||
|
||||
var path = ""; // Construct the directory string
|
||||
for (var i = 0; i < fileExplorerArray.length; i++) {
|
||||
path += fileExplorerArray[i] + "/";
|
||||
}
|
||||
|
||||
var filelist = [];
|
||||
$.each(currentBrowsingList, function() {
|
||||
var lowerCase = this.name.toLowerCase();
|
||||
|
||||
if (lowerCase.indexOf( searchVal.toLowerCase() ) !== -1) {
|
||||
if(this.type=='directory'){
|
||||
filelist.push('<div id="'+this.name+'" class="dirz">'+this.name+'</div>');
|
||||
}else{
|
||||
if(this.artist!=null || this.title!=null){
|
||||
filelist.push('<div data-filetype="'+this.type+'" data-file_location="'+path+this.name+'" class="filez"><span class="pre-char">♬</span> <span class="title">'+this.artist+' - '+this.title+'</span></div>');
|
||||
}else{
|
||||
filelist.push('<div data-filetype="'+this.type+'" data-file_location="'+path+this.name+'" class="filez"><span class="pre-char">♫</span> <span class="title">'+this.name+'</span></div>');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Post the html to the filelist div
|
||||
$('#filelist').html(filelist);
|
||||
});
|
||||
|
||||
$('#search-explorer').on('click', function(){
|
||||
// Hide Filepath
|
||||
$('#search_folders').toggleClass( 'hide' );
|
||||
// Show Search Input
|
||||
$('.directoryName').toggleClass( 'hide' );
|
||||
|
||||
if(!$('#search_folders').hasClass('hide')){
|
||||
$( "#search_folders" ).focus();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
////////////////////////////////////// Share playlists
|
||||
$('#share_playlist_form').on('submit', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
$('#share_it').prop("disabled",true);
|
||||
var shareTimeInDays = $('#share_time').val();
|
||||
|
||||
// Check for special characters
|
||||
if(/^[0-9]*$/.test(shareTimeInDays) == false) {
|
||||
console.log('don\'t do that');
|
||||
$('#share_it').prop("disabled",false);
|
||||
return false;
|
||||
}
|
||||
|
||||
//loop through array and add each file to the playlist
|
||||
var stuff = [];
|
||||
for (let i = 0; i < MSTREAMPLAYER.playlist.length; i++) {
|
||||
//Do something
|
||||
stuff.push(MSTREAMPLAYER.playlist[i].filepath);
|
||||
}
|
||||
|
||||
if(stuff.length == 0){
|
||||
$('#share_it').prop("disabled",false);
|
||||
return;
|
||||
}
|
||||
|
||||
MSTREAMAPI.makeShared(stuff, shareTimeInDays, function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
$('#share_it').prop("disabled",false);
|
||||
var adrs = window.location.protocol + '//' + window.location.host + '/shared/playlist/' + response.id;
|
||||
$('.share-textarea').val(adrs);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
////////////////////////////////////// Save/Load playlists
|
||||
// Save a new playlist
|
||||
$('#save_playlist_form').on('submit', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
// Check for special characters
|
||||
if(/^[a-zA-Z0-9-_ ]*$/.test(title) == false) {
|
||||
console.log('don\'t do that');
|
||||
return false;
|
||||
}
|
||||
|
||||
if(MSTREAMPLAYER.playlist.length == 0){
|
||||
// TODO: Alert user nothing was saved
|
||||
return;
|
||||
}
|
||||
|
||||
$('#save_playlist').prop("disabled",true);
|
||||
var title = $('#playlist_name').val();
|
||||
|
||||
//loop through array and add each file to the playlist
|
||||
var songs = [];
|
||||
for (let i = 0; i < MSTREAMPLAYER.playlist.length; i++) {
|
||||
songs.push(MSTREAMPLAYER.playlist[i].filepath);
|
||||
}
|
||||
|
||||
MSTREAMAPI.savePlaylist(title, songs, function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
$('#save_playlist').prop("disabled",false);
|
||||
$('#close_save_playlist').trigger("click");
|
||||
});
|
||||
});
|
||||
|
||||
// Get all playlists
|
||||
$('.get_all_playlists').on('click', function(){
|
||||
resetPanel('Playlists', 'scrollBoxHeight2');
|
||||
|
||||
MSTREAMAPI.getAllPlaylists( function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
// loop through the json array and make an array of corresponding divs
|
||||
var playlists = [];
|
||||
$.each(response, function() {
|
||||
playlists.push('<div data-playlistname="'+this.name+'" class="playlist_row_container"><span data-playlistname="'+this.name+'" class="playlistz force-width">'+this.name+'</span><span data-playlistname="'+this.name+'" class="deletePlaylist">x</span></div>');
|
||||
});
|
||||
|
||||
// Add playlists to the left panel
|
||||
$('#filelist').html(playlists);
|
||||
});
|
||||
});
|
||||
|
||||
// delete playlist
|
||||
$("#filelist").on('click', '.deletePlaylist', function(){
|
||||
// Get Playlist ID
|
||||
var playlistname = $(this).data('playlistname');
|
||||
var that = this;
|
||||
|
||||
MSTREAMAPI.deletePlaylist(playlistname, function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
$(that).parent().remove();
|
||||
});
|
||||
});
|
||||
|
||||
// load up a playlist
|
||||
$("#filelist").on('click', '.playlistz', function() {
|
||||
var playlistname = $(this).data('playlistname');
|
||||
var name = $(this).html();
|
||||
|
||||
MSTREAMAPI.loadPlaylist(playlistname, function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
// Add the playlist name to the modal
|
||||
$('#playlist_name').val(name);
|
||||
|
||||
// Clear the playlist
|
||||
MSTREAMPLAYER.clearPlaylist();
|
||||
|
||||
// Append the playlist items to the playlist
|
||||
$.each( response, function(i ,item) {
|
||||
MSTREAMPLAYER.addSongWizard(item.filepath , {}, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/////////////// Download Playlist
|
||||
$('#downloadPlaylist').click(function(){
|
||||
// Loop through array and add each file to the playlist
|
||||
var downloadFiles = [];
|
||||
for (let i = 0; i < MSTREAMPLAYER.playlist.length; i++) {
|
||||
downloadFiles.push(MSTREAMPLAYER.playlist[i].filepath);
|
||||
}
|
||||
|
||||
// Use key if necessary
|
||||
if( MSTREAMAPI.currentServer.token){
|
||||
$("#downform").attr("action", "download?token=" + MSTREAMAPI.currentServer.token);
|
||||
}
|
||||
|
||||
$('<input>').attr({
|
||||
type: 'hidden',
|
||||
name: 'fileArray',
|
||||
value: JSON.stringify(downloadFiles),
|
||||
}).appendTo('#downform');
|
||||
|
||||
//submit form
|
||||
$('#downform').submit();
|
||||
// clear the form
|
||||
$('#downform').empty();
|
||||
});
|
||||
|
||||
///////////////////////////// Database Management
|
||||
// The Manage DB panel
|
||||
$('#manage_database').on('click', function(){
|
||||
resetPanel('Database Management', 'scrollBoxHeight2');
|
||||
|
||||
MSTREAMAPI.dbStatus( function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
// If there is an error
|
||||
if(response.error){
|
||||
$('#filelist').html('<p>The database returned the following error:</p><p>' + response.error + '</p>');
|
||||
return;
|
||||
}
|
||||
// Add Beets Msg
|
||||
if(response.dbType == 'beets' || response.dbType == 'beets-default' ){
|
||||
$('#filelist').append('<h3><img style="height:40px;" src="img/database-icon.svg" >Powered by Beets DB</h3>');
|
||||
}
|
||||
// if the DB is locked
|
||||
if(response.locked){
|
||||
$('#filelist').append('<p>The database is currently being built. Currently ' + response.totalFileCount + ' files are in the DB</p><input type="button" value="Check Progress" class="button secondary small" id="check_db_progress" >');
|
||||
return;
|
||||
}
|
||||
// If you got this far the db is made and working
|
||||
$('#filelist').append('<p>Your DB has ' + response.totalFileCount + ' files</p><input type="button" class="button secondary rounded small" value="Build Database" id="build_database">');
|
||||
});
|
||||
});
|
||||
|
||||
// Build the database
|
||||
$('body').on('click', '#build_database', function(){
|
||||
$(this).prop("disabled", true);
|
||||
|
||||
MSTREAMAPI.dbScan( function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
// Append the check db button so the user can start checking right away
|
||||
$('#filelist').append('<input type="button" value="Check Progress" id="check_db_progress" >');
|
||||
});
|
||||
});
|
||||
|
||||
// Check DB build progress
|
||||
$('body').on('click', '#check_db_progress', function(){
|
||||
MSTREAMAPI.dbStatus( function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
$( "#db_progress_report" ).remove();
|
||||
|
||||
// if file_count is 0, report that the the build script is not done counting files
|
||||
if(response.file_count == 0){
|
||||
$('#filelist').append('<p id="db_progress_report">The create database script is still counting the files in the music collection. This operation can take some time. Try again in a bit</p>');
|
||||
return;
|
||||
}
|
||||
|
||||
// Append new <p> tag with id of "db_progress_report"
|
||||
$('#filelist').append('<p id="db_progress_report">Progress: '+ response.files_in_db +'/'+ response.file_count +'</p>');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
//////////////////////////////////// Sort by Albums
|
||||
//Load up album explorer
|
||||
$('.get_all_albums').on('click', function(){
|
||||
resetPanel('Albums', 'scrollBoxHeight2');
|
||||
|
||||
MSTREAMAPI.albums( function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
//parse through the json array and make an array of corresponding divs
|
||||
var albums = [];
|
||||
$.each(response.albums, function(index, value) {
|
||||
albums.push('<div data-album="'+value+'" class="albumz">'+value+' </div>');
|
||||
});
|
||||
|
||||
$('#filelist').html(albums);
|
||||
});
|
||||
});
|
||||
|
||||
// Load up album-songs
|
||||
$("#filelist").on('click', '.albumz', function() {
|
||||
var album = $(this).data('album');
|
||||
|
||||
MSTREAMAPI.albumSongs(album, function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
//clear the list
|
||||
$('#filelist').empty();
|
||||
|
||||
//parse through the json array and make an array of corresponding divs
|
||||
var filelist = [];
|
||||
$.each(response, function() {
|
||||
if(this.metadata.title){
|
||||
filelist.push('<div data-file_location="'+this.filepath+'" class="filez"><span class="pre-char">♫</span> <span class="title">'+this.metadata.title+'</span></div>');
|
||||
}
|
||||
else{
|
||||
filelist.push('<div data-file_location="'+this.filepath+'" class="filez"><span class="pre-char">♬</span> <span class="title">'+this.metadata.filename+'</span></div>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#filelist').html(filelist);
|
||||
});
|
||||
});
|
||||
|
||||
/////////////////////////////////////// Artists
|
||||
// Load up album-songs
|
||||
$('.get_all_artists').on('click', function(){
|
||||
resetPanel('Artists', 'scrollBoxHeight2');
|
||||
|
||||
MSTREAMAPI.artists( function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
//parse through the json array and make an array of corresponding divs
|
||||
var artists = [];
|
||||
$.each(response.artists, function(index,value) {
|
||||
artists.push('<div data-artist="'+value+'" class="artistz">'+value+' </div>');
|
||||
});
|
||||
|
||||
$('#filelist').html(artists);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("#filelist").on('click', '.artistz', function() {
|
||||
var artist = $(this).data('artist');
|
||||
|
||||
MSTREAMAPI.artistAlbums(artist, function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
//clear the list
|
||||
$('#filelist').empty();
|
||||
|
||||
var albums = [];
|
||||
$.each(response.albums, function(index, value) {
|
||||
albums.push('<div data-album="'+value+'" class="albumz">'+value+' </div>');
|
||||
});
|
||||
|
||||
$('#filelist').html(albums);
|
||||
$('.panel_one_name').html('Artists->Albums');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
///////////////////////////// Search Function
|
||||
// Setup the search interface
|
||||
$('#search_database').on('click', function(){
|
||||
resetPanel('Search', 'scrollBoxHeight1');
|
||||
$('#search_container').show();
|
||||
});
|
||||
|
||||
// Auto Search
|
||||
$('#search_it').on('keyup', function(){
|
||||
// TODO: Put this on some kind of time delay. That way rapid keystrokes won't spam the server
|
||||
if($(this).val().length>1){
|
||||
MSTREAMAPI.search($(this).val(), function(response, error){
|
||||
if(error !== false){
|
||||
return boilerplateFailure(response, error);
|
||||
}
|
||||
var htmlString = '';
|
||||
|
||||
if(response.artists.length > 0){
|
||||
htmlString += '<h2 class="search_subtitle"><strong>Artists</strong></h2>';
|
||||
$.each(response.artists, function(index, value) {
|
||||
htmlString += '<div data-artist="'+value+'" class="artistz">'+value+' </div>';
|
||||
});
|
||||
}
|
||||
|
||||
if(response.albums.length > 0){
|
||||
htmlString += '<h2 class="search_subtitle"><strong>Albums</strong></h2>';
|
||||
$.each(response.albums, function(index, value) {
|
||||
htmlString += '<div data-album="'+value+'" class="albumz">'+value+' </div>';
|
||||
});
|
||||
}
|
||||
|
||||
$('#filelist').html(htmlString);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//////////////////////// Jukebox Mode
|
||||
function setupJukeboxPanel(){
|
||||
// Hide the directory bar
|
||||
resetPanel('Jukebox Mode', 'scrollBoxHeight2');
|
||||
|
||||
var newHtml;
|
||||
if(JUKEBOX.stats.live !== false && JUKEBOX.connection !== false){
|
||||
newHtml = createJukeboxPanel();
|
||||
}else{
|
||||
newHtml = '\
|
||||
<p class="jukebox-panel">\
|
||||
<br><br>\
|
||||
<h3>Jukebox Mode allows you to control this page remotely<h3> <br><br>\
|
||||
<div class="jukebox_connect button"> CONNECT IT!</div>\
|
||||
</p>\
|
||||
<img src="public/img/loading.gif" class="hide jukebox-loading">';
|
||||
}
|
||||
|
||||
// Add the content
|
||||
$('#filelist').html(newHtml);
|
||||
}
|
||||
|
||||
// The jukebox panel
|
||||
$('#jukebox_mode').on('click', function(){
|
||||
setupJukeboxPanel();
|
||||
});
|
||||
|
||||
// Setup Jukebox
|
||||
$('body').on('click', '.jukebox_connect', function(){
|
||||
$(this).prop("disabled", true);
|
||||
$(this).hide();
|
||||
$('.jukebox-loading').toggleClass('hide');
|
||||
|
||||
JUKEBOX.createWebsocket( MSTREAMAPI.currentServer.token, function(){
|
||||
// Wait a while and display the status
|
||||
setTimeout(function(){
|
||||
// TODO: Check that status has changed
|
||||
setupJukeboxPanel();
|
||||
},1800);
|
||||
});
|
||||
});
|
||||
|
||||
function createJukeboxPanel(){
|
||||
var returnHtml = '<p class="jukebox-panel">';
|
||||
|
||||
if(JUKEBOX.stats.error !== false){
|
||||
return returnHtml + 'An error occurred. Please refresh the page and try again</p>';
|
||||
}
|
||||
|
||||
if(JUKEBOX.stats.adminCode){
|
||||
returnHtml += '<h1>Code: ' + JUKEBOX.stats.adminCode + '</h1>';
|
||||
}
|
||||
if(JUKEBOX.stats.guestCode){
|
||||
returnHtml += '<h2>Guest Code: ' + JUKEBOX.stats.guestCode + '</h2>';
|
||||
}
|
||||
|
||||
var adrs = window.location.protocol + '//' + window.location.host + '/remote';
|
||||
returnHtml += '<br><h4>Remote Jukebox Controls: <a target="_blank" href="' + adrs + '"> ' + adrs + '</a><h4>';
|
||||
|
||||
return returnHtml + '</p>';
|
||||
}
|
||||
|
||||
});
|
||||
@ -1,102 +0,0 @@
|
||||
var JUKEBOX = (function () {
|
||||
let mstreamModule = {};
|
||||
|
||||
mstreamModule.connection = false;
|
||||
|
||||
// jukebox global variable
|
||||
mstreamModule.stats = {
|
||||
// connection: false,
|
||||
live: false,
|
||||
guestCode: false,
|
||||
adminCode: false,
|
||||
error: false,
|
||||
accessAddress: false
|
||||
};
|
||||
|
||||
// TODO: Move token to the api library
|
||||
mstreamModule.createWebsocket = function(accessKey, callback){
|
||||
if(mstreamModule.stats.live ===true ){
|
||||
return false;
|
||||
}
|
||||
mstreamModule.stats.live = true;
|
||||
// if user is running mozilla then use it's built-in WebSocket
|
||||
window.WebSocket = window.WebSocket || window.MozWebSocket;
|
||||
|
||||
// if browser doesn't support WebSocket, just show some notification and exit
|
||||
if (!window.WebSocket) {
|
||||
// TODO: Make a warning
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Check if websocket has already been created
|
||||
|
||||
// open connection
|
||||
var l = window.location;
|
||||
var wsLink = ((l.protocol === "https:") ? "wss://" : "ws://") + l.host + l.pathname;
|
||||
mstreamModule.connection = new WebSocket(wsLink + 'jukebox/open-connection?token=' + accessKey);
|
||||
|
||||
|
||||
|
||||
mstreamModule.connection.onopen = function () {
|
||||
callback();
|
||||
};
|
||||
|
||||
mstreamModule.connection.onerror = function (error) {
|
||||
// TODO: Error Code
|
||||
console.log('CONNECTION ERROR!!!!!!!!!!!!');
|
||||
};
|
||||
|
||||
// most important part - incoming messages
|
||||
mstreamModule.connection.onmessage = function (message) {
|
||||
// try to parse JSON message. Because we know that the server always returns
|
||||
// JSON this should work without any problem but we should make sure that
|
||||
// the message is not chunked or otherwise damaged.
|
||||
try {
|
||||
var json = JSON.parse(message.data);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle Code
|
||||
if(json.code){
|
||||
mstreamModule.stats.adminCode = json.code;
|
||||
}
|
||||
if(json.guestCode){
|
||||
mstreamModule.stats.guestCode = json.guestCode;
|
||||
}
|
||||
|
||||
|
||||
if(!json.command){
|
||||
return;
|
||||
}
|
||||
|
||||
if(json.command === 'next'){
|
||||
MSTREAMPLAYER.nextSong();
|
||||
return;
|
||||
}
|
||||
if( json.command === 'playPause'){
|
||||
MSTREAMPLAYER.playPause();
|
||||
}
|
||||
if( json.command === 'previous'){
|
||||
MSTREAMPLAYER.previousSong();
|
||||
return;
|
||||
}
|
||||
if( json.command === 'addSong' && json.file){
|
||||
MSTREAMPLAYER.addSongWizard(json.file, {}, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// TODO: Finish this at some point
|
||||
// mstreamModule.commandList = {
|
||||
// 'next': MSTREAMPLAYER.nextSong(),
|
||||
// 'playPause': MSTREAMPLAYER.playPause(),
|
||||
// 'previous': MSTREAMPLAYER.previousSong(),
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
return mstreamModule;
|
||||
}());
|
||||
@ -1,765 +0,0 @@
|
||||
var MSTREAMPLAYER = (function () {
|
||||
let mstreamModule = {};
|
||||
|
||||
|
||||
// Playlist variables
|
||||
mstreamModule.positionCache = {val:-1};
|
||||
// var currentSong;
|
||||
mstreamModule.playlist = [];
|
||||
|
||||
|
||||
// The audioData looks like this
|
||||
// var song = {
|
||||
// "url":"vPath/path/to/song.mp3?token=xxx",
|
||||
// "filepath": "path/to/song.mp3"
|
||||
// }
|
||||
|
||||
|
||||
|
||||
mstreamModule.addSong = function(audioData){
|
||||
if(!audioData.url || audioData.url == false){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle shuffle
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
var pos = Math.floor(Math.random() * (shuffleCache.length + 1));
|
||||
shuffleCache.splice(pos, 0, song);
|
||||
}
|
||||
|
||||
return addSongToPlaylist(audioData);
|
||||
}
|
||||
|
||||
function addSongToPlaylist(song){
|
||||
mstreamModule.playlist.push(song);
|
||||
|
||||
// If this the first song in the list
|
||||
if(mstreamModule.playlist.length === 1){
|
||||
mstreamModule.positionCache.val = 0;
|
||||
return goToSong(mstreamModule.positionCache.val);
|
||||
}
|
||||
|
||||
// TODO: Check if we are at the end of the playlist and nothing is playing.
|
||||
// Start playing if this condition is met
|
||||
|
||||
// Cache song if appropriate
|
||||
var oPlayer = getOtherPlayer();
|
||||
if(oPlayer.playerObject === false && mstreamModule.playlist[mstreamModule.positionCache.val + 1]){
|
||||
// setCachedSong(mstreamModule.positionCache.val + 1);
|
||||
|
||||
clearTimeout(cacheTimer);
|
||||
cacheTimer = setTimeout(function(){ setCachedSong(mstreamModule.positionCache.val + 1) } , 3000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
mstreamModule.clearAndPlay = function(song){
|
||||
// Clear playlist
|
||||
mstreamModule.playlist = [];
|
||||
|
||||
return addSong(song);
|
||||
}
|
||||
|
||||
mstreamModule.clearPlaylist = function(){
|
||||
while(mstreamModule.playlist.length > 0) {mstreamModule.playlist.pop();}
|
||||
mstreamModule.positionCache.val = -1;
|
||||
|
||||
clearEnd();
|
||||
|
||||
// Clear shuffle as well
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
// Clear Shuffle Cache
|
||||
while(shuffleCache.length > 0) {shuffleCache.pop();}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mstreamModule.nextSong = function(){
|
||||
// Stop the current song
|
||||
return goToNextSong();
|
||||
}
|
||||
mstreamModule.previousSong = function(){
|
||||
return goToPreviousSong();
|
||||
}
|
||||
|
||||
|
||||
mstreamModule.goToSongAtPosition = function(position){
|
||||
if(!mstreamModule.playlist[position]){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
clearEnd();
|
||||
|
||||
mstreamModule.positionCache.val = position;
|
||||
return goToSong(mstreamModule.positionCache.val);
|
||||
}
|
||||
|
||||
// TODO: Log Failures
|
||||
mstreamModule.removeSongAtPosition = function(position, sanityCheckUrl){
|
||||
// Check that position is filled
|
||||
if (position > mstreamModule.playlist.length || position < 0) {
|
||||
return false;
|
||||
}
|
||||
// If sanityCheckUrl, check that url are the same
|
||||
if(sanityCheckUrl && sanityCheckUrl != mstreamModule.playlist[position].url){
|
||||
return false;
|
||||
}
|
||||
|
||||
var removedSong = mstreamModule.playlist[position];
|
||||
|
||||
// Remove song
|
||||
mstreamModule.playlist.splice(position, 1);
|
||||
|
||||
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
// Remove song from shuffle Cache
|
||||
for(var i=0, len=shuffleCache.length; i < len; i++){
|
||||
// Check if this is the current song
|
||||
if(removedSong === shuffleCache[i]){
|
||||
shuffleCache.splice(i, 1);
|
||||
}
|
||||
}
|
||||
for(var i=0, len=shufflePrevious.length; i < len; i++){
|
||||
// Check if this is the current song
|
||||
if(removedSong === shufflePrevious[i]){
|
||||
shufflePrevious.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handle case where user removes current song and it's the last song in the playlist
|
||||
if(position === mstreamModule.positionCache.val && position === mstreamModule.playlist.length ){
|
||||
clearEnd();
|
||||
// Go to random song if random is set
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
goToNextSong();
|
||||
}else if(mstreamModule.playerStats.shouldLoop === true){ // Loop is loop is set
|
||||
mstreamModule.positionCache.val = 0;
|
||||
goToSong(mstreamModule.positionCache.val);
|
||||
}else{ // Reset to start is nothing is set
|
||||
mstreamModule.positionCache.val = -1;
|
||||
}
|
||||
}else if(position === mstreamModule.positionCache.val){ // User removes currently playing song
|
||||
// Go to next song
|
||||
clearEnd();
|
||||
|
||||
// If random is set, go to random song
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
goToNextSong();
|
||||
}else{
|
||||
goToSong(mstreamModule.positionCache.val);
|
||||
}
|
||||
|
||||
}else if( position < mstreamModule.positionCache.val){
|
||||
// Lower positioncache by 1 if necessary
|
||||
mstreamModule.positionCache.val--;
|
||||
}else if( position === (mstreamModule.positionCache.val + 1) ){
|
||||
// setCachedSong(mstreamModule.positionCache.val + 1);
|
||||
clearTimeout(cacheTimer);
|
||||
cacheTimer = setTimeout(function(){ setCachedSong(mstreamModule.positionCache.val + 1) } , 3000);
|
||||
}
|
||||
}
|
||||
|
||||
mstreamModule.getCurrentSong = function(){
|
||||
var lPlayer = getCurrentPlayer();
|
||||
return lPlayer.songObject;
|
||||
}
|
||||
|
||||
function goToPreviousSong(){
|
||||
// TODO: If random is set, go to previous song from cache
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
// TODO: Check that there is a previous song to go back to
|
||||
if(shufflePrevious.length <= 1){
|
||||
return;
|
||||
}
|
||||
|
||||
// Pop a song and go to the last song
|
||||
var nextSong = shufflePrevious.pop();
|
||||
shuffleCache.push(nextSong);
|
||||
|
||||
var currentSong = shufflePrevious[shufflePrevious.length-1];
|
||||
|
||||
// Reset position cache
|
||||
for(var i=0, len=mstreamModule.playlist.length; i < len; i++){
|
||||
// Check if this is the current song
|
||||
if(currentSong === mstreamModule.playlist[i]){
|
||||
mstreamModule.positionCache.val = i;
|
||||
}
|
||||
}
|
||||
clearEnd();
|
||||
|
||||
goToSong(mstreamModule.positionCache.val );
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure there is a previous song
|
||||
if(mstreamModule.positionCache.val === 0 || mstreamModule.positionCache.val === -1){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set previous song and play
|
||||
clearEnd();
|
||||
mstreamModule.positionCache.val--;
|
||||
return goToSong(mstreamModule.positionCache.val);
|
||||
}
|
||||
|
||||
function goToNextSong(){
|
||||
// If random is set, go to random song
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
// Chose a random value
|
||||
var nextSong = shuffleCache.pop();
|
||||
|
||||
// Prevent same song from playing twice after a re-shuffle
|
||||
if(nextSong === mstreamModule.getCurrentSong()){
|
||||
console.log('DUPEEEEE');
|
||||
shuffleCache.unshift(nextSong);
|
||||
nextSong = shuffleCache.pop();
|
||||
}
|
||||
|
||||
if(shuffleCache.length === 0){
|
||||
newShuffle();
|
||||
}
|
||||
|
||||
|
||||
// Reset position cache
|
||||
for(var i=0, len=mstreamModule.playlist.length; i < len; i++){
|
||||
// Check if this is the current song
|
||||
if(nextSong === mstreamModule.playlist[i]){
|
||||
mstreamModule.positionCache.val = i;
|
||||
}
|
||||
}
|
||||
clearEnd();
|
||||
|
||||
goToSong(mstreamModule.positionCache.val );
|
||||
|
||||
// Remove duplicates from shuffle previous
|
||||
for(var i=0, len=shufflePrevious.length; i < len; i++){
|
||||
// Check if this is the current song
|
||||
if(nextSong === shufflePrevious[i]){
|
||||
shufflePrevious.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
shufflePrevious.push(nextSong);
|
||||
|
||||
// Load selected song
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the next song exists
|
||||
if(!mstreamModule.playlist[mstreamModule.positionCache.val + 1]){
|
||||
|
||||
// If loop is set and no other song, go back to first song
|
||||
if(mstreamModule.playerStats.shouldLoop === true && mstreamModule.playlist.length > 0){
|
||||
mstreamModule.positionCache.val = 0;
|
||||
clearEnd();
|
||||
|
||||
return goToSong(mstreamModule.positionCache.val);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Load up next song
|
||||
mstreamModule.positionCache.val++;
|
||||
clearEnd();
|
||||
return goToSong(mstreamModule.positionCache.val);
|
||||
}
|
||||
|
||||
|
||||
function getCurrentPlayer(){
|
||||
if(curP === 'A'){
|
||||
return playerA;
|
||||
}else if(curP === 'B'){
|
||||
return playerB;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getOtherPlayer(){
|
||||
if(curP === 'A'){
|
||||
return playerB;
|
||||
}else if(curP === 'B'){
|
||||
return playerA;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function flipFlop(){
|
||||
if(curP === 'A'){
|
||||
curP = 'B';
|
||||
}else if(curP === 'B'){
|
||||
curP = 'A';
|
||||
}
|
||||
|
||||
return curP;
|
||||
}
|
||||
|
||||
|
||||
function goToSong(position){
|
||||
if(!mstreamModule.playlist[position]){
|
||||
return false;
|
||||
}
|
||||
|
||||
var localPlayerObject = getCurrentPlayer();
|
||||
var otherPlayerObject = getOtherPlayer();
|
||||
|
||||
// Stop the current song
|
||||
if(localPlayerObject.playerType === 'aurora' ){
|
||||
localPlayerObject.playerObject.stop();
|
||||
}else if(localPlayerObject.playerType === 'howler'){
|
||||
localPlayerObject.playerObject.unload();
|
||||
}
|
||||
|
||||
// Reset Duration
|
||||
mstreamModule.playerStats.duration = 0;
|
||||
mstreamModule.playerStats.currentTime = 0;
|
||||
|
||||
// TODO: Handle situation where next song is same as current song
|
||||
|
||||
// Song is cached
|
||||
if(otherPlayerObject.songObject === mstreamModule.playlist[position]){
|
||||
// console.log('USING CACHED SONG');
|
||||
flipFlop();
|
||||
// Play
|
||||
mstreamModule.playPause();
|
||||
|
||||
}else{
|
||||
// console.log('DID NOT USE CACHE');
|
||||
setMedia(mstreamModule.playlist[position], localPlayerObject, true);
|
||||
}
|
||||
|
||||
var lPlayer = getCurrentPlayer();
|
||||
var curSong = lPlayer.songObject;
|
||||
// TODO: Handle instace where metadata is empty
|
||||
// mstreamModule.playerStats.metadata = curSong.metadata;
|
||||
if(curSong.metadata){
|
||||
mstreamModule.resetCurrentMetadata();
|
||||
}
|
||||
|
||||
|
||||
// TODO: This is a mess, figure out a better way
|
||||
var newOtherPlayerObject = getOtherPlayer();
|
||||
newOtherPlayerObject.playerType = false;
|
||||
newOtherPlayerObject.playerObject = false;
|
||||
newOtherPlayerObject.songObject= false;
|
||||
|
||||
// Cache next song
|
||||
// The timer prevents excessive cachign when the user starts button mashing
|
||||
// setCachedSong(position + 1);
|
||||
clearTimeout(cacheTimer);
|
||||
cacheTimer = setTimeout(function(){ setCachedSong(position + 1) } , 3000);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
mstreamModule.resetCurrentMetadata = function() {
|
||||
var lPlayer = getCurrentPlayer();
|
||||
var curSong = lPlayer.songObject;
|
||||
// TODO: Handle instace where metadata is empty
|
||||
// mstreamModule.playerStats.metadata = curSong.metadata;
|
||||
if(curSong.metadata){
|
||||
mstreamModule.playerStats.metadata.artist = curSong.metadata.artist;
|
||||
mstreamModule.playerStats.metadata.album = curSong.metadata.album;
|
||||
mstreamModule.playerStats.metadata.track = curSong.metadata.track;
|
||||
mstreamModule.playerStats.metadata.title = curSong.metadata.title;
|
||||
mstreamModule.playerStats.metadata.year = curSong.metadata.year;
|
||||
mstreamModule.playerStats.metadata['album-art'] = curSong.metadata['album-art'];
|
||||
}
|
||||
|
||||
console.log( mstreamModule.playerStats.metadata)
|
||||
}
|
||||
|
||||
|
||||
mstreamModule.resetPositionCache = function(){
|
||||
var len;
|
||||
|
||||
var lPlayer = getCurrentPlayer();
|
||||
var curSong = lPlayer.songObject;
|
||||
|
||||
for(var i=0, len=mstreamModule.playlist.length; i < len; i++){
|
||||
// Check if this is the current song
|
||||
if(curSong === mstreamModule.playlist[i]){
|
||||
mstreamModule.positionCache.val = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No song found, reset
|
||||
mstreamModule.positionCache.val = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========================= Aurora Player ===============
|
||||
// Shell for interacting with Aurora
|
||||
function AVPlayerPlay(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
|
||||
if(localPlayer.playerObject.playing){
|
||||
return;
|
||||
}
|
||||
localPlayer.playerObject.play();
|
||||
mstreamModule.playerStats.playing = true;
|
||||
|
||||
}
|
||||
function AVPlayerPause(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
|
||||
localPlayer.playerObject.pause();
|
||||
mstreamModule.playerStats.playing = false;
|
||||
|
||||
}
|
||||
function AVPlayerPlayPause(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
|
||||
// TODO: Check that media is loaded
|
||||
if(localPlayer.playerObject.playing){
|
||||
localPlayer.playerObject.pause();
|
||||
mstreamModule.playerStats.playing = false;
|
||||
}else{
|
||||
localPlayer.playerObject.play();
|
||||
mstreamModule.playerStats.playing = true;
|
||||
}
|
||||
}
|
||||
// ========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ========================= Howler Player ===============
|
||||
function howlPlayerPlay(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
mstreamModule.playerStats.playing = true;
|
||||
|
||||
localPlayer.playerObject.play();
|
||||
}
|
||||
function howlPlayerPause(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
mstreamModule.playerStats.playing = false;
|
||||
|
||||
localPlayer.playerObject.pause();
|
||||
}
|
||||
function howlPlayerPlayPause(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
|
||||
// TODO: Check that media is loaded
|
||||
if(localPlayer.playerObject.playing()){
|
||||
mstreamModule.playerStats.playing = false;
|
||||
localPlayer.playerObject.pause();
|
||||
}else{
|
||||
localPlayer.playerObject.play();
|
||||
mstreamModule.playerStats.playing = true;
|
||||
|
||||
}
|
||||
}
|
||||
// ========================================================
|
||||
|
||||
|
||||
|
||||
// ========================= Youtube Player ===============
|
||||
var YTPlayer;
|
||||
// TODO:
|
||||
// ========================================================
|
||||
|
||||
|
||||
function clearEnd(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
|
||||
if(localPlayer.playerType === 'aurora' ){
|
||||
localPlayer.playerObject.on("end", function() {
|
||||
return
|
||||
}, false);
|
||||
}else if(localPlayer.playerType === 'howler'){
|
||||
localPlayer.playerObject.off('end');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Player
|
||||
// Event: On Song end
|
||||
// Set Media
|
||||
// Play, pause, skip, etc
|
||||
mstreamModule.playPause = function(){
|
||||
var localPlayer = getCurrentPlayer();
|
||||
|
||||
if(localPlayer.playerType === 'aurora' ){
|
||||
return AVPlayerPlayPause();
|
||||
}else if(localPlayer.playerType === 'howler'){
|
||||
return howlPlayerPlayPause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mstreamModule.playerStats = {
|
||||
duration:0,
|
||||
currentTime:0,
|
||||
playing: false,
|
||||
repeat: false,
|
||||
shuffle:false,
|
||||
metadata: {
|
||||
"artist": false,
|
||||
"album": false,
|
||||
"track": false,
|
||||
"title": false,
|
||||
"year": false,
|
||||
"album-art": false,
|
||||
"filepath": false,
|
||||
}
|
||||
}
|
||||
|
||||
var playerA = {
|
||||
playerType: false,
|
||||
playerObject: false,
|
||||
songObject: false
|
||||
}
|
||||
var playerB = {
|
||||
playerType: false,
|
||||
playerObject: false,
|
||||
songObject: false
|
||||
}
|
||||
|
||||
var curP = 'A';
|
||||
|
||||
function setMedia(song, player, play){
|
||||
|
||||
|
||||
if(song.url.indexOf('.flac') !== -1 && Howler.codecs('flac') === false ){
|
||||
// Set via aurora
|
||||
player.playerType = 'aurora';
|
||||
|
||||
|
||||
player.playerObject = AV.Player.fromURL(song.url);
|
||||
player.playerObject.on("end", function() {
|
||||
callMeOnStreamEnd();
|
||||
}, false);
|
||||
// Handle error event
|
||||
player.playerObject.on("error", function(e) {
|
||||
// TODO: GO TO NEXT SONG
|
||||
}, false);
|
||||
player.playerObject.on("metadata", function() {
|
||||
// Move this to metadata ???
|
||||
if(play == true){
|
||||
AVPlayerPlay();
|
||||
}
|
||||
}, false);
|
||||
|
||||
player.playerObject.preload();
|
||||
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
player.playerType = 'howler';
|
||||
|
||||
player.playerObject = new Howl({
|
||||
src: [song.url],
|
||||
html5: true, // Force to HTML5. Otherwise streaming will suck
|
||||
// onplay: function() { },
|
||||
onload: function() {
|
||||
|
||||
},
|
||||
onend: function() {
|
||||
callMeOnStreamEnd();
|
||||
},
|
||||
onpause: function() {
|
||||
},
|
||||
onstop: function() {
|
||||
},
|
||||
onplay: function(){
|
||||
}
|
||||
});
|
||||
|
||||
if(play == true){
|
||||
howlPlayerPlay();
|
||||
}
|
||||
}
|
||||
|
||||
player.songObject = song;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function callMeOnStreamEnd(){
|
||||
mstreamModule.playerStats.playing= false;
|
||||
|
||||
// Go to next song
|
||||
goToNextSong();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// NOTE: Seektime is in seconds
|
||||
mstreamModule.seek = function(seekTime){
|
||||
var lPlayer = getCurrentPlayer();
|
||||
if(lPlayer.playerType === 'aurora' ){
|
||||
// Do nothing, auroradoesn't support seeking right now
|
||||
return false;
|
||||
}else if(lPlayer.playerType === 'howler'){
|
||||
// Check that the seek number is less than the duration
|
||||
if(seekTime < 0 || seekTime > lPlayer.playerObject._duration){
|
||||
return false;
|
||||
}
|
||||
|
||||
lPlayer.playerObject.seek(seektime)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mstreamModule.seekByPercentage = function(percentage){
|
||||
if(percentage < 0 || percentage > 99){
|
||||
return false;
|
||||
}
|
||||
var lPlayer = getCurrentPlayer();
|
||||
|
||||
if(lPlayer.playerType === 'aurora' ){
|
||||
// Do nothing, auroradoesn't support seeking
|
||||
return false;
|
||||
}else if(lPlayer.playerType === 'howler'){
|
||||
var seektime = (percentage * lPlayer.playerObject._duration)/ 100;
|
||||
lPlayer.playerObject.seek(seektime)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var timers = {};
|
||||
|
||||
function startTime(interval) {
|
||||
if (timers.sliderUpdateInterval) { clearInterval(timers.sliderUpdateInterval); }
|
||||
|
||||
|
||||
timers.sliderUpdateInterval = setInterval( function(){
|
||||
var lPlayer = getCurrentPlayer();
|
||||
|
||||
if(lPlayer.playerType === 'aurora' ){
|
||||
mstreamModule.playerStats.duration = lPlayer.playerObject.duration / 1000;
|
||||
mstreamModule.playerStats.currentTime = lPlayer.playerObject.currentTime / 1000;
|
||||
}else if(lPlayer.playerType === 'howler'){
|
||||
mstreamModule.playerStats.currentTime = lPlayer.playerObject.seek();
|
||||
mstreamModule.playerStats.duration = lPlayer.playerObject._duration;
|
||||
|
||||
}else{
|
||||
// NO PLAYER, set default values
|
||||
mstreamModule.playerStats.currentTime = 0;
|
||||
mstreamModule.playerStats.duration = 0;
|
||||
}
|
||||
|
||||
}, interval);
|
||||
}
|
||||
startTime(100);
|
||||
|
||||
function clearTimer(){
|
||||
clearInterval(timers.sliderUpdateInterval);
|
||||
}
|
||||
|
||||
|
||||
// Timer for caching. Helps prevent excess cahing due to button mashing
|
||||
var cacheTimer;
|
||||
function setCachedSong(position){
|
||||
|
||||
console.log(' ATTEMPTING TO CACHE');
|
||||
if(!mstreamModule.playlist[position]){
|
||||
console.log(' FAILED TO CACHE');
|
||||
return false;
|
||||
}
|
||||
|
||||
var oPlayer = getOtherPlayer();
|
||||
setMedia(mstreamModule.playlist[position], oPlayer, false);
|
||||
console.log(' IT CACHED!!!!!!');
|
||||
console.log(mstreamModule.playlist[position]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Loop
|
||||
mstreamModule.playerStats.shouldLoop = false;
|
||||
mstreamModule.setRepeat = function(newValue){
|
||||
if(typeof(newValue) != "boolean"){
|
||||
return false;
|
||||
}
|
||||
mstreamModule.playerStats.shouldLoop = newValue;
|
||||
return newValue;
|
||||
}
|
||||
mstreamModule.toggleRepeat = function(){
|
||||
mstreamModule.playerStats.shouldLoop = !mstreamModule.playerStats.shouldLoop;
|
||||
return mstreamModule.playerStats.shouldLoop;
|
||||
}
|
||||
|
||||
// Random Song
|
||||
mstreamModule.playerStats.shuffle = false;
|
||||
shuffleCache = []; // Cache the last 5 songs played to avoid repeats
|
||||
shufflePrevious = [];
|
||||
mstreamModule.setShuffle = function(newValue){
|
||||
if(typeof(newValue) != "boolean"){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(newValue===true){
|
||||
newShuffle();
|
||||
}else{
|
||||
turnShuffleOff();
|
||||
}
|
||||
|
||||
mstreamModule.playerStats.shuffle = newValue;
|
||||
return true;
|
||||
}
|
||||
mstreamModule.toggleShuffle = function(){
|
||||
mstreamModule.playerStats.shuffle = !mstreamModule.playerStats.shuffle;
|
||||
if(mstreamModule.playerStats.shuffle === true){
|
||||
newShuffle();
|
||||
}else{
|
||||
turnShuffleOff();
|
||||
}
|
||||
return mstreamModule.playerStats.shuffle;
|
||||
}
|
||||
|
||||
function newShuffle(){
|
||||
shuffleCache = shuffle(mstreamModule.playlist.slice(0));
|
||||
}
|
||||
|
||||
function turnShuffleOff(){
|
||||
shufflePrevious = [];
|
||||
shuffleCache = [];
|
||||
}
|
||||
|
||||
function shuffle(array) {
|
||||
var currentIndex = array.length
|
||||
, temporaryValue
|
||||
, randomIndex
|
||||
;
|
||||
|
||||
// While there remain elements to shuffle...
|
||||
while (0 !== currentIndex) {
|
||||
|
||||
// Pick a remaining element...
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex -= 1;
|
||||
|
||||
// And swap it with the current element.
|
||||
temporaryValue = array[currentIndex];
|
||||
array[currentIndex] = array[randomIndex];
|
||||
array[randomIndex] = temporaryValue;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
// Return an object that is assigned to Module
|
||||
return mstreamModule;
|
||||
}());
|
||||
@ -1,86 +0,0 @@
|
||||
var VUEBROWSER = function() {
|
||||
|
||||
// Auto Focus
|
||||
Vue.directive('focus', {
|
||||
// When the bound element is inserted into the DOM...
|
||||
inserted: function (el) {
|
||||
// Focus the element
|
||||
el.focus()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var loginPanel = new Vue({
|
||||
el: '#login-overlay',
|
||||
data: {
|
||||
needToLogin: false,
|
||||
error: false,
|
||||
errorMessage: 'Login Failed',
|
||||
pending: false
|
||||
},
|
||||
methods: {
|
||||
submitCode: function(e){
|
||||
// Get Code
|
||||
this.pending = true;
|
||||
var that = this;
|
||||
MSTREAMAPI.login($('#login-username').val(), $('#login-password').val(), function(response, error){
|
||||
if(error !== false){
|
||||
// Alert the user
|
||||
that.pending = false;
|
||||
that.error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Eye-candy: change the error message
|
||||
that.errorMessage = "Welcome To mStream!";
|
||||
|
||||
// Add the token to the cookies
|
||||
Cookies.set('token', response.token);
|
||||
|
||||
// Add the token the URL calls
|
||||
MSTREAMAPI.updateCurrentServer($('#login-username').val(), response.token, response.vPath)
|
||||
|
||||
// TODO: Add function to load up either the file browser or artist panel
|
||||
|
||||
// Remove the overlay
|
||||
$('.login-overlay').fadeOut( "slow" ); // TDO: Figure out how to use Vue to fade the modal in and out
|
||||
that.pending = false;
|
||||
that.needToLogin = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function testIt(token){
|
||||
if(token){
|
||||
MSTREAMAPI.currentServer.token = token;
|
||||
}
|
||||
|
||||
MSTREAMAPI.ping( function(response, error){
|
||||
if(error !== false){
|
||||
// NOTE: There needs to be a split here
|
||||
// For the webapp we simply display the login panel
|
||||
loginPanel.needToLogin = true;
|
||||
// TODO: Move this transitionstuff to vue
|
||||
$('.login-overlay').fadeIn( "slow" );
|
||||
// For electron we need to alert the user that user it failed and guide them to the login form
|
||||
|
||||
return;
|
||||
}
|
||||
// set vPath
|
||||
MSTREAMAPI.currentServer.vPath = response.vPath;
|
||||
|
||||
// TODO: Add function to load up either the file browser or artist panel
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: There needs to be a split here
|
||||
// For the normal webap we just get the token
|
||||
// var token = Cookies.get('token');
|
||||
testIt(Cookies.get('token'));
|
||||
// For electron we need to pull it from wherever electron stores things
|
||||
|
||||
|
||||
};
|
||||
@ -1,235 +0,0 @@
|
||||
var VUEPLAYER = function() {
|
||||
|
||||
// Template for playlist items
|
||||
Vue.component('playlist-item', {
|
||||
template: '\
|
||||
<div class="playlist-item" v-bind:class="{ playing: (this.index == positionCache.val) }" >\
|
||||
<span v-on:click="goToSong($event)" class="song-area">{{ comtext }}</span> <span v-on:click="removeSong($event)" class="removeSong">X</span>\
|
||||
</div>\
|
||||
',
|
||||
|
||||
props: [ 'index', 'song'],
|
||||
|
||||
// We need the positionCache to track the currently playing song
|
||||
data: function(){
|
||||
return {
|
||||
positionCache: MSTREAMPLAYER.positionCache,
|
||||
}
|
||||
},
|
||||
|
||||
// Methods used by playlist item events
|
||||
methods: {
|
||||
// Go to a song on item click
|
||||
goToSong: function(event){
|
||||
MSTREAMPLAYER.goToSongAtPosition(this.index);
|
||||
},
|
||||
// Remove song
|
||||
removeSong: function(event){
|
||||
MSTREAMPLAYER.removeSongAtPosition(this.index, false);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
comtext: function() {
|
||||
var returnThis = this.song.filepath;
|
||||
|
||||
if(this.song.metadata.title){
|
||||
returnThis = this.song.metadata.title;
|
||||
if(this.song.metadata.artist){
|
||||
returnThis = this.song.metadata.artist + ' - ' + returnThis;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return returnThis;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Code to update playlist
|
||||
var playlistElement = new Vue({
|
||||
el: '#playlist',
|
||||
data: {
|
||||
playlist: MSTREAMPLAYER.playlist,
|
||||
},
|
||||
methods: {
|
||||
// checkMove is called when a drag-and-drop action happens
|
||||
checkMove: function (event) {
|
||||
MSTREAMPLAYER.resetPositionCache();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var progressBar = new Vue({
|
||||
el: '#mstream-player',
|
||||
data: {
|
||||
playerStats: MSTREAMPLAYER.playerStats,
|
||||
playlist: MSTREAMPLAYER.playlist,
|
||||
positionCache: MSTREAMPLAYER.positionCache,
|
||||
met: MSTREAMPLAYER.playerStats.metadata
|
||||
},
|
||||
computed: {
|
||||
imgsrc: function () {
|
||||
return "/public/img/"+(this.playerStats.playing ? 'pause' : 'play')+"-white.svg";
|
||||
},
|
||||
widthcss: function ( ) {
|
||||
if(this.playerStats.duration === 0){
|
||||
return "width:0";
|
||||
}
|
||||
|
||||
var percentage = 100 - (( this.playerStats.currentTime / this.playerStats.duration) * 100);
|
||||
return "width:calc(100% - "+percentage+"%)";
|
||||
},
|
||||
|
||||
showTime: function(){
|
||||
if (this.playerStats.duration === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var curr = this.playerStats.duration - this.playerStats.currentTime;
|
||||
var minutes = Math.floor(curr / 60);
|
||||
var secondsToCalc = Math.floor(curr % 60) + '';
|
||||
var currentText = minutes + ':' + (secondsToCalc.length < 2 ? '0' + secondsToCalc : secondsToCalc);
|
||||
|
||||
return currentText;
|
||||
},
|
||||
|
||||
currentSongText: function(){
|
||||
// TODO: Handle metadata
|
||||
|
||||
// Call these vars so updates cahnge whenever they do
|
||||
var posit = this.positionCache.val;
|
||||
var plist = this.playlist;
|
||||
var playerStats = this.playerStats;
|
||||
var titleX = this.met.title;
|
||||
var metx = this.met;
|
||||
|
||||
|
||||
|
||||
var currentSong = MSTREAMPLAYER.getCurrentSong();
|
||||
|
||||
if(currentSong === false){
|
||||
return '\u00A0\u00A0\u00A0Welcome To mStream!\u00A0\u00A0\u00A0';
|
||||
}
|
||||
|
||||
// Get current song straight from the source
|
||||
var returnText = '';
|
||||
if(playerStats.metadata && titleX){
|
||||
returnText = titleX;
|
||||
if(playerStats.metadata.artist){
|
||||
returnText = playerStats.metadata.artist + ' - ' + returnText;
|
||||
}
|
||||
}else{
|
||||
// Use filepath instead
|
||||
var filepathArray = currentSong.filepath.split("/");
|
||||
returnText = filepathArray[filepathArray.length-1]
|
||||
}
|
||||
|
||||
console.log(MSTREAMPLAYER.playerStats.metadata);
|
||||
|
||||
|
||||
return '\u00A0\u00A0\u00A0' + returnText + '\u00A0\u00A0\u00A0';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleRepeat: function(){
|
||||
MSTREAMPLAYER.toggleRepeat();
|
||||
},
|
||||
toggleShuffle: function(){
|
||||
MSTREAMPLAYER.toggleShuffle();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var metadataPanel = new Vue({
|
||||
el: '#metadata-panel',
|
||||
data: {
|
||||
meta: MSTREAMPLAYER.playerStats.metadata
|
||||
},
|
||||
computed: {
|
||||
albumArtPath: function(){
|
||||
if(!this.meta['album-art']){
|
||||
return '/public/img/default.png';
|
||||
}
|
||||
return '/album-art/' + this.meta['album-art'];
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Button Events
|
||||
document.getElementById( "progress-bar" ).addEventListener("click",function(event) {
|
||||
var relativeClickPosition = event.clientX - this.getBoundingClientRect().left;
|
||||
var totalWidth = this.getBoundingClientRect().width;
|
||||
var percentage = (relativeClickPosition / totalWidth) * 100;
|
||||
// Set Player time
|
||||
MSTREAMPLAYER.seekByPercentage(percentage);
|
||||
});
|
||||
|
||||
// Button Events
|
||||
document.getElementById( "next-button" ).addEventListener("click",function() {
|
||||
MSTREAMPLAYER.nextSong();
|
||||
});
|
||||
document.getElementById( "play-pause-button" ).addEventListener("click", function() {
|
||||
MSTREAMPLAYER.playPause();
|
||||
});
|
||||
document.getElementById("previous-button").addEventListener("click", function(){
|
||||
MSTREAMPLAYER.previousSong();
|
||||
});
|
||||
|
||||
// This makes the title text scroll back and forth
|
||||
var scrollTimer;
|
||||
var scrollRight = true; //Track Scroll Direction
|
||||
function startTime(interval) {
|
||||
if (scrollTimer) { clearInterval(scrollTimer); }
|
||||
|
||||
scrollTimer = setInterval( function(){
|
||||
// Get the max scroll distance
|
||||
var maxScrollLeft = document.getElementById('title-text').scrollWidth - document.getElementById('title-text').clientWidth;
|
||||
|
||||
// Change the scroll direction if necessary
|
||||
// TODO: Pause for a second when these conditions are hit
|
||||
if(document.getElementById('title-text').scrollLeft > (maxScrollLeft - 1)){
|
||||
scrollRight = false;
|
||||
}
|
||||
if(document.getElementById('title-text').scrollLeft === 0){
|
||||
scrollRight = true;
|
||||
}
|
||||
|
||||
// Do the scroll
|
||||
if(scrollRight === true){
|
||||
document.getElementById('title-text').scrollLeft = document.getElementById('title-text').scrollLeft + 2;
|
||||
}else{
|
||||
document.getElementById('title-text').scrollLeft = document.getElementById('title-text').scrollLeft - 2;
|
||||
}
|
||||
}, interval);
|
||||
}
|
||||
startTime(50);
|
||||
|
||||
|
||||
|
||||
// Change spacebar behviour to Play/PauseListen to every key press user makes
|
||||
// Useful for adding media functionality to certain keys
|
||||
window.addEventListener("keydown", function(event){
|
||||
// Use default behavior if user is in a form
|
||||
var element = event.target.tagName.toLowerCase();
|
||||
if(element == 'input' || element == 'textarea'){
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the key
|
||||
switch (event.keyCode) {
|
||||
case 32: //SpaceBar
|
||||
event.preventDefault();
|
||||
MSTREAMPLAYER.playPause();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}, false);
|
||||
};
|
||||
@ -1,127 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="no-js" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<meta content="yes" name="mobile-web-app-capable">
|
||||
<meta content="yes" name="apple-mobile-web-app-capable">
|
||||
<meta content="black" name="apple-mobile-web-app-status-bar-style">
|
||||
|
||||
<title>mStream Media Player - All your media. Everywhere you go.</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/public/favicon/apple-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/public/favicon/apple-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/public/favicon/apple-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/public/favicon/apple-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/public/favicon/apple-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/public/favicon/apple-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/public/favicon/apple-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/public/favicon/apple-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/public/favicon/apple-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/public/favicon/android-icon-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/public/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/public/favicon/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/public/favicon/favicon-16x16.png">
|
||||
<link rel="manifest" href="/public/favicon/manifest.json">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-TileImage" content="/public/favicon/ms-icon-144x144.png">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
|
||||
|
||||
<!-- GOAL: Remove jquery dependancy
|
||||
Currently mstream.js and mstream.api.js use it -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
|
||||
<!-- Cookie Library -->
|
||||
<script type="text/javascript" src="/public/js/lib/cookie.js"></script>
|
||||
<!-- Vue JS -->
|
||||
<script src="https://unpkg.com/vue/dist/vue.js"></script>
|
||||
<!-- Sortable JS
|
||||
Used to add drag and drop re-arranging to queue -->
|
||||
<script src="https://unpkg.com/sortablejs@latest"></script>
|
||||
<!-- https://github.com/SortableJS/Vue.Draggable - v2.6 -->
|
||||
<script src="/public/js/lib/vue-sortable.js"></script>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
This is the mStream Player and Queue code
|
||||
It must be loaded before all aother mstream libraries because it's a dependancy of those libraries
|
||||
DO NOT Change to order these are loaded in
|
||||
-->
|
||||
<script src="/public/js/lib/aurora.js"></script>
|
||||
<script src="/public/js/lib/flac.js"></script>
|
||||
<script src="/public/js/lib/howler.core.js"></script>
|
||||
<script src="/public/js/mstream.player.js"></script>
|
||||
<!-- END PLAYE AND QUEUE CODE -->
|
||||
|
||||
|
||||
<!-- This API library must be loaded after the player
|
||||
This library provides a convenient way to call the server's API -->
|
||||
<script src="/public/js/mstream.api.js"></script>
|
||||
<!-- Jukebox Control Code -- >
|
||||
<script src="/public/js/mstream.jukebox.js"></script>
|
||||
|
||||
|
||||
<!-- This file is a bunch of jquery that handles the browser -->
|
||||
<!-- It needs to be replaced with vue code.
|
||||
I'm keeping it here for a reference. A lot of code for parsing AJAX responses can be recycled -->
|
||||
<!-- <script type="text/javascript" src="/public/js/mstream.js"></script> -->
|
||||
|
||||
<!-- This is the file that will replace mstream.js -->
|
||||
<script type="text/javascript" src="/public/js/mstream.vue-browser.js"></script>
|
||||
|
||||
|
||||
<!-- Vue Player and Queue controls
|
||||
NOTE: This file is what wires up the MSTREAMPLAYER object to the html
|
||||
Uncomment it once the html has been contructed -->
|
||||
<!-- <script src="/public/js/mstream.vue-player-controls.js"></script> -->
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,400i,800" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/public/css/style.css">
|
||||
<script>
|
||||
// Run Vue code once the page has been loaded
|
||||
$(document).ready(function(){
|
||||
// // invoke vueplayer
|
||||
// Uncomment once player html is constructed
|
||||
// VUEPLAYER();
|
||||
|
||||
VUEBROWSER();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<!-- Login Overlay -->
|
||||
<div id="login-overlay" class="login-overlay hide">
|
||||
|
||||
|
||||
<div class="login-panel">
|
||||
<img class="login-icon" src="/public/img/mstream-icon.svg">
|
||||
<form id="login-form" v-on:submit.prevent="submitCode($event)">
|
||||
<label class="label--magic">
|
||||
<input v-focus type="text" required id="login-username">
|
||||
<span>Username</span>
|
||||
</label>
|
||||
<label class="label--magic">
|
||||
<input required type="password" required id="login-password">
|
||||
<span>Password</span>
|
||||
</label>
|
||||
<button id="login-submit" type="submit" :disabled="pending === true ? true : false">Login</button>
|
||||
</form>
|
||||
|
||||
<div v-show="error" id="login-alert" class="">{{errorMessage}}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
The rest of the webapp goes here
|
||||
</div>
|
||||
</body>
|
||||