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 }