summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/download.php74
-rw-r--r--app/explore.php23
-rw-r--r--app/index.php46
-rw-r--r--app/info.php186
-rw-r--r--app/listing.php24
-rw-r--r--app/lyrics.php8
-rw-r--r--app/modal.php48
-rw-r--r--app/navigation.php3
-rw-r--r--app/player-mobile.php2
-rw-r--r--app/queue.php78
-rw-r--r--app/search.php91
-rw-r--r--app/settings.php2
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>