New Features and Cosmetics
* Adds option to toggle fullscreen * Adds option to hide controls and hide music player(just visualization only) * Font Changes * Adds Stylized tooltips
This commit is contained in:
595
player.js
595
player.js
@@ -1,293 +1,376 @@
|
||||
(function () {
|
||||
let isPlaying = false;
|
||||
let currentTracks = [];
|
||||
let cursor = 0;
|
||||
let howlerInstance;
|
||||
const retroWaveRu = "https://retrowave.ru";
|
||||
let titleEl = document.getElementById("track-name");
|
||||
let coverArtEl = document.getElementsByClassName("music-player")[0];
|
||||
let refreshBtn = document.querySelector(".refresh");
|
||||
const modalEl = document.getElementById("intro-modal");
|
||||
// const uploadInfoEl = document.getElementById("upload-info");
|
||||
const fileUploadEl = document.getElementById("file-upload");
|
||||
let volume = 1;
|
||||
let effectCanvas;
|
||||
let starField;
|
||||
let line3D;
|
||||
const synthwaveColor = 0xff2975;
|
||||
let isPlaying = false;
|
||||
let currentTracks = [];
|
||||
let cursor = 0;
|
||||
let howlerInstance;
|
||||
const retroWaveRu = "https://retrowave.ru";
|
||||
let titleEl = document.getElementById("track-name");
|
||||
let coverArtEl = document.getElementsByClassName("music-player")[0];
|
||||
let refreshBtn = document.querySelector(".refresh");
|
||||
let ovr = document.querySelector(".OVR");
|
||||
let fullScreenBtn = document.querySelector(".fullscreen");
|
||||
const modalEl = document.getElementById("intro-modal");
|
||||
// const uploadInfoEl = document.getElementById("upload-info");
|
||||
const fileUploadEl = document.getElementById("file-upload");
|
||||
let volume = 1;
|
||||
let effectCanvas;
|
||||
let starField;
|
||||
let line3D;
|
||||
const synthwaveColor = 0xff2975;
|
||||
|
||||
openDialog();
|
||||
getMusic();
|
||||
listenUploadFileChange();
|
||||
openDialog();
|
||||
getMusic();
|
||||
listenUploadFileChange();
|
||||
|
||||
coverArtEl.addEventListener("click", (event) => {
|
||||
const isNoPause = event.target.classList.contains("no-pause");
|
||||
if (howlerInstance && !isNoPause) {
|
||||
togglePlay();
|
||||
function initDynamicTooltips() {
|
||||
document.querySelectorAll("[title]").forEach((element) => {
|
||||
const title = element.getAttribute("title");
|
||||
element.removeAttribute("title");
|
||||
element.setAttribute("data-title", title);
|
||||
|
||||
element.addEventListener("mouseenter", function (e) {
|
||||
const tooltip = document.createElement("div");
|
||||
tooltip.textContent = this.getAttribute("data-title");
|
||||
tooltip.style.cssText = `
|
||||
font-family: "VCR", sans-serif;
|
||||
position: fixed;
|
||||
background:rgb(255, 255, 255);
|
||||
color: #000000;
|
||||
border: 1px solidrgb(0, 0, 0);
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
border-radius: 3px;
|
||||
z-index: 10000;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
document.body.appendChild(tooltip);
|
||||
this.tooltipEl = tooltip;
|
||||
|
||||
const rect = this.getBoundingClientRect();
|
||||
tooltip.style.left =
|
||||
rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + "px";
|
||||
tooltip.style.top = rect.top - tooltip.offsetHeight - 8 + "px";
|
||||
});
|
||||
|
||||
element.addEventListener("mouseleave", function () {
|
||||
if (this.tooltipEl) {
|
||||
document.body.removeChild(this.tooltipEl);
|
||||
this.tooltipEl = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", initDynamicTooltips);
|
||||
|
||||
coverArtEl.addEventListener("click", (event) => {
|
||||
const isNoPause = event.target.classList.contains("no-pause");
|
||||
if (howlerInstance && !isNoPause) {
|
||||
togglePlay();
|
||||
}
|
||||
});
|
||||
|
||||
// uploadInfoEl.addEventListener("click", () => {
|
||||
// const confirmText = `Do you really want to change your playlist?\nThis will replace all your retrowave music history.\nIf you are sure about this, make sure to upload a valid json file probably downloaded using history link.`;
|
||||
|
||||
// if (confirm(confirmText)) {
|
||||
// fileUploadEl.click();
|
||||
// }
|
||||
// });
|
||||
|
||||
function listenUploadFileChange() {
|
||||
fileUploadEl.onchange = function () {
|
||||
const selectedFile = fileUploadEl.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(selectedFile, "UTF-8");
|
||||
reader.onload = function (event) {
|
||||
try {
|
||||
const uploadedPlaylist = JSON.parse(event.target.result);
|
||||
localStorage.setItem("retrowave-history", event.target.result);
|
||||
} catch (error) {
|
||||
alert("malformed/invalid json file");
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("initButton")?.addEventListener("click", () => {
|
||||
var hydra = new Hydra({ detectAudio: false });
|
||||
modalEl.classList.remove("open");
|
||||
playMusic();
|
||||
initHydra();
|
||||
initControls();
|
||||
});
|
||||
|
||||
document.getElementById("history").addEventListener("click", () => {
|
||||
downloadHistory();
|
||||
});
|
||||
|
||||
document.addEventListener("keyup", (event) => {
|
||||
const { key } = event;
|
||||
switch (key) {
|
||||
case " ":
|
||||
togglePlay();
|
||||
break;
|
||||
case "a":
|
||||
break;
|
||||
case "d":
|
||||
break;
|
||||
case "w":
|
||||
volumeUp();
|
||||
break;
|
||||
case "s":
|
||||
volumeDown();
|
||||
break;
|
||||
case "n":
|
||||
playNextTrack();
|
||||
break;
|
||||
case "f":
|
||||
toggleFullScreen();
|
||||
break;
|
||||
case "h":
|
||||
toggleControls();
|
||||
break;
|
||||
case 'x':
|
||||
toggleEverything();
|
||||
}
|
||||
});
|
||||
|
||||
let touchstartX = 0;
|
||||
let touchendX = 0;
|
||||
let touchstartY = 0;
|
||||
let touchendY = 0;
|
||||
const musixPlayerEl = document.getElementsByClassName("music-player")[0];
|
||||
if (musixPlayerEl) {
|
||||
musixPlayerEl.addEventListener("touchstart", (e) => {
|
||||
touchstartX = e.changedTouches[0].screenX;
|
||||
touchstartY = e.changedTouches[0].screenY;
|
||||
});
|
||||
|
||||
// uploadInfoEl.addEventListener("click", () => {
|
||||
// const confirmText = `Do you really want to change your playlist?\nThis will replace all your retrowave music history.\nIf you are sure about this, make sure to upload a valid json file probably downloaded using history link.`;
|
||||
|
||||
// if (confirm(confirmText)) {
|
||||
// fileUploadEl.click();
|
||||
// }
|
||||
// });
|
||||
|
||||
function listenUploadFileChange() {
|
||||
fileUploadEl.onchange = function () {
|
||||
const selectedFile = fileUploadEl.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(selectedFile, "UTF-8");
|
||||
reader.onload = function (event) {
|
||||
try {
|
||||
const uploadedPlaylist = JSON.parse(event.target.result);
|
||||
localStorage.setItem("retrowave-history", event.target.result);
|
||||
|
||||
} catch (error) {
|
||||
alert("malformed/invalid json file");
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("initButton")?.addEventListener("click", () => {
|
||||
var hydra = new Hydra({ detectAudio: false })
|
||||
modalEl.classList.remove("open");
|
||||
playMusic();
|
||||
initHydra();
|
||||
initControls();
|
||||
musixPlayerEl.addEventListener("touchend", (e) => {
|
||||
touchendX = e.changedTouches[0].screenX;
|
||||
touchendY = e.changedTouches[0].screenY;
|
||||
checkDirection();
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("history").addEventListener("click", () => {
|
||||
downloadHistory();
|
||||
function checkDirection() {
|
||||
if (touchendX < touchstartX) {
|
||||
// Swipe Left
|
||||
}
|
||||
if (touchendX > touchstartX) {
|
||||
// Swipe Right
|
||||
}
|
||||
|
||||
if (touchendY < touchstartY) {
|
||||
volumeUp();
|
||||
}
|
||||
if (touchendY > touchstartY) {
|
||||
volumeDown();
|
||||
}
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
modalEl.classList.add("open");
|
||||
}
|
||||
|
||||
async function getMusic() {
|
||||
const res = await fetch(
|
||||
`https://retrowave.ru/api/v1/tracks?limit=10&cursor=${cursor}`
|
||||
).then((res) => res.json());
|
||||
const {
|
||||
body: { tracks, cursor: currentCursor },
|
||||
} = res;
|
||||
cursor = currentCursor;
|
||||
currentTracks = tracks;
|
||||
}
|
||||
|
||||
function initPlayer() {}
|
||||
|
||||
function playMusic() {
|
||||
const currentTrack = currentTracks[0];
|
||||
const singleTrack = currentTrack.streamUrl;
|
||||
const fullTrack = `${retroWaveRu}${singleTrack}`;
|
||||
howlerInstance = new Howl({
|
||||
src: [fullTrack],
|
||||
html5: true,
|
||||
onend: function () {
|
||||
playNextTrack();
|
||||
},
|
||||
});
|
||||
setVolume();
|
||||
isPlaying = true;
|
||||
window.hw = howlerInstance;
|
||||
|
||||
document.addEventListener("keyup", (event) => {
|
||||
const { key } = event;
|
||||
console.log("key pressed", key);
|
||||
switch (key) {
|
||||
case " ":
|
||||
togglePlay();
|
||||
break;
|
||||
case "a":
|
||||
break;
|
||||
case "d":
|
||||
break;
|
||||
case "w":
|
||||
volumeUp();
|
||||
break;
|
||||
case "s":
|
||||
volumeDown();
|
||||
break;
|
||||
case "n":
|
||||
playNextTrack();
|
||||
break;
|
||||
updateInfo(currentTrack);
|
||||
|
||||
}
|
||||
});
|
||||
addToHistory(currentTrack);
|
||||
howlerInstance.play();
|
||||
}
|
||||
|
||||
let touchstartX = 0;
|
||||
let touchendX = 0;
|
||||
let touchstartY = 0;
|
||||
let touchendY = 0;
|
||||
const musixPlayerEl = document.getElementsByClassName("music-player")[0];
|
||||
if (musixPlayerEl) {
|
||||
musixPlayerEl.addEventListener("touchstart", (e) => {
|
||||
touchstartX = e.changedTouches[0].screenX;
|
||||
touchstartY = e.changedTouches[0].screenY;
|
||||
});
|
||||
|
||||
musixPlayerEl.addEventListener("touchend", (e) => {
|
||||
touchendX = e.changedTouches[0].screenX;
|
||||
touchendY = e.changedTouches[0].screenY;
|
||||
checkDirection();
|
||||
});
|
||||
function volumeDown() {
|
||||
if (volume > 0.1) {
|
||||
volume -= 0.1;
|
||||
}
|
||||
setVolume();
|
||||
}
|
||||
|
||||
function checkDirection() {
|
||||
if (touchendX < touchstartX) {
|
||||
// Swipe Left
|
||||
}
|
||||
if (touchendX > touchstartX) {
|
||||
// Swipe Right
|
||||
}
|
||||
|
||||
if (touchendY < touchstartY) {
|
||||
volumeUp();
|
||||
}
|
||||
if (touchendY > touchstartY) {
|
||||
volumeDown();
|
||||
}
|
||||
function volumeUp() {
|
||||
if (volume < 1) {
|
||||
volume += 0.1;
|
||||
}
|
||||
setVolume();
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
modalEl.classList.add("open");
|
||||
function setVolume() {
|
||||
howlerInstance.volume(volume);
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
isPlaying = !isPlaying;
|
||||
|
||||
if (isPlaying) {
|
||||
howlerInstance.play();
|
||||
} else {
|
||||
howlerInstance.pause();
|
||||
}
|
||||
}
|
||||
|
||||
async function getMusic() {
|
||||
const res = await fetch(
|
||||
`https://retrowave.ru/api/v1/tracks?limit=10&cursor=${cursor}`
|
||||
).then((res) => res.json());
|
||||
const {
|
||||
body: { tracks, cursor: currentCursor },
|
||||
} = res;
|
||||
cursor = currentCursor;
|
||||
currentTracks = tracks;
|
||||
}
|
||||
function updateInfo(trackDetails) {
|
||||
titleEl.innerText = trackDetails.title;
|
||||
document.title = `Now Playing... ${trackDetails.title}`;
|
||||
coverArtEl.style.backgroundImage = `url("${retroWaveRu}${trackDetails.artworkUrl}")`;
|
||||
ovr.innerHTML = `${trackDetails.title}`
|
||||
}
|
||||
|
||||
function initPlayer() { }
|
||||
function getHistory() {
|
||||
let localHistoryStore = localStorage.getItem("retrowave-history") || "[]";
|
||||
let historyArray = JSON.parse(localHistoryStore);
|
||||
return historyArray;
|
||||
}
|
||||
|
||||
function playMusic() {
|
||||
const currentTrack = currentTracks[0];
|
||||
const singleTrack = currentTrack.streamUrl;
|
||||
const fullTrack = `${retroWaveRu}${singleTrack}`;
|
||||
howlerInstance = new Howl({
|
||||
src: [fullTrack],
|
||||
html5: true,
|
||||
onend: function () {
|
||||
playNextTrack();
|
||||
},
|
||||
});
|
||||
setVolume();
|
||||
isPlaying = true;
|
||||
window.hw = howlerInstance
|
||||
function addToHistory(trackDetails) {
|
||||
let historyArray = getHistory();
|
||||
historyArray.push(trackDetails);
|
||||
localStorage.setItem("retrowave-history", JSON.stringify(historyArray));
|
||||
}
|
||||
|
||||
updateInfo(currentTrack);
|
||||
|
||||
addToHistory(currentTrack);
|
||||
howlerInstance.play();
|
||||
}
|
||||
|
||||
function volumeDown() {
|
||||
console.log(volume);
|
||||
if (volume > 0.1) {
|
||||
volume -= 0.1;
|
||||
}
|
||||
setVolume();
|
||||
}
|
||||
|
||||
function volumeUp() {
|
||||
if (volume < 1) {
|
||||
volume += 0.1;
|
||||
}
|
||||
setVolume();
|
||||
}
|
||||
|
||||
function setVolume() {
|
||||
howlerInstance.volume(volume);
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
isPlaying = !isPlaying;
|
||||
|
||||
if (isPlaying) {
|
||||
howlerInstance.play();
|
||||
} else {
|
||||
howlerInstance.pause();
|
||||
}
|
||||
}
|
||||
|
||||
function updateInfo(trackDetails) {
|
||||
titleEl.innerText = trackDetails.title;
|
||||
document.title = `Now Playing... ${trackDetails.title}`;
|
||||
coverArtEl.style.backgroundImage = `url("${retroWaveRu}${trackDetails.artworkUrl}")`;
|
||||
}
|
||||
|
||||
function getHistory() {
|
||||
let localHistoryStore = localStorage.getItem("retrowave-history") || "[]";
|
||||
let historyArray = JSON.parse(localHistoryStore);
|
||||
return historyArray;
|
||||
}
|
||||
|
||||
function addToHistory(trackDetails) {
|
||||
let historyArray = getHistory();
|
||||
historyArray.push(trackDetails);
|
||||
localStorage.setItem("retrowave-history", JSON.stringify(historyArray));
|
||||
}
|
||||
|
||||
function downloadHistory() {
|
||||
const historyArray = getHistory();
|
||||
let element = document.createElement("a");
|
||||
let playListData = '#EXTM3U';
|
||||
historyArray.forEach((musicData) => {
|
||||
playListData = `${playListData}
|
||||
function downloadHistory() {
|
||||
const historyArray = getHistory();
|
||||
let element = document.createElement("a");
|
||||
let playListData = "#EXTM3U";
|
||||
historyArray.forEach((musicData) => {
|
||||
playListData = `${playListData}
|
||||
|
||||
#EXTINF:${Math.ceil(musicData.duration / 1000)}, ${musicData.title}
|
||||
https://retrowave.ru/${musicData.streamUrl}
|
||||
`;
|
||||
});
|
||||
});
|
||||
|
||||
element.setAttribute(
|
||||
"href",
|
||||
"data:audio/x-mpegurl;;charset=utf-8," +
|
||||
encodeURIComponent(playListData)
|
||||
);
|
||||
element.setAttribute("download", "retrowave_playlist.m3u");
|
||||
element.setAttribute(
|
||||
"href",
|
||||
"data:audio/x-mpegurl;;charset=utf-8," + encodeURIComponent(playListData)
|
||||
);
|
||||
element.setAttribute("download", "retrowave_playlist.m3u");
|
||||
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
function playNextTrack() {
|
||||
resetHowler();
|
||||
currentTracks.shift();
|
||||
if (currentTracks.length <= 3) {
|
||||
getMusic();
|
||||
}
|
||||
playMusic();
|
||||
}
|
||||
|
||||
function playNextTrack() {
|
||||
resetHowler();
|
||||
currentTracks.shift();
|
||||
if (currentTracks.length <= 3) {
|
||||
getMusic();
|
||||
}
|
||||
playMusic();
|
||||
function resetHowler(destroy = false) {
|
||||
howlerInstance.stop();
|
||||
if (destroy) {
|
||||
howlerInstance.unload();
|
||||
howlerInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
function resetHowler(destroy = false) {
|
||||
howlerInstance.stop();
|
||||
if (destroy) {
|
||||
howlerInstance.unload();
|
||||
howlerInstance = null;
|
||||
}
|
||||
function initControls() {
|
||||
refreshBtn.addEventListener("click", () => {
|
||||
playNextTrack();
|
||||
});
|
||||
|
||||
fullScreenBtn.addEventListener("click", () => {
|
||||
toggleFullScreen();
|
||||
});
|
||||
}
|
||||
|
||||
function toggleFullScreen() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initControls() {
|
||||
refreshBtn.addEventListener("click", () => {
|
||||
playNextTrack();
|
||||
});
|
||||
}
|
||||
|
||||
// function initCodef() {
|
||||
// const width = window.innerWidth;
|
||||
// const height = window.innerHeight;
|
||||
// effectCanvas = new canvas(width, height, 'codef-canvas');
|
||||
// starField = new starfield3D(effectCanvas, 500, 2, width, height, width/2, height/2, '#ffffff', 100, 0, 0);
|
||||
// line3D = new codef3D(effectCanvas, 320, 75, 1, 1500 );
|
||||
// line3D.line({x:-320, y:0, z:0},{x:320, y:0, z:0}, new LineBasicMaterial({ color: synthwaveColor, linewidth:2}));
|
||||
// line3D.line({x: 0, y:-240, z:0},{x:0, y:240, z:0}, new LineBasicMaterial({ color: synthwaveColor, linewidth:2}));
|
||||
// renderCodeFx();
|
||||
// }
|
||||
|
||||
// function renderCodeFx() {
|
||||
// effectCanvas.fill('#000000');
|
||||
// line3D.group.rotation.x+=0.01;
|
||||
// line3D.group.rotation.y+=0.02;
|
||||
// line3D.group.rotation.z+=0.04;
|
||||
// starField.draw();
|
||||
// line3D.draw();
|
||||
// requestAnimationFrame(renderCodeFx);
|
||||
// }
|
||||
|
||||
function initHydra() {
|
||||
voronoi(350, 0.15)
|
||||
.modulateScale(osc(8).rotate(Math.sin(time)), .5)
|
||||
.thresh(.8)
|
||||
.modulateRotate(osc(7), .4)
|
||||
.thresh(.7)
|
||||
.diff(src(o0).scale(1.8))
|
||||
.modulateScale(osc(2).modulateRotate(o0, .74))
|
||||
.diff(src(o0).rotate([-.012, .01, -.002, 0]).scrollY(0, [-1 / 199800, 0].fast(0.7)))
|
||||
.brightness([-.02, -.17].smooth().fast(.5))//.modulate(o0, () => a.fft[1] * .2)
|
||||
.out()
|
||||
function toggleControls() {
|
||||
const toggleableElements = document.querySelectorAll(".toggleable");
|
||||
toggleableElements.forEach((el) => {
|
||||
el.classList.toggle("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
function toggleEverything() {
|
||||
musixPlayerEl.classList.toggle("hidden");
|
||||
ovr.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
// function initCodef() {
|
||||
// const width = window.innerWidth;
|
||||
// const height = window.innerHeight;
|
||||
// effectCanvas = new canvas(width, height, 'codef-canvas');
|
||||
// starField = new starfield3D(effectCanvas, 500, 2, width, height, width/2, height/2, '#ffffff', 100, 0, 0);
|
||||
// line3D = new codef3D(effectCanvas, 320, 75, 1, 1500 );
|
||||
// line3D.line({x:-320, y:0, z:0},{x:320, y:0, z:0}, new LineBasicMaterial({ color: synthwaveColor, linewidth:2}));
|
||||
// line3D.line({x: 0, y:-240, z:0},{x:0, y:240, z:0}, new LineBasicMaterial({ color: synthwaveColor, linewidth:2}));
|
||||
// renderCodeFx();
|
||||
// }
|
||||
|
||||
// function renderCodeFx() {
|
||||
// effectCanvas.fill('#000000');
|
||||
// line3D.group.rotation.x+=0.01;
|
||||
// line3D.group.rotation.y+=0.02;
|
||||
// line3D.group.rotation.z+=0.04;
|
||||
// starField.draw();
|
||||
// line3D.draw();
|
||||
// requestAnimationFrame(renderCodeFx);
|
||||
// }
|
||||
|
||||
function initHydra() {
|
||||
try {
|
||||
voronoi(350, 0.15)
|
||||
.modulateScale(osc(8).rotate(Math.sin(time)), 0.5)
|
||||
.thresh(0.8)
|
||||
.modulateRotate(osc(7), 0.4)
|
||||
.thresh(0.7)
|
||||
.diff(src(o0).scale(1.8))
|
||||
.modulateScale(osc(2).modulateRotate(o0, 0.74))
|
||||
.diff(
|
||||
src(o0)
|
||||
.rotate([-0.012, 0.01, -0.002, 0])
|
||||
.scrollY(0, [-1 / 199800, 0].fast(0.7))
|
||||
)
|
||||
.brightness([-0.02, -0.17].smooth().fast(0.5)) //.modulate(o0, () => a.fft[1] * .2)
|
||||
.out();
|
||||
} catch (error) {
|
||||
console.error("Hydra initialization failed:", error);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user