2024-01-22 00:41:59 +01:00
|
|
|
import { Router } from "express";
|
|
|
|
import { existsSync, readFileSync } from "fs";
|
|
|
|
import { FULL_SERVER_ROOT } from "../Modules/Constants";
|
|
|
|
import { CreateBlurl } from "../Modules/BLURL";
|
2024-01-22 23:04:46 +01:00
|
|
|
import { Song } from "../Schemas/Song";
|
2024-01-28 22:02:29 +01:00
|
|
|
import { RequireAuthentication } from "../Modules/Middleware";
|
2024-02-01 21:54:48 +01:00
|
|
|
import { UserPermissions } from "../Schemas/User";
|
2024-02-09 18:52:26 +01:00
|
|
|
import path from "path";
|
2024-01-22 00:41:59 +01:00
|
|
|
|
|
|
|
const App = Router();
|
|
|
|
|
2024-02-04 23:56:27 +01:00
|
|
|
App.get("/api/download/partypacker", (_, res) => res.redirect(`${FULL_SERVER_ROOT}/assets/Partypack-Launcher.zip`))
|
2024-02-04 22:28:42 +01:00
|
|
|
|
2024-01-28 22:02:29 +01:00
|
|
|
App.get("/song/download/:InternalID/:File",
|
|
|
|
RequireAuthentication(),
|
|
|
|
async (req, res) => {
|
2024-01-22 23:04:46 +01:00
|
|
|
//const Song = AvailableFestivalSongs.find(x => x.UUID === req.params.SongUUID);
|
2024-02-05 21:52:39 +01:00
|
|
|
const SongData = await Song.findOne({ where: [ { ID: req.params.InternalID}, { PID: req.params.InternalID } ], relations: { Author: true } });
|
2024-01-22 23:04:46 +01:00
|
|
|
if (!SongData)
|
2024-01-31 00:27:53 +01:00
|
|
|
return res.status(404).send("Song not found.");
|
2024-02-05 21:52:39 +01:00
|
|
|
|
|
|
|
const IsPreview = SongData.ID != SongData.PID && req.params.InternalID == SongData.PID;
|
|
|
|
const ManifestPath = `${SongData.Directory}/${IsPreview ? `PreviewManifest.mpd` : `Manifest.mpd`}`;
|
2024-01-28 22:02:29 +01:00
|
|
|
|
2024-02-01 21:54:48 +01:00
|
|
|
if (SongData.IsDraft && (req.user!.PermissionLevel! < UserPermissions.VerifiedUser && SongData.Author.ID !== req.user!.ID))
|
2024-01-31 00:27:53 +01:00
|
|
|
return res.status(403).send("You cannot use this track, because it's a draft.");
|
2024-01-28 22:02:29 +01:00
|
|
|
|
2024-01-22 23:04:46 +01:00
|
|
|
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${SongData.ID}/`;
|
2024-01-22 00:41:59 +01:00
|
|
|
switch (req.params.File.toLowerCase()) {
|
|
|
|
case "master.blurl":
|
|
|
|
case "main.blurl":
|
|
|
|
return res.set("content-type", "text/plain").send(
|
|
|
|
CreateBlurl({
|
|
|
|
playlists: [
|
|
|
|
{
|
|
|
|
type: "main",
|
|
|
|
language: "en",
|
|
|
|
url: `${BaseURL}master.blurl`,
|
2024-02-05 21:52:39 +01:00
|
|
|
data: readFileSync(ManifestPath).toString().replaceAll("{BASEURL}", BaseURL)
|
2024-01-22 00:41:59 +01:00
|
|
|
}
|
|
|
|
],
|
|
|
|
type: "vod",
|
|
|
|
audioonly: true
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
case "manifest":
|
|
|
|
case "manifest.mpd":
|
2024-02-05 21:52:39 +01:00
|
|
|
return res.set("content-type", "application/dash+xml").send(Buffer.from(readFileSync(ManifestPath).toString().replaceAll("{BASEURL}", BaseURL)));
|
2024-01-22 00:41:59 +01:00
|
|
|
|
|
|
|
case "cover":
|
|
|
|
case "cover.png":
|
2024-01-22 23:04:46 +01:00
|
|
|
return existsSync(`${SongData.Directory}/Cover.png`) ? res.set("content-type", "image/png").send(readFileSync(`${SongData.Directory}/Cover.png`)) : res.sendStatus(404);
|
2024-01-22 00:41:59 +01:00
|
|
|
|
|
|
|
// ! we are not risking a lawsuit
|
|
|
|
//case "midi.dat": // dont forget to encrypt!
|
|
|
|
//return existsSync(`${Song.Directory}/Data.mid`) ? res.set("content-type", "application/octet-stream").send(AesEncrypt(readFileSync(`${Song.Directory}/Data.mid`))) : res.sendStatus(404);
|
|
|
|
|
|
|
|
// funny little tip: you dont actually need to encrypt midis LMFAO
|
|
|
|
case "midi":
|
|
|
|
case "midi.mid":
|
|
|
|
case "midi.midi": // forget to encrypt!
|
2024-01-22 23:04:46 +01:00
|
|
|
return existsSync(`${SongData.Directory}/Data.mid`) ? res.set("content-type", "application/octet-stream").send(readFileSync(`${SongData.Directory}/Data.mid`)) : res.sendStatus(404);
|
2024-01-22 00:41:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!/^[\w\-.]+$/g.test(req.params.File))
|
|
|
|
return res.status(400).send("File name failed validation.");
|
|
|
|
|
2024-02-04 22:28:42 +01:00
|
|
|
if (!req.params.File.endsWith(".m4s") && !req.params.File.endsWith(".webm"))
|
2024-01-22 00:41:59 +01:00
|
|
|
return res.sendStatus(403);
|
|
|
|
|
2024-02-05 21:52:39 +01:00
|
|
|
const ChunkPath = `${SongData.Directory}/${IsPreview ? `PreviewChunks` : `Chunks`}/${req.params.File}`
|
|
|
|
if (!existsSync(ChunkPath))
|
2024-01-22 00:41:59 +01:00
|
|
|
return res.sendStatus(404);
|
|
|
|
|
|
|
|
res.set("content-type", "video/mp4")
|
2024-02-05 21:52:39 +01:00
|
|
|
res.send(readFileSync(ChunkPath));
|
2024-01-22 23:04:46 +01:00
|
|
|
});
|
|
|
|
|
2024-02-01 21:54:48 +01:00
|
|
|
App.get("/:InternalID",
|
2024-02-09 18:52:26 +01:00
|
|
|
(req, res, next) => {
|
|
|
|
// send back index.html when the internal id is not a uuid - causes issues with reloading on sub-pages otherwise
|
|
|
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(req.params.InternalID))
|
|
|
|
return res.sendFile(path.join(process.cwd(), "dist", "index.html"));
|
|
|
|
|
|
|
|
next();
|
|
|
|
},
|
2024-02-05 00:23:14 +01:00
|
|
|
RequireAuthentication(),
|
2024-02-09 18:52:26 +01:00
|
|
|
async (req, res) => {
|
2024-02-05 21:52:39 +01:00
|
|
|
const SongData = await Song.findOne({ where: [ { ID: req.params.InternalID }, { PID: req.params.InternalID } ], relations: { Author: true } });
|
2024-01-22 23:04:46 +01:00
|
|
|
if (!SongData)
|
2024-02-09 18:52:26 +01:00
|
|
|
return res.status(404).send("Track not found.");
|
2024-01-22 23:04:46 +01:00
|
|
|
|
2024-02-05 21:52:39 +01:00
|
|
|
const IsPreview = SongData.ID != SongData.PID && req.params.InternalID == SongData.PID;
|
|
|
|
|
2024-02-04 23:56:27 +01:00
|
|
|
if (SongData.IsDraft && ((req.user ? req.user.PermissionLevel < UserPermissions.VerifiedUser : true) && SongData.Author.ID !== req.user!.ID))
|
2024-01-31 00:27:53 +01:00
|
|
|
return res.status(403).send("You cannot use this track, because it's a draft.");
|
2024-01-28 22:02:29 +01:00
|
|
|
|
2024-02-05 21:52:39 +01:00
|
|
|
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${IsPreview ? SongData.PID : SongData.ID}/`;
|
2024-01-22 23:04:46 +01:00
|
|
|
res.set("content-type", "application/json");
|
|
|
|
res.json({
|
2024-02-05 21:52:39 +01:00
|
|
|
playlist: Buffer.from(readFileSync(`${SongData.Directory}/${IsPreview ? `PreviewManifest.mpd` : `Manifest.mpd`}`).toString().replaceAll("{BASEURL}", BaseURL)).toString("base64"),
|
2024-01-22 23:04:46 +01:00
|
|
|
playlistType: "application/dash+xml",
|
|
|
|
metadata: {
|
|
|
|
assetId: "",
|
|
|
|
baseUrls: [ BaseURL ],
|
|
|
|
supportsCaching: true,
|
|
|
|
ucp: "a",
|
|
|
|
version: Math.floor(Date.now() / 1000)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2024-01-22 00:41:59 +01:00
|
|
|
|
|
|
|
export default {
|
|
|
|
App
|
2024-02-05 00:20:05 +01:00
|
|
|
}
|