WIP
This commit is contained in:
parent
25fcefcb50
commit
07b35b9667
27 changed files with 429 additions and 79 deletions
|
@ -30,6 +30,7 @@ const config = {
|
||||||
...visuals,
|
...visuals,
|
||||||
basePath + 'eventHandler.js',
|
basePath + 'eventHandler.js',
|
||||||
basePath + 'select.js',
|
basePath + 'select.js',
|
||||||
|
basePath + 'keys.js',
|
||||||
basePath + 'startup.js',
|
basePath + 'startup.js',
|
||||||
basePath + 'app.js'
|
basePath + 'app.js'
|
||||||
],
|
],
|
||||||
|
|
|
@ -43,6 +43,7 @@ function buildIconSprites() {
|
||||||
fal.faFolderUpload,
|
fal.faFolderUpload,
|
||||||
fal.faListMusic,
|
fal.faListMusic,
|
||||||
fal.faFileImage,
|
fal.faFileImage,
|
||||||
|
fal.faQuestionCircle,
|
||||||
],
|
],
|
||||||
vt: []
|
vt: []
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
<use href="out/icon-sprite.svg#fal-fa-list-music"></use>
|
<use href="out/icon-sprite.svg#fal-fa-list-music"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="help menu-icon">
|
||||||
|
<svg role="img" class="icon">
|
||||||
|
<use href="out/icon-sprite.svg#fal-fa-question-circle"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button id="previous">
|
<button id="previous">
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384],"value":4096,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}]
|
[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384],"value":4096,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"light","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":100,"min":-100,"value":0,"dataType":"int"},{"group":"light","name":"light-strength","showName":"Light Brightness","type":"slider","value":0.3,"max":1,"min":0,"stepSize":0.05,"tooltip":"brightness of light-point","dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}]
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 9.1 KiB |
|
@ -224,6 +224,10 @@ Node.prototype.toggleCheck = function (className, force) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String.prototype.firstUpper = function () {
|
||||||
|
return this.charAt(0).toUpperCase() + this.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
File.prototype.toBase64 = function (cb) {
|
File.prototype.toBase64 = function (cb) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onloadend = cb;
|
reader.onloadend = cb;
|
||||||
|
@ -1031,7 +1035,7 @@ class Playlist {
|
||||||
this.shuffled = [];
|
this.shuffled = [];
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.page = 0;
|
this.page = 0;
|
||||||
this.isShuffle = false;
|
this.isShuffle = pConf.get("shuffle", false);
|
||||||
$('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
|
$('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
|
||||||
$('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
|
$('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
|
||||||
eventHandler.addEvent('id3-request', this.handle.bind(this));
|
eventHandler.addEvent('id3-request', this.handle.bind(this));
|
||||||
|
@ -1228,11 +1232,17 @@ class GUI {
|
||||||
'inputs/slider',
|
'inputs/slider',
|
||||||
'inputs/switch',
|
'inputs/switch',
|
||||||
'inputs/select',
|
'inputs/select',
|
||||||
'inputs/option'
|
'inputs/option',
|
||||||
|
'help',
|
||||||
]);
|
]);
|
||||||
this.initDropZone();
|
this.initDropZone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openHelp() {
|
||||||
|
gui.modal.renderModal("Help", template.parseTemplate('help', {}));
|
||||||
|
gui.modal.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
initDropZone() {
|
initDropZone() {
|
||||||
let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' ');
|
let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' ');
|
||||||
items.forEach(el => {
|
items.forEach(el => {
|
||||||
|
@ -1253,6 +1263,7 @@ class GUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create config Inputs from JSON
|
// create config Inputs from JSON
|
||||||
|
//@todo add support for gui grouping!
|
||||||
class GUIHelper {
|
class GUIHelper {
|
||||||
static fromJSON(json, conf) {
|
static fromJSON(json, conf) {
|
||||||
let data = [];
|
let data = [];
|
||||||
|
@ -1289,7 +1300,7 @@ class GUIHelper {
|
||||||
for (let i = 0; i < data.name.length; i++) {
|
for (let i = 0; i < data.name.length; i++) {
|
||||||
let newData = {};
|
let newData = {};
|
||||||
Object.assign(newData, data);
|
Object.assign(newData, data);
|
||||||
newData.showName = data.name[i];
|
newData.showName = GUIHelper.richShowName(data, data.name[i].firstUpper());
|
||||||
newData.name = GUIHelper.richName(data, data.props[i]);
|
newData.name = GUIHelper.richName(data, data.props[i]);
|
||||||
content += GUIHelper.createSlider(newData, conf);
|
content += GUIHelper.createSlider(newData, conf);
|
||||||
}
|
}
|
||||||
|
@ -1350,6 +1361,13 @@ class GUIHelper {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static richShowName(data, name) {
|
||||||
|
if (data.group !== "") {
|
||||||
|
return data.group.firstUpper() + ' ' + name
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
static createButton(item, conf) {
|
static createButton(item, conf) {
|
||||||
return `<div class='button spaced' data-action="${item.action}">${item.name}</div>`
|
return `<div class='button spaced' data-action="${item.action}">${item.name}</div>`
|
||||||
}
|
}
|
||||||
|
@ -1632,7 +1650,7 @@ class Notification {
|
||||||
self.type += ' endless';
|
self.type += ' endless';
|
||||||
}
|
}
|
||||||
self.updateContent(self.message);
|
self.updateContent(self.message);
|
||||||
this.outer.appendChild(self.item);
|
this.outer.prepend(self.item);
|
||||||
if (!endless) {
|
if (!endless) {
|
||||||
setTimeout(this.remove.bind(this), self.time)
|
setTimeout(this.remove.bind(this), self.time)
|
||||||
}
|
}
|
||||||
|
@ -1800,8 +1818,14 @@ class Wave extends Visual {
|
||||||
prepare(program) {
|
prepare(program) {
|
||||||
this.position = gl.getAttribLocation(program, "a_position");
|
this.position = gl.getAttribLocation(program, "a_position");
|
||||||
this.color = gl.getUniformLocation(program, "u_color");
|
this.color = gl.getUniformLocation(program, "u_color");
|
||||||
let lightPos = gl.getUniformLocation(program, "u_lightPos");
|
let lightPos = gl.getUniformLocation(program, "u_lightPos"),
|
||||||
gl.uniform3fv(lightPos, vConf.get("light", [0, 5, -56]));
|
light = gl.getUniformLocation(program, "u_light");
|
||||||
|
gl.uniform3fv(lightPos, [
|
||||||
|
vConf.get("light-x", 0),
|
||||||
|
vConf.get("light-y", 5),
|
||||||
|
vConf.get("light-z", -56)
|
||||||
|
]);
|
||||||
|
gl.uniform1f(light, parseFloat(vConf.get("light-strength", 0.3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
afterDraw() {
|
afterDraw() {
|
||||||
|
@ -1904,8 +1928,11 @@ class EventHandler {
|
||||||
this.events = {};
|
this.events = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
addEvent(name, cb) {
|
addEvent(events, cb) {
|
||||||
this.events[name] = cb;
|
let names = events.split(",");
|
||||||
|
for (let name of names) {
|
||||||
|
this.events[name.trim()] = cb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendData(name, data) {
|
sendData(name, data) {
|
||||||
|
@ -1918,12 +1945,18 @@ class EventHandler {
|
||||||
handleEvent(event) {
|
handleEvent(event) {
|
||||||
let data = event.data;
|
let data = event.data;
|
||||||
if (!data.cmd) {
|
if (!data.cmd) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.events[data.cmd]) {
|
if (this.events[data.cmd]) {
|
||||||
|
try {
|
||||||
this.events[data.cmd](data.data);
|
this.events[data.cmd](data.data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[EventHandler] > ' + e.message);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1952,20 +1985,16 @@ async function initHandler() {
|
||||||
player.playStop();
|
player.playStop();
|
||||||
break;
|
break;
|
||||||
case 'shuffle':
|
case 'shuffle':
|
||||||
player.playlist.isShuffle = !player.playlist.isShuffle;
|
|
||||||
toggleShuffle();
|
toggleShuffle();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
|
|
||||||
});
|
});
|
||||||
window.addEventListener('playSong', setActiveOnPlaylist);
|
window.addEventListener('playSong', setActiveOnPlaylist);
|
||||||
|
window.addEventListener('playSong', e => {
|
||||||
|
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
|
||||||
|
});
|
||||||
$('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader));
|
$('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader));
|
||||||
body.addDelegatedEventListener('click', '.readAll', e => {
|
body.addDelegatedEventListener('click', '.readAll', forceAllRead);
|
||||||
let playlist = player.playlist.list;
|
|
||||||
for (let i = 0; i < playlist.length; i++) {
|
|
||||||
playlist[i].getID3Tag(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => {
|
body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => {
|
||||||
let current = $('.current', el.parentNode);
|
let current = $('.current', el.parentNode);
|
||||||
|
@ -2013,6 +2042,23 @@ async function initHandler() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('.help.menu-icon').addEventListener('click', gui.openHelp);
|
||||||
|
|
||||||
|
document.onfullscreenchange = e => {
|
||||||
|
if (body.hasClass('fullscreen')) {
|
||||||
|
body.removeClass('fullscreen')
|
||||||
|
} else {
|
||||||
|
body.addClass('fullscreen')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function forceAllRead() {
|
||||||
|
let playlist = player.playlist.list;
|
||||||
|
for (let i = 0; i < playlist.length; i++) {
|
||||||
|
playlist[i].getID3Tag(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2039,11 +2085,17 @@ function setActiveOnPlaylist(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleShuffle() {
|
function toggleShuffle(updateGUI) {
|
||||||
let active = player.playlist.isShuffle;
|
let active = player.playlist.isShuffle;
|
||||||
$('#shuffle').toggleCheck('active', active);
|
if (updateGUI !== false) {
|
||||||
|
active = !active;
|
||||||
let status = active ? 'enabled' : 'disabled';
|
let status = active ? 'enabled' : 'disabled';
|
||||||
NotificationHandler.createNotification("Shuffle: " + status, "info", 500);
|
NotificationHandler.createNotification("Shuffle: " + status, "info", 500);
|
||||||
|
pConf.set("shuffle", active);
|
||||||
|
pConf.save();
|
||||||
|
player.playlist.isShuffle = active;
|
||||||
|
}
|
||||||
|
$('#shuffle').toggleCheck('active', active);
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePlayButton(status) {
|
function togglePlayButton(status) {
|
||||||
|
@ -2121,6 +2173,65 @@ function togglePlayButton(status) {
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
class KeyHandler {
|
||||||
|
async init() {
|
||||||
|
await this.mediaKeys();
|
||||||
|
await this.addKeyHandler();
|
||||||
|
window.addEventListener('keydown', this.keyHandler.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async mediaKeys() {
|
||||||
|
if ('mediaSession' in navigator) {
|
||||||
|
let media = navigator.mediaSession;
|
||||||
|
media.setActionHandler('play', player.playStop.bind(player));
|
||||||
|
media.setActionHandler('pause', player.playStop.bind(player));
|
||||||
|
media.setActionHandler('previoustrack', player.prevSong.bind(player));
|
||||||
|
media.setActionHandler('nexttrack', player.prevSong.bind(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addKeyHandler() {
|
||||||
|
eventHandler.addEvent('keys-Space', player.playStop.bind(player));
|
||||||
|
eventHandler.addEvent('keys-KeyN', player.nextSong.bind(player));
|
||||||
|
eventHandler.addEvent('keys-KeyV', player.prevSong.bind(player));
|
||||||
|
eventHandler.addEvent('keys-KeyS', playerConf.open.bind(playerConf));
|
||||||
|
eventHandler.addEvent('keys-KeyS-shift', toggleShuffle);
|
||||||
|
eventHandler.addEvent('keys-KeyB', imageUploader.renderModal.bind(imageUploader));
|
||||||
|
eventHandler.addEvent('keys-KeyF-shift', forceAllRead);
|
||||||
|
eventHandler.addEvent('keys-KeyH', gui.openHelp);
|
||||||
|
eventHandler.addEvent('keys-KeyP', e => {
|
||||||
|
player.playlist.renderPagination(player.playlist.page);
|
||||||
|
gui.modal.showModal();
|
||||||
|
});
|
||||||
|
eventHandler.addEvent('keys-Escape, keys-KeyC-shift', e => {
|
||||||
|
gui.modal.resetModal();
|
||||||
|
gui.modal.closeModal();
|
||||||
|
})
|
||||||
|
eventHandler.addEvent('keys-F11', e => {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
document.exitFullscreen().catch(console.error);
|
||||||
|
} else {
|
||||||
|
document.body.requestFullscreen().catch(console.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async keyHandler(event) {
|
||||||
|
let key = event.code,
|
||||||
|
shift = event.shiftKey ? '-shift' : '',
|
||||||
|
ctrl = event.ctrlKey ? '-ctrl' : '',
|
||||||
|
name = 'keys-' + key + shift + ctrl;
|
||||||
|
|
||||||
|
if (eventHandler.handleEvent({
|
||||||
|
data: {
|
||||||
|
cmd: name
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
class Startup {
|
class Startup {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.modules = {
|
this.modules = {
|
||||||
|
@ -2155,7 +2266,8 @@ const shaderHandler = new ShaderHandler(null),
|
||||||
worker = new Worker('/out/js/worker.min.js'),
|
worker = new Worker('/out/js/worker.min.js'),
|
||||||
startup = new Startup(),
|
startup = new Startup(),
|
||||||
eventHandler = new EventHandler(),
|
eventHandler = new EventHandler(),
|
||||||
playerConf = new PlayerConfigHandler();
|
playerConf = new PlayerConfigHandler(),
|
||||||
|
keyHandler = new KeyHandler();
|
||||||
|
|
||||||
let c, gl, cInfo, ctx;
|
let c, gl, cInfo, ctx;
|
||||||
|
|
||||||
|
@ -2192,7 +2304,9 @@ async function startUP() {
|
||||||
await gui.init();
|
await gui.init();
|
||||||
await imageUploader.init();
|
await imageUploader.init();
|
||||||
await playerConf.init();
|
await playerConf.init();
|
||||||
|
await keyHandler.init();
|
||||||
await initHandler();
|
await initHandler();
|
||||||
|
toggleShuffle(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
startUP().then(r => {
|
startUP().then(r => {
|
||||||
|
|
2
out/js/scripts.min.js
vendored
2
out/js/scripts.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
69
out/tpl/help.tpl
Normal file
69
out/tpl/help.tpl
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<!-- help for every key! -->
|
||||||
|
<div class="help-list">
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">h</span>
|
||||||
|
<p>
|
||||||
|
Open Help Modal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">Space</span>
|
||||||
|
<p>
|
||||||
|
Switch between pause and play
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">n</span>
|
||||||
|
<p>
|
||||||
|
Play next song
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">v</span>
|
||||||
|
<p>
|
||||||
|
Play previous song
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">s</span>
|
||||||
|
<p>
|
||||||
|
Open Settings Modal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">Shift + s</span>
|
||||||
|
<p>
|
||||||
|
Toggle Shuffle
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">b</span>
|
||||||
|
<p>
|
||||||
|
Open background settings Modal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">Shift + f</span>
|
||||||
|
<p>
|
||||||
|
Force Song Tagger!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">p</span>
|
||||||
|
<p>
|
||||||
|
Show Playlist Modal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">ESC or Shift + C</span>
|
||||||
|
<p>
|
||||||
|
Close current open Modal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="h2">F11</span>
|
||||||
|
<p>
|
||||||
|
Toggle Fullscreen and hide GUI
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -19,10 +19,11 @@
|
||||||
<span class="focus"></span>
|
<span class="focus"></span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="input floating-label">
|
<label class="input floating-label input-range">
|
||||||
<input class="range" type="range" id="alphaValue" min="0" max="1" value="$alphaValue$" step="0.1">
|
<input class="range" type="range" id="alphaValue" min="0" max="1" value="$alphaValue$" step="0.1">
|
||||||
<span class="input-label">Alpha</span>
|
<span class="input-label">Alpha</span>
|
||||||
<span class="min">0</span>
|
<span class="min">0</span>
|
||||||
|
<span class="current">$alphaValue$</span>
|
||||||
<span class="max">1</span>
|
<span class="max">1</span>
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -51,6 +51,36 @@
|
||||||
"stepSize": 0.01,
|
"stepSize": 0.01,
|
||||||
"dataType": "float"
|
"dataType": "float"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "light",
|
||||||
|
"name": [
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z"
|
||||||
|
],
|
||||||
|
"type": "slider",
|
||||||
|
"max": 100,
|
||||||
|
"min": -100,
|
||||||
|
"value": 0,
|
||||||
|
"dataType": "int"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "light",
|
||||||
|
"name": "light-strength",
|
||||||
|
"showName": "Light Brightness",
|
||||||
|
"type": "slider",
|
||||||
|
"value": 0.3,
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"stepSize": 0.05,
|
||||||
|
"tooltip": "brightness of light-point",
|
||||||
|
"dataType": "float"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "",
|
"group": "",
|
||||||
"name": "baseColor",
|
"name": "baseColor",
|
||||||
|
|
|
@ -9,7 +9,8 @@ const shaderHandler = new ShaderHandler(null),
|
||||||
worker = new Worker('/out/js/worker.min.js'),
|
worker = new Worker('/out/js/worker.min.js'),
|
||||||
startup = new Startup(),
|
startup = new Startup(),
|
||||||
eventHandler = new EventHandler(),
|
eventHandler = new EventHandler(),
|
||||||
playerConf = new PlayerConfigHandler();
|
playerConf = new PlayerConfigHandler(),
|
||||||
|
keyHandler = new KeyHandler();
|
||||||
|
|
||||||
let c, gl, cInfo, ctx;
|
let c, gl, cInfo, ctx;
|
||||||
|
|
||||||
|
@ -46,7 +47,9 @@ async function startUP() {
|
||||||
await gui.init();
|
await gui.init();
|
||||||
await imageUploader.init();
|
await imageUploader.init();
|
||||||
await playerConf.init();
|
await playerConf.init();
|
||||||
|
await keyHandler.init();
|
||||||
await initHandler();
|
await initHandler();
|
||||||
|
toggleShuffle(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
startUP().then(r => {
|
startUP().then(r => {
|
||||||
|
|
|
@ -3,8 +3,11 @@ class EventHandler {
|
||||||
this.events = {};
|
this.events = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
addEvent(name, cb) {
|
addEvent(events, cb) {
|
||||||
this.events[name] = cb;
|
let names = events.split(",");
|
||||||
|
for (let name of names) {
|
||||||
|
this.events[name.trim()] = cb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendData(name, data) {
|
sendData(name, data) {
|
||||||
|
@ -17,12 +20,18 @@ class EventHandler {
|
||||||
handleEvent(event) {
|
handleEvent(event) {
|
||||||
let data = event.data;
|
let data = event.data;
|
||||||
if (!data.cmd) {
|
if (!data.cmd) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.events[data.cmd]) {
|
if (this.events[data.cmd]) {
|
||||||
|
try {
|
||||||
this.events[data.cmd](data.data);
|
this.events[data.cmd](data.data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[EventHandler] > ' + e.message);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,20 +60,16 @@ async function initHandler() {
|
||||||
player.playStop();
|
player.playStop();
|
||||||
break;
|
break;
|
||||||
case 'shuffle':
|
case 'shuffle':
|
||||||
player.playlist.isShuffle = !player.playlist.isShuffle;
|
|
||||||
toggleShuffle();
|
toggleShuffle();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
|
|
||||||
});
|
});
|
||||||
window.addEventListener('playSong', setActiveOnPlaylist);
|
window.addEventListener('playSong', setActiveOnPlaylist);
|
||||||
|
window.addEventListener('playSong', e => {
|
||||||
|
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
|
||||||
|
});
|
||||||
$('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader));
|
$('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader));
|
||||||
body.addDelegatedEventListener('click', '.readAll', e => {
|
body.addDelegatedEventListener('click', '.readAll', forceAllRead);
|
||||||
let playlist = player.playlist.list;
|
|
||||||
for (let i = 0; i < playlist.length; i++) {
|
|
||||||
playlist[i].getID3Tag(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => {
|
body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => {
|
||||||
let current = $('.current', el.parentNode);
|
let current = $('.current', el.parentNode);
|
||||||
|
@ -112,6 +117,23 @@ async function initHandler() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('.help.menu-icon').addEventListener('click', gui.openHelp);
|
||||||
|
|
||||||
|
document.onfullscreenchange = e => {
|
||||||
|
if (body.hasClass('fullscreen')) {
|
||||||
|
body.removeClass('fullscreen')
|
||||||
|
} else {
|
||||||
|
body.addClass('fullscreen')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function forceAllRead() {
|
||||||
|
let playlist = player.playlist.list;
|
||||||
|
for (let i = 0; i < playlist.length; i++) {
|
||||||
|
playlist[i].getID3Tag(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,11 +160,17 @@ function setActiveOnPlaylist(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleShuffle() {
|
function toggleShuffle(updateGUI) {
|
||||||
let active = player.playlist.isShuffle;
|
let active = player.playlist.isShuffle;
|
||||||
$('#shuffle').toggleCheck('active', active);
|
if (updateGUI !== false) {
|
||||||
|
active = !active;
|
||||||
let status = active ? 'enabled' : 'disabled';
|
let status = active ? 'enabled' : 'disabled';
|
||||||
NotificationHandler.createNotification("Shuffle: " + status, "info", 500);
|
NotificationHandler.createNotification("Shuffle: " + status, "info", 500);
|
||||||
|
pConf.set("shuffle", active);
|
||||||
|
pConf.save();
|
||||||
|
player.playlist.isShuffle = active;
|
||||||
|
}
|
||||||
|
$('#shuffle').toggleCheck('active', active);
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePlayButton(status) {
|
function togglePlayButton(status) {
|
||||||
|
|
|
@ -13,11 +13,17 @@ class GUI {
|
||||||
'inputs/slider',
|
'inputs/slider',
|
||||||
'inputs/switch',
|
'inputs/switch',
|
||||||
'inputs/select',
|
'inputs/select',
|
||||||
'inputs/option'
|
'inputs/option',
|
||||||
|
'help',
|
||||||
]);
|
]);
|
||||||
this.initDropZone();
|
this.initDropZone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openHelp() {
|
||||||
|
gui.modal.renderModal("Help", template.parseTemplate('help', {}));
|
||||||
|
gui.modal.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
initDropZone() {
|
initDropZone() {
|
||||||
let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' ');
|
let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' ');
|
||||||
items.forEach(el => {
|
items.forEach(el => {
|
||||||
|
@ -38,6 +44,7 @@ class GUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create config Inputs from JSON
|
// create config Inputs from JSON
|
||||||
|
//@todo add support for gui grouping!
|
||||||
class GUIHelper {
|
class GUIHelper {
|
||||||
static fromJSON(json, conf) {
|
static fromJSON(json, conf) {
|
||||||
let data = [];
|
let data = [];
|
||||||
|
@ -74,7 +81,7 @@ class GUIHelper {
|
||||||
for (let i = 0; i < data.name.length; i++) {
|
for (let i = 0; i < data.name.length; i++) {
|
||||||
let newData = {};
|
let newData = {};
|
||||||
Object.assign(newData, data);
|
Object.assign(newData, data);
|
||||||
newData.showName = data.name[i];
|
newData.showName = GUIHelper.richShowName(data, data.name[i].firstUpper());
|
||||||
newData.name = GUIHelper.richName(data, data.props[i]);
|
newData.name = GUIHelper.richName(data, data.props[i]);
|
||||||
content += GUIHelper.createSlider(newData, conf);
|
content += GUIHelper.createSlider(newData, conf);
|
||||||
}
|
}
|
||||||
|
@ -135,6 +142,13 @@ class GUIHelper {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static richShowName(data, name) {
|
||||||
|
if (data.group !== "") {
|
||||||
|
return data.group.firstUpper() + ' ' + name
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
static createButton(item, conf) {
|
static createButton(item, conf) {
|
||||||
return `<div class='button spaced' data-action="${item.action}">${item.name}</div>`
|
return `<div class='button spaced' data-action="${item.action}">${item.name}</div>`
|
||||||
}
|
}
|
||||||
|
|
59
raw/javascript/keys.js
Normal file
59
raw/javascript/keys.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
class KeyHandler {
|
||||||
|
async init() {
|
||||||
|
await this.mediaKeys();
|
||||||
|
await this.addKeyHandler();
|
||||||
|
window.addEventListener('keydown', this.keyHandler.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async mediaKeys() {
|
||||||
|
if ('mediaSession' in navigator) {
|
||||||
|
let media = navigator.mediaSession;
|
||||||
|
media.setActionHandler('play', player.playStop.bind(player));
|
||||||
|
media.setActionHandler('pause', player.playStop.bind(player));
|
||||||
|
media.setActionHandler('previoustrack', player.prevSong.bind(player));
|
||||||
|
media.setActionHandler('nexttrack', player.prevSong.bind(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addKeyHandler() {
|
||||||
|
eventHandler.addEvent('keys-Space', player.playStop.bind(player));
|
||||||
|
eventHandler.addEvent('keys-KeyN', player.nextSong.bind(player));
|
||||||
|
eventHandler.addEvent('keys-KeyV', player.prevSong.bind(player));
|
||||||
|
eventHandler.addEvent('keys-KeyS', playerConf.open.bind(playerConf));
|
||||||
|
eventHandler.addEvent('keys-KeyS-shift', toggleShuffle);
|
||||||
|
eventHandler.addEvent('keys-KeyB', imageUploader.renderModal.bind(imageUploader));
|
||||||
|
eventHandler.addEvent('keys-KeyF-shift', forceAllRead);
|
||||||
|
eventHandler.addEvent('keys-KeyH', gui.openHelp);
|
||||||
|
eventHandler.addEvent('keys-KeyP', e => {
|
||||||
|
player.playlist.renderPagination(player.playlist.page);
|
||||||
|
gui.modal.showModal();
|
||||||
|
});
|
||||||
|
eventHandler.addEvent('keys-Escape, keys-KeyC-shift', e => {
|
||||||
|
gui.modal.resetModal();
|
||||||
|
gui.modal.closeModal();
|
||||||
|
})
|
||||||
|
eventHandler.addEvent('keys-F11', e => {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
document.exitFullscreen().catch(console.error);
|
||||||
|
} else {
|
||||||
|
document.body.requestFullscreen().catch(console.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async keyHandler(event) {
|
||||||
|
let key = event.code,
|
||||||
|
shift = event.shiftKey ? '-shift' : '',
|
||||||
|
ctrl = event.ctrlKey ? '-ctrl' : '',
|
||||||
|
name = 'keys-' + key + shift + ctrl;
|
||||||
|
|
||||||
|
if (eventHandler.handleEvent({
|
||||||
|
data: {
|
||||||
|
cmd: name
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ class Notification {
|
||||||
self.type += ' endless';
|
self.type += ' endless';
|
||||||
}
|
}
|
||||||
self.updateContent(self.message);
|
self.updateContent(self.message);
|
||||||
this.outer.appendChild(self.item);
|
this.outer.prepend(self.item);
|
||||||
if (!endless) {
|
if (!endless) {
|
||||||
setTimeout(this.remove.bind(this), self.time)
|
setTimeout(this.remove.bind(this), self.time)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Playlist {
|
||||||
this.shuffled = [];
|
this.shuffled = [];
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.page = 0;
|
this.page = 0;
|
||||||
this.isShuffle = false;
|
this.isShuffle = pConf.get("shuffle", false);
|
||||||
$('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
|
$('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
|
||||||
$('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
|
$('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
|
||||||
eventHandler.addEvent('id3-request', this.handle.bind(this));
|
eventHandler.addEvent('id3-request', this.handle.bind(this));
|
||||||
|
|
|
@ -224,6 +224,10 @@ Node.prototype.toggleCheck = function (className, force) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String.prototype.firstUpper = function () {
|
||||||
|
return this.charAt(0).toUpperCase() + this.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
File.prototype.toBase64 = function (cb) {
|
File.prototype.toBase64 = function (cb) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onloadend = cb;
|
reader.onloadend = cb;
|
||||||
|
|
|
@ -70,8 +70,14 @@ class Wave extends Visual {
|
||||||
prepare(program) {
|
prepare(program) {
|
||||||
this.position = gl.getAttribLocation(program, "a_position");
|
this.position = gl.getAttribLocation(program, "a_position");
|
||||||
this.color = gl.getUniformLocation(program, "u_color");
|
this.color = gl.getUniformLocation(program, "u_color");
|
||||||
let lightPos = gl.getUniformLocation(program, "u_lightPos");
|
let lightPos = gl.getUniformLocation(program, "u_lightPos"),
|
||||||
gl.uniform3fv(lightPos, vConf.get("light", [0, 5, -56]));
|
light = gl.getUniformLocation(program, "u_light");
|
||||||
|
gl.uniform3fv(lightPos, [
|
||||||
|
vConf.get("light-x", 0),
|
||||||
|
vConf.get("light-y", 5),
|
||||||
|
vConf.get("light-z", -56)
|
||||||
|
]);
|
||||||
|
gl.uniform1f(light, parseFloat(vConf.get("light-strength", 0.3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
afterDraw() {
|
afterDraw() {
|
||||||
|
|
|
@ -164,3 +164,33 @@ modal-footer playlist {
|
||||||
.menus {
|
.menus {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.fullscreen {
|
||||||
|
.menus {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: 0 1em;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #232323;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h2 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 10px 0 5px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: .8em;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,12 @@ playlist {
|
||||||
position: unset;
|
position: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.playlist-content {
|
||||||
|
h1 {
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
// fragment shaders don't have a default precision so we need
|
|
||||||
// to pick one. mediump is a good default. It means "medium precision"
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
in vec4 fragNormal;
|
in vec4 fragNormal;
|
||||||
in vec3 v_surfaceToLight;
|
in vec3 v_surfaceToLight;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
// fragment shaders don't have a default precision so we need
|
|
||||||
// to pick one. mediump is a good default. It means "medium precision"
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
uniform vec4 u_color;
|
uniform vec4 u_color;
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
// fragment shaders don't have a default precision so we need
|
|
||||||
// to pick one. mediump is a good default. It means "medium precision"
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
in vec4 pos;
|
in vec4 pos;
|
||||||
in vec3 v_surfaceToLight;
|
in vec3 v_surfaceToLight;
|
||||||
in vec3 baseColor;
|
uniform vec3 u_baseColor;
|
||||||
in vec3 maxColor;
|
uniform vec3 u_maxColor;
|
||||||
uniform vec4 u_color;
|
uniform float u_light;
|
||||||
|
|
||||||
out vec4 outColor;
|
out vec4 outColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 fragNormal = normalize(pos);
|
vec4 fragNormal = normalize(pos);
|
||||||
float u_light = 0.3;
|
|
||||||
float light = max(dot(fragNormal.xyz, normalize(v_surfaceToLight).xyz), u_light);
|
float light = max(dot(fragNormal.xyz, normalize(v_surfaceToLight).xyz), u_light);
|
||||||
float y = pos.z;
|
float y = pos.z;
|
||||||
if (y < 0.0) {
|
if (y < 0.0) {
|
||||||
y = y * -1.0;
|
y = y * -1.0;
|
||||||
}
|
}
|
||||||
vec3 color = mix(baseColor, maxColor, y);
|
vec3 color = mix(u_baseColor, u_maxColor, y);
|
||||||
outColor = vec4(color, 1.0);
|
outColor = vec4(color, 1.0);
|
||||||
outColor.rgb *= light;
|
outColor.rgb *= light;
|
||||||
}
|
}
|
|
@ -3,18 +3,12 @@
|
||||||
in vec3 a_position;
|
in vec3 a_position;
|
||||||
uniform mat4 u_matrix;
|
uniform mat4 u_matrix;
|
||||||
uniform vec3 u_lightPos;
|
uniform vec3 u_lightPos;
|
||||||
uniform vec3 u_baseColor;
|
|
||||||
uniform vec3 u_maxColor;
|
|
||||||
|
|
||||||
out vec4 pos;
|
out vec4 pos;
|
||||||
out vec3 v_surfaceToLight;
|
out vec3 v_surfaceToLight;
|
||||||
out vec3 maxColor;
|
|
||||||
out vec3 baseColor;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
pos = u_matrix * vec4(a_position, 1);
|
pos = u_matrix * vec4(a_position, 1);
|
||||||
gl_Position = pos;
|
gl_Position = pos;
|
||||||
v_surfaceToLight = u_lightPos - pos.xyz;
|
v_surfaceToLight = u_lightPos - pos.xyz;
|
||||||
maxColor = u_maxColor;
|
|
||||||
baseColor = u_baseColor;
|
|
||||||
}
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
// fragment shaders don't have a default precision so we need
|
|
||||||
// to pick one. mediump is a good default. It means "medium precision"
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
in vec4 pos;
|
in vec4 pos;
|
||||||
in vec3 baseColor;
|
|
||||||
in vec3 maxColor;
|
uniform vec3 u_baseColor;
|
||||||
|
uniform vec3 u_maxColor;
|
||||||
|
|
||||||
out vec4 outColor;
|
out vec4 outColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -14,6 +13,6 @@ void main() {
|
||||||
if (y < 0.0) {
|
if (y < 0.0) {
|
||||||
y = y * -1.0;
|
y = y * -1.0;
|
||||||
}
|
}
|
||||||
vec3 color = mix(baseColor, maxColor, y);
|
vec3 color = mix(u_baseColor, u_maxColor, y);
|
||||||
outColor = vec4(color, 1.0);
|
outColor = vec4(color, 1.0);
|
||||||
}
|
}
|
|
@ -2,18 +2,11 @@
|
||||||
|
|
||||||
in vec3 a_position;
|
in vec3 a_position;
|
||||||
uniform mat4 u_matrix;
|
uniform mat4 u_matrix;
|
||||||
uniform vec3 u_baseColor;
|
|
||||||
uniform vec3 u_maxColor;
|
|
||||||
|
|
||||||
out vec4 pos;
|
out vec4 pos;
|
||||||
out vec3 maxColor;
|
|
||||||
out vec3 baseColor;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
pos = u_matrix * vec4(a_position, 1);
|
pos = u_matrix * vec4(a_position, 1);
|
||||||
pos.y = pos.y * 0.6;
|
pos.y = pos.y * 0.6;
|
||||||
gl_Position = pos;
|
gl_Position = pos;
|
||||||
|
|
||||||
maxColor = u_maxColor;
|
|
||||||
baseColor = u_baseColor;
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue