diff options
author | RaindropsSys <raindrops@equestria.dev> | 2023-10-26 16:39:03 +0200 |
---|---|---|
committer | RaindropsSys <raindrops@equestria.dev> | 2023-10-26 16:39:03 +0200 |
commit | 9f9d66afebc59c6c265c4424f7b8fb36d8876541 (patch) | |
tree | 8af8fc2fb1634a2f53df35544081a48956e6efad /app | |
parent | ae187b6d75c8079da0be1dc288613bad8466fe61 (diff) | |
download | mist-9f9d66afebc59c6c265c4424f7b8fb36d8876541.tar.gz mist-9f9d66afebc59c6c265c4424f7b8fb36d8876541.tar.bz2 mist-9f9d66afebc59c6c265c4424f7b8fb36d8876541.zip |
Updated 34 files and added 146 files (automated)
Diffstat (limited to 'app')
-rw-r--r-- | app/download.php | 74 | ||||
-rw-r--r-- | app/explore.php | 23 | ||||
-rw-r--r-- | app/index.php | 46 | ||||
-rw-r--r-- | app/info.php | 186 | ||||
-rw-r--r-- | app/listing.php | 24 | ||||
-rw-r--r-- | app/lyrics.php | 8 | ||||
-rw-r--r-- | app/modal.php | 48 | ||||
-rw-r--r-- | app/navigation.php | 3 | ||||
-rw-r--r-- | app/player-mobile.php | 2 | ||||
-rw-r--r-- | app/queue.php | 78 | ||||
-rw-r--r-- | app/search.php | 91 | ||||
-rw-r--r-- | app/settings.php | 2 |
12 files changed, 542 insertions, 43 deletions
diff --git a/app/download.php b/app/download.php new file mode 100644 index 0000000..dd1321c --- /dev/null +++ b/app/download.php @@ -0,0 +1,74 @@ +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $songs; + +if (!isset($_GET["i"]) || !isset($songs[$_GET["i"]])) { + die(); +} + +$song = $songs[$_GET["i"]]; +$fileName = str_replace(["/", "<", ">", ":", "\"", "\\", "|", "?", "*"], "-", $song["artist"] . " - " . $song["title"]); + +function getSize($bytes) { + if ($bytes < 1024) { + return $bytes; + } + + if ($bytes < 1024**2) { + return round($bytes / 1024, 1) . " KB"; + } + + if ($bytes < 1024**3) { + return round($bytes / 1024**2, 1) . " MB"; + } + + return round($bytes / 1024**3, 1) . " GB"; +} + +?> +<!doctype html> +<html lang="en"> +<head> + <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>download</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/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform" style="background-color: transparent !important;"> + <script> + if (navigator.userAgent.includes("MistNative/darwin") || navigator.userAgent.includes("MistNative/win32")) { + document.getElementById("native-css").disabled = false; + document.body.classList.remove("crossplatform"); + } + </script> + <div style="padding: 1rem;"> + <p> + <?= $song["artist"] ?> — <?= $song["title"] ?><br> + <?= $song["album"] ?> + </p> + + <p>Select the version of the song you would like to download:</p> + <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> + <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> + + <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 diff --git a/app/explore.php b/app/explore.php index a51731b..4887022 100644 --- a/app/explore.php +++ b/app/explore.php @@ -1,4 +1,4 @@ -<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; ?> +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $songs; global $albums; ?> <!doctype html> <html lang="en"> <head> @@ -25,7 +25,26 @@ </script> <div class="container"> <br> - <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Explore<input placeholder="Filter" id="filter" class="form-control" style="width: 256px; float: right;" onchange="updateFilter();" onkeyup="updateFilter();"></h2> + <h2 style="margin-top: 10px; margin-bottom: 20px; margin-left: 10px;">Explore</h2> + + <form action="search.php"> + <div style="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> + </div> + </form> + + <hr style="margin-left: 10px;"> + + <?php if (count($songs) === 0 && count($albums) === 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;">Upload music to this server</h4> + <p style="max-width: 300px; margin-left: auto; margin-right: auto;">Add millions of songs and let your users play them whenever they want.</p> + </div> + </div> + <?php endif; ?> <div id="album-grid" style="display: grid; grid-template-columns: repeat(5, 1fr);"> <?php global $albums; diff --git a/app/index.php b/app/index.php index e6d93ff..0f22e2f 100644 --- a/app/index.php +++ b/app/index.php @@ -23,12 +23,12 @@ <meta name="apple-mobile-web-app-status-bar-style" content="white-translucent" media="(prefers-color-scheme: light)"> <meta name="description" content="Mist Audio Player"> </head> -<body> +<body <?php if (!str_contains($_SERVER['HTTP_USER_AGENT'], "MistNative/")): ?>class="web"<?php endif; ?>> <script> if (location.hash.trim() === "") location.hash = "#/albums"; if (window.MistNative) { - MistNative.version("<?= explode("|", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")))[0] ?>", "<?= trim(file_get_contents("/opt/spotify/build.txt")) ?>"); + MistNative.version("<?= explode("|", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")))[0] ?>", "<?= trim(file_exists("/opt/spotify/build.txt") ? file_get_contents("/opt/spotify/build.txt") : "trunk") ?>"); MistNative.userInfo(`<?= str_replace("`", "\\`", json_encode($_PROFILE)) ?>`); } </script> @@ -182,8 +182,8 @@ } if (playlist[currentPlaylistPosition + 1]) { - playSong(playlist[currentPlaylistPosition + 1], "keep"); currentPlaylistPosition++; + playSong(playlist[currentPlaylistPosition], "keep", false); } else { stop(); } @@ -307,18 +307,18 @@ 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).toFixed(1) + " kHz</span>"; + 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 = "inline"; + 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 = "inline"; + 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).toFixed(1) + " kHz</span>"; + 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>"; } } @@ -347,30 +347,34 @@ } } - window.redownloadFavorite = async () => { + window.redownloadFavorites = async () => { document.getElementById("loading-text").innerText = "Downloading favorites..."; - window.favorites = await (await fetch("/api/getFavorites.php")).json(); + window.favorites = await (await fetch("/api/getFavorites.php?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); } window.redownloadLibrary = async () => { document.getElementById("loading-text").innerText = "Downloading library..."; - window.favorites = await (await fetch("/api/getLibrary.php")).json(); + window.library = await (await fetch("/api/getLibrary.php?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); } (async () => { document.getElementById("loading-text").innerText = "Downloading list of songs..."; - window.songs = await (await fetch("/assets/content/songs.json")).json(); + window.songs = await (await fetch("/assets/content/songs.json?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); document.getElementById("loading-text").innerText = "Downloading list of albums..."; - window.albums = await (await fetch("/assets/content/albums.json")).json(); + window.albums = await (await fetch("/assets/content/albums.json?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); document.getElementById("loading-text").innerText = "Downloading favorites..."; - window.favorites = await (await fetch("/api/getFavorites.php")).json(); + window.favorites = await (await fetch("/api/getFavorites.php?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); + + document.getElementById("loading-text").innerText = "Downloading library..."; + window.library = await (await fetch("/api/getLibrary.php?_=" + [...crypto.getRandomValues(new Uint8Array(40))].map(m=>('0'+m.toString(16)).slice(-2)).join(''))).json(); document.getElementById("loading-text").innerText = "Saving database..."; await localforage.setItem("albums", window.albums); await localforage.setItem("songs", window.songs); await localforage.setItem("favorites", window.favorites); + await localforage.setItem("library", window.library); document.getElementById("loading-text").innerText = "Done loading."; document.getElementById("loading").style.display = "none"; @@ -455,7 +459,7 @@ window.currentPlaylistID = null; - window.playSong = async (id, playlistID) => { + window.playSong = async (id, playlistID, updatePosition) => { playerDocument.getElementById("player-audio").pause(); playerDocument.getElementById("player-audio").currentTime = 0; @@ -471,6 +475,8 @@ } else if (playlistID !== "keep") { window.playlist = [id]; window.currentPlaylistPosition = 0; + } else if (typeof updatePosition !== "boolean" || updatePosition) { + window.currentPlaylistPosition = window.playlist.indexOf(id) ?? 0; } } else { window.currentPlaylistID = null; @@ -481,6 +487,7 @@ window.currentSong = songs[id]; window.currentSongID = id; updateDisplay(); + if (document.getElementById("ui").contentWindow.refreshQueue) document.getElementById("ui").contentWindow.refreshQueue(); if (!window.preloaded[id]) { if (localStorage.getItem("data-saving") === "true") { @@ -547,5 +554,16 @@ } } </script> + + <iframe id="modal" src="modal.php" style="width: 100vw; height: 100vh; border: none; inset: 0; position: fixed; z-index: 99999; display: none;"></iframe> + <script> + function openModal(title, url) { + document.getElementById("modal").style.display = ""; + document.getElementById("modal").contentWindow.document.getElementById("modal-title").innerText = title; + document.getElementById("modal").contentWindow.document.getElementById("modal-frame").src = url; + document.getElementById("modal").contentWindow.document.getElementById("modal-frame").style.height = "calc(100vh - 130px)"; + document.getElementById("modal").contentWindow._modal.show(); + } + </script> </body> </html>
\ No newline at end of file diff --git a/app/info.php b/app/info.php new file mode 100644 index 0000000..cfc7bc2 --- /dev/null +++ b/app/info.php @@ -0,0 +1,186 @@ +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $songs; + +if (!isset($_GET["i"]) || !isset($songs[$_GET["i"]])) { + die(); +} + +$song = $songs[$_GET["i"]]; +$fileName = str_replace(["/", "<", ">", ":", "\"", "\\", "|", "?", "*"], "-", $song["artist"] . " - " . $song["title"]); + +function getSize($bytes) { + if ($bytes < 1024) { + return $bytes; + } + + if ($bytes < 1024**2) { + return round($bytes / 1024, 1) . " KB"; + } + + if ($bytes < 1024**3) { + return round($bytes / 1024**2, 1) . " MB"; + } + + return round($bytes / 1024**3, 1) . " GB"; +} + +function timeToDuration($seconds) { + $hours = floor($seconds / 3600); + $minutes = floor($seconds / 60) - ($hours * 60); + $seconds = floor($seconds) - ($hours * 3600) - ($minutes * 60); + $parts = []; + + if ($hours > 0) $parts[] = $hours . " hour" . ($hours > 1 ? "s" : ""); + if ($minutes > 0) $parts[] = $minutes . " minute" . ($minutes > 1 ? "s" : ""); + if ($seconds > 0) $parts[] = $seconds . " second" . ($seconds > 1 ? "s" : ""); + + return implode(", ", $parts); +} + +function getBitRate($bits) { + $bitsValue = $bits . " bps"; + + if ($bits > 1000) { + if ($bits > 1000000) { + if ($bits > 1000000000) { + $bitsValue = round($bits / 1000000000, 2) . " Gbps"; + } else { + $bitsValue = round($bits / 1000000, 2) . " Mbps"; + } + } else { + $bitsValue = round($bits / 1000, 2) . " kbps"; + } + } + + $bytesValue = ($bits / 8) . " B/s"; + + if (($bits / 8) > 1000) { + if (($bits / 8) > 1000000) { + if (($bits / 8) > 1000000000) { + $bytesValue = round(($bits / 8) / 1000000000, 2) . " GB/s"; + } else { + $bytesValue = round(($bits / 8) / 1000000, 2) . " MB/s"; + } + } else { + $bytesValue = round(($bits / 8) / 1000, 2) . " kB/s"; + } + } + + return $bitsValue . " (" . $bytesValue . ")"; +} + +function getChannelConfiguration($c) { + if ($c === 1) return " (Mono)"; + if ($c === 2) return " (Stereo)"; + if ($c === 3) return " (Stereo+1)"; + if ($c === 4) return " (3.1)"; + if ($c === 5) return " (4.1 or 5.0 Surround)"; + if ($c === 6) return " (5.1 Surround)"; + if ($c === 7) return " (6.1 or 7.0 Surround)"; + if ($c === 8) return " (7.1 Surround)"; + if ($c === 9) return " (9.0 or 7.2 Spatial Audio)"; + if ($c === 10) return " (9.1 Spatial Audio)"; + if ($c >= 11) return " (Dolby Atmos)"; + return ""; +} + +?> +<!doctype html> +<html lang="en"> +<head> + <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>info</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/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform" style="background-color: transparent !important;"> + <script> + if (navigator.userAgent.includes("MistNative/darwin") || navigator.userAgent.includes("MistNative/win32")) { + document.getElementById("native-css").disabled = false; + document.body.classList.remove("crossplatform"); + } + </script> + <div style="padding: 1rem;"> + <div style="display: grid; grid-template-columns: 96px 1fr; grid-gap: 15px;"> + <img alt="" src="/assets/content/<?= $_GET["i"] ?>.jpg" style="aspect-ratio: 1; width: 96px;"> + <div style="display: flex; align-items: center; width: 100%;"> + <div> + <div style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; max-width: 100%;"><b><?= $song["title"] ?></b></div> + <div style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; max-width: 100%;"><?= $song["artist"] ?></div> + <div style="white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; max-width: 100%;"><?= $song["album"] ?></div> + </div> + </div> + </div> + + <hr> + + <table style="width: 100%;"> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Track</td> + <td><?php if (isset($song["disc"])): ?>Disc <?= $song["disc"] ?>, track <?php endif; ?><?= $song["track"] > 0 ? $song["track"] : "-" ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Duration</td> + <td><?= timeToDuration($song["length"]) ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Bit rate</td> + <td><?= getBitRate($song["bitRate"]) ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Sample rate</td> + <td><?= $song["sampleRate"] ?> Hz</td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Bits per sample</td> + <td><?= $song["bitDepth"] ?> bits</td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Channels</td> + <td><?= $song["channels"] ?><?= getChannelConfiguration($song["channels"]) ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Year</td> + <td><?= $song["date"] ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">High-resolution</td> + <td><?= $song["hiRes"] ? "Yes" : "No" ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Mist Stella</td> + <td>No</td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">Copyright</td> + <td><?= trim($song["copyright"] ?? "") !== "" ? $song["copyright"] : "-" ?></td> + </tr> + <tr> + <td style="width: calc(100% / 3); text-align: right; padding-right: 10px; opacity: .5;">File size (lossless)</td> + <td><?= getSize(filesize($_SERVER['DOCUMENT_ROOT'] . "/assets/content/" . $_GET["i"] . ".flac")) ?></td> + </tr> + <tr> + <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> + </table> + </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 diff --git a/app/listing.php b/app/listing.php index 8ea25d4..9d50178 100644 --- a/app/listing.php +++ b/app/listing.php @@ -148,30 +148,6 @@ if (!$presetList) { } <?php endif; ?> - async function favoriteSong(id) { - document.getElementById("btn-favorite-" + id + "-icon").src = "/assets/icons/favorite-on.svg"; - 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).onclick = () => { - favoriteSong(id); - } - await fetch("/api/removeFavorite.php?i=" + id); - - <?php if (isset($favoritesList)): ?> - location.reload(); - <?php endif; ?> - - window.parent.redownloadFavorites(); - } - 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, { diff --git a/app/lyrics.php b/app/lyrics.php index fbdca1d..4a7537d 100644 --- a/app/lyrics.php +++ b/app/lyrics.php @@ -59,7 +59,7 @@ </div> <div id="lyrics-unsynced" style="display: none; position: fixed; inset: 16px; overflow: auto;"></div> - <div id="lyrics-synced" style="display: none; position: fixed; left: 16px; right: 16px; top: 0; bottom: 0; z-index: 5;"></div> + <div id="lyrics-synced" style="text-align: center; display: none; position: fixed; left: 16px; right: 16px; top: 0; bottom: 0; z-index: 5;"></div> <div id="lyrics-synced-fade" style="display: none; position: fixed; inset: 0; z-index: 10; background-image: linear-gradient(180deg, rgba(255,0,0,0) 25%, rgba(255,255,255,1) 100%);"></div> </div> @@ -88,6 +88,10 @@ document.getElementById("not-playing").style.display = "none"; if (!window.lyrics[lastID]) { + window.lyricsLoadTimeout = setTimeout(() => { + location.reload(); + }, 10000); + try { window.lyrics[lastID] = await (await fetch("/api/lyrics.php?id=" + lastID)).json() } catch (e) { @@ -97,6 +101,8 @@ } } + clearTimeout(window.lyricsLoadTimeout); + if (window.lyrics[lastID] && window.lyrics[lastID].payload) { if (window.lyrics[lastID].synced) { document.getElementById("lyrics-synced").style.display = ""; diff --git a/app/modal.php b/app/modal.php new file mode 100644 index 0000000..b939996 --- /dev/null +++ b/app/modal.php @@ -0,0 +1,48 @@ +<!doctype html> +<html lang="en"> +<head> + <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>modal</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/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform" style="background-color: transparent !important;"> + <script> + if (navigator.userAgent.includes("MistNative/darwin") || navigator.userAgent.includes("MistNative/win32")) { + document.getElementById("native-css").disabled = false; + document.body.classList.remove("crossplatform"); + } + </script> + <div class="modal fade" id="modal"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title" id="modal-title">Title</h4> + <button type="button" class="btn-close" data-bs-dismiss="modal"></button> + </div> + + <div class="modal-body" style="padding: 0;"> + <iframe id="modal-frame" style="width: 100%; border-bottom-right-radius: 0.5rem; margin-bottom: -6px; border-bottom-left-radius: 0.5rem; height: calc(100vh - 130px);"></iframe> + </div> + </div> + </div> + </div> + + <script> + window._modal = new bootstrap.Modal(document.getElementById("modal")); + + document.getElementById("modal").addEventListener("hidden.bs.modal", () => { + window.parent.document.getElementById("modal").style.display = "none"; + }); + </script> +</body> +</html>
\ No newline at end of file diff --git a/app/navigation.php b/app/navigation.php index f07c38f..00d1457 100644 --- a/app/navigation.php +++ b/app/navigation.php @@ -51,6 +51,9 @@ <div id="lyrics" class="navigation-item" onclick="window.parent.showLyrics();"> <img class="icon" alt="" src="/assets/icons/lyrics.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Lyrics</span> </div> + <div id="queue" class="navigation-item" onclick="window.parent.openUI('queue');"> + <img class="icon" alt="" src="/assets/icons/playlist.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Queue</span> + </div> <div id="settings" class="navigation-item" onclick="window.parent.openUI('settings');"> <img class="icon" alt="" src="/assets/icons/settings.svg" style="vertical-align: middle; width: 32px;"><span style="vertical-align: middle; margin-left: 5px;" class="navigation-desktop">Settings</span> </div> diff --git a/app/player-mobile.php b/app/player-mobile.php index cab8ada..8308eda 100644 --- a/app/player-mobile.php +++ b/app/player-mobile.php @@ -26,7 +26,7 @@ <div id="act-2" onclick="window.parent.hideMobilePlayer();" style="position: fixed; z-index: 9999999; bottom: 0; right: 0; top: 0; width: 20px;"></div> <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="z-index: 10; position: fixed;inset: 0; backdrop-filter: blur(100px);"></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: 0; left: 0; right: 0; height: 64px; z-index: 9999;"> <div class="container" style="display: grid; grid-template-columns: 1fr 1.5fr 1fr;"> <div id="buttons" style="height: 48px;position: fixed;width: max-content;left: 0;right: 0;margin: 8px auto;bottom: 30px;"> diff --git a/app/queue.php b/app/queue.php new file mode 100644 index 0000000..86fda0e --- /dev/null +++ b/app/queue.php @@ -0,0 +1,78 @@ +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; ?> +<!doctype html> +<html lang="en"> +<head> + <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>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"> + <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/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform"> + <script> + if (navigator.userAgent.includes("MistNative/darwin") || navigator.userAgent.includes("MistNative/win32")) { + document.getElementById("native-css").disabled = false; + document.body.classList.remove("crossplatform"); + } + </script> + <div class="container"> + <br> + <h2 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. + </div> + </div> + + <script> + function refreshQueue() { + let list = window.parent.playlist.slice(window.parent.currentPlaylistPosition + 1); + + if (list.length === 0) { + document.getElementById('main-list').style.display = "none"; + document.getElementById('empty').style.display = ""; + } else { + document.getElementById('main-list').style.display = ""; + document.getElementById('empty').style.display = "none"; + + document.getElementById('main-list').innerHTML = list.map(i => [window.parent.songs[i], i]).map((i, j) => ` + <div data-item-track="${i[0].title}" data-item-artist="${i[0].artist}" id="item-${i[1]}" class="list-group-item track" style="display: grid; grid-template-columns: 32px 1fr max-content;"> + <div style="opacity: .5; display: flex; align-items: center; justify-content: left;"></div> + <div style="display: grid; grid-template-columns: 48px 1fr; grid-gap: 10px;"> + <img src="/assets/content/${i[1]}.jpg" style="width: 48px; height: 48px;"> + <div style="width: 50vw; height: 3em; display: flex; align-items: center; justify-content: left;"> + <div style="max-width: 100%;"><div style="max-width: 100%; white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis;">${i[0].title}</div><div style="max-width: 100%; opacity: .5; white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis;">${i[0].artist}</div></div> + </div> + </div> + <div class="list-actions"> + <span onclick="removeSong(${j + window.parent.currentPlaylistPosition + 1});" class="player-btn" style="border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; height: 48px; width: 48px;" id="btn-remove-${i[1]}"> + <img class="icon" alt="" src="/assets/icons/remove.svg" style="pointer-events: none; width: 32px; height: 32px;" id="btn-remove-${i[1]}-icon"> + </span> + <span onclick="window.parent.playSong('${i[1]}', 'keep');" class="player-btn" style="border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; height: 48px; width: 48px;" id="btn-play-${i[1]}"> + <img class="icon" alt="" src="/assets/icons/play.svg" style="pointer-events: none; width: 32px; height: 32px;" id="btn-play-${i[1]}-icon"> + </span> + </div> + </div> + `).join(""); + } + } + + function removeSong(index) { + window.parent.playlist.splice(index, 1); + refreshQueue(); + } + + refreshQueue(); + </script> + + <br><br> +</body> +</html>
\ No newline at end of file diff --git a/app/search.php b/app/search.php new file mode 100644 index 0000000..2f5f164 --- /dev/null +++ b/app/search.php @@ -0,0 +1,91 @@ +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; + +if (!isset($_GET["q"]) || trim($_GET["q"]) === "" || trim(preg_replace("/ +/m", " ", preg_replace("/[^a-z\d ]/m", " ", strtolower($_GET["q"])))) === "") { + header("Location: explore.php"); + die(); +} + +?> +<!doctype html> +<html lang="en"> +<head> + <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>search</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/shortcuts.js"></script> + <link id="native-css" href="/assets/native.css" rel="stylesheet" disabled> +</head> +<body class="crossplatform"> + <script> + if (navigator.userAgent.includes("MistNative/darwin") || navigator.userAgent.includes("MistNative/win32")) { + document.getElementById("native-css").disabled = false; + document.body.classList.remove("crossplatform"); + } + </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; + + function getMatches($item) { + $n = 0; + $query = preg_replace("/ +/m", " ", preg_replace("/[^a-z\d ]/m", " ", strtolower($_GET["q"]))); + + if (isset($item["title"]) && str_contains(preg_replace("/ +/m", " ", preg_replace("/[^a-z\d ]/m", " ", strtolower($item["title"]))), $query)) $n++; + if (isset($item["artist"]) && str_contains(preg_replace("/ +/m", " ", preg_replace("/[^a-z\d ]/m", " ", strtolower($item["artist"]))), $query)) $n++; + if (isset($item["album"]) && str_contains(preg_replace("/ +/m", " ", preg_replace("/[^a-z\d ]/m", " ", strtolower($item["album"]))), $query)) $n++; + + return $n; + } + + $albums = array_filter($albums, function ($i) { + return getMatches($i) > 0; + }); + + uasort($albums, function ($a, $b) { + return strcmp($a["title"], $b["title"]); + }); + + uasort($albums, function ($a, $b) { + return strcmp($a["artist"], $b["artist"]); + }); + + uasort($albums, function ($a, $b) { + return getMatches($b) - getMatches($a); + }); + + $songs = array_filter($songs, function ($i) { + return getMatches($i) > 0; + }); + + uasort($songs, function ($a, $b) { + return getMatches($b) - getMatches($a); + }); + + ?> + + <div id="album-grid" style="display: grid; grid-template-columns: repeat(5, 1fr);"> + <?php foreach ($albums as $id => $album): ?> + <a id="album-<?= $id ?>" data-item-track="<?= $album["title"] ?>" data-item-artist="<?= $album["artist"] ?>" href="listing.php?a=<?= $id ?>" style="padding: 10px; color: inherit; text-decoration: inherit;" class="album"> + <img class="album-list-art" alt="" src="/assets/content/<?= $id ?>.jpg" style="width: 100%; aspect-ratio: 1; border-radius: 5px; margin-bottom: 5px;"> + <div class="album-list-item" style="max-width: calc(80vw / 5); white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis;"><?= $album["title"] ?></div> + <div class="album-list-item" style="max-width: calc(80vw / 5); opacity: .5; white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis;"><?= $album["artist"] ?></div> + </a> + <?php endforeach; ?> + </div> + <?php global $songs; displayList($songs); ?> + </div> + + <br><br> +</body> +</html>
\ No newline at end of file diff --git a/app/settings.php b/app/settings.php index 4c2a287..a80fad1 100644 --- a/app/settings.php +++ b/app/settings.php @@ -60,7 +60,7 @@ <?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 <?= file_get_contents("/opt/spotify/build.txt") ?>) · © <?= 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") ?>) · © <?= date('Y') ?> Equestria.dev</span> </div> <?php endif; ?> </div> |