mirror of
https://github.com/IrosTheBeggar/mStream.git
synced 2025-10-27 07:31:02 +00:00
459 lines
12 KiB
HTML
459 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 Media Player - All your media. Everywhere you go.</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">
|
|
|
|
<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: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 /* .fade-leave-active in <2.1.8 */ {
|
|
opacity: 0
|
|
}
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<!--TODO: This needs to be removed before feature goes live -->
|
|
<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
|
|
|
|
|
|
// 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, 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;
|
|
|
|
// TODO: do something based on data type
|
|
if(this.type === 'file'){
|
|
console.log('FILE CLICK!');
|
|
console.log(this.item.path);
|
|
// 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(){
|
|
console.log(this.fileExplorer);
|
|
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(){
|
|
console.log('BACK CLICK');
|
|
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>
|