Jukebox Incoming

This commit is contained in:
IrosTheBeggar 2017-02-01 14:07:14 -05:00
parent c5658f5666
commit 8b320240fc
8 changed files with 590 additions and 183 deletions

View File

@ -1,18 +1,36 @@
// Websocket Server
const WebSocketServer = require('ws').Server;
const fe = require('path');
// list of currently connected clients (users)
var clients = { };
// TODO: Any code in here will be limitted in functionality
// Any code in here will be limitted in functionality
var guests = { };
const allowedCommands = [
'next',
'previous',
'playPause',
'addSong',
'getPlaylist',
'removeSong',
];
const guestCommands = [
'addSong',
'getPlaylist'
];
exports.setup = function(mstream, server, program){
const wss = new WebSocketServer({ server: server });
// This callback function is called every time someone
// tries to connect to the WebSocket server
// TODO: Add authentication step with jwt if necessary
// TODO: https://gist.github.com/jfromaniello/8418116
wss.on('connection', function(connection) {
// accept connection - you should check 'request.origin' to make sure that
@ -32,7 +50,6 @@ exports.setup = function(mstream, server, program){
}
// Add code to clients object
clients[code] = connection;
// Connect guest code to standard code
@ -42,16 +59,15 @@ exports.setup = function(mstream, server, program){
// Send Code
connection.send(JSON.stringify( { code: code, guestCode: guestcode} ));
// user sent some message
connection.on('message', function(message) {
// Send client code back
connection.send(JSON.stringify( { code: code, guestCode: guestcode} ));
});
// user disconnected
connection.on('close', function(connection) {
// Remove client from array
delete guests[guestcode];
delete clients[code];
@ -84,16 +100,16 @@ exports.setup = function(mstream, server, program){
// TODO: Get Album Art calls
mstream.post( '/push-to-client', function(req, res){
// Send codes to client
mstream.post( '/jukebox/push-to-client', function(req, res){
// Get client id
const json = JSON.parse(req.body.json);
// Check if client ID exists
var clientCode = json.code;
const command = json.command;
//
// Check that code exists
if(!(clientCode in clients) && !(clientCode in guests)){
res.status(500).json({ error: 'Client code not found' });
return;
@ -116,4 +132,24 @@ exports.setup = function(mstream, server, program){
res.json({ status: 'done' });
});
mstream.get('/jukebox/does-code-exist', function(req, res){
// Get client id
const json = JSON.parse(req.body.json);
const clientCode = json.code;
var status;
// Check that code exists
if(!(clientCode in clients) && !(clientCode in guests)){
re.json({ status: false });
return;
}
var guestStatus = (clientCode in guests);
res.json({ status: true, guestStatus: guestStatus });
});
}

View File

@ -336,7 +336,7 @@ mstream.post('/dirparser', function (req, res) {
var extension = getFileType(files[i]);
if (fileTypesArray.indexOf(extension) > -1 && masterFileTypesArray.indexOf(extension) > -1) {
filesArray.push({
type:extension,
type:extension, // TODO: Should this be changed
name:files[i]
});
}
@ -430,7 +430,9 @@ mstream.post( '/get-album-art', function(req, res){
// JukeBox
const jukebox = require('./modules/jukebox.js');
jukebox.setup(mstream, server, program);
mstream.all('/remote', function (req, res) {
res.sendFile( fe.join('public', 'remote.html'), { root: __dirname });
});
////////////////////////////////////////////////////////////////////////////

81
public/css/remote.css Normal file
View File

@ -0,0 +1,81 @@
body, html {
height: 100%; }
body{
background: url(../img/struckaxiom.png) top left repeat;
}
.header{
margin: 0;
padding: 0;
background-color: #333;
height:25px;
}
.mstream-image{
height:100%;
background-color: white;
border-radius: 0 0 3px 3px;
box-shadow: 0 0 5px #8D8D8D;
}
.logo-box{
overflow-y: visible;
width: 1px;
height: 140%;
margin-left: 35px;
z-index: 99;
}
.browser-header{
height: 48px;
background-color: white;
font-family: "Open Sans", "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
background-color: #F5F5F5;
border-bottom: 1px solid #b4b4b4;
}
.browser-header-text{
font-size: 20px;
font-weight: 500;
padding-top:16px;
padding-left: 10px;
}
.browser-container{
height:calc(100% - 80px);
background-color: rgba(250,250,250, .50);
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
}
#browser{
list-style: none;
padding: 0;
overflow-y: scroll;
height: calc(100% - 40px);
}
.browser-tools{
height:0px;
}
.browser-item{
height:auto;
border-bottom:solid 1px #b4b4b4;
cursor: pointer;
width: 100%;
background: white;
color: #252525;
font-size: 10pt;
text-shadow: 0 1px white;
font-weight: 300;
overflow: hidden;
padding: 0 !important;
}
.browser-item:hover{
background-color: #F5F5F5;
}

View File

@ -74,147 +74,3 @@ body{
.playlist-item:hover{
background-color: #F5F5F5;
}
.mstream-player{
position:absolute;
bottom:0;
width:100%;
height: 50px;
overflow: hidden;
box-shadow: 0px -2px 3px rgba(50, 50, 50, 0.75);
}
.previous-button{
position:relative;
height: 100%;
width:50px;
background-color: #333333;
float:left;
overflow:hidden;
box-shadow: 5px 0 8px -2px rgba(31, 73, 125, 0.8), -5px 0 8px -2px rgba(31, 73, 125, 0.8);
cursor:pointer;
}
.play-pause-button{
height: 100%;
width:50px;
background-color: rgb(102, 132, 178);
float:left;
overflow:hidden;
cursor:pointer;
}
.next-border{
height: 100%;
padding: 8px;
}
.previous-border{
height: 100%;
padding: 8px;
}
.play-pause-border{
border-left: 5px solid rgb(102, 132, 178);
border-top: 5px solid rgb(102, 132, 178);
height: 100%;
padding: 3px;
}
.next-button{
height: 100%;
width:50px;
background-color: #333333;
float:left;
position: relative;
overflow:hidden;
box-shadow: 5px 0 8px -2px rgba(31, 73, 125, 0.8), -5px 0 8px -2px rgba(31, 73, 125, 0.8);
cursor:pointer;
}
.progress-bar{
height: 100%;
width: calc(100% - 152px);
background-color: rgba(51,51,51,.15);
float:left;
overflow-x: hidden;
border-right: 2px solid rgba(51,51,51,.15);
cursor: crosshair;
}
.removeSong{
width: 29px !important;
height: 33px;
background-color: rgba(255,0,0, .7);
float: right;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 3px 10px 0 rgba(0, 0, 0, 0.19);
text-align: center;
font-size: 28px;
text-shadow: 0 1px darkred;
padding-bottom: 5px;
font-family: "Arial Black", Gadget, sans-serif;
opacity: 0.9;
}
.removeSong:hover{
opacity: 1;
background-color: rgba(255,0,0, .85);
}
.song-area{
display: block;
width: calc(100% - 49px);
float: left;
padding-top: 12px;
padding-bottom: 10px;
padding-left: 12px;
}
.titlebar{
height: 50%;
width:calc(100% - 190px);
background-color: rgba(255, 255, 255,0.7);
position: absolute;
margin-top: 10px;
border-radius: 5px;
box-shadow: 3px 3px 3px #888888;
margin-left: 20px;
overflow-y: hidden;
white-space: nowrap; /* This is the secret to make text scroll left-to-right*/
}
.pbar{
background-color: rgb(102, 132, 178);
height: 100%;
}
.playing{
background-color: #E6EBFA !important;
}
.title-text{
width: calc(100% - 100px);
float:left;
font-family: '8BITWONDERNominal';
font-weight: normal;
font-style: normal;
margin-top: 3px;
margin-left: 2px;
padding-bottom: 17px; /* This pushes the scr */
overflow-x: scroll;
overflow-y: hidden;
height: 100%;
}
.duration-text{
float: right;
width: 90px;
font-family: '8BITWONDERNominal';
font-weight: normal;
font-style: normal;
margin-top: 3px;
text-align: right;
margin-right: 5px;
}

View File

@ -1,8 +1,11 @@
var MSTREAMAPI = (function () {
let mstreamModule = {};
mstreamModule.listOfServers = [];
// TODO: Server Configs
mstreamModule.listOfServers = [];
mstreamModule.currentServer = {
host:"",
username:"",
@ -13,49 +16,75 @@ var MSTREAMAPI = (function () {
mstreamModule.currentProperties = {
currentList: false
// Can be anything in the title array
}
var currentListTypes = [
'filebrowser',
'albums',
'artists',
'search',
'playlists'
];
var dataList = [];
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;
while(mstreamModule.dataList.length > 0){
mstreamModule.dataList.pop();
}
}
mstreamModule.manuallyClearData = function(){
clearAndSetDataList(false);
}
var fileExplorerArray = {
// This goes by the following pattern
// path-segemnt: scroll pos
// music: 70
// folder: 20
// ACDC: 0
// Greatest Hits: 0
};
function getDirectoryContents(filepath){
// TODO: TURN THIS INTO MAP
var fileExplorerArray = [
{name:'/', position:0}
];
function getDirectoryContents(){
// Construct the directory string
var directoryString = "";
for (var i = 0; i < fileExplorerArray.length; i++) {
directoryString += fileExplorerArray[i] + "/";
// Ignore root directory
if(fileExplorerArray[i].name !== '/'){
directoryString += fileExplorerArray[i].name + "/";
}
}
// If the scraper option is checked, then tell dirparer to use getID3
$.post('dirparser', {dir: directoryString, filetypes: filetypes}, function(response) {
clearDatalist();
$.post('dirparser', {dir: directoryString, filetypes: '["flac", "mp3", "ogg", "wav"]'}, function(response) {
clearAndSetDataList('filebrowser');
var parsedResponse = $.parseJSON(dir);
var parsedResponse = $.parseJSON(response);
var path = parsedResponse.path;
$.each(parsedResponse.contents, function() {
dataList.push(
mstreamModule.dataList.push(
{
type: this.type,
type: (this.type === 'directory' ? "directory" : "file"),
path: path + this.name,
name: this.name,
artist: false, // TODO:
title: false // TODO:
}
@ -67,6 +96,50 @@ var MSTREAMAPI = (function () {
mstreamModule.getCurrentDirectoryContents = function(){
getDirectoryContents();
}
mstreamModule.goToNextDirectory = function(folder, currentScrollPosition = 0){
if(currentScrollPosition != 0 ){
// TODO: Save Scroll Position
}
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(dataList[dataList.length-1].name === '/'){
return false;
}
fileExplorerArray.pop();
getDirectoryContents();
// TODO: Return Current Scroll Position
}
mstreamModule.getCurrentScrollPosition = function(){
return dataList[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
@ -130,13 +203,13 @@ var MSTREAMAPI = (function () {
});
request.done(function( msg ) {
clearDatalist();
clearAndSetDataList('playlists');
var parsedResponse = $.parseJSON(msg);
//parse through the json array and make an array of corresponding divs
var playlists = [];
$.each(parsedResponse, function() {
dataList.push(
mstreamModule.dataList.push(
{
type: 'playlist',
name: this.name
@ -151,12 +224,7 @@ var MSTREAMAPI = (function () {
}
// TODO: Can thie be cahnged to a reset of the variable
function clearDatalist(){
while(dataList.length > 0){
dataList.pop();
}
}

View File

@ -367,7 +367,7 @@ $(document).ready(function(){
}
// Load up the file explorer
$('.get_file_explorer').on('click', loadFileExplorer);
// $('.get_file_explorer').on('click', loadFileExplorer);
// when you click on a directory, go to that directory
$("#filelist").on('click', 'div.dirz', function() {

362
public/remote.html Normal file
View File

@ -0,0 +1,362 @@
<head>
<!-- mStream CSS -->
<link rel="stylesheet" href="/public/css/remote.css">
<!-- Pure CSS -->
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.1/build/pure-min.css"
integrity="sha384-CCTZv2q9I9m3UOxRLaJneXrrqKwUNOzZ6NGEUMwHtShDJ+nCoiXJCAgi05KfkLGY"
crossorigin="anonymous">
<script src="/public/js/mstream.api.js"></script>
<!-- https://github.com/js-cookie/js-cookie -->
<script type="text/javascript" src="/public/js/lib/cookie.js"></script>
<!-- Vue JS -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<style>
.remote-controls{
position:absolute;
bottom:0;
width:100%;
height: 50px;
overflow: hidden;
}
.previous-button{
position:relative;
height: 100%;
width:50px;
background-color: #333333;
float:left;
overflow:hidden;
box-shadow: 5px 0 8px -2px rgba(31, 73, 125, 0.8), -5px 0 8px -2px rgba(31, 73, 125, 0.8);
cursor:pointer;
}
.play-pause-button{
height: 100%;
width:50px;
background-color: rgb(102, 132, 178);
float:left;
overflow:hidden;
cursor:pointer;
}
.next-border{
height: 100%;
padding: 8px;
}
.previous-border{
height: 100%;
padding: 8px;
}
.play-pause-border{
border-left: 5px solid rgb(102, 132, 178);
border-top: 5px solid rgb(102, 132, 178);
height: 100%;
padding: 3px;
}
.next-button{
height: 100%;
width:50px;
background-color: #333333;
float:left;
position: relative;
overflow:hidden;
box-shadow: 5px 0 8px -2px rgba(31, 73, 125, 0.8), -5px 0 8px -2px rgba(31, 73, 125, 0.8);
cursor:pointer;
}
.login-overlay{
position:fixed;
padding:0;
margin:0;
top:0;
left:0;
width: 100%;
height: 100%;
background:rgba(255,255,255,0.9);
z-index:9;
}
</style>
<!--TODO: This needs to be removed before feature goes live -->
<script>
var remoteProperties = {
code: 0
};
window.onload = function(){
// TODO: Pull code from cookies and try it
function tryToGetCookieAndEatIt(){
var token = Cookies.get('code');
}
// Prompt user for code
// Check code against server
// Store code
// Function to send commands to server
function sendCommandToServer(command){
var request = $.ajax({
url: "jukebox/push-to-client",
type: "POST",
data: { album : album },
});
request.done(function( msg ) {
var parsedMessage = $.parseJSON(msg);
});
request.fail(function( jqXHR, textStatus ) {
// TODO:
});
}
function checkCodeAgainstServer(code){
$.post('jukebox/does-code-exist', {code: code}, function(response) {
var parsedResponse = $.parseJSON(response);
var status = parsedResponse.status;
// TODO:
if(status === false){
}else{
// Set Code
remoteProperties.code = code;
// Save to cookie
Cookies.set('code', token);
}
});
}
var loginPanel = new Vue({
el: '#login-overlay',
data: {
remote: remoteProperties,
},
computed: {
titleText: function(){
// FIXME: Temp workaround
return 'TITLE GOES HERE';
}
},
methods: {
submitCode: function(){
console.log('FORM SUBMIT');
// Get Code
// Check Code Against Server
// Update vars as necessary
return;
}
}
});
// Template for data items
Vue.component('browser-item', {
template: '<div v-on:click="handleClick($event)" class="browser-item">{{displayText}}</div>',
props: ['type', 'item'],
// We need the positionCache to track the currently playing song
data: function(){
return {
// positionCache: MSTREAM.positionCache,
}
},
// Methods used by playlist item events
methods: {
// On Click
handleClick: function(event){
console.log('CLICK');
// get data type
var thisType = this.type;
// TODO: do something based on data type
if(this.type === 'file'){
console.log('FILE CLICK!');
// TODO: Send to remote
}else if( this.type === 'directory'){
console.log('DIRECTORY CLICK!');
MSTREAMAPI.goToNextDirectory(this.item.name);
}
},
},
computed: {
displayText: function(){
// Get Type
// render based on type
// FIXME: Temp workaround
return this.item.name;
}
}
});
var browserPanel = new Vue({
el: '#browser-container',
data: {
dataList: MSTREAMAPI.dataList,
currentProperties: MSTREAMAPI.currentProperties
},
computed: {
titleText: function(){
// FIXME: Temp workaround
return 'TITLE GOES HERE';
}
}
});
MSTREAMAPI.getCurrentDirectoryContents();
}
</script>
</head>
<body>
<div id="login-overlay" class="login-overlay">
<div class="pure-g">
<div class="pure-u-1-3"><p></p></div>
<div class="pure-u-1-3">
<transition appear>
<img src="/public/img/mstream-icon.svg">
</transition >
<form id="login-form" v-on:submit.prevent="submitCode">
<div>
<label>Code: </label>
<input required type="password" class="form-control" id="login-password">
</div>
<button id="login-submit" type="submit" class="btn">Login</button
</form>
<div id="login-alert" class="">Login Failed</div>
</div>
<div class="pure-u-1-3"><p></p></div>
</div>
</div>
<!-- Header -->
<div class="header">
<div class="logo-box">
<img class="mstream-image" src="/public/img/mstream-logo.svg">
</div>
</div>
<!-- Browser -->
<div id="browser-container" class="browser-container">
<!-- Browser Header -->
<div class="browser-header">
<div class="browser-header-text">{{titleText}}</div>
<div class="button-group">
<div class="repeat"></div>
<div></div>
<div></div>
</div>
</div>
<!-- ToolBar -->
<div class="browser-tools">
</div>
<!-- List Area -->
<div id="browser">
<div v-for="(item, index) in dataList" :item="item" :type="item.type" is="browser-item" :key="index">
</div>
</div>
</div>
<!-- Controls -->
<div class="remote-controls">
<div id="previous-button" class="previous-button">
<div class="previous-border">
<img class="previous-image" src="/public/img/previous-white.svg">
</div>
</div>
<div id="play-pause-button" class="play-pause-button">
<div id="play-pause-border" class="play-pause-border">
<img id="play-pause-image" class="play-pause-image" src="/public/img/play-white.svg" >
</div>
</div>
<div id="next-button" class="next-button">
<div class="next-border">
<img class="mext-image" src="/public/img/next-white.svg">
</div>
</div>
</div>
</body>

View File

@ -1,6 +1,8 @@
<head>
<!-- mStream CSS -->
<link rel="stylesheet" href="/public/css/shared.css">
<link rel="stylesheet" href="/public/css/mstream-player.css">
<!-- Pure CSS -->
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.1/build/pure-min.css" integrity="sha384-CCTZv2q9I9m3UOxRLaJneXrrqKwUNOzZ6NGEUMwHtShDJ+nCoiXJCAgi05KfkLGY" crossorigin="anonymous">
@ -37,7 +39,7 @@
//
}
MSTREAM.addSong('/Darn Coyotes - See You in Hell- I Guess/Darn Coyotes - See You in Hell, I Guess - 02 We Oughtta Make Like Antelope and Split.mp3');
MSTREAM.addSong('/Darn Coyotes - See You in Hell- I Guess/Darn Coyotes - See You in Hell, I Guess - 06 We Are Not the Friends You Are Looking For.mp3');
MSTREAM.addSong('/TV Torso - Clear Lake Strangler (1)/TV Torso - Clear Lake Strangler - 02 Prismatic Ideation.flac');