diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/.DS_Store | bin | 6148 -> 6148 bytes | |||
-rw-r--r-- | app/index.php | 6 | ||||
-rw-r--r-- | app/studio.php | 445 | ||||
-rw-r--r-- | app/ui/listing.php | 27 | ||||
-rw-r--r-- | app/ui/lyrics.php | 134 | ||||
-rw-r--r-- | app/ui/navigation.php | 12 | ||||
-rw-r--r-- | app/ui/settings.php | 11 | ||||
-rw-r--r-- | app/ui/stella.php | 2 | ||||
-rw-r--r-- | app/ui/video.php | 7 |
9 files changed, 138 insertions, 506 deletions
diff --git a/app/.DS_Store b/app/.DS_Store Binary files differindex 60beba8..c348c57 100644 --- a/app/.DS_Store +++ b/app/.DS_Store diff --git a/app/index.php b/app/index.php index 82b4791..8a6aff6 100644 --- a/app/index.php +++ b/app/index.php @@ -56,7 +56,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } if (window.MistNative) { - MistNative.version("<?= explode("|", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")))[0] ?>", "<?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>"); + MistNative.version("<?= explode("|", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")))[0] ?>", "<?= trim(file_exists($_SERVER['DOCUMENT_ROOT'] . "/build.txt") ? file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/build.txt") : (file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk")) ?>"); MistNative.userInfo(`<?= str_replace("`", "\\`", json_encode($_PROFILE)) ?>`); } </script> @@ -209,6 +209,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI if (name === "lyrics") showLyrics(); if (name === "search") name = "home"; if (name === "stella") name = "settings"; + if (name === "video") name = "settings"; Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); document.getElementById("navigation").contentDocument.getElementById(name).classList.add("active"); } catch (e) { @@ -456,6 +457,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } playerDocument.getElementById("player-audio").onplay = () => { + document.getElementById("lyrics-page").contentWindow.updateVideo(); updateAndroidNotification(); if (window.preloadedGains[window.currentSongID]) { @@ -497,6 +499,8 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } playerDocument.getElementById("player-audio").onpause = () => { + document.getElementById("lyrics-page").contentWindow.updateVideo(); + if (window.preloadedGains[window.currentSongID]) { try { window.currentNormalizationSource.disconnect(); diff --git a/app/studio.php b/app/studio.php deleted file mode 100644 index 4f002f7..0000000 --- a/app/studio.php +++ /dev/null @@ -1,445 +0,0 @@ -<?php header("X-Frame-Options: SAMEORIGIN"); require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; ?> -<!doctype html> -<html lang="en"> -<head> - <script> - if (typeof require === "undefined") { - location.href = "/app/"; - } - </script> - <style> - * { - outline: none; - user-select: none; - -webkit-user-drag: none; - } - - *:hover { - outline: 1px solid #00bbff; - } - - button.active { - outline: 1px solid #00ff04 !important; - } - - *::placeholder { - font-style: italic; - } - </style> - <meta charset="UTF-8"> - <meta name="viewport" - content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> - <meta http-equiv="X-UA-Compatible" content="ie=edge"> - <title>Mist Studio</title> - <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> - <script src="/assets/js/pako.js"></script> - <script src="/assets/js/stella.js"></script> -</head> -<body style="background-color: #111111;"> - <div id="app" style="position: fixed; inset: 0; background-color: #111111; display: none;"> - <div id="header" style="position: fixed; top: 5px; left: 5px; right: 5px; height: 48px; background-color: #222222;"></div> - <div id="controls" style="position: fixed; top: 60px; left: 5px; bottom: 5px; width: 256px; background-color: #222222;"> - <div style="background-color: #333; height: 24px;" id="seek"> - <div style="background-color: #444; height: 24px; pointer-events: none;" id="seek-inner"></div> - <span id="seek-cursor" style="pointer-events: none; top: -24px; background-color: red; width: 1px; height: 24px; position: relative; display: none;"></span> - </div> - <script> - document.getElementById("seek").onmouseenter = () => { - document.getElementById("seek-cursor").style.display = "inline-block"; - } - - document.getElementById("seek").onmouseleave = () => { - document.getElementById("seek-cursor").style.display = "none"; - } - - document.getElementById("seek").onmousemove = (e) => { - document.getElementById("seek-cursor").style.left = ((e.offsetX / document.getElementById("seek").clientWidth) * 100) + "%"; - } - - document.getElementById("seek").onclick = (e) => { - let factor = e.offsetX / document.getElementById("seek").clientWidth; - document.getElementById("audio-stream-0").currentTime = document.getElementById("audio-stream-0").duration * factor; - - if (playingStella) { - document.getElementById("audio-stream-1").currentTime = - document.getElementById("audio-stream-2").currentTime = - document.getElementById("audio-stream-3").currentTime = - document.getElementById("audio-stream-4").currentTime = - document.getElementById("audio-stream-5").currentTime = document.getElementById("audio-stream-0").currentTime; - } - } - </script> - <div style="color: white; text-align: center; margin-top: 20px;" id="status">Empty queue</div> - <div id="buttons" style="text-align: center;"> - <button id="btn-prev" style="border: 1px solid #151515; background-color: #333; color: white;"><</button> - <button id="btn-play" onclick="togglePlay();" style="border: 1px solid #151515; background-color: #333; color: white;">Play</button> - <button id="btn-next" style="border: 1px solid #151515; background-color: #333; color: white;">></button> - </div> - <div id="details" style="margin-top: 20px; color: white; font-family: monospace;"> - <div id="time">--:--.--- / ---:--.---</div> - <div id="delay">------ samples</div> - <div id="delay-ms">---- ms</div> - </div> - <div id="clip-indicators" style="display: grid; height: 8px; grid-template-columns: repeat(6, 1fr); pointer-events: none; grid-gap: 2px; position: absolute; bottom: 210px; left: 0; right: 0; margin: 10px; margin-bottom: 5px;"> - <div id="ci-0" style="background-color: #151515;"></div> - <div id="ci-1" style="background-color: #151515;"></div> - <div id="ci-2" style="background-color: #151515;"></div> - <div id="ci-3" style="background-color: #151515;"></div> - <div id="ci-4" style="background-color: #151515;"></div> - <div id="ci-5" style="background-color: #151515;"></div> - </div> - <div id="meters" style="display: grid; grid-template-columns: repeat(6, 1fr); pointer-events: none; grid-gap: 2px; position: absolute; bottom: 0; left: 0; right: 0; margin: 10px;"> - <div id="meter-0" style="background-image: linear-gradient(0deg, rgba(124,255,129,1) 0%, rgba(253,255,124,1) 75%, rgba(255,124,124,1) 100%); height: 200px; display: flex; align-items: start;"> - <div id="meter-0-inner" style="background-color: #151515; width: 100%; height: 100%;"></div> - </div> - <div id="meter-1" style="background-image: linear-gradient(0deg, rgba(124,255,129,1) 0%, rgba(253,255,124,1) 75%, rgba(255,124,124,1) 100%); height: 200px; display: flex; align-items: start;"> - <div id="meter-1-inner" style="background-color: #151515; width: 100%; height: 100%;"></div> - </div> - <div id="meter-2" style="background-image: linear-gradient(0deg, rgba(124,255,129,1) 0%, rgba(253,255,124,1) 75%, rgba(255,124,124,1) 100%); height: 200px; display: flex; align-items: start;"> - <div id="meter-2-inner" style="background-color: #151515; width: 100%; height: 100%;"></div> - </div> - <div id="meter-3" style="background-image: linear-gradient(0deg, rgba(124,255,129,1) 0%, rgba(253,255,124,1) 75%, rgba(255,124,124,1) 100%); height: 200px; display: flex; align-items: start;"> - <div id="meter-3-inner" style="background-color: #151515; width: 100%; height: 100%;"></div> - </div> - <div id="meter-4" style="background-image: linear-gradient(0deg, rgba(124,255,129,1) 0%, rgba(253,255,124,1) 75%, rgba(255,124,124,1) 100%); height: 200px; display: flex; align-items: start;"> - <div id="meter-4-inner" style="background-color: #151515; width: 100%; height: 100%;"></div> - </div> - <div id="meter-5" style="background-image: linear-gradient(0deg, rgba(124,255,129,1) 0%, rgba(253,255,124,1) 75%, rgba(255,124,124,1) 100%); height: 200px; display: flex; align-items: start;"> - <div id="meter-5-inner" style="background-color: #151515; width: 100%; height: 100%;"></div> - </div> - </div> - </div> - <div id="queue" style="position: fixed; top: 60px; left: 269px; right: 5px; height: calc((100vh - 72px) / 2); background-color: #222222; overflow: auto;"></div> - <div id="library" style="display: grid; grid-template-rows: 42px 1fr; position: fixed; top: calc((60px) + (100vh - 58px) / 2); left: 269px; right: 5px; height: calc((100vh - 72px) / 2); background-color: #222222;"> - <input id="query" placeholder="Search..." style="background-color: #252525; border: 1px solid #151515; color: white;" onkeydown="refreshSearch();" onkeyup="refreshSearch();" onchange="refreshSearch();"> - <div id="items" style="overflow: auto;"></div> - </div> - </div> - - <audio id="audio-stream-0"></audio> - <audio id="audio-stream-1"></audio> - <audio id="audio-stream-2"></audio> - <audio id="audio-stream-3"></audio> - <audio id="audio-stream-4"></audio> - <audio id="audio-stream-5"></audio> - - <script> - for (let id of [0, 1, 2, 3, 4, 5]) { - let audioContext = new AudioContext(); - let analyser = audioContext.createAnalyser(); - let source = audioContext.createMediaElementSource(document.getElementById("audio-stream-" + id)); - let javascriptNode = audioContext.createScriptProcessor(2048, 1, 1); - - analyser.fftSize = 1024; - - source.connect(analyser); - analyser.connect(javascriptNode); - javascriptNode.connect(audioContext.destination); - source.connect(audioContext.destination); - - javascriptNode.onaudioprocess = function() { - let array = new Uint8Array(analyser.frequencyBinCount); - analyser.getByteFrequencyData(array); - let values = 0; - - let length = array.length; - for (let i = 0; i < length; i++) { - values += (array[i]); - } - - let average = values / length; - let value = average / (playingStella ? 40 : 60); - - if (value < 0) value = 0; - if (value > 3) { - value = 3; - if (document.getElementById("ci-" + id).style.backgroundColor !== "rgb(21, 21, 21)") document.getElementById("ci-" + id).style.backgroundColor = "#ff7c7c"; - } else { - if (document.getElementById("ci-" + id).style.backgroundColor !== "rgb(21, 21, 21)") document.getElementById("ci-" + id).style.backgroundColor = "#8dff7c"; - } - - document.getElementById("meter-" + id + "-inner").style.height = Math.abs(100 - ((value / 3) * 100)) + "%"; - } - } - - window.queue = []; - window.shouldPlay = false; - - function getTime(time) { - let minutes = Math.floor(time / 60); - let seconds = Math.floor(time) - minutes * 60; - let ms = Math.floor(time * 1000) - minutes * 60000 - seconds * 1000; - - return "00".substring(0, 2 - minutes.toString().length) + minutes + ":" + "00".substring(0, 2 - seconds.toString().length) + seconds + "." + "000".substring(0, 3 - ms.toString().length) + ms - } - - function togglePlay() { - if (!shouldPlay) { - document.getElementById("btn-play").classList.add("active"); - } else { - document.getElementById("btn-play").classList.remove("active"); - } - - shouldPlay = !shouldPlay; - } - - document.getElementById("audio-stream-0").onplay = () => { - if (playingStella) { - document.getElementById("ci-0").style.backgroundColor = "#8dff7c"; - document.getElementById("ci-1").style.backgroundColor = "#8dff7c"; - document.getElementById("ci-2").style.backgroundColor = "#8dff7c"; - document.getElementById("ci-3").style.backgroundColor = "#8dff7c"; - document.getElementById("ci-4").style.backgroundColor = "#8dff7c"; - document.getElementById("ci-5").style.backgroundColor = "#8dff7c"; - } else { - document.getElementById("ci-0").style.backgroundColor = "#8dff7c"; - document.getElementById("ci-1").style.backgroundColor = "#151515"; - document.getElementById("ci-2").style.backgroundColor = "#151515"; - document.getElementById("ci-3").style.backgroundColor = "#151515"; - document.getElementById("ci-4").style.backgroundColor = "#151515"; - document.getElementById("ci-5").style.backgroundColor = "#151515"; - } - - window.uiUpdateInt = setInterval(() => { - document.getElementById("time").innerText = getTime(document.getElementById("audio-stream-0").currentTime) + " / -" + getTime(Math.abs(document.getElementById("audio-stream-0").duration - document.getElementById("audio-stream-0").currentTime)); - - if (playingStella) { - let delay = Math.max( - document.getElementById("audio-stream-1").currentTime - document.getElementById("audio-stream-0").currentTime, - document.getElementById("audio-stream-2").currentTime - document.getElementById("audio-stream-0").currentTime, - document.getElementById("audio-stream-3").currentTime - document.getElementById("audio-stream-0").currentTime, - document.getElementById("audio-stream-4").currentTime - document.getElementById("audio-stream-0").currentTime, - document.getElementById("audio-stream-5").currentTime - document.getElementById("audio-stream-0").currentTime, - ); - - let oldReal; - let real = oldReal = Math.round(delay * 44100); - real = "00000".substring(0, 5 - Math.abs(real).toString().length) + Math.abs(real); - let realMs = "000".substring(0, 3 - Math.abs(Math.round(real / 44.100)).toString().length) + Math.abs(Math.round(real / 44.100)); - - document.getElementById("delay").innerText = (oldReal > 0 ? "+" : (oldReal < 0 ? "-" : "0")) + real + " samples"; - document.getElementById("delay-ms").innerText = (oldReal > 0 ? "+" : (oldReal < 0 ? "-" : "0")) + realMs + " ms"; - } else { - document.getElementById("delay").innerText = "------ samples"; - document.getElementById("delay-ms").innerText = "---- ms"; - } - }); - } - - document.getElementById("audio-stream-0").onpause = () => { - clearInterval(window.uiUpdateInt); - document.getElementById("ci-0").style.backgroundColor = "#151515"; - document.getElementById("ci-1").style.backgroundColor = "#151515"; - document.getElementById("ci-2").style.backgroundColor = "#151515"; - document.getElementById("ci-3").style.backgroundColor = "#151515"; - document.getElementById("ci-4").style.backgroundColor = "#151515"; - document.getElementById("ci-5").style.backgroundColor = "#151515"; - } - - document.getElementById("audio-stream-0").onended = () => { - queue.shift(); - refreshQueue(); - - if (queue.length > 0) { - loadSong(); - } else { - document.getElementById("audio-stream-0").src = ""; - document.getElementById("audio-stream-1").src = ""; - document.getElementById("audio-stream-2").src = ""; - document.getElementById("audio-stream-3").src = ""; - document.getElementById("audio-stream-4").src = ""; - document.getElementById("audio-stream-5").src = ""; - document.getElementById("btn-play").classList.remove("active"); - shouldPlay = false; - document.getElementById("time").innerText = "--:--.--- / ---:--.---"; - } - } - - function loadSong() { - window.playingStella = false; - let id = queue[0]; - - if (window.preloaded[id] instanceof Stella) { - document.getElementById("audio-stream-0").src = window.preloaded[id].urls.hpf; - document.getElementById("audio-stream-1").src = window.preloaded[id].urls.bass; - document.getElementById("audio-stream-2").src = window.preloaded[id].urls.drums; - document.getElementById("audio-stream-3").src = window.preloaded[id].urls.other; - document.getElementById("audio-stream-4").src = window.preloaded[id].urls.piano; - document.getElementById("audio-stream-5").src = window.preloaded[id].urls.vocals; - window.playingStella = true; - } else { - document.getElementById("audio-stream-0").src = window.preloadedURLs[id]; - } - } - - setInterval(() => { - if (shouldPlay && document.getElementById("audio-stream-0").paused) { - document.getElementById("audio-stream-0").play(); - - if (playingStella) { - document.getElementById("audio-stream-1").currentTime = - document.getElementById("audio-stream-2").currentTime = - document.getElementById("audio-stream-3").currentTime = - document.getElementById("audio-stream-4").currentTime = - document.getElementById("audio-stream-5").currentTime = document.getElementById("audio-stream-0").currentTime; - document.getElementById("audio-stream-1").play(); - document.getElementById("audio-stream-2").play(); - document.getElementById("audio-stream-3").play(); - document.getElementById("audio-stream-4").play(); - document.getElementById("audio-stream-5").play(); - } - } else if (!shouldPlay && !document.getElementById("audio-stream-0").paused) { - document.getElementById("audio-stream-0").pause(); - - if (playingStella) { - document.getElementById("audio-stream-1").pause(); - document.getElementById("audio-stream-2").pause(); - document.getElementById("audio-stream-3").pause(); - document.getElementById("audio-stream-4").pause(); - document.getElementById("audio-stream-5").pause(); - } - } - - document.getElementById("seek-inner").style.width = ((document.getElementById("audio-stream-0").currentTime / document.getElementById("audio-stream-0").duration) * 100) + "%"; - - refreshStatus(); - }, 1000); - - window.onload = async () => { - window.songs = await (await fetch("/assets/content/songs.json?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); - - document.getElementById("items").innerHTML = Object.entries(songs).map(i => ` - <div onclick="enqueue('${i[0]}');" class="item" id="song-${i[0]}" style="display: grid; grid-template-columns: 1fr 1fr 1fr; color: white;"> - <div style="white-space: nowrap;overflow: hidden !important;text-overflow: ellipsis;">${i[1].title}</div> - <div style="white-space: nowrap;overflow: hidden !important;text-overflow: ellipsis;">${i[1].artist}</div> - <div style="white-space: nowrap;overflow: hidden !important;text-overflow: ellipsis;">${i[1].album}</div> - </div> - `).join(""); - document.getElementById("app").style.display = ""; - } - - function refreshQueue() { - document.getElementById("queue").innerHTML = queue.map((i, j) => [i, songs[i], j]).map(i => ` - <div class="queue" id="queue-${i[0]}" onclick="playImmediately(${i[2]});" style="display: grid; grid-template-columns: 1fr 1fr 1fr; color: white;"> - <div style="white-space: nowrap;overflow: hidden !important;text-overflow: ellipsis;">${i[1].title}</div> - <div style="white-space: nowrap;overflow: hidden !important;text-overflow: ellipsis;">${i[1].artist}</div> - <div style="white-space: nowrap;overflow: hidden !important;text-overflow: ellipsis;">${i[1].album}</div> - </div> - `).join(""); - preloadMore(); - } - - function playImmediately(index) { - let item = queue.splice(index, 1); - queue.unshift(item); - document.getElementById("audio-stream-0").pause(); - - if (playingStella) { - document.getElementById("audio-stream-1").pause(); - document.getElementById("audio-stream-2").pause(); - document.getElementById("audio-stream-3").pause(); - document.getElementById("audio-stream-4").pause(); - document.getElementById("audio-stream-5").pause(); - } - refreshQueue(); - loadSong(); - } - - function refreshSearch() { - let query = document.getElementById("query").value.trim().toLowerCase(); - - if (query === "") { - Array.from(document.getElementsByClassName("item")).map(i => i.style.display = "grid"); - } else { - Array.from(document.getElementsByClassName("item")).map(i => { - let id = i.id.substring(5); - let song = songs[id]; - - if (song.title.toLowerCase().includes(query) || song.artist.toLowerCase().includes(query)) { - i.style.display = "grid"; - } else { - i.style.display = "none"; - } - }); - } - } - - function enqueue(id) { - queue.push(id); - refreshQueue(); - refreshStatus(); - } - - function refreshStatus() { - if (window.preloaded[queue[0]]) { - if (document.getElementById("audio-stream-0").paused) { - document.getElementById("status").innerText = "Ready to play"; - } else { - if (playingStella) { - document.getElementById("status").innerText = "Playing 6 streams"; - } else { - document.getElementById("status").innerText = "Playing 1 stream"; - } - } - } else { - if (queue.length > 0) { - document.getElementById("status").innerText = "Waiting for audio"; - } else { - document.getElementById("status").innerText = "Empty queue"; - } - } - } - - window.preloaded = {}; - window.preloadedURLs = {}; - window.preloadedBlobs = {}; - - async function preloadMore() { - for (let i = 0; i < queue.length; i++) { - if (queue[i]) { - let id = queue[i]; - let stellaCompatible = await (await fetch("/api/hasStella.php?id=" + id)).text() === "true"; - - if (stellaCompatible) { - if (!window.preloaded[id]) { - window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella"); - if (i === 0) loadSong(); - } - } else { - if (!window.preloaded[id]) { - if (localStorage.getItem("data-saving") === "true") { - window.preloaded[id] = await (await fetch("/assets/content/" + id + ".m4a")).arrayBuffer(); - window.preloadedBlobs[id] = new Blob([window.preloaded[id]], { type: "audio/mp4" }); - } else { - window.preloaded[id] = await (await fetch("/assets/content/" + id + ".flac")).arrayBuffer(); - window.preloadedBlobs[id] = new Blob([window.preloaded[id]], { type: "audio/flac" }); - } - } - - if (!window.preloadedURLs[id]) { - window.preloadedURLs[id] = URL.createObjectURL(window.preloadedBlobs[id]); - if (i === 0) loadSong(); - } - } - - refreshStatus(); - } - } - - cleanupPreload(); - } - - function cleanupPreload() { - let keys = Object.keys(window.preloaded).slice(-30); - - for (let key of Object.keys(window.preloaded)) { - if (!keys.includes(key)) { - if (window.preloadedURLs[key]) URL.revokeObjectURL(window.preloadedURLs[key]); - delete window.preloaded[key]; - } - } - } - </script> -</body> -</html>
\ No newline at end of file diff --git a/app/ui/listing.php b/app/ui/listing.php index 29a1bce..fdef674 100644 --- a/app/ui/listing.php +++ b/app/ui/listing.php @@ -37,9 +37,9 @@ if (!$presetList) { } } -if (!isset($onlyStella)) $onlyStella = false; +if (!isset($onlyStella)) $onlyStella = 0; -if ($onlyStella) { +if ($onlyStella === 1) { $hasAlbum = false; $list = $songs; @@ -56,6 +56,23 @@ if ($onlyStella) { $list = array_filter($list, function ($i) { return file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $i . ".stella"); }, ARRAY_FILTER_USE_KEY); +} elseif ($onlyStella === 2) { + $hasAlbum = false; + $list = $songs; + + foreach ($albums as $id => $album) { + foreach ($album["tracks"] as $track) { + $list[$track]["_albumID"] = $id; + } + } + + uasort($list, function ($a, $b) { + return strcmp($a["title"], $b["title"]); + }); + + $list = array_filter($list, function ($i) { + return file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $i . ".webm"); + }, ARRAY_FILTER_USE_KEY); } ?> @@ -76,8 +93,10 @@ if ($onlyStella) { Favorites <?php elseif ($hasAlbum): ?> <?= $albums[$_GET["a"]]["title"] ?> - <?php elseif ($onlyStella): ?> + <?php elseif ($onlyStella === 1): ?> Mist Stella + <?php elseif ($onlyStella === 2): ?> + Music videos <?php else: ?> Songs <?php endif; ?> @@ -97,7 +116,7 @@ if ($onlyStella) { <div id="ui-back-button" onclick="history.back();" style="display: flex; align-items: center; justify-content: center; text-align: center;<?php if (!$hasAlbum): ?>pointer-events: none; opacity: 0;<?php endif; ?>"> <img src="/assets/icons/back.svg" alt="Back" class="icon"> </div> - <div style="display: flex; align-items: center; justify-content: center; text-align: center;"><b><?php if (!$hasAlbum && !isset($favoritesList) && !$onlyStella): ?>Songs<?php elseif ($onlyStella): ?>Mist Stella<?php endif; ?></b></div> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;"><b><?php if (!$hasAlbum && !isset($favoritesList) && $onlyStella === 0): ?>Songs<?php elseif ($onlyStella === 1): ?>Mist Stella<?php elseif ($onlyStella === 2): ?>Music videos<?php endif; ?></b></div> <?php if (!$hasAlbum): ?> <div> <input placeholder="Filter" id="filter" class="form-control" style="width: 256px;height: 32px;border-top: none;" onchange="updateFilter();" onkeyup="updateFilter();"> diff --git a/app/ui/lyrics.php b/app/ui/lyrics.php index 274ff93..103167c 100644 --- a/app/ui/lyrics.php +++ b/app/ui/lyrics.php @@ -46,7 +46,10 @@ <body style="background-color: transparent;"> <script src="/assets/js/common.js"></script> <div id="lyrics-outer"> - <div id="not-playing" style="position: fixed; inset: 16px; display: flex; align-items: center; justify-content: center; opacity: .5; text-align: center;"> + <div id="not-playing" style="display: none; position: fixed; inset: 16px; align-items: center; justify-content: center; opacity: .5; text-align: center;"> + Lyrics and music videos will appear here when you start playing a song. If supported, lyrics will also scroll as the song plays. + </div> + <div id="not-playing-2" style="display: none; position: fixed; inset: 16px; align-items: center; justify-content: center; opacity: .5; text-align: center;"> Lyrics will appear here when you start playing a song. If supported, they will also scroll as the song plays. </div> @@ -58,94 +61,119 @@ Loading lyrics... </div> + <video id="video" style="width: 100%; height: 100%; display: none; position: fixed; inset: 0; background-color: black;"></video> + <div id="lyrics-unsynced" style="display: none; position: fixed; inset: 16px; overflow: auto;"></div> <div id="lyrics-synced" style="text-align: center; display: none; position: fixed; left: 16px; right: 16px; top: 0; bottom: 0; z-index: 5;"></div> <div id="lyrics-synced-fade" style="display: none; position: fixed; inset: 0; z-index: 10; background-image: linear-gradient(180deg, rgba(255,0,0,0) 25%, rgba(255,255,255,1) 100%);"></div> </div> <script> + let lastID = null; let lastTimestamp = null; window.lyrics = {}; setInterval(async () => { + if (!window.delays) window.delays = await (await fetch("/assets/delays.json")).json(); + if (window.parent.currentSongID !== lastID) { lastID = window.parent.currentSongID; if (lastID === null) { document.getElementById("lyrics-synced").style.display = "none"; + document.getElementById("video").style.display = "none"; document.getElementById("lyrics-synced-fade").style.display = "none"; document.getElementById("lyrics-unsynced").style.display = "none"; document.getElementById("not-available").style.display = "none"; document.getElementById("loading").style.display = "none"; - document.getElementById("not-playing").style.display = "flex"; + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "flex"; } else { document.getElementById("lyrics-synced").style.display = "none"; + document.getElementById("video").style.display = "none"; document.getElementById("lyrics-synced-fade").style.display = "none"; document.getElementById("lyrics-unsynced").style.display = "none"; document.getElementById("not-available").style.display = "none"; document.getElementById("loading").style.display = "flex"; - document.getElementById("not-playing").style.display = "none"; - - if (!window.lyrics[lastID]) { - window.lyricsLoadTimeout = setTimeout(() => { - location.reload(); - }, 10000); - - try { - window.lyrics[lastID] = await (await fetch("/api/lyrics.php?id=" + lastID)).json() - } catch (e) { - window.lyrics[lastID] = { - synced: false, - payload: null + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "none"; + + window.hasVideo = await (await fetch("/api/hasVideo.php?id=" + lastID)).json(); + + if (window.hasVideo && !window.parent.document.getElementById("lyrics-page").classList.contains("mobile-show") && localStorage.getItem("data-saving") !== "true") { + document.getElementById("lyrics-synced").style.display = "none"; + document.getElementById("lyrics-synced-fade").style.display = "none"; + document.getElementById("lyrics-unsynced").style.display = "none"; + document.getElementById("video").style.display = "block"; + document.getElementById("not-available").style.display = "none"; + document.getElementById("loading").style.display = "none"; + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "none"; + + document.getElementById("video").src = "/assets/content/" + lastID + ".webm"; + } else { + if (!window.lyrics[lastID]) { + window.lyricsLoadTimeout = setTimeout(() => { + location.reload(); + }, 10000); + + try { + window.lyrics[lastID] = await (await fetch("/api/lyrics.php?id=" + lastID)).json(); + } catch (e) { + window.lyrics[lastID] = { + synced: false, + payload: null + } } - } - clearTimeout(window.lyricsLoadTimeout); + clearTimeout(window.lyricsLoadTimeout); - if (window.lyrics[lastID] && window.lyrics[lastID].payload) { - if (window.lyrics[lastID].synced) { - document.getElementById("lyrics-synced").style.display = ""; - document.getElementById("lyrics-synced-fade").style.display = ""; - document.getElementById("lyrics-unsynced").style.display = "none"; - document.getElementById("not-available").style.display = "none"; - document.getElementById("loading").style.display = "none"; - document.getElementById("not-playing").style.display = "none"; + if (window.lyrics[lastID] && window.lyrics[lastID].payload) { + if (window.lyrics[lastID].synced) { + document.getElementById("video").style.display = "none"; + document.getElementById("lyrics-synced").style.display = ""; + document.getElementById("lyrics-synced-fade").style.display = ""; + document.getElementById("lyrics-unsynced").style.display = "none"; + document.getElementById("not-available").style.display = "none"; + document.getElementById("loading").style.display = "none"; + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "none"; - document.getElementById("lyrics-synced").style.top = "0px"; - document.getElementById("lyrics-synced").innerHTML = "<div style='height: 16px;'></div>" + window.lyrics[lastID].payload.map(i => ` + document.getElementById("lyrics-synced").style.top = "0px"; + document.getElementById("lyrics-synced").innerHTML = "<div style='height: 16px;'></div>" + window.lyrics[lastID].payload.map(i => ` <div class="synced-lyrics-item" id="synced-lyrics-${i.startTimeMs}">${i.words}</div> `).join("") + "<div style='height: 16px;'></div>"; + } else { + document.getElementById("video").style.display = "none"; + document.getElementById("lyrics-synced").style.display = "none"; + document.getElementById("lyrics-synced-fade").style.display = "none"; + document.getElementById("lyrics-unsynced").style.display = ""; + document.getElementById("not-available").style.display = "none"; + document.getElementById("loading").style.display = "none"; + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "none"; + + document.getElementById("lyrics-unsynced").scrollTop = false; + document.getElementById("lyrics-unsynced").innerText = window.lyrics[lastID].payload.replaceAll("\n\n\n", "\n"); + } } else { + document.getElementById("video").style.display = "none"; document.getElementById("lyrics-synced").style.display = "none"; document.getElementById("lyrics-synced-fade").style.display = "none"; - document.getElementById("lyrics-unsynced").style.display = ""; - document.getElementById("not-available").style.display = "none"; + document.getElementById("lyrics-unsynced").style.display = "none"; + document.getElementById("not-available").style.display = "flex"; document.getElementById("loading").style.display = "none"; - document.getElementById("not-playing").style.display = "none"; - - document.getElementById("lyrics-unsynced").scrollTop = false; - document.getElementById("lyrics-unsynced").innerText = window.lyrics[lastID].payload.replaceAll("\n\n\n", "\n"); + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "none"; } - } else { - document.getElementById("lyrics-synced").style.display = "none"; - document.getElementById("lyrics-synced-fade").style.display = "none"; - document.getElementById("lyrics-unsynced").style.display = "none"; - document.getElementById("not-available").style.display = "flex"; - document.getElementById("loading").style.display = "none"; - document.getElementById("not-playing").style.display = "none"; } } } } if (window.lyrics[lastID] && window.lyrics[lastID].synced) { + document.getElementById("video").style.display = "none"; document.getElementById("lyrics-synced").style.display = ""; document.getElementById("lyrics-synced-fade").style.display = ""; document.getElementById("lyrics-unsynced").style.display = "none"; document.getElementById("not-available").style.display = "none"; document.getElementById("loading").style.display = "none"; - document.getElementById("not-playing").style.display = "none"; + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "none"; for (let item of [...window.lyrics[lastID].payload].reverse()) { if (parseInt(item.startTimeMs) / 1000 <= window.parent.document.getElementById("player").contentDocument.getElementById("player-audio").currentTime) { @@ -161,6 +189,30 @@ } } }, 100); + + window.updateVideo = () => { + if (window.hasVideo && !window.parent.document.getElementById("lyrics-page").classList.contains("mobile-show") && localStorage.getItem("data-saving") !== "true") { + document.getElementById("video").currentTime = window.parent.playerDocument.getElementById("player-audio").currentTime + (window.delays[window.parent.currentSongID] ?? 0); + + if (window.parent.playerDocument.getElementById("player-audio").paused) { + document.getElementById("video").pause(); + } else { + document.getElementById("video").play(); + } + } + } + + document.onvisibilitychange = () => { + window.updateVideo(); + } + + setInterval(() => { + if (Math.abs(window.parent.playerDocument.getElementById("player-audio").currentTime - document.getElementById("video").currentTime) > 0.5 + Math.abs((window.delays[window.parent.currentSongID] ?? 0))) { + window.updateVideo(); + } + }, 1000); + + document.getElementById(localStorage.getItem("data-saving") === "true" ? "not-playing-2" : "not-playing").style.display = "flex"; </script> </body> </html>
\ No newline at end of file diff --git a/app/ui/navigation.php b/app/ui/navigation.php index 0125418..617bc63 100644 --- a/app/ui/navigation.php +++ b/app/ui/navigation.php @@ -39,6 +39,12 @@ <div id="home" class="navigation-item" onclick="window.parent.openUI('home');"> <img class="icon" alt="" src="/assets/icons/home.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Home</span> </div> + <div id="lyrics" class="navigation-item" onclick="window.parent.showLyrics();"> + <img class="icon" alt="" src="/assets/icons/now.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Now playing</span> + </div> + <div id="queue" class="navigation-item" onclick="window.parent.openUI('queue');"> + <img class="icon" alt="" src="/assets/icons/playlist.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Queue</span> + </div> <div id="library" class="navigation-item-mobile navigation-item" onclick="window.parent.openUI('library');"> <img class="icon" alt="" src="/assets/icons/library.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Library</span> </div> @@ -51,12 +57,6 @@ <div id="favorites" class="navigation-item-desktop navigation-item" onclick="window.parent.openUI('favorites');"> <img class="icon" alt="" src="/assets/icons/favorites.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Favorites</span> </div> - <div id="lyrics" class="navigation-item" onclick="window.parent.showLyrics();"> - <img class="icon" alt="" src="/assets/icons/lyrics.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Lyrics</span> - </div> - <div id="queue" class="navigation-item" onclick="window.parent.openUI('queue');"> - <img class="icon" alt="" src="/assets/icons/playlist.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Queue</span> - </div> <div id="settings" class="navigation-item" onclick="window.parent.openUI('settings');"> <img class="icon" alt="" src="/assets/icons/settings.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Settings</span> </div> diff --git a/app/ui/settings.php b/app/ui/settings.php index 4897f06..d4cb6f2 100644 --- a/app/ui/settings.php +++ b/app/ui/settings.php @@ -33,7 +33,7 @@ <input onchange="saveDS();" class="form-check-input" type="checkbox" role="switch" id="data-saving"> <label class="form-check-label" for="data-saving"> Enable data saving - <div class="text-muted small">Data saving disables playing lossless and high-resolution audio. Instead, you will get 256 kbps AAC-encoded audio, which is highly efficient. If you use Bluetooth headphones, the difference should be unnoticeable.</div> + <div class="text-muted small">Data saving disables playing lossless and high-resolution audio, as well as music videos. Instead, you will get 256 kbps AAC-encoded audio, which is highly efficient. If you use Bluetooth headphones, the difference should be unnoticeable.</div> </label> </div> <script> @@ -251,16 +251,11 @@ <hr> <?php if (str_contains($_SERVER['HTTP_USER_AGENT'], "MistNative/")): ?> - <a onclick="window.parent.MistNative.about();" href="#">About Mist</a><span id="studio" style="display: none;"> · <a onclick="window.parent.MistNative.studio();" href="#">Switch to Mist Studio</a></span> - <script> - if (window.parent.MistNative.studio) { - document.getElementById("studio").style.display = ""; - } - </script> + <a onclick="window.parent.MistNative.about();" href="#">About Mist</a> <?php else: ?> <div class="text-muted"> <img class="icon" src="/assets/logo-transparent.svg" style="vertical-align: middle; filter: grayscale(1) invert(1); width: 32px; height: 32px;" alt=""> - <span style="vertical-align: middle;">Mist version <?= str_replace("|", " ", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")) ?> (build <?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>)<span id="copyright-separator-desktop"> · </span><span id="copyright-separator-mobile"><br></span>© <?= date('Y') ?> Equestria.dev</span> + <span style="vertical-align: middle;">Mist version <?= str_replace("|", " ", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")) ?> (build <?= trim(file_exists($_SERVER['DOCUMENT_ROOT'] . "/build.txt") ? file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/build.txt") : (file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk")) ?>)<span id="copyright-separator-desktop"> · </span><span id="copyright-separator-mobile"><br></span>© <?= date('Y') ?> Equestria.dev</span> </div> <style> @media (min-width: 768px) { diff --git a/app/ui/stella.php b/app/ui/stella.php index 143a172..c0f1fc6 100644 --- a/app/ui/stella.php +++ b/app/ui/stella.php @@ -2,6 +2,6 @@ header("X-Frame-Options: SAMEORIGIN"); unset($_GET["a"]); -$onlyStella = true; +$onlyStella = 1; global $onlyStella; require_once "./listing.php";
\ No newline at end of file diff --git a/app/ui/video.php b/app/ui/video.php new file mode 100644 index 0000000..fa11e6c --- /dev/null +++ b/app/ui/video.php @@ -0,0 +1,7 @@ +<?php + +header("X-Frame-Options: SAMEORIGIN"); +unset($_GET["a"]); +$onlyStella = 2; +global $onlyStella; +require_once "./listing.php";
\ No newline at end of file |