summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaindropsSys <raindrops@equestria.dev>2023-11-26 17:10:11 +0100
committerRaindropsSys <raindrops@equestria.dev>2023-11-26 17:10:11 +0100
commit417c403ba8e46dd97ffe656fc8761cb8c6380652 (patch)
tree208c8d5aa23ca4b8ddc35d2b37159ab4ab07b901
parente1fe55e82cc4af07a5e4544de1629b9d25c9ce2b (diff)
downloadmist-417c403ba8e46dd97ffe656fc8761cb8c6380652.tar.gz
mist-417c403ba8e46dd97ffe656fc8761cb8c6380652.tar.bz2
mist-417c403ba8e46dd97ffe656fc8761cb8c6380652.zip
Updated 13 files and added app/studio.php (automated)
-rw-r--r--.DS_Storebin20484 -> 20484 bytes
-rw-r--r--app/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--app/index.php68
-rw-r--r--app/studio.php445
-rw-r--r--app/ui/player.php6
-rw-r--r--app/ui/queue.php11
-rw-r--r--app/ui/settings.php7
-rw-r--r--assets/.DS_Storebin10244 -> 10244 bytes
-rw-r--r--assets/js/common.js2
-rwxr-xr-xbuild.sh2
-rw-r--r--desktop/main.js48
-rw-r--r--desktop/preload.js1
-rw-r--r--includes/session.php1
-rw-r--r--version2
14 files changed, 573 insertions, 20 deletions
diff --git a/.DS_Store b/.DS_Store
index dc47f7c..0cb036b 100644
--- a/.DS_Store
+++ b/.DS_Store
Binary files differ
diff --git a/app/.DS_Store b/app/.DS_Store
index b01224a..60beba8 100644
--- a/app/.DS_Store
+++ b/app/.DS_Store
Binary files differ
diff --git a/app/index.php b/app/index.php
index 31ca9aa..82b4791 100644
--- a/app/index.php
+++ b/app/index.php
@@ -553,7 +553,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI
}
}
- function updateDisplay(initial) {
+ window.updateDisplay = (initial) => {
if (initial) {
if (playerDocument.getElementById("player-audio").paused) {
document.title = "Mist";
@@ -740,6 +740,14 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI
window.library = await (await fetch("/api/getLibrary.php?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json();
}
+ window.redownloadMedia = async () => {
+ document.getElementById("loading-text").innerText = "Downloading list of songs...";
+ 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("loading-text").innerText = "Downloading list of albums...";
+ window.albums = await (await fetch("/assets/content/albums.json?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json();
+ }
+
(async () => {
document.getElementById("loading-text").innerText = "Downloading list of songs...";
window.songs = await (await fetch("/assets/content/songs.json?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json();
@@ -943,10 +951,12 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI
}
if (stellaCompatible) {
- window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella");
- window.preloadedGains[id] = await normalizeAudio(window.preloaded[id].stems.vocals.buffer, 0, true);
- window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id].stems.vocals.buffer, .05, true);
- window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id].stems.vocals.buffer, .1, true);
+ if (!window.preloaded[id]) {
+ window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella");
+ window.preloadedGains[id] = await normalizeAudio(window.preloaded[id].stems.vocals.buffer, 0, true);
+ window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id].stems.vocals.buffer, .05, true);
+ window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id].stems.vocals.buffer, .1, true);
+ }
} else {
if (!window.preloaded[id]) {
window.buffering = true;
@@ -971,9 +981,45 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI
if (window.currentSongID !== id) return;
+ try {
+ window.currentNormalizationSource.disconnect();
+
+ if (playingStella) {
+ window.currentNormalizationSource2.disconnect();
+ window.currentNormalizationSource3.disconnect();
+ }
+
+ window.preloadedGains[window.currentSongID].disconnect();
+ } catch (e) {
+ console.error(e);
+ }
+
+ if (playingStella) {
+ for (let player of [
+ window.currentNormalizationSource2,
+ window.currentNormalizationSource3,
+ window.currentNormalizationSource4,
+ window.currentNormalizationSource5
+ ]) {
+ try {
+ player.disconnect();
+ window.preloadedGainsBoosted1[window.currentSongID].disconnect();
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ try {
+ window.currentNormalizationSource1.disconnect();
+ window.preloadedGainsBoosted2[window.currentSongID].disconnect();
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
if (!stellaCompatible) {
if (!window.preloadedURLs[id]) {
- window.preloadedURLs[id] = localStorage.getItem("data-saving") ? URL.createObjectURL(window.preloadedBlobs[id]) : URL.createObjectURL(window.preloadedBlobs[id]);
+ window.preloadedURLs[id] = URL.createObjectURL(window.preloadedBlobs[id]);
}
} else {
window.playingStella = true;
@@ -1079,10 +1125,12 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI
}
if (stellaCompatible) {
- window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella");
- window.preloadedGains[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, 0, true);
- window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .05, true);
- window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .1, true);
+ if (!window.preloaded[id]) {
+ window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella");
+ window.preloadedGains[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, 0, true);
+ window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .05, true);
+ window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .1, true);
+ }
} else {
if (!window.preloaded[id]) {
if (localStorage.getItem("data-saving") === "true") {
diff --git a/app/studio.php b/app/studio.php
new file mode 100644
index 0000000..4f002f7
--- /dev/null
+++ b/app/studio.php
@@ -0,0 +1,445 @@
+<?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/player.php b/app/ui/player.php
index 47526f5..3fcdec1 100644
--- a/app/ui/player.php
+++ b/app/ui/player.php
@@ -168,7 +168,11 @@
window.parent.redoNavigation("home");
}
- function openAlbum() {
+ async function openAlbum() {
+ if (Object.entries(window.parent.albums).filter(i => i[1].tracks.includes(window.parent.currentSongID))) {
+ await window.parent.redownloadMedia();
+ }
+
window.parent.location.hash = "#/albums/" + Object.entries(window.parent.albums).filter(i => i[1].tracks.includes(window.parent.currentSongID))[0][0];
window.parent.redoNavigation("albums");
}
diff --git a/app/ui/queue.php b/app/ui/queue.php
index 81a7079..7d38625 100644
--- a/app/ui/queue.php
+++ b/app/ui/queue.php
@@ -25,7 +25,10 @@
<script src="/assets/js/common.js"></script>
<div class="container">
<br>
- <h2 class="desktop-title" style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Queue</h2>
+ <h2 class="desktop-title" style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">
+ Queue
+ <span class="btn btn-primary" style="float: right;" onclick="clearQueue();">Clear queue</span>
+ </h2>
<div class="list-group" style="margin-left: 10px; margin-top: 20px;" id="main-list"></div>
<div class="text-muted" style="margin-left: 10px; margin-top: 20px; display: none;" id="empty">
There are no songs playing next. To add songs to the queue, browse your library and select Add to queue.
@@ -33,6 +36,11 @@
</div>
<script>
+ function clearQueue() {
+ window.parent.playlist = window.parent.playlist.slice(0, window.parent.currentPlaylistPosition + 1);
+ refreshQueue();
+ }
+
function refreshQueue() {
let list = window.parent.playlist.slice(window.parent.currentPlaylistPosition + 1);
@@ -68,6 +76,7 @@
function removeSong(index) {
window.parent.playlist.splice(index, 1);
refreshQueue();
+ window.parent.updateDisplay(true);
}
refreshQueue();
diff --git a/app/ui/settings.php b/app/ui/settings.php
index 26217f0..4897f06 100644
--- a/app/ui/settings.php
+++ b/app/ui/settings.php
@@ -251,7 +251,12 @@
<hr>
<?php if (str_contains($_SERVER['HTTP_USER_AGENT'], "MistNative/")): ?>
- <a onclick="window.parent.MistNative.about();" href="#">About Mist</a>
+ <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>
<?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="">
diff --git a/assets/.DS_Store b/assets/.DS_Store
index b5223c8..b8d5529 100644
--- a/assets/.DS_Store
+++ b/assets/.DS_Store
Binary files differ
diff --git a/assets/js/common.js b/assets/js/common.js
index 6ccd25c..0762d07 100644
--- a/assets/js/common.js
+++ b/assets/js/common.js
@@ -18,5 +18,5 @@ document.head.append(style);
if (navigator.userAgent.includes("MistNative/darwin")) {
if (document.getElementById("native-css")) document.getElementById("native-css").disabled = false;
- document.body.classList.remove("crossplatform");
+ if (document.body) document.body.classList.remove("crossplatform");
} \ No newline at end of file
diff --git a/build.sh b/build.sh
index 79c358a..39bac3f 100755
--- a/build.sh
+++ b/build.sh
@@ -1,6 +1,6 @@
#!/bin/bash
cd desktop
-#npx electron-packager . Mist --asar --overwrite --platform=darwin --arch=x64 --icon=../assets/logo.icns --out=../build
+npx electron-packager . Mist --asar --overwrite --platform=darwin --arch=x64 --icon=../assets/logo.icns --out=../build
npx electron-packager . Mist --asar --overwrite --platform=darwin --arch=arm64 --icon=../assets/logo.icns --out=../build
#npx electron-packager . Mist --asar --overwrite --platform=linux --arch=x64 --icon=../assets/logo-display.png --out=../build
#npx electron-packager . Mist --asar --overwrite --platform=linux --arch=arm64 --icon=../assets/logo-display.png --out=../build
diff --git a/desktop/main.js b/desktop/main.js
index 1d0b322..02f153d 100644
--- a/desktop/main.js
+++ b/desktop/main.js
@@ -229,7 +229,6 @@ function getCopyrightYear() {
}
}
-app.setName("Mist");
app.setAsDefaultProtocolClient("mist");
app.setAboutPanelOptions({
applicationName: "Mist",
@@ -239,10 +238,12 @@ app.setAboutPanelOptions({
website: "https://mist.equestria.horse/app/"
});
-const gotTheLock = app.requestSingleInstanceLock();
+if (app.getName() !== "Electron") {
+ const gotTheLock = app.requestSingleInstanceLock();
-if (!gotTheLock) {
- app.quit();
+ if (!gotTheLock) {
+ app.quit();
+ }
}
let loggedIn = false;
@@ -337,6 +338,40 @@ const createWindow = () => {
});
}
+const studioMode = () => {
+ if (global.studioWindow) return;
+
+ global.studioWindow = new BrowserWindow({
+ width: require('electron').screen.getPrimaryDisplay().workAreaSize.width,
+ minWidth: 1280,
+ height: require('electron').screen.getPrimaryDisplay().workAreaSize.height,
+ minHeight: 720,
+ autoHideMenuBar: true,
+ title: "Mist Studio",
+ backgroundColor: "#111111",
+ webPreferences: {
+ nodeIntegration: true,
+ contextIsolation: false,
+ scrollBounce: true,
+ enableWebSQL: false
+ }
+ });
+
+ updateMenu(studioWindow);
+
+ studioWindow.webContents.setUserAgent(studioWindow.webContents.getUserAgent() + " MistNative/" + process.platform);
+ studioWindow.loadURL("https://mist.equestria.horse/app/studio.php");
+
+ studioWindow.webContents.setWindowOpenHandler((details) => {
+ shell.openExternal(details.url);
+ return { action: "deny" };
+ });
+
+ studioWindow.on('close', () => {
+ global.studioWindow = null;
+ });
+}
+
ipcMain.handle('auth', () => {
shell.openExternal("https://mist.equestria.horse/oauth/native/");
});
@@ -345,6 +380,11 @@ ipcMain.handle('about', () => {
app.showAboutPanel();
});
+ipcMain.handle('studio', () => {
+ studioMode();
+ mainWindow.close();
+});
+
ipcMain.handle('userInfo', (e, userInfo) => {
global.userInfo = JSON.parse(userInfo);
updateMenu(mainWindow);
diff --git a/desktop/preload.js b/desktop/preload.js
index 2b37f60..4c85000 100644
--- a/desktop/preload.js
+++ b/desktop/preload.js
@@ -6,4 +6,5 @@ contextBridge.exposeInMainWorld('MistNative', {
about: () => ipcRenderer.invoke('about'),
notification: (song, img) => ipcRenderer.invoke('notification', song, img),
userInfo: (ui) => ipcRenderer.invoke('userInfo', ui),
+ studio: () => ipcRenderer.invoke('studio')
}) \ No newline at end of file
diff --git a/includes/session.php b/includes/session.php
index ee6cd85..3936a20 100644
--- a/includes/session.php
+++ b/includes/session.php
@@ -136,6 +136,7 @@ function displayList($list, $hasAlbum = false) { global $albums; global $favorit
window.parent.playSong(id);
} else {
window.parent.playlist.push(id);
+ window.parent.updateDisplay(true);
}
}
diff --git a/version b/version
index 0bfbd57..7b378be 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.8.2 \ No newline at end of file
+1.8.4 \ No newline at end of file