321 lines
8.9 KiB
JavaScript
321 lines
8.9 KiB
JavaScript
import ReconnectingWebSocket from 'reconnecting-websocket';
|
|
import Sortable from 'sortablejs';
|
|
|
|
const DND_DELAY = 300;
|
|
|
|
const addInteractHandler = (source, handler) => {
|
|
source.addEventListener("click", e => {
|
|
console.log("click", e);
|
|
handler(e, false);
|
|
});
|
|
|
|
let touchStart;
|
|
source.addEventListener("touchstart", e => {
|
|
console.log("touch start", touchStart, e.timeStamp, e);
|
|
touchStart = e.timeStamp;
|
|
}, {passive: true})
|
|
|
|
source.addEventListener("touchend", e => {
|
|
console.log("touch end", touchStart, e);
|
|
console.log(e.timeStamp - touchStart)
|
|
if ((e.timeStamp - touchStart) < DND_DELAY) {
|
|
handler(e, true);
|
|
}
|
|
}, {passive: true});
|
|
}
|
|
|
|
window.onload = function () {
|
|
const ws = new ReconnectingWebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/ws");
|
|
|
|
const items = document.querySelector("#items");
|
|
const amb = document.querySelector("#ambiance")
|
|
const submit = document.querySelector("#addplaylist")
|
|
const output = document.querySelector(".container2 p#output")
|
|
const info = document.querySelector("#info")
|
|
const link = document.querySelector("#link")
|
|
const time = document.querySelector("#time")
|
|
const waiting = document.querySelector("#waiting")
|
|
const pvol = document.querySelector("#playlist-volume")
|
|
const avol = document.querySelector("#ambiance-volume")
|
|
|
|
pvol.addEventListener("change", (e) => {
|
|
e.target.nextElementSibling.value = e.target.value
|
|
ws.send(JSON.stringify({
|
|
"event": "vol_set",
|
|
"payload": {
|
|
"type": "playlist",
|
|
"vol": e.target.value
|
|
}
|
|
}))
|
|
})
|
|
|
|
pvol.nextElementSibling.addEventListener("change", (e) => {
|
|
e.target.previousElementSibling.value = e.target.value
|
|
e.target.previousElementSibling.dispatchEvent(new Event('change'));
|
|
})
|
|
|
|
avol.addEventListener("change", (e) => {
|
|
e.target.nextElementSibling.value = e.target.value
|
|
ws.send(JSON.stringify({
|
|
"event": "vol_set",
|
|
"payload": {
|
|
"type": "ambiance",
|
|
"vol": e.target.value
|
|
}
|
|
}))
|
|
})
|
|
|
|
avol.nextElementSibling.addEventListener("change", (e) => {
|
|
e.target.previousElementSibling.value = e.target.value
|
|
e.target.previousElementSibling.dispatchEvent(new Event('change'));
|
|
})
|
|
|
|
addInteractHandler(
|
|
document.querySelector("input#next"), (e) => {
|
|
ws.send(JSON.stringify({
|
|
"event": "next"
|
|
}))
|
|
})
|
|
|
|
addInteractHandler(
|
|
document.querySelector("input#prev"), (e) => {
|
|
ws.send(JSON.stringify({
|
|
"event": "prev"
|
|
}))
|
|
})
|
|
|
|
Sortable.create(items, {
|
|
delay: DND_DELAY,
|
|
delayOnTouchOnly: true,
|
|
group: "dndmusicbot-playlists",
|
|
filter: ".locked",
|
|
onMove: (e) => {
|
|
if (e.related) {
|
|
return !e.related.classList.contains('locked');
|
|
}
|
|
},
|
|
invertSwap: true,
|
|
store: {
|
|
get: function (sortable) {
|
|
var order = localStorage.getItem(sortable.options.group.name);
|
|
return order ? order.split('|') : [];
|
|
},
|
|
set: function (sortable) {
|
|
var order = sortable.toArray();
|
|
localStorage.setItem(sortable.options.group.name, order.join('|'));
|
|
}
|
|
}
|
|
})
|
|
|
|
Sortable.create(amb, {
|
|
delay: DND_DELAY,
|
|
delayOnTouchOnly: true,
|
|
group: "dndmusicbot-ambiance",
|
|
filter: ".locked",
|
|
onMove: (e) => {
|
|
if (e.related) {
|
|
return !e.related.classList.contains('locked');
|
|
}
|
|
},
|
|
invertSwap: true,
|
|
store: {
|
|
get: function (sortable) {
|
|
var order = localStorage.getItem(sortable.options.group.name);
|
|
return order ? order.split('|') : [];
|
|
},
|
|
set: function (sortable) {
|
|
var order = sortable.toArray();
|
|
localStorage.setItem(sortable.options.group.name, order.join('|'));
|
|
}
|
|
}
|
|
})
|
|
|
|
ws.onopen = (e) => {
|
|
waiting.classList.add("u-hidden")
|
|
}
|
|
|
|
ws.onclose = (e) => {
|
|
waiting.classList.remove("u-hidden")
|
|
}
|
|
|
|
ws.onmessage = (e) => {
|
|
data = JSON.parse(e.data)
|
|
switch (data.event) {
|
|
case "volume":
|
|
pvol.value = data.payload.playlist
|
|
pvol.nextElementSibling.value = data.payload.playlist
|
|
avol.value = data.payload.ambiance
|
|
avol.nextElementSibling.value = data.payload.ambiance
|
|
break
|
|
case "ambiance_add":
|
|
const container = document.querySelector("#ambiance")
|
|
var newdiv = document.createElement('div');
|
|
newdiv.className = "item"
|
|
newdiv.dataset.id = data.payload.id
|
|
newdiv.innerText = data.payload.title
|
|
addInteractHandler(newdiv, (e, isTouch) => {
|
|
isTouch && e.preventDefault()
|
|
|
|
var id = e.target.dataset.id
|
|
ws.send(JSON.stringify({
|
|
"event": ((id === "reset") ? "ambiance_stop" : "ambiance_play"),
|
|
"payload": id
|
|
}))
|
|
})
|
|
|
|
container.insertBefore(newdiv, document.querySelector("#ambiance div:last-child"))
|
|
break
|
|
case "ambiance_play":
|
|
document.querySelectorAll("#ambiance > div").forEach((e) => {e.style.removeProperty("background-color")})
|
|
document.querySelector(`#ambiance > div[data-id='${data.payload.id}']`).style.backgroundColor = "burlywood"
|
|
ambiance.style.pointerEvents = 'auto'
|
|
break
|
|
case "ambiance_stop":
|
|
document.querySelectorAll("#ambiance > div").forEach((el) => {
|
|
el.style.removeProperty("background-color")
|
|
})
|
|
break
|
|
case "song_info":
|
|
document.querySelectorAll("#items > div").forEach((el) => {
|
|
el.style.removeProperty("background-color")
|
|
})
|
|
if (data.payload.pause) {
|
|
info.classList.add("u-hidden")
|
|
} else {
|
|
info.classList.remove("u-hidden")
|
|
link.children.channel.innerText = data.payload.channel
|
|
link.children.title.innerText = data.payload.current
|
|
link.href = "https://youtu.be/" + data.payload.song
|
|
link.target = "_blank"
|
|
time.innerText = `${msToTime(data.payload.position)} / ${msToTime(data.payload.len)}`
|
|
document.querySelector(`#items > div[data-id='${data.payload.playlist}']`).style.backgroundColor = "burlywood"
|
|
}
|
|
setTimeout(() => {
|
|
items.style.pointerEvents = 'auto'
|
|
}, 1000);
|
|
break
|
|
case "song_position":
|
|
time.innerText = `${msToTime(data.payload.position)} / ${msToTime(data.payload.len)}`
|
|
info.classList.remove("u-hidden")
|
|
break
|
|
case "stop":
|
|
setTimeout(() => {
|
|
items.style.pointerEvents = 'auto'
|
|
}, 2000);
|
|
|
|
info.classList.add("u-hidden")
|
|
break
|
|
case "new_playlist":
|
|
addPlaylist(data.payload);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
|
|
addInteractHandler(document.querySelector("#addambiance"), (e, isTouch) => {
|
|
isTouch && e.preventDefault()
|
|
|
|
//output.innerText = ""
|
|
var title = document.querySelector("#inputambiance > input[name='title']")
|
|
var url = document.querySelector("#inputambiance > input[name='url']")
|
|
if (title.value == "" || url.value == "") {
|
|
console.log("Title or Url is empty!")
|
|
return
|
|
}
|
|
|
|
ws.send(JSON.stringify({
|
|
"event": "ambiance_add",
|
|
"payload": {
|
|
"title": title.value,
|
|
"url": url.value
|
|
}
|
|
}))
|
|
|
|
title.value = ""
|
|
url.value = ""
|
|
})
|
|
|
|
addInteractHandler(submit, (e, isTouch) => {
|
|
isTouch && e.preventDefault()
|
|
|
|
//output.innerText = ""
|
|
var title = document.querySelector("#inputplaylist > input[name='title']")
|
|
var url = document.querySelector("#inputplaylist > input[name='url']")
|
|
if (title.value == "" || url.value == "") {
|
|
output.innerText = "Title or Url is empty!"
|
|
return
|
|
}
|
|
|
|
ws.send(JSON.stringify({
|
|
"event": "add_playlist",
|
|
"payload": {
|
|
"title": title.value,
|
|
"url": url.value
|
|
}
|
|
}))
|
|
|
|
title.innerText = ""
|
|
url.innerText = ""
|
|
})
|
|
|
|
document.querySelectorAll("#items .item").forEach(item => addInteractHandler(item, (e, isTouch) => {
|
|
isTouch && e.preventDefault()
|
|
e.target.parentElement.style.pointerEvents = 'none'
|
|
const disableui = setTimeout((t) => {
|
|
t.style.pointerEvents = 'auto'
|
|
}, 3000, e.target.parentElement);
|
|
|
|
var id = e.target.dataset.id
|
|
ws.send(JSON.stringify({
|
|
"event": ((id === "reset") ? "stop" : "load_playlist"),
|
|
"payload": id
|
|
}))
|
|
}));
|
|
|
|
document.querySelectorAll("#ambiance .item").forEach(item => addInteractHandler(item, (e, isTouch) => {
|
|
isTouch && e.preventDefault()
|
|
e.target.parentElement.style.pointerEvents = 'none'
|
|
const disableui = setTimeout((t) => {
|
|
t.style.pointerEvents = 'auto'
|
|
}, 3000, e.target.parentElement);
|
|
|
|
var id = e.target.dataset.id
|
|
ws.send(JSON.stringify({
|
|
"event": ((id === "reset") ? "ambiance_stop" : "ambiance_play"),
|
|
"payload": id
|
|
}))
|
|
}));
|
|
};
|
|
|
|
function addPlaylist(payload) {
|
|
const container = document.querySelector("body > div.container")
|
|
var newdiv = document.createElement('div');
|
|
newdiv.className = "item"
|
|
newdiv.dataset.id = payload.url
|
|
newdiv.innerText = payload.title
|
|
addInteractHandler(newdiv, (e, isTouch) => {
|
|
isTouch && e.preventDefault()
|
|
|
|
var id = e.target.dataset.id
|
|
ws.send(JSON.stringify({
|
|
"event": ((id === "reset") ? "stop" : "load_playlist"),
|
|
"payload": id
|
|
}))
|
|
})
|
|
|
|
container.insertBefore(newdiv, document.querySelector("body > div.container div:last-child"))
|
|
|
|
output.innerText = "New playlist was added: " + payload.title
|
|
}
|
|
|
|
function msToTime(duration) {
|
|
var milliseconds = parseInt((duration % 1000) / 100),
|
|
seconds = Math.floor((duration / 1000) % 60),
|
|
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
|
|
|
minutes = (minutes < 10) ? "0" + minutes : minutes;
|
|
seconds = (seconds < 10) ? "0" + seconds : seconds;
|
|
|
|
return minutes + ":" + seconds
|
|
}
|