moved everything to webpack, migrated to bootstrap v4
This commit is contained in:
413
src/frontend/app.js
Normal file
413
src/frontend/app.js
Normal file
@@ -0,0 +1,413 @@
|
||||
import { socket } from './common.js';
|
||||
import 'howler';
|
||||
import 'datatables';
|
||||
|
||||
let tableAPI = null;
|
||||
let table = null;
|
||||
let columns = [
|
||||
{ data: 'name' }
|
||||
];
|
||||
let currentSong = null;
|
||||
let songDir = [];
|
||||
let player = null;
|
||||
let aLoopTime = null;
|
||||
let bLoopTime = null;
|
||||
|
||||
// init socket io
|
||||
$(function () {
|
||||
table = $('#songlist').DataTable({
|
||||
paging: true,
|
||||
bLengthChange : false,
|
||||
searching: true,
|
||||
data: [],
|
||||
autoWidth: false,
|
||||
columns: columns
|
||||
});
|
||||
tableAPI = $('#songlist').dataTable();
|
||||
table.on( 'draw.dt', function () {
|
||||
tableClickListener();
|
||||
});
|
||||
|
||||
//socket io stuff
|
||||
socket.emit('update', 'songs');
|
||||
|
||||
socket.on('update_songs', function(msg){
|
||||
if(msg != null){
|
||||
songDir = msg;
|
||||
$( '#dir-dropdown' ).html("");
|
||||
songDir.forEach(function(dir){
|
||||
$( '#dir-dropdown' ).append('<option>'+dir.name+'</option>');
|
||||
});
|
||||
updateTable();
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('message', function(msg){
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
//buttons
|
||||
|
||||
$( '#playBtn' ).click(function(){
|
||||
if(player == null)
|
||||
return
|
||||
if(player.isPlaying()){
|
||||
player.pause();
|
||||
}else{
|
||||
player.play();
|
||||
}
|
||||
});
|
||||
|
||||
$( '#stopBtn' ).click(function(){
|
||||
if(player == null)
|
||||
return
|
||||
player.stop();
|
||||
});
|
||||
|
||||
$( '#aLoopBtn' ).click(function(){
|
||||
if(player == null)
|
||||
return
|
||||
aLoopTime = player.getPosition();
|
||||
$( '#bLoopBtn' ).prop("disabled", false);
|
||||
$( '#txt-aLoop' ).html(player.formatTime(Math.round(aLoopTime)));
|
||||
});
|
||||
|
||||
$( '#bLoopBtn' ).click(function(){
|
||||
if(player == null)
|
||||
return
|
||||
bLoopTime = player.getPosition();
|
||||
$( '#aLoopBtn' ).prop("disabled", false);
|
||||
$( '#resetLoopBtn' ).prop("disabled", false);
|
||||
$( '#txt-bLoop' ).html(player.formatTime(Math.round(bLoopTime)));
|
||||
});
|
||||
|
||||
$( '#resetLoopBtn' ).click(function(){
|
||||
if(player == null)
|
||||
return
|
||||
resetLoop();
|
||||
});
|
||||
|
||||
//dropdown
|
||||
|
||||
$( '#dir-dropdown' ).change(function () {
|
||||
updateTable();
|
||||
});
|
||||
|
||||
//sliders
|
||||
|
||||
$( '#position-slider' ).change(function () {
|
||||
player.seek(this.value/1000);
|
||||
updateDisplay();
|
||||
});
|
||||
|
||||
$( '#speed-slider' ).change(function () {
|
||||
updateDisplay();
|
||||
});
|
||||
|
||||
$( '#volume-slider' ).change(function () {
|
||||
updateDisplay();
|
||||
});
|
||||
|
||||
$('input[type=range]').on('input', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
$('#extFile').change(function () {
|
||||
let event = this
|
||||
if (event.files.length > 0) {
|
||||
let file = event.files[0];
|
||||
let reader = new FileReader();
|
||||
let songName = event.files[0].name;
|
||||
reader.addEventListener('load', function() {
|
||||
let data = reader.result;
|
||||
if(player != null && player.isPlaying())
|
||||
player.pause();
|
||||
player = new Player(songName, data);
|
||||
player.init();
|
||||
updateDisplay();
|
||||
});
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
updateDisplay();
|
||||
});
|
||||
|
||||
function updateDisplay(){
|
||||
let playBtn = $( '#playBtn' );
|
||||
let stopBtn = $( '#stopBtn' );
|
||||
let aLoopBtn = $( '#aLoopBtn' );
|
||||
let bLoopBtn = $( '#bLoopBtn' );
|
||||
let resetLoopBtn= $( '#resetLoopBtn' );
|
||||
|
||||
let volumeSl = $( '#volume-slider' );
|
||||
let speedSl = $( '#speed-slider' );
|
||||
let positionSl = $( '#position-slider' );
|
||||
|
||||
let speedTxt = $( '#txt-speed' );
|
||||
let volumeTxt = $( '#txt-volume' );
|
||||
|
||||
if(player == null){
|
||||
playBtn.prop("disabled", true);
|
||||
stopBtn.prop("disabled", true);
|
||||
aLoopBtn.prop("disabled", true);
|
||||
bLoopBtn.prop("disabled", true);
|
||||
resetLoopBtn.prop("disabled", true);
|
||||
positionSl.prop("disabled", true);
|
||||
}else{
|
||||
stopBtn.prop("disabled", false);
|
||||
positionSl.prop("disabled", false);
|
||||
aLoopBtn.prop("disabled", false);
|
||||
player.rate(speedSl[0].value/100);
|
||||
player.volume(volumeSl[0].value/1000);
|
||||
}
|
||||
speedTxt.html((speedSl[0].value) + '%');
|
||||
volumeTxt.html(Math.round(volumeSl[0].value/10) + '%');
|
||||
}
|
||||
|
||||
function updateTable(){
|
||||
songDir.forEach(function(dir){
|
||||
if($( '#dir-dropdown' ).find(":selected").text() == dir.name){
|
||||
table.clear();
|
||||
if(dir.files.length!=0)
|
||||
tableAPI.fnAddData(dir.files);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function tableClickListener(){
|
||||
$( "tbody tr").unbind();
|
||||
$( "tbody tr").hover(function(){
|
||||
let selected = $(this).hasClass("ui-state-highlight");
|
||||
$("tbody tr").removeClass("ui-state-highlight");
|
||||
if (!selected) {
|
||||
$(this).addClass("ui-state-highlight");
|
||||
}
|
||||
});
|
||||
|
||||
$( "tbody tr").click(function(){
|
||||
let songName = $(this)[0].lastChild.innerText;
|
||||
if(player != null && player.isPlaying())
|
||||
player.pause();
|
||||
player = new Player(songName);
|
||||
player.init();
|
||||
updateDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
function resetLoop(){
|
||||
aLoopTime = null;
|
||||
bLoopTime = null;
|
||||
$( '#txt-aLoop' ).html('0:00');
|
||||
$( '#txt-bLoop' ).html('0:00');
|
||||
$( '#bLoopBtn' ).prop("disabled", true);
|
||||
$( '#resetLoopBtn' ).prop("disabled", true);
|
||||
}
|
||||
|
||||
let Player = function(songName, data=null) {
|
||||
if(data != null){
|
||||
this.song = songName;
|
||||
this.url = data;
|
||||
}else{
|
||||
this.song = songName;
|
||||
let dir = $( '#dir-dropdown' ).find(":selected").text();
|
||||
this.url = './songs/' + dir + '/' + songName;
|
||||
}
|
||||
this.howl = null;
|
||||
resetLoop();
|
||||
|
||||
// Display the title of the first track.
|
||||
$( '#txt-titel' ).text(songName);
|
||||
};
|
||||
|
||||
Player.prototype = {
|
||||
/**
|
||||
* Play a song.
|
||||
*/
|
||||
init: function() {
|
||||
$( '#playBtn' ).prop("disabled", true);
|
||||
let self = this;
|
||||
let sound;
|
||||
// If we already loaded this track, use the current one.
|
||||
// Otherwise, setup and load a new Howl.
|
||||
if (self.howl) {
|
||||
sound = self.howl;
|
||||
} else {
|
||||
sound = self.howl = new Howl({
|
||||
src: [self.url],
|
||||
html5: true, // Force to HTML5 so that the audio can stream in (best for large files).
|
||||
onplay: function() {
|
||||
updateDisplay();
|
||||
console.log('pressed play button');
|
||||
$( '#playBtn' ).removeClass('btn-succes').addClass('btn-warning');
|
||||
$( '#playBtn svg' ).removeClass('fa-play').addClass('fa-pause');
|
||||
// Start upating the progress of the track.
|
||||
requestAnimationFrame(self.step.bind(self));
|
||||
},
|
||||
onload: function() {
|
||||
$( '#txt-duration' ).text(self.formatTime(Math.round(sound.duration())));
|
||||
$( '#playBtn' ).prop("disabled", false);
|
||||
self.step();
|
||||
},
|
||||
onend: function() {
|
||||
$( '#playBtn' ).removeClass('btn-warning').addClass('btn-succes');
|
||||
$( '#playBtn svg' ).removeClass('fa-pause').addClass('fa-play');
|
||||
self.seek(0);
|
||||
},
|
||||
onpause: function() {
|
||||
$( '#playBtn' ).removeClass('btn-warning').addClass('btn-succes');
|
||||
$( '#playBtn svg' ).removeClass('fa-pause').addClass('fa-play');
|
||||
},
|
||||
onstop: function() {
|
||||
},
|
||||
onseek: function() {
|
||||
requestAnimationFrame(self.step.bind(self));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
play: function() {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
// Puase the sound.
|
||||
sound.play();
|
||||
},
|
||||
|
||||
/**
|
||||
* Pause the currently playing track.
|
||||
*/
|
||||
pause: function() {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
// Puase the sound.
|
||||
sound.pause();
|
||||
},
|
||||
|
||||
/**
|
||||
* stop the currently playing track.
|
||||
*/
|
||||
stop: function() {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
// stop the sound.
|
||||
sound.pause();
|
||||
self.seek(0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the volume and update the volume slider display.
|
||||
* @param {Number} val Volume between 0 and 1.
|
||||
*/
|
||||
volume: function(val) {
|
||||
let self = this;
|
||||
|
||||
// Update the global volume (affecting all Howls).
|
||||
Howler.volume(val);
|
||||
},
|
||||
|
||||
/**
|
||||
* Seek to a new position in the currently playing track.
|
||||
* @param {Number} per Percentage through the song to skip.
|
||||
*/
|
||||
seek: function(per) {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
// Convert the percent into a seek position.
|
||||
sound.seek(sound.duration() * per);
|
||||
},
|
||||
|
||||
/**
|
||||
* change rate of current song
|
||||
* @param {Number} speed from 0.5 to 4 times
|
||||
*/
|
||||
rate: function(speed) {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
// Convert the percent into a seek position.
|
||||
sound.rate(speed);
|
||||
},
|
||||
|
||||
/**
|
||||
* The step called within requestAnimationFrame to update the playback position.
|
||||
*/
|
||||
step: function() {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
// Determine our current seek position.
|
||||
let seek = sound.seek() || 0;
|
||||
if(aLoopTime != null && bLoopTime != null){
|
||||
if(seek > bLoopTime)
|
||||
sound.seek(aLoopTime);
|
||||
}
|
||||
$( '#txt-timer' ).text(self.formatTime(Math.round(seek)));
|
||||
$( '#position-slider' ).val(((seek / sound.duration())*1000) || 0);
|
||||
|
||||
// If the sound is still playing, continue stepping.
|
||||
if (sound.playing()) {
|
||||
requestAnimationFrame(self.step.bind(self));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* true, if playing
|
||||
*/
|
||||
isPlaying: function() {
|
||||
let self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
let sound = self.howl;
|
||||
|
||||
if(sound == null)
|
||||
return false;
|
||||
|
||||
return (sound.playing());
|
||||
},
|
||||
|
||||
/**
|
||||
* get raw position in seconds
|
||||
* @return {long} seconds
|
||||
*/
|
||||
getPosition: function() {
|
||||
let self = this;
|
||||
let sound = self.howl;
|
||||
if (sound == null)
|
||||
return 0;
|
||||
return sound.seek();
|
||||
},
|
||||
|
||||
/**
|
||||
* Format the time from seconds to M:SS.
|
||||
* @param {Number} secs Seconds to format.
|
||||
* @return {String} Formatted time.
|
||||
*/
|
||||
formatTime: function(secs) {
|
||||
let minutes = Math.floor(secs / 60) || 0;
|
||||
let seconds = (secs - minutes * 60) || 0;
|
||||
|
||||
return minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
|
||||
}
|
||||
};
|
||||
|
||||
function clog(msg){
|
||||
console.log(msg);
|
||||
socket.emit('log', msg);
|
||||
}
|
||||
Reference in New Issue
Block a user