summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--includes/check.js117
-rw-r--r--includes/process.js21
-rw-r--r--includes/session.php16
-rw-r--r--includes/stella.js97
-rw-r--r--includes/watcher.js2
6 files changed, 241 insertions, 12 deletions
diff --git a/includes/.DS_Store b/includes/.DS_Store
index ab60279..fce60f4 100644
--- a/includes/.DS_Store
+++ b/includes/.DS_Store
Binary files differ
diff --git a/includes/check.js b/includes/check.js
new file mode 100644
index 0000000..d5ff55f
--- /dev/null
+++ b/includes/check.js
@@ -0,0 +1,117 @@
+const fs = require('fs');
+const cp = require('child_process');
+const path = require('path');
+const crypto = require('crypto');
+
+const songs = require('../assets/content/songs.json');
+const albums = require('../assets/content/albums.json');
+
+function scandir(dir) {
+ let count = 0;
+
+ function updateScandirDisplay() {
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ process.stdout.write("Scanning... " + count);
+ }
+
+ return new Promise((res, rej) => {
+ const walk = (dir, done) => {
+ let results = [];
+ fs.readdir(dir, function(err, list) {
+ count++;
+ updateScandirDisplay();
+ if (err) return done(err);
+ let pending = list.length;
+
+ if (!pending) return done(null, results);
+ list.forEach(function(file) {
+ count++;
+ updateScandirDisplay();
+ file = path.resolve(dir, file);
+ fs.stat(file, function(err, stat) {
+ if (stat && stat.isDirectory()) {
+ walk(file, function(err, res) {
+ results = results.concat(res);
+ if (!--pending) done(null, results);
+ });
+ } else {
+ results.push(file);
+ if (!--pending) done(null, results);
+ }
+ });
+ });
+ });
+ }
+
+ walk(dir, (err, data) => {
+ if (err) {
+ rej(err);
+ } else {
+ res(data);
+ }
+ })
+ });
+}
+
+(async () => {
+ let list = (await scandir("../assets/content/_")).filter(i => i.endsWith(".flac"));
+ process.stdout.write(" files found\n");
+ let index = 0;
+ console.log("Checking for corrupted files...");
+
+ for (let file of list) {
+ process.stdout.cursorTo(0);
+ process.stdout.write(index + "/" + list.length);
+ let metadata = JSON.parse(cp.execFileSync("ffprobe", ["-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", file]).toString());
+ if (!metadata['format']['tags']) {
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log(file + ": is corrupted or has no metadata");
+ }
+ index++;
+ }
+
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log("Checking for missing album art...");
+
+ index = 0;
+ for (let id of Object.keys(songs)) {
+ process.stdout.cursorTo(0);
+ process.stdout.write(index + "/" + (Object.keys(songs).length + Object.keys(albums).length));
+ if (fs.existsSync("../assets/content/" + id + ".jpg")) {
+ if (crypto.createHash("md5").update(fs.readFileSync("../assets/content/" + id + ".jpg")).digest("hex") === "fd89a584ae426e72801f2a4c2653ae7a") {
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log(songs[id].title + " (" + id + "): using placeholder album art");
+ }
+ } else {
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log(songs[id].title + " (" + id + "): no album art file");
+ }
+ index++;
+ }
+
+ for (let id of Object.keys(albums)) {
+ process.stdout.cursorTo(0);
+ process.stdout.write(index + "/" + (Object.keys(songs).length + Object.keys(albums).length));
+ if (fs.existsSync("../assets/content/" + id + ".jpg")) {
+ if (crypto.createHash("md5").update(fs.readFileSync("../assets/content/" + id + ".jpg")).digest("hex") === "fd89a584ae426e72801f2a4c2653ae7a") {
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log(albums[id].title + " (" + id + "): using placeholder album art");
+ }
+ } else {
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log(albums[id].title + " (" + id + "): no album art file");
+ }
+ index++;
+ }
+
+ process.stdout.clearLine(null);
+ process.stdout.cursorTo(0);
+ console.log("Done.");
+})(); \ No newline at end of file
diff --git a/includes/process.js b/includes/process.js
index 0c34f94..14dc963 100644
--- a/includes/process.js
+++ b/includes/process.js
@@ -139,7 +139,7 @@ function timeToString(time) {
album: substitute(metadata['format']['tags']['ALBUM'] ?? metadata['format']['tags']['album'] ?? "Unknown album"),
artist: (substitute(metadata['format']['tags']['ARTIST'] ?? metadata['format']['tags']['artist'] ?? "Unknown artist")).replaceAll(";", ", ").replaceAll(" & ", ", ").replaceAll("&", ", ").replaceAll(", and ", ", "),
albumArtist: (substitute(metadata['format']['tags']['album_artist'] ?? metadata['format']['tags']['ARTIST'] ?? metadata['format']['tags']['artist'] ?? "Unknown artist")).replaceAll(";", ", ").replaceAll("&", ", ").replaceAll(", and ", ", "),
- date: parseInt(((metadata['format']['tags']['DATE'] ?? metadata['format']['tags']['date']).split("-")[0]).substring(0, 4)) ?? 0,
+ date: parseInt(((metadata['format']['tags']['DATE'] ?? metadata['format']['tags']['date'] ?? "0").split("-")[0]).substring(0, 4)) ?? 0,
track: parseInt(metadata['format']['tags']['track']) ?? 0,
disc: parseInt(metadata['format']['tags']['disc']) ?? 1,
copyright: metadata['format']['tags']['COPYRIGHT'] ?? metadata['format']['tags']['copyright'] ?? "",
@@ -217,16 +217,16 @@ function timeToString(time) {
console.log("Collecting albums...");
for (let song of Object.keys(songs)) {
- if (Object.values(albums).filter(i => i.title === songs[song].album).length > 0) {
+ if (Object.values(albums).filter(i => i.title === songs[song].album && i.artist === songs[song].albumArtist).length > 0) {
Object.values(albums).filter(i => i.title === songs[song].album)[0].tracks.push(song);
Object.values(albums).filter(i => i.title === songs[song].album)[0].hiRes = Object.values(albums).filter(i => i.title === songs[song].album)[0].hiRes || songs[song].hiRes;
} else {
let albumID = uuid();
if (fs.existsSync("/opt/mist")) {
- fs.copyFileSync("/opt/mist/jpeg/" + song + ".jpg", "/opt/mist/jpeg/" + albumID + ".jpg")
+ if (fs.existsSync("/opt/mist/jpeg/" + song + ".jpg")) fs.copyFileSync("/opt/mist/jpeg/" + song + ".jpg", "/opt/mist/jpeg/" + albumID + ".jpg");
} else {
- fs.copyFileSync("../assets/content/" + song + ".jpg", "../assets/content/" + albumID + ".jpg")
+ if (fs.existsSync("../assets/content/" + song + ".jpg")) fs.copyFileSync("../assets/content/" + song + ".jpg", "../assets/content/" + albumID + ".jpg");
}
albums[albumID] = {
@@ -254,6 +254,7 @@ function timeToString(time) {
}
}
+ console.log("Linking...");
let idList = [...Object.keys(songs), ...Object.keys(albums)];
if (fs.existsSync("/opt/mist")) {
@@ -314,6 +315,18 @@ function timeToString(time) {
album["tracks"] = [...new Set(album["tracks"])].filter(i => songs[i]).sort((a, b) => {
return songs[a]['track'] - songs[b]['track'];
});
+
+ if (fs.existsSync("/opt/mist")) {
+ if (!fs.existsSync("/opt/mist/jpeg/" + albumID + ".jpg")) fs.copyFileSync("../assets/default.jpg", "/opt/mist/jpeg/" + albumID + ".jpg");
+ } else {
+ if (!fs.existsSync("../assets/content/" + albumID + ".jpg")) fs.copyFileSync("../assets/default.jpg", "../assets/content/" + albumID + ".jpg");
+ }
+ }
+
+ for (let albumID of Object.keys(albums)) {
+ if (albums[albumID]["tracks"].length === 0) {
+ delete albums[albumID];
+ }
}
console.log("Writing metadata...");
diff --git a/includes/session.php b/includes/session.php
index 36c5216..e9f9f30 100644
--- a/includes/session.php
+++ b/includes/session.php
@@ -72,12 +72,14 @@ function displayList($list, $hasAlbum = false) { global $albums; global $favorit
<img class="icon" alt="" src="/assets/icons/menu.svg" style="pointer-events: none; width: 32px; height: 32px;" id="btn-menu-<?= $id ?>-icon">
</span>
<ul class="dropdown-menu">
- <li><a onclick="playNext('<?= $id ?>');" class="dropdown-item" href="#"><img alt="" src="/assets/icons/playnext.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Play next</a></li>
- <li><a onclick="enqueue('<?= $id ?>');" class="dropdown-item" href="#"><img alt="" src="/assets/icons/add.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Add to queue</a></li>
- <li><hr class="dropdown-divider"></hr></li>
- <li><a id="btn-favorite-<?= $id ?>" onclick="<?= in_array($id, $favorites) ? "un" : "" ?>favoriteSong('<?= $id ?>');" class="dropdown-item" href="#"><img id="btn-favorite-<?= $id ?>-icon" alt="" src="/assets/icons/favorite-<?= in_array($id, $favorites) ? "on" : "off" ?>.svg" style="pointer-events: none; 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></li>
- <li><a onclick="downloadSong('<?= $id ?>');" class="dropdown-item" href="#"><img alt="" src="/assets/icons/download.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Download</a></li>
- <li><a onclick="getInfo('<?= $id ?>');" class="dropdown-item" href="#"><img alt="" src="/assets/icons/info.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Get info</a></li>
+ <li><a onclick="playNext('<?= $id ?>');" class="dropdown-item" style="cursor: pointer;"><img alt="" src="/assets/icons/playnext.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Play next</a></li>
+ <li><a onclick="enqueue('<?= $id ?>');" class="dropdown-item" style="cursor: pointer;"><img alt="" src="/assets/icons/add.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Add to queue</a></li>
+ <li><hr class="dropdown-divider"></li>
+ <li><a id="btn-favorite-<?= $id ?>" onclick="<?= in_array($id, $favorites) ? "un" : "" ?>favoriteSong('<?= $id ?>');" class="dropdown-item" style="cursor: pointer;"><img id="btn-favorite-<?= $id ?>-icon" alt="" src="/assets/icons/favorite-<?= in_array($id, $favorites) ? "on" : "off" ?>.svg" style="pointer-events: none; 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></li>
+ <li><a onclick="downloadSong('<?= $id ?>');" class="dropdown-item" style="cursor: pointer;"><img alt="" src="/assets/icons/download.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Download</a></li>
+ <li><a onclick="getInfo('<?= $id ?>');" class="dropdown-item" style="cursor: pointer;"><img alt="" src="/assets/icons/info.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Get info</a></li>
+ <li><hr class="dropdown-divider"></li>
+ <li><a onclick="navigator.clipboard.writeText('<?= $id ?>');" class="dropdown-item" style="cursor: pointer;"><img alt="" src="/assets/icons/copy.svg" style="pointer-events: none; width: 24px; height: 24px; margin-right: 5px;">Copy ID</a></li>
</ul>
</span>
</div>
@@ -100,7 +102,7 @@ function displayList($list, $hasAlbum = false) { global $albums; global $favorit
if (window.parent.playlist.length === 0) {
window.parent.playSong(id);
} else {
- window.parent.playlist.splice(window.parent.currentPlaylistPosition, 0, id);
+ window.parent.playlist.splice(window.parent.currentPlaylistPosition + 1, 0, id);
}
}
diff --git a/includes/stella.js b/includes/stella.js
new file mode 100644
index 0000000..e38afc5
--- /dev/null
+++ b/includes/stella.js
@@ -0,0 +1,97 @@
+const songs = require('../assets/content/songs.json');
+const cp = require('child_process');
+const zlib = require('zlib');
+const fs = require("fs");
+const crypto = require("crypto");
+
+if (!process.argv[2]) {
+ console.log("Error: Please pass the ID of a song to encode in Stella as a parameter.");
+ return;
+}
+
+if (!songs[process.argv[2]]) {
+ console.log("Error: No song with ID " + process.argv[2] + " could be found.");
+ return;
+}
+
+let song = songs[process.argv[2]];
+console.log("Song: " + song['artist'] + " - " + song['title']);
+
+console.log("Preparing to split stems...");
+cp.execSync("ssh zephyrheights rm -rf /root/StellaTemp", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights mkdir -p /root/StellaTemp", { stdio: "inherit" });
+cp.execSync("scp /opt/mist/flac/" + process.argv[2] + ".flac zephyrheights:/root/StellaTemp/source.flac", { stdio: "inherit" });
+
+console.log("Downsampling to 16bit 44.1kHz...");
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/source.flac -ar 44100 -c:a pcm_s16le /root/StellaTemp/source.wav", { stdio: "inherit" });
+
+console.log("Splitting into 5 stems...");
+cp.execSync("ssh zephyrheights spleeter separate -p spleeter:5stems -b 512k -o /root/StellaTemp/stems -f {instrument}.{codec} /root/StellaTemp/source.flac");
+cp.execSync("ssh zephyrheights mv /root/StellaTemp/stems/* /root/StellaTemp/");
+cp.execSync("ssh zephyrheights rmdir /root/StellaTemp/stems");
+
+console.log("Applying spatial effects to stems...");
+cp.execSync("ssh zephyrheights sox -S /root/StellaTemp/source.wav /root/StellaTemp/hpf.wav sinc 11k -t 1", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/drums.wav -af \"bass=5\" /root/StellaTemp/drums_pre.wav", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/other.wav -af apulsator=hz=0.015 -af aecho=1.0:0.7:20:0.5 /root/StellaTemp/other_pre.wav", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights sox /root/StellaTemp/hpf.wav /root/StellaTemp/hpf_out.wav reverb 30 25 75", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights sox /root/StellaTemp/bass.wav /root/StellaTemp/bass_out.wav reverb 30 25 75", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights sox /root/StellaTemp/drums_pre.wav /root/StellaTemp/drums_out.wav reverb 30 25 75", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights sox /root/StellaTemp/other_pre.wav /root/StellaTemp/other_out.wav reverb 30 25 75", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights sox /root/StellaTemp/piano.wav /root/StellaTemp/piano_out.wav reverb 30 25 75", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights sox /root/StellaTemp/vocals.wav /root/StellaTemp/vocals_out.wav reverb 30 25 75", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/vocals_out.wav /root/StellaTemp/vocals.flac", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/piano_out.wav /root/StellaTemp/piano.flac", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/other_out.wav /root/StellaTemp/other.flac", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/drums_out.wav /root/StellaTemp/drums.flac", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/bass_out.wav /root/StellaTemp/bass.flac", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights ffmpeg -i /root/StellaTemp/hpf_out.wav /root/StellaTemp/hpf.flac", { stdio: "inherit" });
+
+console.log("Exporting...");
+cp.execSync("ssh zephyrheights mkdir /root/StellaTemp/out", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights mv /root/StellaTemp/vocals.flac /root/StellaTemp/piano.flac /root/StellaTemp/other.flac /root/StellaTemp/drums.flac /root/StellaTemp/bass.flac /root/StellaTemp/hpf.flac /root/StellaTemp/out", { stdio: "inherit" });
+cp.execSync("mkdir tmp", { stdio: "inherit" });
+cp.execSync("scp zephyrheights:/root/StellaTemp/out/* tmp", { stdio: "inherit" });
+cp.execSync("ssh zephyrheights rm -rf /root/StellaTemp", { stdio: "inherit" });
+
+console.log("Preparing Stella file...");
+
+let files = {
+ bass: zlib.deflateRawSync(fs.readFileSync("tmp/bass.flac")),
+ drums: zlib.deflateRawSync(fs.readFileSync("tmp/drums.flac")),
+ hpf: zlib.deflateRawSync(fs.readFileSync("tmp/hpf.flac")),
+ other: zlib.deflateRawSync(fs.readFileSync("tmp/other.flac")),
+ piano: zlib.deflateRawSync(fs.readFileSync("tmp/piano.flac")),
+ vocals: zlib.deflateRawSync(fs.readFileSync("tmp/vocals.flac")),
+}
+
+let magic = Buffer.from("00ffff4f00000100", "hex");
+let metadata = Buffer.from(zlib.deflateRawSync(JSON.stringify({
+ version: "1.0",
+ id: crypto.randomBytes(16).toString("base64").replace(/[^a-zA-Z\d]/g, "").toUpperCase().substring(0, 10),
+ stems: {
+ bass: [512, files.bass.length],
+ drums: [512 + files.bass.length, files.drums.length],
+ hpf: [512 + files.bass.length + files.drums.length, files.hpf.length],
+ other: [512 + files.bass.length + files.drums.length + files.hpf.length, files.other.length],
+ piano: [512 + files.bass.length + files.drums.length + files.hpf.length + files.other.length, files.piano.length],
+ vocals: [512 + files.bass.length + files.drums.length + files.hpf.length + files.other.length + files.piano.length, files.vocals.length],
+ }
+})));
+let padding = Buffer.from("00".repeat(504 - metadata.length), "hex");
+
+let header = Buffer.concat([magic, metadata, padding]);
+if (header.length !== 512) {
+ console.log("Error: Invalid header length.");
+ return;
+}
+
+let file = Buffer.concat([header, files.bass, files.drums, files.hpf, files.other, files.piano, files.vocals]);
+
+console.log("Writing Stella file...");
+fs.writeFileSync("tmp/export.stella", file);
+
+console.log("Adding to server...");
+fs.copyFileSync("tmp/export.stella", "/opt/mist/jpeg/" + process.argv[2] + ".stella");
+if (!fs.existsSync("../assets/content/" + process.argv[2] + ".stella")) fs.symlinkSync("/opt/mist/jpeg/" + process.argv[2] + ".stella", "../assets/content/" + process.argv[2] + ".stella");
+fs.rmSync("tmp", { recursive: true }); \ No newline at end of file
diff --git a/includes/watcher.js b/includes/watcher.js
index b96c93d..d9d74fc 100644
--- a/includes/watcher.js
+++ b/includes/watcher.js
@@ -3,7 +3,7 @@ const fs = require('fs');
let lastUpdate = 0;
watch('..', { recursive: true }, function(evt, name) {
- if (name.includes("includes/users") || name.includes("includes/tokens") || name.includes("assets/content")) return;
+ if (name.includes("includes/users") || name.includes("includes/tokens") || name.includes("includes/app.json") || name.includes("assets/content")) return;
if (new Date().getTime() - lastUpdate > 60000) {
let buildFile = fs.readFileSync("/opt/spotify/build.txt").toString().trim();