mStream/public/remote.html
2019-02-26 11:10:47 -08:00

403 lines
12 KiB
HTML

<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">
<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">
<title>mStream Remote</title>
<!-- 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">
<!--
This is the mStream Player stack
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>
<!-- 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>
<script src="/public/js/remote.js"></script>
<style>
.remote-controls {
position: absolute;
bottom: 0;
width: 100%;
height: 50px;
overflow: hidden;
}
.previous-button {
position: relative;
height: 100%;
width: 34%;
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: 33%;
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: 33%;
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;
}
.center {
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
}
.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;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s
}
.fade-enter,
.fade-leave-to {
opacity: 0
}
</style>
<script>
var remoteProperties = {
code: 0,
guest: false,
error: false,
token: false
};
// Auto Focus
Vue.directive('focus', {
// When the bound element is inserted into the DOM...
inserted: function (el) {
// Focus the element
el.focus()
}
})
window.onload = function () {
$.ajaxPrefilter(function (options) {
options.beforeSend = function (xhr) {
xhr.setRequestHeader('x-access-token', remoteProperties.token);
}
});
// TODO: Handle guest controls
// Prompt user for code
// Check code against server
// Store code
// Function to send commands to server
function sendCommandToServer(command, file = false) {
var sendData = JSON.stringify({
code: remoteProperties.code,
command: command,
file: file,
});
var request = $.ajax({
url: "jukebox/push-to-client",
type: "POST",
contentType: "application/json",
dataType: "json",
data: sendData
});
request.done(function (msg) {
// var parsedMessage = $.parseJSON(msg);
// TODO:
});
request.fail(function (jqXHR, textStatus) {
// TODO: IF you get an access denied error, try re-validating the code. It could be the token expired
// TODO: clear remoteProperties if token doesn't exist anymore
});
}
$('#next-button').on('click', function () {
sendCommandToServer('next');
});
$('#play-pause-button').on('click', function () {
sendCommandToServer('playPause');
});
$('#previous-button').on('click', function () {
sendCommandToServer('previous');
});
var loginPanel = new Vue({
el: '#login-overlay',
data: {
remote: remoteProperties,
},
methods: {
submitCode: function (e) {
// Get Code
var loginCode = $('#login-code').val();
// Check Code Against Server
var request = $.ajax({
url: "jukebox/does-code-exist",
type: "POST",
contentType: "application/json",
dataType: "json",
data: JSON.stringify({ code: loginCode })
});
request.done(function (msg) {
// var parsedMessage = $.parseJSON(msg);
if (msg.status === true) {
remoteProperties.error = false;
remoteProperties.code = loginCode;
remoteProperties.guest = msg.guestStatus;
remoteProperties.token = msg.token;
// Load up the panel
MSTREAMAPI.getCurrentDirectoryContents();
} else {
remoteProperties.error = 'Code Not Found';
}
});
request.fail(function (jqXHR, textStatus) {
remoteProperties.error = 'Failed To Connect To Server';
});
}
}
});
// 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: MSTREAMPLAYER.positionCache,
}
},
// Methods used by playlist item events
methods: {
// On Click
handleClick: function (event) {
// get data type
var thisType = this.type;
// do something based on data type
if (this.type === 'file') {
// TODO: Send to remote
sendCommandToServer('addSong', this.item.path);
} else if (this.type === 'directory') {
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,
fileExplorer: MSTREAMAPI.fileExplorerArray
},
computed: {
titleText: function () {
// FIXME: Temp workaround
return 'File Explorer';
},
filepath: function () {
if (this.fileExplorer.length === 1) {
return '/';
}
var directoryString = '';
for (var i = 0; i < this.fileExplorer.length; i++) {
// Ignore root directory
if (this.fileExplorer[i].name !== '/') {
directoryString += "/" + this.fileExplorer[i].name;
}
}
return directoryString;
}
}
});
$('#backbutton').on('click', function () {
MSTREAMAPI.goBackDirectory();
});
}
</script>
</head>
<body>
<div v-if="remote.code === 0" 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">
<img class="login-icon" src="/public/img/mstream-icon.svg">
<form id="login-form" class="login-form" v-on:submit.prevent="submitCode($event)">
<div>
<label>Code</label>
<input v-focus required type="text" class="form-control" id="login-code">
</div>
<button id="login-submit" type="submit" class="btn">Login</button </form>
<div v-show="remote.error !== false" id="login-alert" class="">{{remote.error}}</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 id="backbutton" class="back-button">
<img src="/public/img/back-arrow.svg">
</div>
<div class="filepath">{{filepath}}</div>
</div>
<!-- List Area -->
<div id="browser" class="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">
<img class="previous-image center" src="/public/img/previous-white.svg">
</div>
<div id="play-pause-button" class="play-pause-button">
<img id="play-pause-image" class="play-pause-image center" src="/public/img/play-white.svg">
</div>
<div id="next-button" class="next-button">
<img class="mext-image center" src="/public/img/next-white.svg">
</div>
</div>
</body>