diff options
author | RaindropsSys <raindrops@equestria.dev> | 2023-10-30 23:08:45 +0100 |
---|---|---|
committer | RaindropsSys <raindrops@equestria.dev> | 2023-10-30 23:08:45 +0100 |
commit | 41c51b8bdb9c8e9fa4a7d56f260d594739d4107e (patch) | |
tree | 4bb3e824d636c7cf8cb39fd0e1aa25c49c339164 /app | |
parent | 4d4308c46d4f7801c657cc79d2243e1a81831334 (diff) | |
download | mist-41c51b8bdb9c8e9fa4a7d56f260d594739d4107e.tar.gz mist-41c51b8bdb9c8e9fa4a7d56f260d594739d4107e.tar.bz2 mist-41c51b8bdb9c8e9fa4a7d56f260d594739d4107e.zip |
Updated 35 files and added 28 files (automated)
Diffstat (limited to 'app')
-rw-r--r-- | app/.DS_Store | bin | 6148 -> 6148 bytes | |||
-rw-r--r-- | app/index.php | 538 | ||||
-rw-r--r-- | app/notes/update-0.3.0.php | 26 | ||||
-rw-r--r-- | app/notes/update-0.4.0.php | 26 | ||||
-rw-r--r-- | app/notes/update-1.0.0.php | 12 | ||||
-rw-r--r-- | app/ui/albums.php | 20 | ||||
-rw-r--r-- | app/ui/download.php | 5 | ||||
-rw-r--r-- | app/ui/explore.php | 18 | ||||
-rw-r--r-- | app/ui/info.php | 58 | ||||
-rw-r--r-- | app/ui/library.php | 46 | ||||
-rw-r--r-- | app/ui/listing.php | 244 | ||||
-rw-r--r-- | app/ui/lyrics.php | 4 | ||||
-rw-r--r-- | app/ui/modal.php | 29 | ||||
-rw-r--r-- | app/ui/navigation.php | 9 | ||||
-rw-r--r-- | app/ui/player-mobile.php | 60 | ||||
-rw-r--r-- | app/ui/player.php | 99 | ||||
-rw-r--r-- | app/ui/queue.php | 4 | ||||
-rw-r--r-- | app/ui/search.php | 13 | ||||
-rw-r--r-- | app/ui/settings.php | 50 | ||||
-rw-r--r-- | app/ui/update.php | 26 | ||||
-rw-r--r-- | app/ui/welcome-dp.php | 59 |
21 files changed, 1082 insertions, 264 deletions
diff --git a/app/.DS_Store b/app/.DS_Store Binary files differindex 1848aaa..3c6d6b1 100644 --- a/app/.DS_Store +++ b/app/.DS_Store diff --git a/app/index.php b/app/index.php index 3dc4b44..6989866 100644 --- a/app/index.php +++ b/app/index.php @@ -15,6 +15,9 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI <!--<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>--> <script src="/assets/localforage.min.js"></script> <script src="/assets/js/shortcuts.js"></script> + <script src="/assets/js/normalizer.js"></script> + <script src="/assets/js/pako.js"></script> + <script src="/assets/js/stella.js"></script> <link rel="shortcut icon" href="/assets/logo-display.svg" type="image/svg+xml"> <link rel="manifest" href="/manifest.json" /> <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)"> @@ -30,10 +33,16 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } </style> </head> -<body <?php if (!str_contains($_SERVER['HTTP_USER_AGENT'], "MistNative/")): ?>class="web"<?php endif; ?>> +<body <?php if (!str_contains($_SERVER['HTTP_USER_AGENT'], "MistNative/")): ?>class="web"<?php else: ?>class="native"<?php endif; ?>> <script src="/assets/js/common.js"></script> <script> - if (location.hash.trim() === "") location.hash = "#/albums"; + if (location.hash.trim() === "") { + if (window.innerWidth < 863) { + location.hash = "#/library"; + } else { + location.hash = "#/albums"; + } + } 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") ?>"); @@ -41,7 +50,15 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } </script> <div id="loading" style="z-index: 999999; position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background-color: white;"> - <span id="loading-text">Initializing...</span> + <img src="/assets/logo-transparent.svg" style="width: 256px; height: 256px;" alt="Mist"> + <span id="loading-text" style="display: none;">Initializing...</span> + </div> + + <div id="mobile-navbar" style="padding: 0 10px; border-bottom: 1px solid rgba(0, 0, 0, .1); height: 48px; top: var(--android-status-bar); left: 0; right: 0; position: fixed; display: none; grid-template-columns: max-content 1fr;"> + <div style="display: flex; align-items: center;" onclick="document.getElementById('ui').contentWindow.history.back();"> + <img alt="Back" src="/assets/icons/back-mobile.svg" style="height: 32px; width: 32px;"> + </div> + <div style="display: flex; align-items: center; margin-left: 10px;" id="mobile-navbar-title"></div> </div> <iframe title="Player" id="player" src="ui/player.php" style="position: fixed; top: var(--android-status-bar); left: 320px; right: 0; width: calc(100vw - 320px); height: 64px; border-bottom: 1px solid rgba(0, 0, 0, .25); z-index: 9999;"></iframe> @@ -52,12 +69,42 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI <iframe title="Mobile player" style="background: #ddd; width: 100%; height: 100%;" src="ui/player-mobile.php" id="player-mobile"></iframe> </div> + <div id="mouse-logging" style="display: block; inset: 0; position: fixed; z-index: 99999999; pointer-events: none;"></div> + <iframe id="modal" src="ui/modal.php" style="width: 100vw; height: 100vh; border: none; inset: 0; position: fixed; z-index: 99999999; display: none;"></iframe> + <script> + window.modalLoaded = false; + document.getElementById("modal").onload = () => { + window.modalLoaded = true; + + <?php $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); global $_PROFILE; if (in_array($_PROFILE["id"], $app["dp"])): ?> + openModal("Mist Developer Preview", "welcome-dp.php", false); + <?php else: ?> + if (localStorage.getItem("welcomed") !== "true") { + openModal("Welcome to Mist", "welcome.php", true); + } else { + if (localStorage.getItem("lastUpdate") !== "<?= trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")) ?>|<?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>") { + openModal("What's new in Mist?", "update.php", true); + } + } + <?php endif; ?> + } + window.onerror = (_1, _2, _3, _4, err) => { - document.body.innerHTML = ""; - let pre = document.createElement("pre"); - pre.innerText = err.stack; - document.body.append(pre); + let loadWait = setInterval(() => { + if (window.modalLoaded) { + clearInterval(loadWait); + + document.getElementById("modal").style.display = ""; + document.getElementById("modal").contentDocument.getElementById("error-content").innerText = err.stack; + document.getElementById("modal").contentWindow._error.show(); + + setInterval(() => { + document.getElementById("modal").contentWindow._modal.hide(); + window.parent.document.getElementById("modal").style.display = ""; + }); + } + }); } window.playlist = []; @@ -67,7 +114,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("lyrics-page").style.display = ""; document.getElementById("ui").style.display = "none"; Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); - document.getElementById("navigation").contentDocument.getElementById("lyrics").classList.add("active"); + if (document.getElementById("navigation").contentDocument.getElementById("lyrics")) document.getElementById("navigation").contentDocument.getElementById("lyrics").classList.add("active"); } function openUI(name) { @@ -77,20 +124,45 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("ui").style.display = ""; document.getElementById("ui").src = "ui/" + name + ".php"; Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); - document.getElementById("navigation").contentDocument.getElementById(name).classList.add("active"); + if (document.getElementById("navigation").contentDocument.getElementById(name)) document.getElementById("navigation").contentDocument.getElementById(name).classList.add("active"); } - let name = location.hash.split("/")[1]; + window.onhashchange = window.loadHash = () => { + window.name = location.hash.split("/")[1]; - if (name === "lyrics") { - document.getElementById("ui").src = "ui/albums.php"; - } else if (name === "albums" && location.hash.split("/")[2]) { - document.getElementById("ui").src = "ui/listing.php?a=" + location.hash.split("/")[2]; - } else { - document.getElementById("ui").src = "ui/" + name + ".php"; + function setSrcIfDifferent(src) { + if (document.getElementById("ui").contentWindow.location.pathname.substring(5) !== src) { + document.getElementById("ui").src = src; + } + } + + if (name === "lyrics") { + showLyrics(); + } else if (name === "albums" && location.hash.split("/")[2]) { + document.getElementById("lyrics-page").style.display = "none"; + document.getElementById("ui").style.display = ""; + setSrcIfDifferent("ui/listing.php?a=" + location.hash.split("/")[2]); + } else if (name === "search" && location.hash.split("/")[2]) { + document.getElementById("lyrics-page").style.display = "none"; + document.getElementById("ui").style.display = ""; + setSrcIfDifferent("ui/search.php?q=" + location.hash.split("/")[2]); + name = "explore"; + } else { + document.getElementById("lyrics-page").style.display = "none"; + document.getElementById("ui").style.display = ""; + setSrcIfDifferent("ui/" + name + ".php"); + } } - document.getElementById("navigation").onload = () => { + loadHash(); + + document.getElementById("ui").onload = () => { + window.resizeHandler(); + document.getElementById("mobile-navbar-title").innerText = document.getElementById("ui").contentDocument.title; + } + + document.getElementById("navigation").onload = window.redoNavigation = (name) => { + if (!name || typeof name !== "string") name = window.name; if (name === "lyrics") showLyrics(); Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); document.getElementById("navigation").contentDocument.getElementById(name).classList.add("active"); @@ -100,35 +172,63 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("player").onload = document.getElementById("player-mobile").onload = () => { loadedPlayers++; - if (loadedPlayers === 2) continueLoading(); + if (loadedPlayers === 2) { + window.resizeHandler(); + continueLoading(); + } } - window.onresize = window.onload = () => { + window.onresize = window.onload = window.resizeHandler = () => { if (window.innerWidth <= 863) { - document.getElementById("player").contentDocument.getElementById("player").classList.add("mobilified"); + if (document.getElementById("player").contentDocument && document.getElementById("player").contentDocument.getElementById("player")) document.getElementById("player").contentDocument.getElementById("player").classList.add("mobilified"); + if (document.getElementById("ui").contentDocument && document.getElementById("ui").contentDocument.body) document.getElementById("ui").contentDocument.body.classList.add("mobile-ui"); } else { - document.getElementById("player").contentDocument.getElementById("player").classList.remove("mobilified"); + if (document.getElementById("player").contentDocument && document.getElementById("player").contentDocument.getElementById("player")) document.getElementById("player").contentDocument.getElementById("player").classList.remove("mobilified"); + if (document.getElementById("ui").contentDocument && document.getElementById("ui").contentDocument.body) document.getElementById("ui").contentDocument.body.classList.remove("mobile-ui"); } } + window.needInitializeNormalizer = false; + function continueLoading() { window.playerDocument = document.getElementById("player").contentDocument; window.playerDocumentMobile = document.getElementById("player-mobile").contentDocument; + window.needInitializeNormalizer = true; if (!localStorage.getItem("data-saving")) { localStorage.setItem("data-saving", "false"); } + if (!localStorage.getItem("normalize")) { + localStorage.setItem("normalize", "true"); + } + if (!localStorage.getItem("desktop-notification")) { localStorage.setItem("desktop-notification", "true"); } playerDocument.getElementById("seekbar-container").onclick = (e) => { playerDocument.getElementById("player-audio").currentTime = (e.offsetX / playerDocument.getElementById("seekbar-container").clientWidth) * playerDocument.getElementById("player-audio").duration; + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } } playerDocumentMobile.getElementById("seekbar-container").onclick = (e) => { playerDocument.getElementById("player-audio").currentTime = (e.offsetX / playerDocumentMobile.getElementById("seekbar-container").clientWidth) * playerDocument.getElementById("player-audio").duration; + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } } function parseTime(subject, max) { @@ -147,6 +247,18 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI next(); } + window.seekTo = (time) => { + document.getElementById("player").contentDocument.getElementById("player-audio").currentTime = time; + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + } + window.stop = () => { if (window.MistAndroid) { window.MistAndroid.removeNotification(); @@ -166,16 +278,10 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("player").contentWindow.location.reload(); document.getElementById("player").onload = () => { window.playerDocument = document.getElementById("player").contentDocument; + window.needInitializeNormalizer = true; - if (window.innerWidth <= 863) { - document.getElementById("player").contentDocument.getElementById("player").classList.add("mobilified"); - } else { - document.getElementById("player").contentDocument.getElementById("player").classList.remove("mobilified"); - } - - playerDocument.getElementById("player-audio").ontimeupdate = playerDocument.getElementById("player-audio").onchange = playerDocument.getElementById("player-audio").onunload = playerDocument.getElementById("player-audio").onstop = playerDocument.getElementById("player-audio").onplay = playerDocument.getElementById("player-audio").onpause = () => { - updateDisplay(); - } + window.resizeHandler(); + initializePlayerDocument(); } document.getElementById("player-mobile").contentWindow.location.reload(); @@ -192,6 +298,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI if (window.currentSongID !== null) { document.getElementById("player-mobile-container").style.bottom = "0"; document.getElementById("lyrics-page").classList.add("mobile-show"); + document.getElementById("lyrics-page").style.pointerEvents = "none"; } } @@ -200,6 +307,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("player-mobile-container").style.bottom = "-100vh"; document.getElementById("lyrics-page").classList.remove("mobile-show"); + document.getElementById("lyrics-page").style.pointerEvents = ""; } document.getElementById("player-mobile-container").onclick = (e) => { @@ -210,8 +318,12 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI window.currentPlaylistPosition = 0; window.buffering = false; + window.calledNextRecently = false; window.next = () => { + if (window.calledNextRecently) return; + window.calledNextRecently = true; + if (window.repeat) { playlist.push(playlist[currentPlaylistPosition]); } @@ -225,6 +337,9 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } window.previous = () => { + if (window.calledNextRecently) return; + window.calledNextRecently = true; + if (playlist[currentPlaylistPosition - 1]) { playSong(playlist[currentPlaylistPosition - 1], "keep"); currentPlaylistPosition--; @@ -233,22 +348,130 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } } - playerDocument.getElementById("player-audio").ontimeupdate = playerDocument.getElementById("player-audio").onchange = playerDocument.getElementById("player-audio").onunload = playerDocument.getElementById("player-audio").onstop = playerDocument.getElementById("player-audio").onplay = playerDocument.getElementById("player-audio").onpause = () => { - updateDisplay(); + function initializePlayerDocument() { + playerDocument.getElementById("player-audio").ontimeupdate = playerDocument.getElementById("player-audio").onchange = playerDocument.getElementById("player-audio").onunload = playerDocument.getElementById("player-audio").onstop = () => { + updateDisplay(); + + if (playingStella) { + if (playerDocument.getElementById("player-audio").paused) { + if (!playerDocument.getElementById("player-audio-stella-side1").paused) { + playerDocument.getElementById("player-audio-stella-side1").pause(); + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (!playerDocument.getElementById("player-audio-stella-side2").paused) { + playerDocument.getElementById("player-audio-stella-side2").pause(); + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (!playerDocument.getElementById("player-audio-stella-side3").paused) { + playerDocument.getElementById("player-audio-stella-side3").pause(); + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (!playerDocument.getElementById("player-audio-stella-side4").paused) { + playerDocument.getElementById("player-audio-stella-side4").pause(); + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (!playerDocument.getElementById("player-audio-stella-side5").paused) { + playerDocument.getElementById("player-audio-stella-side5").pause(); + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + } else { + if (playerDocument.getElementById("player-audio-stella-side1").paused) { + playerDocument.getElementById("player-audio-stella-side1").play(); + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (playerDocument.getElementById("player-audio-stella-side2").paused) { + playerDocument.getElementById("player-audio-stella-side2").play(); + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (playerDocument.getElementById("player-audio-stella-side3").paused) { + playerDocument.getElementById("player-audio-stella-side3").play(); + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (playerDocument.getElementById("player-audio-stella-side4").paused) { + playerDocument.getElementById("player-audio-stella-side4").play(); + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + if (playerDocument.getElementById("player-audio-stella-side5").paused) { + playerDocument.getElementById("player-audio-stella-side5").play(); + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } + } + } + } + + playerDocument.getElementById("player-audio").onplay = () => { + if (window.preloadedGains[window.currentSongID]) { + window.currentNormalizationSource.connect(window.preloadedGains[window.currentSongID]); + window.currentNormalizationSource2.connect(window.preloadedGainsBoosted1[window.currentSongID]); + window.currentNormalizationSource3.connect(window.preloadedGainsBoosted2[window.currentSongID]); + window.preloadedGains[window.currentSongID].connect(window.currentNormalizationContext.destination); + } + + if (playingStella) { + for (let player of [ + window.currentNormalizationSource2, + window.currentNormalizationSource3, + window.currentNormalizationSource4, + window.currentNormalizationSource5 + ]) { + player.connect(window.preloadedGainsBoosted1[window.currentSongID]); + window.preloadedGainsBoosted1[window.currentSongID].connect(window.currentNormalizationContext.destination); + } + + window.currentNormalizationSource1.connect(window.preloadedGainsBoosted2[window.currentSongID]); + window.preloadedGainsBoosted2[window.currentSongID].connect(window.currentNormalizationContext.destination); + } + + updateDisplay(); + } + + playerDocument.getElementById("player-audio").onpause = () => { + if (window.preloadedGains[window.currentSongID]) { + try { + window.currentNormalizationSource.disconnect(window.preloadedGains[window.currentSongID]); + window.preloadedGains[window.currentSongID].disconnect(window.currentNormalizationContext.destination); + } 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]); + window.preloadedGainsBoosted1[window.currentSongID].disconnect(window.currentNormalizationContext.destination); + } catch (e) { + console.error(e); + } + } + + try { + window.currentNormalizationSource1.disconnect(window.preloadedGainsBoosted2[window.currentSongID]); + window.preloadedGainsBoosted2[window.currentSongID].disconnect(window.currentNormalizationContext.destination); + } catch (e) { + console.error(e); + } + } + } + + if (playerDocument.getElementById("player-audio").currentTime >= playerDocument.getElementById("player-audio").duration) { + next(); + return; + } + + updateDisplay(); + } } + initializePlayerDocument(); + function updateDisplay() { if (window.MistAndroid && currentSong) { - window.MistAndroid.setNotificationData(currentSong.title, - currentSong.artist, - currentSong.album, - Math.round(playerDocument.getElementById("player-audio").currentTime * 1000), - Math.round(playerDocument.getElementById("player-audio").duration * 1000), - !playerDocument.getElementById("player-audio").paused), - buffering, - shuffle, - repeat - ; + window.MistAndroid.setNotificationData(currentSong.title, currentSong.artist, currentSong.album, Math.round(playerDocument.getElementById("player-audio").currentTime * 1000), Math.round(playerDocument.getElementById("player-audio").duration * 1000), !playerDocument.getElementById("player-audio").paused, buffering); } if (playerDocument.getElementById("player-audio").paused) { @@ -297,12 +520,28 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } else { playerDocument.getElementById("player-audio").currentTime = 0; } + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } }); document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("seekforward", (e) => { let time = e.seekOffset ?? 10; if (playerDocument.getElementById("player-audio").currentTime + time < playerDocument.getElementById("player-audio").duration) { playerDocument.getElementById("player-audio").currentTime += time; + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } } else { next(); } @@ -310,6 +549,14 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("seekto", (e) => { if (e.seekTime) { playerDocument.getElementById("player-audio").currentTime = e.seekTime; + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } } }); document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("previoustrack", () => { @@ -317,6 +564,14 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI previous(); } else { playerDocument.getElementById("player-audio").currentTime = 0; + + if (playingStella) { + playerDocument.getElementById("player-audio-stella-side1").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side2").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side3").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side4").currentTime = playerDocument.getElementById("player-audio").currentTime; + playerDocument.getElementById("player-audio-stella-side5").currentTime = playerDocument.getElementById("player-audio").currentTime; + } } }); document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("nexttrack", () => { @@ -354,33 +609,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI playerDocument.getElementById("btn-play-icon").src = playerDocumentMobile.getElementById("btn-play-icon").src = "/assets/icons/pause.svg"; } - if (localStorage.getItem("data-saving") === "true") { - playerDocument.getElementById("badge-lossy").style.display = "inline"; - playerDocument.getElementById("badge-cd").style.display = "none"; - playerDocument.getElementById("badge-hires").style.display = "none"; - playerDocumentMobile.getElementById("badge-lossy").style.display = "inline"; - playerDocumentMobile.getElementById("badge-cd").style.display = "none"; - playerDocumentMobile.getElementById("badge-hires").style.display = "none"; - } else { - if (window.currentSong && window.currentSong.hiRes) { - playerDocument.getElementById("badge-lossy").style.display = "none"; - playerDocument.getElementById("badge-cd").style.display = "none"; - playerDocument.getElementById("badge-hires").style.display = "inline"; - playerDocument.getElementById("badge-hires").innerHTML = "<span style='display: grid; grid-template-columns: max-content max-content'><span><img src='/assets/icons/lossless.svg' alt='' class='player-badge-icon' style='filter: invert(1);'>Hi-Res Lossless</span><span class='player-badge-desktop'>" + window.currentSong.bitDepth + "-bit " + (window.currentSong.sampleRate / 1000) + " kHz</span>"; - playerDocumentMobile.getElementById("badge-lossy").style.display = "none"; - playerDocumentMobile.getElementById("badge-cd").style.display = "none"; - playerDocumentMobile.getElementById("badge-hires").style.display = "inline"; - } else if (window.currentSong) { - playerDocument.getElementById("badge-lossy").style.display = "none"; - playerDocument.getElementById("badge-cd").style.display = "inline"; - playerDocument.getElementById("badge-hires").style.display = "none"; - playerDocumentMobile.getElementById("badge-lossy").style.display = "none"; - playerDocumentMobile.getElementById("badge-cd").style.display = "inline"; - playerDocumentMobile.getElementById("badge-hires").style.display = "none"; - playerDocument.getElementById("badge-cd").innerHTML = "<span style='display: grid; grid-template-columns: max-content max-content;'><span><img src='/assets/icons/lossless.svg' alt='' class='player-badge-icon' style='filter: invert(1);'>Lossless</span><span class='player-badge-desktop'>" + window.currentSong.bitDepth + "-bit " + (window.currentSong.sampleRate / 1000) + " kHz</span>"; - } - } - if (window.currentSong) { playerDocument.getElementById("title").innerText = playerDocumentMobile.getElementById("title").innerText = window.currentSong.title; playerDocument.getElementById("artist").innerText = playerDocumentMobile.getElementById("artist").innerText = window.currentSong.artist; @@ -441,9 +669,15 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("loading").style.display = "none"; })(); + window.playingStella = false; window.currentSong = null; window.currentSongID = null; window.preloaded = {}; + window.preloadedURLs = {}; + window.preloadedGains = {}; + window.preloadedGainsBoosted1 = {}; + window.preloadedGainsBoosted2 = {}; + window.preloadedBlobs = {}; window.shuffle = false; window.repeat = false; @@ -545,8 +779,33 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI window.currentPlaylistID = null; window.playSong = async (id, playlistID, updatePosition) => { + playerDocument.getElementById("badge-cd").style.display = "none"; + playerDocument.getElementById("badge-stella").style.display = "none"; + playerDocument.getElementById("badge-hires").style.display = "none"; + playerDocumentMobile.getElementById("badge-cd").style.display = "none"; + playerDocumentMobile.getElementById("badge-stella").style.display = "none"; + playerDocumentMobile.getElementById("badge-hires").style.display = "none"; + document.getElementById("player").contentWindow.buildTooltips(); + + if (!window.currentNormalizationContext) { + window.currentNormalizationContext = new AudioContext(); + window.currentNormalizationContext2 = new AudioContext(); + window.currentNormalizationContext3 = new AudioContext(); + } + + if (window.needInitializeNormalizer) { + window.currentNormalizationSource = window.currentNormalizationContext.createMediaElementSource(playerDocument.getElementById("player-audio")); + window.currentNormalizationSource1 = window.currentNormalizationContext.createMediaElementSource(playerDocument.getElementById("player-audio-stella-side1")); + window.currentNormalizationSource2 = window.currentNormalizationContext.createMediaElementSource(playerDocument.getElementById("player-audio-stella-side2")); + window.currentNormalizationSource3 = window.currentNormalizationContext.createMediaElementSource(playerDocument.getElementById("player-audio-stella-side3")); + window.currentNormalizationSource4 = window.currentNormalizationContext.createMediaElementSource(playerDocument.getElementById("player-audio-stella-side4")); + window.currentNormalizationSource5 = window.currentNormalizationContext.createMediaElementSource(playerDocument.getElementById("player-audio-stella-side5")); + window.needInitializeNormalizer = false; + } + playerDocument.getElementById("player-audio").pause(); playerDocument.getElementById("player-audio").currentTime = 0; + window.playingStella = false; if (playlistID) { if (playlistID === "favorites") { @@ -560,7 +819,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI } else if (playlistID !== "keep") { window.playlist = [id]; window.currentPlaylistPosition = 0; - } else if (typeof updatePosition !== "boolean" || updatePosition) { + } else if (updatePosition !== false) { window.currentPlaylistPosition = window.playlist.indexOf(id) ?? 0; } } else { @@ -574,22 +833,100 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI updateDisplay(); if (document.getElementById("ui").contentWindow.refreshQueue) document.getElementById("ui").contentWindow.refreshQueue(); - if (!window.preloaded[id]) { - window.buffering = true; + let stellaCompatible = false; - if (localStorage.getItem("data-saving") === "true") { - window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".m4a")).arrayBuffer()], { type: "audio/mp4" })); - } else { - window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".flac")).arrayBuffer()], { type: "audio/flac" })); + if (localStorage.getItem("enable-stella") === "true" && localStorage.getItem("data-saving") !== "true") { + stellaCompatible = await (await fetch("/api/hasStella.php?id=" + id)).text() === "true"; + } + + if (stellaCompatible) { + window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella"); + window.preloadedGains[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, 0); + window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .05); + window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .1); + } else { + if (!window.preloaded[id]) { + window.buffering = true; + + 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" }); + } + + window.preloadedGains[id] = await normalizeAudio(window.preloaded[id], 0); + window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id], .05); + window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id], .1); } } cleanupPreload(); preloadMore(); - playerDocument.getElementById("player-audio").src = window.preloaded[id]; - playerDocument.getElementById("player-audio").play(); - window.buffering = false; + if (!stellaCompatible) { + if (!window.preloadedURLs[id]) { + window.preloadedURLs[id] = localStorage.getItem("data-saving") ? URL.createObjectURL(window.preloadedBlobs[id]) : URL.createObjectURL(window.preloadedBlobs[id]); + } + } else { + window.playingStella = true; + playerDocument.getElementById("player-audio").src = window.preloaded[id].urls.hpf; + playerDocument.getElementById("player-audio-stella-side1").src = window.preloaded[id].urls.bass; + playerDocument.getElementById("player-audio-stella-side2").src = window.preloaded[id].urls.drums; + playerDocument.getElementById("player-audio-stella-side3").src = window.preloaded[id].urls.other; + playerDocument.getElementById("player-audio-stella-side4").src = window.preloaded[id].urls.piano; + playerDocument.getElementById("player-audio-stella-side5").src = window.preloaded[id].urls.vocals; + playerDocument.getElementById("player-audio").play(); + window.buffering = false; + } + + if (!stellaCompatible) { + playerDocument.getElementById("player-audio").src = window.preloadedURLs[id]; + playerDocument.getElementById("player-audio").play(); + window.buffering = false; + } + + window.calledNextRecently = false; + + if (localStorage.getItem("data-saving") === "true") { + playerDocument.getElementById("badge-cd").style.display = "none"; + playerDocument.getElementById("badge-stella").style.display = "none"; + playerDocument.getElementById("badge-hires").style.display = "none"; + playerDocumentMobile.getElementById("badge-cd").style.display = "none"; + playerDocumentMobile.getElementById("badge-stella").style.display = "none"; + playerDocumentMobile.getElementById("badge-hires").style.display = "none"; + document.getElementById("player").contentWindow.buildTooltips(); + } else { + if (window.playingStella) { + playerDocument.getElementById("badge-cd").style.display = "none"; + playerDocument.getElementById("badge-stella").style.display = "inline"; + playerDocument.getElementById("badge-hires").style.display = "none"; + playerDocumentMobile.getElementById("badge-cd").style.display = "none"; + playerDocumentMobile.getElementById("badge-stella").style.display = "inline"; + playerDocumentMobile.getElementById("badge-hires").style.display = "none"; + document.getElementById("player").contentWindow.buildTooltips(); + } else { + if (window.currentSong && window.currentSong.hiRes) { + playerDocument.getElementById("badge-cd").style.display = "none"; + playerDocument.getElementById("badge-stella").style.display = "none"; + playerDocument.getElementById("badge-hires").style.display = "inline"; + playerDocument.getElementById("badge-hires").title = "<b>Hi-Res Lossless</b><br>" + window.currentSong.bitDepth + "-bit " + (window.currentSong.sampleRate / 1000) + " kHz"; + playerDocumentMobile.getElementById("badge-cd").style.display = "none"; + playerDocumentMobile.getElementById("badge-stella").style.display = "none"; + playerDocumentMobile.getElementById("badge-hires").style.display = "inline"; + document.getElementById("player").contentWindow.buildTooltips(); + } else if (window.currentSong) { + playerDocument.getElementById("badge-cd").style.display = "inline"; + playerDocument.getElementById("badge-hires").style.display = "none"; + playerDocumentMobile.getElementById("badge-cd").style.display = "inline"; + playerDocumentMobile.getElementById("badge-stella").style.display = "none"; + playerDocumentMobile.getElementById("badge-hires").style.display = "none"; + playerDocument.getElementById("badge-cd").title = "<b>Lossless</b><br>" + window.currentSong.bitDepth + "-bit " + (window.currentSong.sampleRate / 1000) + " kHz"; + document.getElementById("player").contentWindow.buildTooltips(); + } + } + } if (window.MistNative && localStorage.getItem("desktop-notification") === "true") { window.MistNative.notification(currentSong, await (async function() { @@ -620,6 +957,8 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI if (window.MistAndroid) { window.MistAndroid.updateNotificationAlbumArt("https://" + location.hostname + "/albumart.php?i=" + currentSongID); } + + await fetch("/api/addHistory.php?i=" + currentSongID); } } @@ -627,12 +966,30 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI for (let i = 1; i <= 10; i++) { if (playlist[currentPlaylistPosition + i]) { let id = playlist[currentPlaylistPosition + i]; + let stellaCompatible = false; - if (!window.preloaded[id]) { - if (localStorage.getItem("data-saving") === "true") { - window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".m4a")).arrayBuffer()], { type: "audio/mp4" })); - } else { - window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".flac")).arrayBuffer()], { type: "audio/flac" })); + if (localStorage.getItem("enable-stella") === "true" && localStorage.getItem("data-saving") !== "true") { + stellaCompatible = await (await fetch("/api/hasStella.php?id=" + id)).text() === "true"; + } + + if (stellaCompatible) { + window.preloaded[id] = await Stella.build("/assets/content/" + id + ".stella"); + window.preloadedGains[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, 0); + window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .05); + window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id].stems.other.buffer, .1); + } 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" }); + } + + window.preloadedGains[id] = await normalizeAudio(window.preloaded[id], 0); + window.preloadedGainsBoosted1[id] = await normalizeAudio(window.preloaded[id], .05); + window.preloadedGainsBoosted2[id] = await normalizeAudio(window.preloaded[id], .1); } } } @@ -646,14 +1003,13 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI for (let key of Object.keys(window.preloaded)) { if (!keys.includes(key)) { - URL.revokeObjectURL(window.preloaded[key]); + if (window.preloadedURLs[key]) URL.revokeObjectURL(window.preloadedURLs[key]); delete window.preloaded[key]; } } } </script> - <iframe id="modal" src="ui/modal.php" style="width: 100vw; height: 100vh; border: none; inset: 0; position: fixed; z-index: 99999; display: none;"></iframe> <script> function openModal(title, url, hideTitle) { document.getElementById("modal").style.display = ""; @@ -670,16 +1026,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFI document.getElementById("modal").contentWindow._modal.show(); } - document.getElementById("modal").onload = () => { - if (localStorage.getItem("welcomed") !== "true") { - openModal("Welcome to Mist", "welcome.php", true); - } else { - if (localStorage.getItem("lastUpdate") !== "<?= trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")) ?>|<?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>") { - openModal("What's new in Mist?", "update.php", true); - } - } - } - if (!localStorage.getItem("rich-presence")) { localStorage.setItem("rich-presence", "true"); } diff --git a/app/notes/update-0.3.0.php b/app/notes/update-0.3.0.php new file mode 100644 index 0000000..5661329 --- /dev/null +++ b/app/notes/update-0.3.0.php @@ -0,0 +1,26 @@ +<?php if (!isset($releaseNotes)) die(); ?> +<h2 style="margin-top: 30px;">What's new in Mist?</h2> + +<div style="text-align: left; margin-top: 50px;"> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-security.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>Enhanced security</b></div> + <div>Listening to your favorite songs shouldn't come at the expense of your security and your privacy. Mist now includes protections against the most common forms of attacks.</div> + </div> + </div> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-connect.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>Mist is now on Discord</b></div> + <div>If you are using the desktop app, Mist can now use Discord Rich Presence to show the music you are playing to all of your friends. You will also need the Discord desktop app.</div> + </div> + </div> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-android.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>Native Android application</b></div> + <div>You can now quit using the Progressive Web App, and instead use the official Mist Android app. Lower battery and resource usage, along with better OS integrations.</div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/app/notes/update-0.4.0.php b/app/notes/update-0.4.0.php new file mode 100644 index 0000000..ebdb695 --- /dev/null +++ b/app/notes/update-0.4.0.php @@ -0,0 +1,26 @@ +<?php if (!isset($releaseNotes)) die(); ?> +<h2 style="margin-top: 30px;">What's new in Mist?</h2> + +<div style="text-align: left; margin-top: 50px;"> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-normalizer.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>No more volume switcheroo</b></div> + <div>Mist now automagically normalizes audio volume when you are listening to music. You don't need to do any other change and this is enabled by default. Enjoy!</div> + </div> + </div> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-mobile.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>Improved mobile experience</b></div> + <div>Using Mist on the go shouldn't be frustrating, that's why we refined your mobile experience to make it more pleasant and smoother.</div> + </div> + </div> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-navigation.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>Never get lost again</b></div> + <div>Navigating Mist was very frustrating and non-pleasant, so we fixed it. You now get improved navigation controls on both desktop and mobile.</div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/app/notes/update-1.0.0.php b/app/notes/update-1.0.0.php new file mode 100644 index 0000000..8ced54f --- /dev/null +++ b/app/notes/update-1.0.0.php @@ -0,0 +1,12 @@ +<?php if (!isset($releaseNotes)) die(); ?> +<h2 style="margin-top: 30px;">What's new in Mist?</h2> + +<div style="text-align: left; margin-top: 50px;"> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> + <img src="/assets/icons/notes-release.svg" style="width: 48px;" class="icon" alt=""> + <div> + <div><b>Stable Mist is there</b></div> + <div>As the Developer Preview programme ends, Mist is now stable and can be enjoyed by numerous users. But wait, there's still more coming!</div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/app/ui/albums.php b/app/ui/albums.php index e383f81..1ee5822 100644 --- a/app/ui/albums.php +++ b/app/ui/albums.php @@ -11,7 +11,7 @@ <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>albums</title> + <title>Albums</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/dark.css" rel="stylesheet"> <link href="/assets/styles.css" rel="stylesheet"> @@ -21,11 +21,21 @@ <script src="/assets/js/shortcuts.js"></script> <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> </head> -<body class="crossplatform"> +<body class="crossplatform has-navigation"> + <div id="ui-navigation" style="z-index: 999; background-color: rgba(255, 255, 255, .75); position: fixed; top: 0; left: 0; right: 0; height: 32px; backdrop-filter: blur(50px); -webkit-backdrop-filter: blur(50px);"> + <div style="display: grid; grid-template-columns: max-content 1fr max-content; height: 100%;" class="container"> + <div id="ui-back-button" onclick="history.back();" style="display: flex; align-items: center; justify-content: center; text-align: center; opacity: 0; pointer-events: none;"> + <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>Albums</b></div> + <div> + <input placeholder="Filter" id="filter" class="form-control" style="width: 256px;height: 32px;border-top: none;" onchange="updateFilter();" onkeyup="updateFilter();"> + </div> + </div> + </div> <script src="/assets/js/common.js"></script> <div class="container"> <br> - <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Albums<input placeholder="Filter" id="filter" class="form-control" style="width: 256px; float: right;" onchange="updateFilter();" onkeyup="updateFilter();"></h2> <div id="album-grid" style="display: grid; grid-template-columns: repeat(5, 1fr);"> <?php global $albums; @@ -86,11 +96,11 @@ document.getElementById("search-results").style.display = "grid"; document.getElementById("album-grid").style.display = "none"; - let results = fuse.search(query); + let results = items.filter(i => i.title.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " ").includes(query.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " ")) || i.artist.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " ").includes(query.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " "))); document.getElementById("search-results").innerHTML = ""; for (let result of results) { - document.getElementById("search-results").innerHTML += document.getElementById(result.item.id).outerHTML; + document.getElementById("search-results").innerHTML += document.getElementById(result.id).outerHTML; } } else { document.getElementById("search-results").style.display = "none"; diff --git a/app/ui/download.php b/app/ui/download.php index 13f76b0..dad8eef 100644 --- a/app/ui/download.php +++ b/app/ui/download.php @@ -58,6 +58,11 @@ function getSize($bytes) { <ul class="list-group"> <li class="list-group-item">Lossless (FLAC, <?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".flac")) ?>)<a style="float: right;" download="<?= $fileName ?>.flac" href="/assets/content/<?= $_GET["i"] ?>.flac">Download</a></li> <li class="list-group-item">AAC-LC (MP4, <?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".m4a")) ?>)<a style="float: right;" download="<?= $fileName ?>.m4a" href="/assets/content/<?= $_GET["i"] ?>.m4a">Download</a></li> + <?php if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".stella")): ?> + <li class="list-group-item">Mist Stella (<?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".stella")) ?>)<a style="float: right;" download="<?= $fileName ?>.stella" href="/assets/content/<?= $_GET["i"] ?>.stella">Download</a></li> + <?php else: ?> + <li class="list-group-item">Mist Stella (not available)</li> + <?php endif; ?> <li class="list-group-item">Album art (JPEG, <?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".jpg")) ?>)<a style="float: right;" download="<?= $fileName ?>.jpg" href="/assets/content/<?= $_GET["i"] ?>.jpg">Download</a></li> </ul> </div> diff --git a/app/ui/explore.php b/app/ui/explore.php index 7edfdf1..c93beff 100644 --- a/app/ui/explore.php +++ b/app/ui/explore.php @@ -11,7 +11,7 @@ <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>explore</title> + <title>Explore</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/dark.css" rel="stylesheet"> <link href="/assets/styles.css" rel="stylesheet"> @@ -21,13 +21,23 @@ <script src="/assets/js/shortcuts.js"></script> <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> </head> -<body class="crossplatform"> +<body class="crossplatform has-navigation"> + <div id="ui-navigation" style="z-index: 999; background-color: rgba(255, 255, 255, .75); position: fixed; top: 0; left: 0; right: 0; height: 32px; backdrop-filter: blur(50px); -webkit-backdrop-filter: blur(50px);"> + <div style="display: grid; grid-template-columns: max-content 1fr max-content; height: 100%;" class="container"> + <div id="ui-back-button" onclick="history.back();" style="display: flex; align-items: center; justify-content: center; text-align: center; opacity: 0; pointer-events: none;"> + <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>Explore</b></div> + <div style="opacity: 0; pointer-events: none;"> + <input placeholder="Filter" id="filter" class="form-control" style="width: 256px;height: 32px;border-top: none;" onchange="updateFilter();" onkeyup="updateFilter();"> + </div> + </div> + </div> <script src="/assets/js/common.js"></script> <div class="container"> <br> - <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Explore</h2> - <form action="search.php"> + <form action="search.php" onsubmit="window.parent.location.hash = '#/search/' + encodeURIComponent(document.getElementById('search').value);"> <div style="width: calc(100% - 20px); margin-left: 10px;" class="input-group mb-3"> <input name="q" type="text" id="search" class="form-control" placeholder="Search on Mist"> <button class="btn btn-outline-secondary" type="submit" id="btn-search">Search</button> diff --git a/app/ui/info.php b/app/ui/info.php index fd4fad5..d21e431 100644 --- a/app/ui/info.php +++ b/app/ui/info.php @@ -1,4 +1,4 @@ -<?php header("X-Frame-Options: SAMEORIGIN"); require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $songs; +<?php header("X-Frame-Options: SAMEORIGIN"); require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $songs; global $favorites; if (!isset($_GET["i"]) || !isset($songs[$_GET["i"]])) { die(); @@ -158,7 +158,22 @@ function getChannelConfiguration($c) { </tr> <tr> <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Mist Stella</td> - <td>No</td> + <td> + <?php if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".stella")): ?> + <?php + + $handle = fopen($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".stella", "r"); + fseek($handle, 8); + $contents = fread($handle, 512 - 8); + fclose($handle); + $metadata = json_decode(trim(zlib_decode($contents)), true); + + ?> + Yes (<?php if (isset($metadata["id"])): ?>#<?= $metadata["id"] ?>, <?php endif; ?>Version <?= $metadata["version"] ?>) + <?php else: ?> + No + <?php endif; ?> + </td> </tr> <tr> <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Copyright</td> @@ -172,7 +187,20 @@ function getChannelConfiguration($c) { <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">File size (AAC-LC)</td> <td><?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".m4a")) ?></td> </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">File size (Stella)</td> + <td> + <?php if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".stella")): ?> + <?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".stella")) ?> + <?php else: ?> + - + <?php endif; ?> + </td> + </tr> </table> + + <hr><?php $id = $_GET["i"]; ?> + <a class="btn btn-primary" onclick="<?= in_array($id, $favorites) ? "un" : "" ?>favoriteSong('<?= $id ?>');" id="btn-favorite-<?= $id ?>"><img id="btn-favorite-<?= $id ?>-icon" alt="" src="/assets/icons/favorite-<?= in_array($id, $favorites) ? "on" : "off" ?>.svg" style="pointer-events: none; filter: invert(1); width: 24px; height: 24px; margin-right: 5px;"><span id="btn-favorite-<?= $id ?>-text"><?= in_array($id, $favorites) ? "Remove from favorites" : "Add to favorites" ?></span></a> </div> <script> @@ -182,6 +210,32 @@ function getChannelConfiguration($c) { window.parent.document.getElementById("modal-frame").style.height = document.body.clientHeight + "px"; } }); + + async function favoriteSong(id) { + document.getElementById("btn-favorite-" + id + "-icon").src = "/assets/icons/favorite-on.svg"; + document.getElementById("btn-favorite-" + id + "-text").innerText = "Remove from favorites"; + document.getElementById("btn-favorite-" + id).onclick = () => { + unfavoriteSong(id); + } + await fetch("/api/addFavorite.php?i=" + id); + + window.parent.redownloadFavorites(); + } + + async function unfavoriteSong(id) { + document.getElementById("btn-favorite-" + id + "-icon").src = "/assets/icons/favorite-off.svg"; + document.getElementById("btn-favorite-" + id + "-text").innerText = "Add to favorites"; + document.getElementById("btn-favorite-" + id).onclick = () => { + favoriteSong(id); + } + await fetch("/api/removeFavorite.php?i=" + id); + + <?php if (isset($favoritesList)): ?> + location.reload(); + <?php endif; ?> + + window.parent.redownloadFavorites(); + } </script> </body> </html>
\ No newline at end of file diff --git a/app/ui/library.php b/app/ui/library.php new file mode 100644 index 0000000..c6b5658 --- /dev/null +++ b/app/ui/library.php @@ -0,0 +1,46 @@ +<?php header("X-Frame-Options: SAMEORIGIN"); require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $songs; global $albums; ?> +<!doctype html> +<html lang="en"> +<head> + <script> + if (typeof window.parent.openModal === "undefined") { + location.href = "/app/#/library"; + } + </script> + <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>Library</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> + <link href="/assets/dark.css" rel="stylesheet"> + <link href="/assets/styles.css" rel="stylesheet"> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> + <script src="/assets/localforage.min.js"></script> + <script src="/assets/fuse.min.js"></script> + <script src="/assets/js/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform"> + <script> + window.parent.location.hash = "#/search/<?= rawurlencode($_GET["q"]) ?>"; + </script> + <script src="/assets/js/common.js"></script> + <div class="container"> + <br> + <div style="margin-left: 10px;" class="list-group"> + <a class="list-group-item list-group-item-action" href="albums.php"> + <img src="/assets/icons/album.svg" style="filter: brightness(0%); margin-right: 5px; vertical-align: middle;">Albums + </a> + <a class="list-group-item list-group-item-action" href="songs.php"> + <img src="/assets/icons/song.svg" style="filter: brightness(0%); margin-right: 5px; vertical-align: middle;">Songs + </a> + <a class="list-group-item list-group-item-action" href="favorites.php"> + <img src="/assets/icons/favorites.svg" style="filter: brightness(0%); margin-right: 5px; vertical-align: middle;">Favorites + </a> + </div> + </div> + + <br><br> +</body> +</html>
\ No newline at end of file diff --git a/app/ui/listing.php b/app/ui/listing.php index 9ad227f..d32525b 100644 --- a/app/ui/listing.php +++ b/app/ui/listing.php @@ -50,7 +50,15 @@ if (!$presetList) { <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>listing</title> + <title> + <?php if (isset($favoritesList) && !$hasAlbum): ?> + Favorites + <?php elseif ($hasAlbum): ?> + <?= $albums[$_GET["a"]]["title"] ?> + <?php else: ?> + Songs + <?php endif; ?> + </title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/dark.css" rel="stylesheet"> <link href="/assets/styles.css" rel="stylesheet"> @@ -60,130 +68,156 @@ if (!$presetList) { <script src="/assets/fuse.min.js"></script> <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> </head> -<body class="crossplatform"> +<body class="crossplatform has-navigation"> + <div id="ui-navigation" style="z-index: 999; background-color: rgba(255, 255, 255, .75); position: fixed; top: 0; left: 0; right: 0; height: 32px; backdrop-filter: blur(50px); -webkit-backdrop-filter: blur(50px);"> + <div style="display: grid; grid-template-columns: max-content 1fr max-content; height: 100%;" class="container"> + <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)): ?>Songs<?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();"> + </div> + <?php endif; ?> + </div> + </div> <script src="/assets/js/common.js"></script> <?php if ($hasAlbum): ?> <script> window.parent.location.hash = "#/albums/<?= $_GET["a"] ?>"; </script> <?php endif; ?> - <div id="content" style="position: fixed; inset: 0; z-index: 10; overflow: auto; backdrop-filter: blur(50vw); -webkit-backdrop-filter: blur(50vw);"> - <div class="container"> - <br> - <?php if (!$hasAlbum && !isset($favoritesList)): ?> - <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Songs<input placeholder="Filter" id="filter" class="form-control" style="width: 256px; float: right;" onchange="updateFilter();" onkeyup="updateFilter();"></h2> - <?php elseif (isset($favoritesList)): ?> - <div id="album-info" style="display: grid; grid-template-columns: 20vw 1fr; margin-top: 10px; margin-left: 10px; grid-gap: 30px;"> - <img id="album-info-art" alt="" src="/assets/favorites.svg" style="height: 20vw; width: 20vw; border-radius: .75vw;"> - <div id="album-info-text" style="padding: 30px 0; display: grid; grid-template-rows: 1fr max-content;"> - <div><h2>Favorites</h2> - <h2 style="opacity: .5;"><?= $_PROFILE["name"] ?></h2> - <div style="opacity: .5;"> - Click on the heart icon near a song to add it to this list. - </div> - </div> - <div id="album-info-buttons"> - <a class="btn btn-primary <?= count(array_keys($list)) <= 0 ? "disabled" : "" ?>" onclick="window.parent.playSong('<?= array_keys($list)[0] ?? '' ?>', 'favorites');" style="width: 100px;">Play</a> - <a class="btn btn-outline-primary <?= count(array_keys($list)) <= 0 ? "disabled" : "" ?>" style="width: 100px;" onclick="window.parent.shuffleList('favorites');">Shuffle</a> - <input placeholder="Filter" id="filter" class="form-control" style="width: 256px; float: right;" onchange="updateFilter();" onkeyup="updateFilter();"> + <div class="container"> + <br> + <?php if (isset($favoritesList) && !$hasAlbum): ?> + <div id="album-info" style="display: grid; grid-template-columns: 20vw 1fr; margin-top: 10px; margin-left: 10px; grid-gap: 30px;"> + <img id="album-info-art" alt="" src="/assets/favorites.svg" style="height: 20vw; width: 20vw; border-radius: .75vw;"> + <div id="album-info-text" style="padding: 30px 0; display: grid; grid-template-rows: 1fr max-content;"> + <div><h2>Favorites</h2> + <h2 style="opacity: .5;"><?= $_PROFILE["name"] ?></h2> + <div style="opacity: .5;"> + Click on the heart icon near a song to add it to this list. </div> </div> + <div id="album-info-buttons"> + <a class="btn btn-primary <?= count(array_keys($list)) <= 0 ? "disabled" : "" ?>" onclick="window.parent.playSong('<?= array_keys($list)[0] ?? '' ?>', 'favorites');" style="width: 100px;">Play</a> + <a class="btn btn-outline-primary <?= count(array_keys($list)) <= 0 ? "disabled" : "" ?>" style="width: 100px;" onclick="window.parent.shuffleList('favorites');">Shuffle</a> + </div> </div> - <?php else: ?> - <div id="album-info" style="display: grid; grid-template-columns: 20vw 1fr; margin-top: 10px; margin-left: 10px; grid-gap: 30px;"> - <img id="album-info-art" alt="" src="/assets/content/<?= $_GET["a"] ?>.jpg" style="height: 20vw; width: 20vw; border-radius: .75vw;"> - <div id="album-info-text" style="padding: 30px 0; display: grid; grid-template-rows: 1fr max-content;"> - <div><h2><?= $albums[$_GET["a"]]["title"] ?></h2> - <h2 style="opacity: .5;"><?= $albums[$_GET["a"]]["artist"] ?></h2> - <div style="opacity: .5;"> + </div> + <?php elseif ($hasAlbum): + + $albums[$_GET["a"]]["stella"] = false; + + foreach ($albums[$_GET["a"]]["tracks"] as $track) { + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $track . ".stella")) { + $albums[$_GET["a"]]["stella"] = true; + } + } + + ?> + <div id="album-info" style="display: grid; grid-template-columns: 20vw 1fr; margin-top: 10px; margin-left: 10px; grid-gap: 30px;"> + <img id="album-info-art" alt="" src="/assets/content/<?= $_GET["a"] ?>.jpg" style="height: 20vw; width: 20vw; border-radius: .75vw;"> + <div id="album-info-text" style="padding: 30px 0; display: grid; grid-template-rows: 1fr max-content;"> + <div><h2><?= $albums[$_GET["a"]]["title"] ?></h2> + <h2 style="opacity: .5;"><?= $albums[$_GET["a"]]["artist"] ?></h2> + <div style="opacity: .5;"> + <?php if (isset($albums[$_GET["a"]]["date"]) && $albums[$_GET["a"]]["date"] > 0): ?> <?= $albums[$_GET["a"]]["date"] ?> - <?php if ($albums[$_GET["a"]]["hiRes"]): ?> - · <img src='/assets/icons/lossless.svg' alt='' class='icon player-badge-icon'>Hi-Res Lossless - <?php endif; ?> - </div> - </div> - <div id="album-info-buttons" <?php if (!in_array($_GET["a"], $library)): ?>class="nolibrary"<?php endif; ?>> - <?php if (in_array($_GET["a"], $library)): ?> - <a class="btn btn-primary" onclick="window.parent.playSong('<?= array_keys($list)[0] ?>', 'album:<?= $_GET["a"] ?>');" style="width: 100px;">Play</a> - <a class="btn btn-outline-primary" style="width: 100px;" onclick="window.parent.shuffleList('album:<?= $_GET["a"] ?>');">Shuffle</a> - <?php else: ?> - <a class="btn btn-primary" onclick="window.addToLibrary();" id="library-button" style="width: 200px;">Add to library</a> - <?php endif ?> + <?php if ($albums[$_GET["a"]]["hiRes"] || $albums[$_GET["a"]]["stella"]): ?> · <?php endif; ?> + <?php endif; if ($albums[$_GET["a"]]["hiRes"]): ?> + <img src='/assets/icons/lossless.svg' alt='' class='icon player-badge-icon'>Hi-Res Lossless + <?php if ($albums[$_GET["a"]]["stella"]): ?><span class="mist-stella"> · </span><?php endif; ?> + <?php endif; if ($albums[$_GET["a"]]["stella"]): ?> + <span class="mist-stella"><img src='/assets/icons/stella.svg' alt='' style="height: 14.4px !important; width: 14.4px !important;" class='icon player-badge-icon'>Mist Stella</span> + <?php endif; ?> </div> </div> - </div> - <?php endif; ?> - <?php displayList($list, $hasAlbum); ?> - <div class="list-group" style="margin-left: 10px; margin-top: 20px; display: none;" id="search-results"></div> - <?php if (count($list) === 0): ?> - <div class="text-muted" style="position: fixed; display: flex; inset: 0; align-items: center; justify-content: center;"> - <div style="text-align: center;"> - <img class="icon" src="/assets/logo-transparent.svg" style="filter: grayscale(1) invert(1); width: 96px; height: 96px;" alt=""> - <h4 style="opacity: .75;">Add music to your library</h4> - <p style="max-width: 300px; margin-left: auto; margin-right: auto;">Browse millions of songs and collect your favorites here.</p> - <div class="btn btn-primary" onclick="window.parent.openUI('explore');">Browse Mist</div> + <div id="album-info-buttons" <?php if (!in_array($_GET["a"], $library)): ?>class="nolibrary"<?php endif; ?>> + <?php if (in_array($_GET["a"], $library)): ?> + <a class="btn btn-primary" onclick="window.parent.playSong('<?= array_keys($list)[0] ?>', 'album:<?= $_GET["a"] ?>');" style="width: 100px;">Play</a> + <a class="btn btn-outline-primary" style="width: 100px;" onclick="window.parent.shuffleList('album:<?= $_GET["a"] ?>');">Shuffle</a> + <?php else: ?> + <a class="btn btn-primary" onclick="window.addToLibrary();" id="library-button" style="width: 200px;">Add to library</a> + <?php endif ?> </div> </div> - <?php endif; ?> - <?php if ($hasAlbum && in_array($_GET["a"], $library)): ?> - <br> - <div><a class="link" id="library-button" href="#" onclick="removeFromLibrary();">Remove from library</a></div> - <?php endif; ?> + </div> + <?php endif; ?> + <?php displayList($list, $hasAlbum); ?> + <div class="list-group" style="margin-left: 10px; margin-top: 20px; display: none;" id="search-results"></div> + <?php if (count($list) === 0 && !isset($favoritesList)): ?> + <div class="text-muted" style="position: fixed; display: flex; inset: 0; align-items: center; justify-content: center;"> + <div style="text-align: center;"> + <img class="icon" src="/assets/logo-transparent.svg" style="filter: grayscale(1) invert(1); width: 96px; height: 96px;" alt=""> + <h4 style="opacity: .75;">Add music to your library</h4> + <p style="max-width: 300px; margin-left: auto; margin-right: auto;">Browse millions of songs and collect your favorites here.</p> + <div class="btn btn-primary" onclick="window.parent.openUI('explore');">Browse Mist</div> + </div> + </div> + <?php endif; ?> + <br> + <div style="margin-left: 10px;"> + <?php if ($hasAlbum && trim($albums[$_GET["a"]]["copyright"]) !== ""): ?> + <div class="text-muted"><?= $albums[$_GET["a"]]["copyright"] ?></div> + <?php endif; if ($hasAlbum && in_array($_GET["a"], $library)): ?><a class="link" id="library-button" href="#" onclick="removeFromLibrary();">Remove from library</a> + <?php endif; ?> </div> + </div> - <script> - <?php if ($hasAlbum): ?> - async function addToLibrary() { - document.getElementById("library-button").classList.add("disabled"); - await fetch("/api/addLibrary.php?i=<?= $_GET["a"] ?>"); - window.parent.redownloadLibrary(); - location.reload(); - } + <script> + <?php if ($hasAlbum): ?> + async function addToLibrary() { + document.getElementById("library-button").classList.add("disabled"); + await fetch("/api/addLibrary.php?i=<?= $_GET["a"] ?>"); + window.parent.redownloadLibrary(); + location.reload(); + } - async function removeFromLibrary() { - document.getElementById("library-button").classList.add("disabled"); - await fetch("/api/removeLibrary.php?i=<?= $_GET["a"] ?>"); - window.parent.redownloadLibrary(); - location.reload(); - } - <?php endif; ?> + async function removeFromLibrary() { + document.getElementById("library-button").classList.add("disabled"); + await fetch("/api/removeLibrary.php?i=<?= $_GET["a"] ?>"); + window.parent.redownloadLibrary(); + location.reload(); + } + <?php endif; ?> + + let items = Array.from(document.getElementsByClassName("track")).map(i => { return { title: i.getAttribute("data-item-track"), artist: i.getAttribute("data-item-artist"), id: i.id } }); + + const fuse = new Fuse(items, { + keys: [ + { + name: 'title', + weight: 1 + }, + { + name: 'artist', + weight: .5 + } + ] + }); + + function updateFilter() { + let query = document.getElementById("filter").value.trim(); + + if (query !== "") { + document.getElementById("search-results").style.display = "flex"; + document.getElementById("main-list").style.display = "none"; + + let results = items.filter(i => i.title.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " ").includes(query.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " ")) || i.artist.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " ").includes(query.toLowerCase().replace(/[^a-z\d ]/mg, " ").replace(/ +/mg, " "))); + document.getElementById("search-results").innerHTML = ""; - let items = Array.from(document.getElementsByClassName("track")).map(i => { return { title: i.getAttribute("data-item-track"), artist: i.getAttribute("data-item-artist"), id: i.id } }); - - const fuse = new Fuse(items, { - keys: [ - { - name: 'title', - weight: 1 - }, - { - name: 'artist', - weight: .5 - } - ] - }); - - function updateFilter() { - let query = document.getElementById("filter").value.trim(); - - if (query !== "") { - document.getElementById("search-results").style.display = "flex"; - document.getElementById("main-list").style.display = "none"; - - let results = fuse.search(query); - document.getElementById("search-results").innerHTML = ""; - - for (let result of results) { - document.getElementById("search-results").innerHTML += document.getElementById(result.item.id).outerHTML; - } - } else { - document.getElementById("search-results").style.display = "none"; - document.getElementById("main-list").style.display = "flex"; + for (let result of results) { + document.getElementById("search-results").innerHTML += document.getElementById(result.id).outerHTML; } + } else { + document.getElementById("search-results").style.display = "none"; + document.getElementById("main-list").style.display = "flex"; } - </script> + } + </script> - <br><br> - </div> + <br><br> </body> </html>
\ No newline at end of file diff --git a/app/ui/lyrics.php b/app/ui/lyrics.php index bd24832..8f50931 100644 --- a/app/ui/lyrics.php +++ b/app/ui/lyrics.php @@ -43,7 +43,7 @@ } </style> </head> -<body class="crossplatform" style="background-color: transparent !important;"> +<body class="crossplatform" 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;"> @@ -112,6 +112,7 @@ document.getElementById("loading").style.display = "none"; document.getElementById("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 => ` <div class="synced-lyrics-item" id="synced-lyrics-${i.startTimeMs}">${i.words}</div> `).join("") + "<div style='height: 16px;'></div>"; @@ -123,6 +124,7 @@ 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"); } } else { diff --git a/app/ui/modal.php b/app/ui/modal.php index b8f1cfb..ec3e663 100644 --- a/app/ui/modal.php +++ b/app/ui/modal.php @@ -45,5 +45,34 @@ window.parent.document.getElementById("modal").style.display = "none"; }); </script> + + <div class="modal fade" id="error" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header" id="modal-header"> + <h4 class="modal-title">An internal error has occurred</h4> + </div> + + <div class="modal-body"> + <div class="alert alert-danger"> + <p>If this is the first time you see this, simply restart Mist and try again.</p> + <p>If you have been seeing this multiple times, you might be encountering a bug and you should report it to your administrator.</p> + <a onclick="window.parent.location.reload();" class="btn btn-danger">Reload</a> + + <hr> + <pre id="error-content"></pre> + </div> + </div> + </div> + </div> + </div> + + <script> + window._error = new bootstrap.Modal(document.getElementById("error")); + + document.getElementById("error").addEventListener("shown.bs.modal", () => { + window.parent.document.getElementById("modal").style.display = ""; + }); + </script> </body> </html>
\ No newline at end of file diff --git a/app/ui/navigation.php b/app/ui/navigation.php index 9344791..dacaa94 100644 --- a/app/ui/navigation.php +++ b/app/ui/navigation.php @@ -39,13 +39,16 @@ <div id="explore" class="navigation-item" onclick="window.parent.openUI('explore');"> <img class="icon" alt="" src="/assets/icons/explore.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Explore</span> </div> - <div id="albums" class="navigation-item active" onclick="window.parent.openUI('albums');"> + <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> + <div id="albums" class="navigation-item-desktop navigation-item active" onclick="window.parent.openUI('albums');"> <img class="icon" alt="" src="/assets/icons/album.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Albums</span> </div> - <div id="songs" class="navigation-item" onclick="window.parent.openUI('songs');"> + <div id="songs" class="navigation-item-desktop navigation-item" onclick="window.parent.openUI('songs');"> <img class="icon" alt="" src="/assets/icons/song.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Songs</span> </div> - <div id="favorites" class="navigation-item" onclick="window.parent.openUI('favorites');"> + <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();"> diff --git a/app/ui/player-mobile.php b/app/ui/player-mobile.php index 5266961..e49e852 100644 --- a/app/ui/player-mobile.php +++ b/app/ui/player-mobile.php @@ -44,12 +44,51 @@ document.head.append(style); </script> - <div id="act-1" onclick="window.parent.hideMobilePlayer();" style="position: fixed; z-index: 9999999; top: 0; left: 0; right: 0; height: calc(20px + var(--android-status-bar));"></div> - <div id="act-2" onclick="window.parent.hideMobilePlayer();" style="position: fixed; z-index: 9999999; bottom: 0; left: 0; right: 0; height: calc(20px + var(--android-navigation-bar))"></div> - <div id="act-3" onclick="window.parent.hideMobilePlayer();" style="position: fixed; z-index: 9999999; bottom: 0; left: 0; top: 0; width: 20px;"></div> - <div id="act-4" onclick="window.parent.hideMobilePlayer();" style="position: fixed; z-index: 9999999; bottom: 0; right: 0; top: 0; width: 20px;"></div> + <div id="act" onclick="window.parent.hideMobilePlayer();" style="position: fixed; z-index: 9999999; top: 0; bottom: calc(175px + var(--android-navigation-bar)); height: calc(100vh - (175px + var(--android-navigation-bar))); left: 0; right: 0;"></div> <div id="android" style="position: fixed; z-index: 9999999; bottom: 0; right: 0; left: 0; height: var(--android-navigation-bar); border-top: 1px solid rgba(255, 255, 255, .1);"></div> + <script> + window.transitionTimeout = null; + window.lastPosY = null; + window.initialPosY = null; + window.log = false; + + document.body.ontouchmove = (e) => { + if (window.log) { + window.lastPosY = e.touches[0].clientY; + let diff = initialPosY - lastPosY; + + if (diff < 0) { + window.parent.document.getElementById("player-mobile-container").style.bottom = diff + "px"; + window.parent.document.getElementById("lyrics-page").classList.remove("mobile-show"); + } + } + } + + document.getElementById("act").ontouchstart = (e) => { + window.initialPosY = e.touches[0].clientY; + + if (window.parent.currentSongID !== null) { + window.log = true; + window.parent.document.getElementById("player-mobile-container").style.transition = ""; + } + } + + document.body.ontouchend = () => { + if (window.log) { + window.log = false; + window.parent.document.getElementById("player-mobile-container").style.transition = "bottom 200ms ease 0s"; + window.parent.document.getElementById("player-mobile-container").style.bottom = "0"; + window.parent.document.getElementById("lyrics-page").classList.add("mobile-show"); + let diff = initialPosY - lastPosY; + + if (diff < 0 && diff <= -10) { + window.parent.hideMobilePlayer(); + } + } + } + </script> + <div id="album-art-bg" style="position: fixed;inset: 0;background-position: center;background-size: cover; z-index: 5;"></div> <div id="album-art-bg2" style="background-color: rgba(0, 0, 0, .1); z-index: 10; position: fixed;inset: 0; backdrop-filter: blur(100px);"></div> <div id="player" class="bg-white mobile-player" style="background-color: transparent !important; color: white;position: fixed; bottom: var(--android-navigation-bar); left: 0; right: 0; height: 64px; z-index: 9999;"> @@ -87,14 +126,19 @@ </div> <div style="text-align: right; display: flex; align-items: center; justify-content: right;" id="badges"> <span id="badge-lossy" style="display: none;"></span> - <span id="badge-cd" style="display: block;border: 1px solid transparent;color: rgba(255, 255, 255, .75);background-color: rgba(255, 255, 255, .25);padding: 2px 5px;border-radius: 5px;font-size: 12px;position: fixed;margin-left: auto;margin-right: auto;width: max-content;left: 0;right: 0;bottom: calc(120px + var(--android-navigation-bar));"> + <span id="badge-cd" style="display: block;border: 1px solid transparent;color: rgba(255, 255, 255, .5);background-color: rgba(255, 255, 255, .1);padding: 2px 5px;border-radius: 5px;font-size: 12px;position: fixed;margin-left: auto;margin-right: auto;width: max-content;left: 0;right: 0;bottom: calc(120px + var(--android-navigation-bar));"> + <span style="display: grid; grid-template-columns: max-content max-content"> + <span><img src="/assets/icons/lossless.svg" alt="" class="player-badge-icon" style="filter: invert(1); opacity: .5;">Lossless</span> + </span> + </span> + <span id="badge-hires" style="display: block;border: 1px solid transparent;color: rgba(255, 255, 255, .5);background-color: rgba(255, 255, 255, .1);padding: 2px 5px;border-radius: 5px;font-size: 12px;position: fixed;margin-left: auto;margin-right: auto;width: max-content;left: 0;right: 0;bottom: calc(120px + var(--android-navigation-bar));"> <span style="display: grid; grid-template-columns: max-content max-content"> - <span><img src="/assets/icons/lossless.svg" alt="" class="player-badge-icon" style="filter: invert(1); opacity: .75;">Lossless</span> + <span><img src="/assets/icons/lossless.svg" alt="" class="player-badge-icon" style="filter: invert(1); opacity: .5;">Hi-Res Lossless</span> </span> </span> - <span id="badge-hires" style="display: block;border: 1px solid transparent;color: rgba(255, 255, 255, .75);background-color: rgba(255, 255, 255, .25);padding: 2px 5px;border-radius: 5px;font-size: 12px;position: fixed;margin-left: auto;margin-right: auto;width: max-content;left: 0;right: 0;bottom: calc(120px + var(--android-navigation-bar));"> + <span id="badge-stella" style="display: block;border: 1px solid transparent;color: rgba(255, 255, 255, .5);background-color: rgba(255, 255, 255, .1);padding: 2px 5px;border-radius: 5px;font-size: 12px;position: fixed;margin-left: auto;margin-right: auto;width: max-content;left: 0;right: 0;bottom: calc(120px + var(--android-navigation-bar));"> <span style="display: grid; grid-template-columns: max-content max-content"> - <span><img src="/assets/icons/lossless.svg" alt="" class="player-badge-icon" style="filter: invert(1); opacity: .75;">Hi-Res Lossless</span> + <span><img src="/assets/icons/stella.svg" alt="" class="player-badge-icon" style="height: 14.4px; width: 14.4px !important; filter: invert(1); opacity: .5;">Mist Stella</span> </span> </span> </div> diff --git a/app/ui/player.php b/app/ui/player.php index ea60889..8043a5d 100644 --- a/app/ui/player.php +++ b/app/ui/player.php @@ -20,10 +20,60 @@ <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> </head> <body class="crossplatform"> + <script> + window.transitionTimeout = null; + window.lastPosY = null; + window.log = false; + + document.body.ontouchmove = (e) => { + if (window.log) { + console.log(e.touches[0]); + window.lastPosY = e.touches[0].clientY + + window.parent.document.getElementById("player").offsetTop + + (window.parent.innerHeight - window.parent.document.getElementById("player").offsetTop); + window.parent.document.getElementById("player-mobile-container").style.bottom = -(e.touches[0].clientY + + window.parent.document.getElementById("player").offsetTop + + (window.parent.innerHeight - window.parent.document.getElementById("player").offsetTop)) + "px"; + console.log(window.parent.document.getElementById("player-mobile-container").style.bottom); + } + } + + document.body.ontouchstart = () => { + if (window.parent.currentSongID !== null) { + window.log = true; + window.parent.document.getElementById("player-mobile-container").style.transition = ""; + } + } + + document.body.ontouchend = () => { + if (window.log) { + window.log = false; + window.parent.document.getElementById("player-mobile-container").style.transition = "bottom 200ms ease 0s"; + window.parent.document.getElementById("player-mobile-container").style.bottom = "-100vh"; + + if (lastPosY) { + console.log(window.parent.innerHeight - lastPosY); + + if (window.parent.innerHeight - lastPosY >= 10 && lastPosY >= 10) { + window.parent.showMobilePlayer(); + } + } + + window.lastPosY = null; + } + } + </script> <script src="/assets/js/common.js"></script> <div id="player" class="bg-white desktop-player" style="position: fixed; bottom: 0; left: 0; right: 0; height: 64px; z-index: 9999;"> <div id="desktop-player-action" onclick="window.parent.showMobilePlayer();" style="display: none;"></div> + <audio id="player-audio"></audio> + <audio id="player-audio-stella-side1"></audio> + <audio id="player-audio-stella-side2"></audio> + <audio id="player-audio-stella-side3"></audio> + <audio id="player-audio-stella-side4"></audio> + <audio id="player-audio-stella-side5"></audio> + <div class="container" style="display: grid; grid-template-columns: 1fr 1.5fr 1fr;"> <div id="buttons" style="height: 48px; margin-top: 8px; margin-bottom: 8px;"> <span onclick="window.parent.toggleShuffle();" class="player-btn" style="border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; height: 48px; width: 48px;" id="btn-shuffle"> @@ -43,12 +93,15 @@ </span> </div> <div> + <span data-bs-html="true" data-bs-toggle="tooltip" id="badge-cd" style="z-index: 9999; display: none;position: absolute;margin-left: 71px;"><img src="/assets/icons/lossless.svg" alt="" style="height: 12px;opacity: .5;" class="icon"></span> + <span data-bs-html="true" data-bs-toggle="tooltip" id="badge-hires" style="z-index: 9999; display: none;position: absolute;margin-left: 71px;"><img src="/assets/icons/lossless.svg" alt="" style="height: 12px;opacity: .5;" class="icon"></span> + <span data-bs-html="true" title="<b>Mist Stella</b>" data-bs-toggle="tooltip" id="badge-stella" style="z-index: 9999; display: none;position: absolute;margin-left: 71px;"><img src="/assets/icons/stella.svg" alt="" style="height: 12px;opacity: .5;" class="icon"></span> <div id="info" style="display: none; grid-template-columns: 64px 1fr; height: 64px; border-left: 1px solid rgba(0, 0, 0, .25); border-right: 1px solid rgba(0, 0, 0, .25);"> <img alt="" id="album-art" style="background-color: rgba(0, 0, 0, .1); height: 64px; width: 64px;"> - <div id="info-grid" style="display: grid; grid-template-rows: 2px 22px 22px 12px 6px;"> + <div id="info-grid" style="z-index: 9; display: grid; grid-template-rows: 2px 22px 22px 12px 6px;"> <div id="info-grid-sep"></div> - <div id="info-grid-title" style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; display: flex; font-size: 0.91rem; align-items: end; text-align: center; justify-content: center;"><span id="title">Title</span></div> - <div id="info-grid-info" style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; display: flex; font-size: 0.91rem; align-items: start; text-align: center; justify-content: center; opacity: .5;"><span id="artist">Artist</span><span class="player-badge-desktop"> — <span id="album">Album</span></span></div> + <div id="info-grid-title" style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; display: flex; font-size: 0.91rem; align-items: end; text-align: center; justify-content: center;"><span onclick="openSong();" class="clickable" id="title">Title</span></div> + <div id="info-grid-info" style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; display: flex; font-size: 0.91rem; align-items: start; text-align: center; justify-content: center; opacity: .5;"><span onclick="openArtist();" class="clickable" id="artist">Artist</span><span class="player-badge-desktop"> — <span onclick="openAlbum();" class="clickable" id="album">Album</span></span></div> <div id="info-grid-time" style="font-size: 9px; opacity: .5; margin-left: 2px; margin-right: 2px;"> <span id="elapsed-time">0:00</span> <span id="remaining-time" style="float: right;">-0:00</span> @@ -66,11 +119,45 @@ </div> </div> <div style="text-align: right; display: flex; align-items: center; justify-content: right;" id="badges"> - <span id="badge-lossy" style="display: none; border: 1px solid #a402b6; color: white; background-color: #a402b6; padding: 2px 5px; border-radius: 5px; font-size: 12px;"><span style="display: grid; grid-template-columns: max-content max-content"><span>AAC-LC</span><span class="player-badge-desktop">256 kbps</span></span></span> - <span id="badge-cd" style="display: none; border: 1px solid #02b6a7; color: white; background-color: #02b6a7; padding: 2px 5px; border-radius: 5px; font-size: 12px;"></span> - <span id="badge-hires" style="display: none; border: 1px solid #b66e02; color: white; background-color: #b66e02; padding: 2px 5px; border-radius: 5px; font-size: 12px;"></span> + </div> </div> </div> + + <script> + window.buildTooltips = () => { + console.log("Build tooltip"); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + } + + if (navigator.userAgent.includes("MistNative/win32")) { + document.getElementById("badges").style.marginRight = "96px"; + } + + function openSong() { + window.parent.openModal((window.parent.songs[window.parent.currentSongID]?.artist ?? "Unknown artist") + " - " + (window.parent.songs[window.parent.currentSongID]?.title ?? "Unknown song"), "info.php?i=" + window.parent.currentSongID); + } + + function openArtist() { + window.parent.location.hash = "#/search/" + encodeURIComponent(window.parent.songs[window.parent.currentSongID]?.artist ?? "Unknown artist"); + window.parent.document.getElementById("ui").src = "ui/search.php?q=" + encodeURIComponent(window.parent.songs[window.parent.currentSongID]?.artist ?? "Unknown artist") + window.parent.redoNavigation("explore"); + } + + function openAlbum() { + 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"); + } + </script> + <style> + .clickable { + cursor: pointer; + } + + .clickable:hover { + text-decoration: underline; + } + </style> </body> </html>
\ No newline at end of file diff --git a/app/ui/queue.php b/app/ui/queue.php index c4403a4..81a7079 100644 --- a/app/ui/queue.php +++ b/app/ui/queue.php @@ -11,7 +11,7 @@ <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>queue</title> + <title>Queue</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/dark.css" rel="stylesheet"> <link href="/assets/styles.css" rel="stylesheet"> @@ -25,7 +25,7 @@ <script src="/assets/js/common.js"></script> <div class="container"> <br> - <h2 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</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. diff --git a/app/ui/search.php b/app/ui/search.php index 7d9fbba..0a1c3be 100644 --- a/app/ui/search.php +++ b/app/ui/search.php @@ -18,7 +18,7 @@ if (!isset($_GET["q"]) || trim($_GET["q"]) === "" || trim(preg_replace("/ +/m", <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>search</title> + <title>Search results for "<?= strip_tags($_GET["q"]) ?>"</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/dark.css" rel="stylesheet"> <link href="/assets/styles.css" rel="stylesheet"> @@ -28,11 +28,18 @@ if (!isset($_GET["q"]) || trim($_GET["q"]) === "" || trim(preg_replace("/ +/m", <script src="/assets/js/shortcuts.js"></script> <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> </head> -<body class="crossplatform"> +<body class="crossplatform has-navigation"> + <div id="ui-navigation" style="z-index: 999; background-color: rgba(255, 255, 255, .75); position: fixed; top: 0; left: 0; right: 0; height: 32px; backdrop-filter: blur(50px); -webkit-backdrop-filter: blur(50px);"> + <div style="display: grid; grid-template-columns: max-content 1fr max-content; height: 100%;" class="container"> + <div id="ui-back-button" onclick="history.back();" style="display: flex; align-items: center; justify-content: center; text-align: center;"> + <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>Search results for "<?= strip_tags($_GET["q"]) ?>"</b></div> + </div> + </div> <script src="/assets/js/common.js"></script> <div class="container"> <br> - <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Search results for "<?= strip_tags($_GET["q"]) ?>"</h2> <?php global $albums; global $songs; diff --git a/app/ui/settings.php b/app/ui/settings.php index e6dc9ba..34cd095 100644 --- a/app/ui/settings.php +++ b/app/ui/settings.php @@ -11,7 +11,7 @@ <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>settings</title> + <title>Settings</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/dark.css" rel="stylesheet"> <link href="/assets/styles.css" rel="stylesheet"> @@ -25,12 +25,12 @@ <script src="/assets/js/common.js"></script> <div class="container"> <br> - <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Settings</h2> + <h2 class="desktop-title" style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Settings</h2> <div style="margin-left: 10px;"> <div class="form-check form-switch"> <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</label> - <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 high 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. 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> <script> if (localStorage.getItem("data-saving") === "true") document.getElementById("data-saving").checked = true; @@ -40,6 +40,35 @@ } </script> + <div class="form-check form-switch" style="margin-top: 10px;"> + <input onchange="saveN();" class="form-check-input" type="checkbox" role="switch" id="normalize"> + <label class="form-check-label" for="normalize">Normalize loudness</label> + <div class="text-muted small">Normalizing adjusts the volume each song is played at to be the same level for every song. This will avoid you having to change your device's volume between each track, and should typically not be turned off. Powered by ReplayGain.</div> + </div> + <script> + if (localStorage.getItem("normalize") === "true") document.getElementById("normalize").checked = true; + function saveN() { + localStorage.setItem("normalize", document.getElementById("normalize").checked ? "true" : "false"); + window.parent.location.reload(); + } + </script> + + <div class="form-check form-switch" id="stella" style="display: none;margin-top: 10px;"> + <input onchange="saveST();" class="form-check-input" type="checkbox" role="switch" id="enable-stella"> + <label class="form-check-label" for="enable-stella">Mist Stella</label> + <div class="text-muted small">Enjoy your music is a unique way thanks to the Mist Stella spatial audio technology. Stella makes your music feel like it's coming from all around you, giving you a concert-like experience. Note that Stella uses slightly more bandwidth than lossless streaming.</div> + </div> + <script> + if (localStorage.getItem("show-stella-settings") === "true") document.getElementById("stella").style.display = ""; + + if (localStorage.getItem("enable-stella") === "true") document.getElementById("enable-stella").checked = true; + function saveST() { + localStorage.setItem("enable-stella", document.getElementById("enable-stella").checked ? "true" : "false"); + localStorage.setItem("show-stella-settings", "true"); + window.parent.location.reload(); + } + </script> + <?php if (str_contains($_SERVER['HTTP_USER_AGENT'], "MistNative/")): ?> <div class="form-check form-switch" style="margin-top: 10px;"> <input onchange="saveDN();" class="form-check-input" type="checkbox" role="switch" id="desktop-notification"> @@ -82,8 +111,21 @@ <?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") ?>) · © <?= 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("/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) { + #copyright-separator-mobile { + display: none; + } + } + + @media (max-width: 767px) { + #copyright-separator-desktop { + display: none; + } + } + </style> <?php endif; ?> </div> </body> diff --git a/app/ui/update.php b/app/ui/update.php index 1117e95..b50dc21 100644 --- a/app/ui/update.php +++ b/app/ui/update.php @@ -25,31 +25,7 @@ <script src="/assets/js/common.js"></script> <div style="padding: 1rem;"> <div style="text-align: center;"> - <h2 style="margin-top: 30px;">What's new in Mist?</h2> - - <div style="text-align: left; margin-top: 50px;"> - <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> - <img src="/assets/icons/notes-security.svg" style="width: 48px;" class="icon" alt=""> - <div> - <div><b>Enhanced security</b></div> - <div>Listening to your favorite songs shouldn't come at the expense of your security and your privacy. Mist now includes protections against the most common forms of attacks.</div> - </div> - </div> - <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> - <img src="/assets/icons/notes-connect.svg" style="width: 48px;" class="icon" alt=""> - <div> - <div><b>Mist is now on Discord</b></div> - <div>If you are using the desktop app, Mist can now use Discord Rich Presence to show the music you are playing to all of your friends. You will also need the Discord desktop app.</div> - </div> - </div> - <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 20px; margin-bottom: 20px;"> - <img src="/assets/icons/notes-android.svg" style="width: 48px;" class="icon" alt=""> - <div> - <div><b>Native Android application</b></div> - <div>You can now quit using the Progressive Web App, and instead use the official Mist Android app. Lower battery and resource usage, along with better OS integrations.</div> - </div> - </div> - </div> + <?php $releaseNotes = true; require_once "../notes/update-1.0.0.php" ?> <a style="margin-top: 50px; margin-bottom: 30px; display: block;" class="btn btn-primary" onclick="localStorage.setItem('lastUpdate', '<?= trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")) ?>|<?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>'); window.parent._modal.hide();">Continue</a> </div> diff --git a/app/ui/welcome-dp.php b/app/ui/welcome-dp.php new file mode 100644 index 0000000..3986ca3 --- /dev/null +++ b/app/ui/welcome-dp.php @@ -0,0 +1,59 @@ +<?php header("X-Frame-Options: SAMEORIGIN"); require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; ?> +<!doctype html> +<html lang="en"> +<head> + <script> + if (typeof window.parent.parent.openModal === "undefined") { + location.href = "/app/"; + } + </script> + <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>welcome-dp</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> + <link href="/assets/dark.css" rel="stylesheet"> + <link href="/assets/styles.css" rel="stylesheet"> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> + <script src="/assets/localforage.min.js"></script> + <script src="/assets/fuse.min.js"></script> + <script src="/assets/js/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform" style="background-color: transparent !important;"> + <script src="/assets/js/common.js"></script> + <div style="padding: 1rem;"> + <div> + <div class="alert alert-warning"> + <p><b>This is a Developer Preview of Mist meant to be used only for development and experimental purposes.</b></p> + <p>If you want to use Mist as your primary streaming platform, you need to register and wait for the stable release instead. Equestria.dev makes no guarantee whatsoever that user data on Developer Preview will remain and reserves the right to revoke access at any time.</p> + <p>Thank you for making third-party Mist applications and helping develop the ecosystem. If you need assistance in building your application or hosting it, please <a href="mailto:raindrops@equestria.dev" target="_blank">contact us</a>.</p> + <div>While building your application, please keep the following rules in mind:</div> + <ul> + <li>Sharing content from Mist publicly is illegal.</li> + <li>Applications must not use Mist in an abusive way.</li> + <li>Applications must not give guest access to Mist.</li> + <li>Applications must credit Mist.</li> + <li>Applications must be free to use.</li> + <li>Do not alter sound quality without notice.</li> + <li>Mist Stella implementations should get credit.</li> + </ul> + <div>Thanks. (version <?= explode("|", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")))[0] ?>, build <?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>)</div> + </div> + <div class="btn btn-primary" onclick="window.parent._modal.hide();">Close</div> + <hr> + <div class="small text-muted">"Mist", "Mist Stella", the Mist logo and the Mist Stella logo are trademarks of Equestria.dev Developers. Mist and Mist Stella are © <?= date('Y') ?> Equestria.dev developers, released under the MIT license.</div> + </div> + </div> + + <script> + window.sizeInterval = setInterval(() => { + if (document.body.clientHeight > 0) { + clearInterval(sizeInterval); + window.parent.document.getElementById("modal-frame").style.height = document.body.clientHeight + "px"; + } + }); + </script> +</body> +</html>
\ No newline at end of file |