Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
54f1f876af | ||
|
7a39a4f928 | ||
|
fe7c678955 | ||
|
698fc4a7b5 | ||
|
894e9f67f5 | ||
|
b6bb64a438 | ||
|
001660e77f | ||
|
eb6d471b20 | ||
|
c8ad8bd381 | ||
|
7cb0bfc7a8 | ||
|
1cd0d4fc79 | ||
|
7a95c7a537 | ||
|
bb1bb3db16 | ||
|
63c1706594 | ||
|
a74e03cde9 | ||
|
58c8eb49b6 | ||
|
03106e9da7 | ||
|
63dbbda060 |
|
@ -3,11 +3,12 @@ BODY_SIZE_LIMIT=50mb
|
||||||
PROJECT_NAME=Partypack Service
|
PROJECT_NAME=Partypack Service
|
||||||
SERVER_URL=localhost
|
SERVER_URL=localhost
|
||||||
DASHBOARD_URL=localhost:5173 # set this to wherever you're hosting the Partypack dashboard
|
DASHBOARD_URL=localhost:5173 # set this to wherever you're hosting the Partypack dashboard
|
||||||
|
USE_HTTPS=true # set this to false if you're debugging on your computer
|
||||||
ENVIRONMENT=dev
|
ENVIRONMENT=dev
|
||||||
COOKIE_SIGN_KEY=
|
COOKIE_SIGN_KEY=
|
||||||
ADMIN_KEY=
|
ADMIN_KEY=
|
||||||
|
JWT_KEY=
|
||||||
BOT_TOKEN=
|
BOT_TOKEN=
|
||||||
USE_HTTPS=true # set this to false if you're debugging on your computer
|
|
||||||
|
|
||||||
MAX_AMOUNT_OF_DRAFTS_AT_ONCE=30
|
MAX_AMOUNT_OF_DRAFTS_AT_ONCE=30
|
||||||
DISCORD_SERVER_ID=
|
DISCORD_SERVER_ID=
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DataSource } from "typeorm";
|
import { DataSource, IsNull } from "typeorm";
|
||||||
import { ENVIRONMENT, SAVED_DATA_PATH } from "../Modules/Constants";
|
import { ENVIRONMENT, SAVED_DATA_PATH } from "../Modules/Constants";
|
||||||
import { Song } from "../Schemas/Song";
|
import { Song } from "../Schemas/Song";
|
||||||
import { ForcedCategory } from "../Schemas/ForcedCategory";
|
import { ForcedCategory } from "../Schemas/ForcedCategory";
|
||||||
import { User } from "../Schemas/User";
|
import { User } from "../Schemas/User";
|
||||||
import { Rating } from "../Schemas/Rating";
|
import { Rating } from "../Schemas/Rating";
|
||||||
import { DiscordRole } from "../Schemas/DiscordRole";
|
import { DiscordRole } from "../Schemas/DiscordRole";
|
||||||
|
import { Debug } from "../Modules/Logger";
|
||||||
|
|
||||||
export const DBSource = new DataSource({
|
export const DBSource = new DataSource({
|
||||||
type: "better-sqlite3",
|
type: "better-sqlite3",
|
||||||
|
@ -26,6 +27,21 @@ export const DBSource = new DataSource({
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await DBSource.initialize();
|
await DBSource.initialize();
|
||||||
|
|
||||||
|
// Look for songs without a PID here so we can resolve problems before we do anything else
|
||||||
|
const SongsWithNoPID = await Song.find({ where: { PID: IsNull() } });
|
||||||
|
Debug(`We have ${SongsWithNoPID.length} song${SongsWithNoPID.length != 1 ? "s" : ""} with no PID`);
|
||||||
|
|
||||||
|
SongsWithNoPID.forEach(async (Song) => {
|
||||||
|
Debug(`Fixing up ${Song.Name} PID`);
|
||||||
|
|
||||||
|
// Existing songs that actually need separate PIDs (> 2 channels) will need to have their audio reuploaded entirely
|
||||||
|
// This is faster than checking to see if they all actually need one though...
|
||||||
|
Song.PID = Song.ID;
|
||||||
|
|
||||||
|
await Song.save();
|
||||||
|
Debug(`${Song.Name} PID is now ${Song.PID} to match ${Song.ID}`);
|
||||||
|
})
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -68,7 +68,7 @@ async function Initialize() {
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
// ! FESTIVAL-SPECIFIC STUFF
|
// ! FESTIVAL-SPECIFIC STUFF
|
||||||
import axios from "axios";
|
//import axios from "axios";
|
||||||
|
|
||||||
axios.defaults.validateStatus = () => true;
|
//axios.defaults.validateStatus = () => true;
|
||||||
axios.defaults.headers.common["X-Do-Not-Redirect"] = "true";
|
//axios.defaults.headers.common["X-Do-Not-Redirect"] = "true";
|
|
@ -1,3 +1,4 @@
|
||||||
|
import axios from "axios";
|
||||||
import { Err } from "./Logger";
|
import { Err } from "./Logger";
|
||||||
import { red } from "colorette";
|
import { red } from "colorette";
|
||||||
import { FULL_SERVER_ROOT } from "./Constants";
|
import { FULL_SERVER_ROOT } from "./Constants";
|
||||||
|
@ -11,31 +12,15 @@ let LastContentDownloadDate: Date = new Date(0); // set it to 1970 as default cu
|
||||||
GenerateFortnitePages(null);
|
GenerateFortnitePages(null);
|
||||||
|
|
||||||
export async function GenerateFortnitePages(ForUser: User | null): Promise<{ Success: boolean, FNPages: { [key: string]: unknown } | null }> {
|
export async function GenerateFortnitePages(ForUser: User | null): Promise<{ Success: boolean, FNPages: { [key: string]: unknown } | null }> {
|
||||||
let status;
|
const { status, data } = // check if 30 minutes have passed since last content update. if so, get a new copy of pages, if not, fuck off
|
||||||
let data;
|
|
||||||
if (FullFortnitePages === null || Date.now() > LastContentDownloadDate.getTime() + 30 * 60 * 1000) {
|
|
||||||
const response = await fetch("https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game");
|
|
||||||
status = response.status;
|
|
||||||
data = await response.json();
|
|
||||||
} else {
|
|
||||||
status = 200;
|
|
||||||
data = FullFortnitePages;
|
|
||||||
}
|
|
||||||
/*const { status, data } = // check if 30 minutes have passed since last content update. if so, get a new copy of pages, if not, fuck off
|
|
||||||
FullFortnitePages === null || Date.now() > LastContentDownloadDate.getTime() + 30 * 60 * 1000 ?
|
FullFortnitePages === null || Date.now() > LastContentDownloadDate.getTime() + 30 * 60 * 1000 ?
|
||||||
await fetch("https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game") :
|
await axios.get("https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game") :
|
||||||
FullFortnitePages;*/
|
{ status: 200, data: FullFortnitePages };
|
||||||
|
|
||||||
let OGSparks;
|
|
||||||
|
|
||||||
if (OriginalSparks === null || Date.now() > LastContentDownloadDate.getTime() + 30 * 60 * 1000) {
|
|
||||||
const response = await fetch("https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game/spark-tracks");
|
|
||||||
OGSparks = {status: 200, data: await response.json()}
|
|
||||||
} else {
|
|
||||||
OGSparks = OriginalSparks
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
const OGSparks =
|
||||||
|
OriginalSparks === null || Date.now() > LastContentDownloadDate.getTime() + 30 * 60 * 1000 ?
|
||||||
|
await axios.get("https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game/spark-tracks") :
|
||||||
|
{ status: 200, data: OriginalSparks };
|
||||||
|
|
||||||
FullFortnitePages = {
|
FullFortnitePages = {
|
||||||
...data,
|
...data,
|
||||||
|
@ -111,7 +96,7 @@ export async function GenerateFortnitePages(ForUser: User | null): Promise<{ Suc
|
||||||
siv: "Vocals", // siv - Vocals ID to use (only Vocals possible)
|
siv: "Vocals", // siv - Vocals ID to use (only Vocals possible)
|
||||||
qi: JSON.stringify({ // qi - Query Information (frontend related display stuff and language vocals channel related stuff)
|
qi: JSON.stringify({ // qi - Query Information (frontend related display stuff and language vocals channel related stuff)
|
||||||
sid: Song.ID, // sid - Song UUID
|
sid: Song.ID, // sid - Song UUID
|
||||||
pid: Song.ID, // pid - Playlist Asset ID
|
pid: Song.PID, // pid - Playlist Asset ID
|
||||||
title: OriginalTrack._title, // title - Song Name - same as _title
|
title: OriginalTrack._title, // title - Song Name - same as _title
|
||||||
tracks: [
|
tracks: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import axios from "axios";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import qs from "querystring";
|
import qs from "querystring";
|
||||||
import j from "joi";
|
import j from "joi";
|
||||||
|
@ -15,15 +16,11 @@ const App = Router();
|
||||||
|
|
||||||
// ? hacky, if you want, make it less hacky
|
// ? hacky, if you want, make it less hacky
|
||||||
async function QuickRevokeToken(res: Response, Token: string) {
|
async function QuickRevokeToken(res: Response, Token: string) {
|
||||||
|
await axios.post("https://discord.com/api/oauth2/token/revoke", qs.stringify({ token: Token, token_type_hint: "access_token" }), {
|
||||||
await fetch("https://discord.com/api/oauth2/token/revoke", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"Authorization": `Basic ${Buffer.from(`${DISCORD_CLIENT_ID}:${DISCORD_CLIENT_SECRET}`).toString("base64")}`
|
"Authorization": `Basic ${Buffer.from(`${DISCORD_CLIENT_ID}:${DISCORD_CLIENT_SECRET}`).toString("base64")}`
|
||||||
},
|
}
|
||||||
body: qs.stringify({ token: Token, token_type_hint: "access_token" })
|
|
||||||
|
|
||||||
})
|
})
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -39,36 +36,33 @@ App.get("/discord",
|
||||||
|
|
||||||
//const Discord = await axios.post(`https://discord.com/api/oauth2/token`, qs.stringify({ grant_type: "authorization_code", code: req.query.code as string, redirect_uri: `${FULL_SERVER_ROOT}/api/discord` }), { auth: { username: DISCORD_CLIENT_ID!, password: DISCORD_CLIENT_SECRET! } });
|
//const Discord = await axios.post(`https://discord.com/api/oauth2/token`, qs.stringify({ grant_type: "authorization_code", code: req.query.code as string, redirect_uri: `${FULL_SERVER_ROOT}/api/discord` }), { auth: { username: DISCORD_CLIENT_ID!, password: DISCORD_CLIENT_SECRET! } });
|
||||||
|
|
||||||
const Discord = await fetch(
|
const Discord = await axios.post(
|
||||||
"https://discord.com/api/oauth2/token",
|
"https://discord.com/api/oauth2/token",
|
||||||
|
qs.stringify({ grant_type: "authorization_code", code: req.query.code as string, redirect_uri: `${FULL_SERVER_ROOT}/api/discord` }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"Authorization": `Basic ${Buffer.from(`${DISCORD_CLIENT_ID}:${DISCORD_CLIENT_SECRET}`).toString("base64")}`
|
"Authorization": `Basic ${Buffer.from(`${DISCORD_CLIENT_ID}:${DISCORD_CLIENT_SECRET}`).toString("base64")}`
|
||||||
},
|
}
|
||||||
body: qs.stringify({ grant_type: "authorization_code", code: req.query.code as string, redirect_uri: `${FULL_SERVER_ROOT}/api/discord` })
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (Discord.status !== 200)
|
if (Discord.status !== 200)
|
||||||
return res.status(500).send("Failed to request OAuth token from Discord's services.");
|
return res.status(500).send("Failed to request OAuth token from Discord's services.");
|
||||||
|
|
||||||
const DiscordData = await Discord.json() as any; // :waaaaa:
|
const DiscordData = Discord.data; // :waaaaa:
|
||||||
|
|
||||||
if (!DiscordData.scope.includes("identify"))
|
if (!DiscordData.scope.includes("identify"))
|
||||||
return (await QuickRevokeToken(res, DiscordData.access_token)).status(400).send("Missing identify scope. Please check if your OAuth link is correctly set up!");
|
return (await QuickRevokeToken(res, DiscordData.access_token)).status(400).send("Missing identify scope. Please check if your OAuth link is correctly set up!");
|
||||||
|
|
||||||
|
|
||||||
const UserData = await fetch("https://discord.com/api/v10/users/@me", {
|
const UserData = await axios.get("https://discord.com/api/v10/users/@me", {
|
||||||
method: "GET",
|
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `${DiscordData.token_type} ${DiscordData.access_token}`
|
"Authorization": `${DiscordData.token_type} ${DiscordData.access_token}`
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const UserDataBody = await UserData.json() as any;
|
const UserDataBody = UserData.data;
|
||||||
|
|
||||||
if (UserData.status !== 200)
|
if (UserData.status !== 200)
|
||||||
return (await QuickRevokeToken(res, DiscordData.access_token)).status(500).send("Failed to request user data from Discord's services.");
|
return (await QuickRevokeToken(res, DiscordData.access_token)).status(500).send("Failed to request user data from Discord's services.");
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { CreateBlurl } from "../Modules/BLURL";
|
||||||
import { Song } from "../Schemas/Song";
|
import { Song } from "../Schemas/Song";
|
||||||
import { RequireAuthentication } from "../Modules/Middleware";
|
import { RequireAuthentication } from "../Modules/Middleware";
|
||||||
import { UserPermissions } from "../Schemas/User";
|
import { UserPermissions } from "../Schemas/User";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
const App = Router();
|
const App = Router();
|
||||||
|
|
||||||
|
@ -14,10 +15,13 @@ App.get("/song/download/:InternalID/:File",
|
||||||
RequireAuthentication(),
|
RequireAuthentication(),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
//const Song = AvailableFestivalSongs.find(x => x.UUID === req.params.SongUUID);
|
//const Song = AvailableFestivalSongs.find(x => x.UUID === req.params.SongUUID);
|
||||||
const SongData = await Song.findOne({ where: { ID: req.params.InternalID }, relations: { Author: true } });
|
const SongData = await Song.findOne({ where: [ { ID: req.params.InternalID}, { PID: req.params.InternalID } ], relations: { Author: true } });
|
||||||
if (!SongData)
|
if (!SongData)
|
||||||
return res.status(404).send("Song not found.");
|
return res.status(404).send("Song not found.");
|
||||||
|
|
||||||
|
const IsPreview = SongData.ID != SongData.PID && req.params.InternalID == SongData.PID;
|
||||||
|
const ManifestPath = `${SongData.Directory}/${IsPreview ? `PreviewManifest.mpd` : `Manifest.mpd`}`;
|
||||||
|
|
||||||
if (SongData.IsDraft && (req.user!.PermissionLevel! < UserPermissions.VerifiedUser && SongData.Author.ID !== req.user!.ID))
|
if (SongData.IsDraft && (req.user!.PermissionLevel! < UserPermissions.VerifiedUser && SongData.Author.ID !== req.user!.ID))
|
||||||
return res.status(403).send("You cannot use this track, because it's a draft.");
|
return res.status(403).send("You cannot use this track, because it's a draft.");
|
||||||
|
|
||||||
|
@ -32,7 +36,7 @@ async (req, res) => {
|
||||||
type: "main",
|
type: "main",
|
||||||
language: "en",
|
language: "en",
|
||||||
url: `${BaseURL}master.blurl`,
|
url: `${BaseURL}master.blurl`,
|
||||||
data: readFileSync(`${SongData.Directory}/Manifest.mpd`).toString().replaceAll("{BASEURL}", BaseURL)
|
data: readFileSync(ManifestPath).toString().replaceAll("{BASEURL}", BaseURL)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
type: "vod",
|
type: "vod",
|
||||||
|
@ -42,7 +46,7 @@ async (req, res) => {
|
||||||
|
|
||||||
case "manifest":
|
case "manifest":
|
||||||
case "manifest.mpd":
|
case "manifest.mpd":
|
||||||
return res.set("content-type", "application/dash+xml").send(Buffer.from(readFileSync(`${SongData.Directory}/Manifest.mpd`).toString().replaceAll("{BASEURL}", BaseURL)));
|
return res.set("content-type", "application/dash+xml").send(Buffer.from(readFileSync(ManifestPath).toString().replaceAll("{BASEURL}", BaseURL)));
|
||||||
|
|
||||||
case "cover":
|
case "cover":
|
||||||
case "cover.png":
|
case "cover.png":
|
||||||
|
@ -63,29 +67,39 @@ async (req, res) => {
|
||||||
return res.status(400).send("File name failed validation.");
|
return res.status(400).send("File name failed validation.");
|
||||||
|
|
||||||
if (!req.params.File.endsWith(".m4s") && !req.params.File.endsWith(".webm"))
|
if (!req.params.File.endsWith(".m4s") && !req.params.File.endsWith(".webm"))
|
||||||
return res.sendStatus(403);
|
return res.sendStatus(403).send("Unsupported File Format.");
|
||||||
|
|
||||||
if (!existsSync(`${SongData.Directory}/Chunks/${req.params.File}`))
|
const ChunkPath = `${SongData.Directory}/${IsPreview ? `PreviewChunks` : `Chunks`}/${req.params.File}`
|
||||||
return res.sendStatus(404);
|
if (!existsSync(ChunkPath))
|
||||||
|
return res.status(404).send("Chunk not found.");
|
||||||
|
|
||||||
res.set("content-type", "video/mp4")
|
res.set("content-type", "video/mp4")
|
||||||
res.send(readFileSync(`${SongData.Directory}/Chunks/${req.params.File}`));
|
res.send(readFileSync(ChunkPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
App.get("/:InternalID",
|
App.get("/:InternalID",
|
||||||
|
(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();
|
||||||
|
},
|
||||||
RequireAuthentication(),
|
RequireAuthentication(),
|
||||||
async (req, res, next) => {
|
async (req, res) => {
|
||||||
const SongData = await Song.findOne({ where: { ID: req.params.InternalID }, relations: { Author: true } });
|
const SongData = await Song.findOne({ where: [ { ID: req.params.InternalID }, { PID: req.params.InternalID } ], relations: { Author: true } });
|
||||||
if (!SongData)
|
if (!SongData)
|
||||||
return next(); // trust me bro
|
return res.status(404).send("Track not found.");
|
||||||
|
|
||||||
|
const IsPreview = SongData.ID != SongData.PID && req.params.InternalID == SongData.PID;
|
||||||
|
|
||||||
if (SongData.IsDraft && ((req.user ? req.user.PermissionLevel < UserPermissions.VerifiedUser : true) && SongData.Author.ID !== req.user!.ID))
|
if (SongData.IsDraft && ((req.user ? req.user.PermissionLevel < UserPermissions.VerifiedUser : true) && SongData.Author.ID !== req.user!.ID))
|
||||||
return res.status(403).send("You cannot use this track, because it's a draft.");
|
return res.status(403).send("You cannot use this track, because it's a draft.");
|
||||||
|
|
||||||
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${SongData.ID}/`;
|
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${IsPreview ? SongData.PID : SongData.ID}/`;
|
||||||
res.set("content-type", "application/json");
|
res.set("content-type", "application/json");
|
||||||
res.json({
|
res.json({
|
||||||
playlist: Buffer.from(readFileSync(`${SongData.Directory}/Manifest.mpd`).toString().replaceAll("{BASEURL}", BaseURL)).toString("base64"),
|
playlist: Buffer.from(readFileSync(`${SongData.Directory}/${IsPreview ? `PreviewManifest.mpd` : `Manifest.mpd`}`).toString().replaceAll("{BASEURL}", BaseURL)).toString("base64"),
|
||||||
playlistType: "application/dash+xml",
|
playlistType: "application/dash+xml",
|
||||||
metadata: {
|
metadata: {
|
||||||
assetId: "",
|
assetId: "",
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { Song, SongStatus } from "../Schemas/Song";
|
||||||
import { Debug } from "../Modules/Logger";
|
import { Debug } from "../Modules/Logger";
|
||||||
import { magenta } from "colorette";
|
import { magenta } from "colorette";
|
||||||
import { fromBuffer } from "file-type";
|
import { fromBuffer } from "file-type";
|
||||||
import { rmSync, writeFileSync, renameSync, readFileSync } from "fs";
|
import { v4 } from "uuid";
|
||||||
|
import { rmSync, writeFileSync, renameSync, readFileSync, existsSync, mkdirSync } from "fs";
|
||||||
import { FULL_SERVER_ROOT, MAX_AMOUNT_OF_DRAFTS_AT_ONCE, SAVED_DATA_PATH } from "../Modules/Constants";
|
import { FULL_SERVER_ROOT, MAX_AMOUNT_OF_DRAFTS_AT_ONCE, SAVED_DATA_PATH } from "../Modules/Constants";
|
||||||
import { UserPermissions } from "../Schemas/User";
|
import { UserPermissions } from "../Schemas/User";
|
||||||
|
|
||||||
|
@ -182,45 +183,115 @@ App.post("/upload/audio",
|
||||||
if (req.user!.PermissionLevel! < UserPermissions.Administrator && SongData.Author.ID !== req.user!.ID)
|
if (req.user!.PermissionLevel! < UserPermissions.Administrator && SongData.Author.ID !== req.user!.ID)
|
||||||
return res.status(403).send("You don't have permission to upload to this song.");
|
return res.status(403).send("You don't have permission to upload to this song.");
|
||||||
|
|
||||||
|
const ChunksPath = `${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Chunks`;
|
||||||
|
|
||||||
if (SongData.HasAudio) {
|
if (SongData.HasAudio) {
|
||||||
if (SongData.Status !== SongStatus.BROKEN && SongData.Status !== SongStatus.DEFAULT && SongData.Status !== SongStatus.DENIED && SongData.Status !== SongStatus.PUBLIC)
|
if (SongData.Status !== SongStatus.BROKEN && SongData.Status !== SongStatus.DEFAULT && SongData.Status !== SongStatus.DENIED && SongData.Status !== SongStatus.PUBLIC)
|
||||||
return res.status(400).send("You cannot update this song at this moment.");
|
return res.status(400).send("You cannot update this song at this moment.");
|
||||||
|
|
||||||
rmSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Chunks`, { recursive: true });
|
rmSync(ChunksPath, { recursive: true });
|
||||||
SongData.HasAudio = false;
|
SongData.HasAudio = false;
|
||||||
SongData.IsDraft = true;
|
SongData.IsDraft = true;
|
||||||
SongData.Status = SongStatus.PROCESSING;
|
SongData.Status = SongStatus.PROCESSING;
|
||||||
await SongData.save();
|
await SongData.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeFileSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Audio.${ext}`, Decoded);
|
if (!existsSync(ChunksPath))
|
||||||
|
mkdirSync(ChunksPath);
|
||||||
|
|
||||||
|
const AudioPath = `${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}`;
|
||||||
|
|
||||||
|
await writeFileSync(`${AudioPath}/Audio.${ext}`, Decoded);
|
||||||
ffmpeg()
|
ffmpeg()
|
||||||
.input(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Audio.${ext}`)
|
.input(`${AudioPath}/Audio.${ext}`)
|
||||||
.audioCodec("libopus")
|
.audioCodec("libopus")
|
||||||
.outputOptions([
|
.outputOptions([
|
||||||
"-use_timeline 1",
|
"-use_timeline 1",
|
||||||
"-f dash",
|
"-f dash",
|
||||||
"-mapping_family 255"
|
"-mapping_family 255"
|
||||||
])
|
])
|
||||||
.output(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Chunks/Manifest.mpd`)
|
.output(`${AudioPath}/Chunks/Manifest.mpd`)
|
||||||
.on("start", cl => Debug(`ffmpeg running with ${magenta(cl)}`))
|
.on("start", cl => Debug(`ffmpeg running with ${magenta(cl)}`))
|
||||||
.on("end", async () => {
|
.on("end", async () => {
|
||||||
Debug("Ffmpeg finished running");
|
Debug("Ffmpeg finished running");
|
||||||
rmSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Audio.${ext}`);
|
|
||||||
|
|
||||||
renameSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Chunks/Manifest.mpd`, `${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Manifest.mpd`);
|
// Check channels
|
||||||
// i love creating thread-safe code that always works! (never gonna error trust me)
|
ffmpeg.ffprobe(`${AudioPath}/Audio.${ext}`, async (err, metadata) => {
|
||||||
writeFileSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Manifest.mpd`, readFileSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Manifest.mpd`).toString().replace(/<ProgramInformation>[\w\d\r\n\t]*<\/ProgramInformation>/i, "<BaseURL>{BASEURL}</BaseURL>"));
|
if (err) // FUCK
|
||||||
|
return console.log(err);
|
||||||
|
|
||||||
await SongData.reload();
|
if (metadata.streams[0].codec_type == "audio" && metadata.streams[0].channels! > 2)
|
||||||
SongData.HasAudio = true;
|
{
|
||||||
|
Debug("Creating preview stream as it's needed...");
|
||||||
|
|
||||||
|
// Oh shit!! we need a preview stream!! so let's make one.
|
||||||
|
|
||||||
|
// Make a dir for it first
|
||||||
|
if (!existsSync(`${AudioPath}/PreviewChunks`))
|
||||||
|
mkdirSync(`${AudioPath}/PreviewChunks`, { recursive: true });
|
||||||
|
|
||||||
|
// Then, figure out which channels from the original file to put into each channel on the output file.
|
||||||
|
// We already ran ffprobe earlier so we can just reuse that lol
|
||||||
|
|
||||||
|
// Output with 10 channels is "pan=stereo|c0=c0+c2+c4+c6+c8|c1=c1+c3+c5+c7+c9", ffmpeg uses this to decide how to downmix
|
||||||
|
var FilterLeft = "pan=stereo|c0=";
|
||||||
|
var FilterRight = "|c1=";
|
||||||
|
for (var i = 0; i < metadata.streams[0].channels!; i++)
|
||||||
|
{
|
||||||
|
if (i == 0 || i % 2 == 0)
|
||||||
|
FilterLeft += `${i == 0 ? "" : "+"}c${i}` // out for 0 = "c0", out for 2 = "+c2"
|
||||||
|
else
|
||||||
|
FilterRight += `${i == 1 ? "" : "+"}c${i}` // out for 1 = "c1", out for 3 = "+c3"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to wait for this before removing the source file, but since it won't ALWAYS get called we don't put the rmSync in here
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
ffmpeg()
|
||||||
|
.input(`${AudioPath}/Audio.${ext}`)
|
||||||
|
.audioCodec("libopus")
|
||||||
|
.audioFilter(FilterLeft + FilterRight)
|
||||||
|
.outputOptions([
|
||||||
|
"-use_timeline 1",
|
||||||
|
"-f dash",
|
||||||
|
"-ac 2", // downmix
|
||||||
|
"-t 30" // max of 30 seconds (requested by mc)
|
||||||
|
])
|
||||||
|
.output(`${AudioPath}/PreviewChunks/PreviewManifest.mpd`)
|
||||||
|
.on("start", cl => Debug(`Creating preview stream with ${magenta(cl)}`))
|
||||||
|
.on("end", async () => {
|
||||||
|
Debug("Preview stream created");
|
||||||
|
// Move the mpd out
|
||||||
|
renameSync(`${AudioPath}/PreviewChunks/PreviewManifest.mpd`, `${AudioPath}/PreviewManifest.mpd`);
|
||||||
|
writeFileSync(`${AudioPath}/PreviewManifest.mpd`, readFileSync(`${AudioPath}/PreviewManifest.mpd`).toString().replace(/<ProgramInformation>[\w\d\r\n\t]*<\/ProgramInformation>/i, "<BaseURL>{BASEURL}</BaseURL>"));
|
||||||
|
SongData.PID = v4();
|
||||||
await SongData.save();
|
await SongData.save();
|
||||||
|
resolve();
|
||||||
})
|
})
|
||||||
.on("error", async (e, stdout, stderr) => {
|
.on("error", async (e, stdout, stderr) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
console.log(stdout);
|
console.log(stdout);
|
||||||
console.error(stderr);
|
console.error(stderr);
|
||||||
rmSync(`${SAVED_DATA_PATH}/Songs/${req.body.TargetSong}/Audio.${ext}`);
|
reject(e);
|
||||||
|
}).run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rmSync(`${AudioPath}/Audio.${ext}`);
|
||||||
|
|
||||||
|
renameSync(`${AudioPath}/Chunks/Manifest.mpd`, `${AudioPath}/Manifest.mpd`);
|
||||||
|
// i love creating thread-safe code that always works! (never gonna error trust me)
|
||||||
|
writeFileSync(`${AudioPath}/Manifest.mpd`, readFileSync(`${AudioPath}/Manifest.mpd`).toString().replace(/<ProgramInformation>[\w\d\r\n\t]*<\/ProgramInformation>/i, "<BaseURL>{BASEURL}</BaseURL>"));
|
||||||
|
|
||||||
|
await SongData.reload();
|
||||||
|
SongData.HasAudio = true;
|
||||||
|
await SongData.save();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on("error", async (e, stdout, stderr) => {
|
||||||
|
console.error(e);
|
||||||
|
console.log(stdout);
|
||||||
|
console.error(stderr);
|
||||||
|
rmSync(`${AudioPath}/Audio.${ext}`);
|
||||||
|
|
||||||
await SongData.reload();
|
await SongData.reload();
|
||||||
SongData.Status = SongStatus.BROKEN;
|
SongData.Status = SongStatus.BROKEN;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Router } from "express";
|
||||||
import { FullFortnitePages, GenerateFortnitePages } from "../Modules/FNUtil";
|
import { FullFortnitePages, GenerateFortnitePages } from "../Modules/FNUtil";
|
||||||
import { IS_DEBUG } from "../Modules/Constants";
|
import { IS_DEBUG } from "../Modules/Constants";
|
||||||
import { RequireAuthentication } from "../Modules/Middleware";
|
import { RequireAuthentication } from "../Modules/Middleware";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const App = Router();
|
const App = Router();
|
||||||
|
|
||||||
|
@ -41,11 +42,11 @@ App.get("/content/api/pages/fortnite-game/:Section", RequireAuthentication(), as
|
||||||
if (!CachedSection)
|
if (!CachedSection)
|
||||||
return res.status(404).send("funny section not found haha kill me");
|
return res.status(404).send("funny section not found haha kill me");
|
||||||
|
|
||||||
const ContentFromServer = await fetch(`https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game/${CachedSection._title}`)
|
const ContentFromServer = await axios.get(`https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game/${CachedSection._title}`)
|
||||||
if (ContentFromServer.status !== 200)
|
if (ContentFromServer.status !== 200)
|
||||||
return res.status(404).json({ error: IS_DEBUG ? await ContentFromServer.text() : "Fortnite server returned an error." });
|
return res.status(404).json({ error: IS_DEBUG ? ContentFromServer.data : "Fortnite server returned an error." });
|
||||||
|
|
||||||
res.json(await ContentFromServer.json());
|
res.json(ContentFromServer.data);
|
||||||
})
|
})
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -21,6 +21,9 @@ export class Song extends BaseEntity {
|
||||||
@PrimaryGeneratedColumn("uuid")
|
@PrimaryGeneratedColumn("uuid")
|
||||||
ID: string;
|
ID: string;
|
||||||
|
|
||||||
|
@Column("uuid", { nullable: true })
|
||||||
|
PID?: string;
|
||||||
|
|
||||||
@ManyToOne(() => User, U => U.CreatedTracks)
|
@ManyToOne(() => User, U => U.CreatedTracks)
|
||||||
Author: User;
|
Author: User;
|
||||||
|
|
||||||
|
@ -102,6 +105,10 @@ export class Song extends BaseEntity {
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
Setup() {
|
Setup() {
|
||||||
this.ID = v4();
|
this.ID = v4();
|
||||||
|
|
||||||
|
if (this.PID === undefined) // im lazy but this will work regardless
|
||||||
|
this.PID = this.ID; // By default they should be the same to save space, if we *need* a preview stream later we can change this when processing audio
|
||||||
|
|
||||||
this.Directory = `${SAVED_DATA_PATH}/Songs/${this.ID}`;
|
this.Directory = `${SAVED_DATA_PATH}/Songs/${this.ID}`;
|
||||||
if (!existsSync(join(this.Directory, "Chunks")))
|
if (!existsSync(join(this.Directory, "Chunks")))
|
||||||
mkdirSync(join(this.Directory, "Chunks"), { recursive: true });
|
mkdirSync(join(this.Directory, "Chunks"), { recursive: true });
|
||||||
|
|
224
Server/package-lock.json
generated
224
Server/package-lock.json
generated
|
@ -10,6 +10,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node-cron": "^3.0.11",
|
"@types/node-cron": "^3.0.11",
|
||||||
|
"axios": "^1.6.7",
|
||||||
"better-sqlite3": "^9.3.0",
|
"better-sqlite3": "^9.3.0",
|
||||||
"colorette": "^2.0.20",
|
"colorette": "^2.0.20",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
"joi": "^17.12.0",
|
"joi": "^17.12.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"typeorm": "^0.3.19",
|
"typeorm": "^0.3.19",
|
||||||
"underscore": "^1.13.6",
|
"underscore": "^1.13.6",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
@ -40,6 +42,17 @@
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
|
"version": "0.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@discordjs/builders": {
|
"node_modules/@discordjs/builders": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz",
|
||||||
|
@ -175,6 +188,28 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.4.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||||
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
|
@ -242,6 +277,26 @@
|
||||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@tsconfig/node10": {
|
||||||
|
"version": "1.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||||
|
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node12": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node14": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node16": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
|
||||||
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.3",
|
"version": "1.19.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
||||||
|
@ -420,6 +475,25 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/acorn": {
|
||||||
|
"version": "8.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||||
|
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/acorn-walk": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||||
|
@ -458,6 +532,11 @@
|
||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||||
|
},
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
@ -468,6 +547,21 @@
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
||||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
|
||||||
|
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.4",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
@ -837,6 +931,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
@ -901,6 +1006,11 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/create-require": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
|
@ -949,6 +1059,14 @@
|
||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
@ -974,6 +1092,14 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/discord-api-types": {
|
"node_modules/discord-api-types": {
|
||||||
"version": "0.37.61",
|
"version": "0.37.61",
|
||||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
|
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
|
||||||
|
@ -1189,6 +1315,25 @@
|
||||||
"which": "bin/which"
|
"which": "bin/which"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||||
|
@ -1204,6 +1349,19 @@
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
|
@ -1549,6 +1707,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.8.0.tgz",
|
||||||
"integrity": "sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q=="
|
"integrity": "sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/make-error": {
|
||||||
|
"version": "1.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||||
|
},
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
@ -1864,6 +2027,11 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -2368,6 +2536,48 @@
|
||||||
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
|
||||||
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
|
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-node": {
|
||||||
|
"version": "10.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@tsconfig/node14": "^1.0.0",
|
||||||
|
"@tsconfig/node16": "^1.0.2",
|
||||||
|
"acorn": "^8.4.1",
|
||||||
|
"acorn-walk": "^8.1.1",
|
||||||
|
"arg": "^4.1.0",
|
||||||
|
"create-require": "^1.1.0",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"make-error": "^1.1.1",
|
||||||
|
"v8-compile-cache-lib": "^3.0.1",
|
||||||
|
"yn": "3.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ts-node": "dist/bin.js",
|
||||||
|
"ts-node-cwd": "dist/bin-cwd.js",
|
||||||
|
"ts-node-esm": "dist/bin-esm.js",
|
||||||
|
"ts-node-script": "dist/bin-script.js",
|
||||||
|
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||||
|
"ts-script": "dist/bin-script-deprecated.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/core": ">=1.2.50",
|
||||||
|
"@swc/wasm": ">=1.2.50",
|
||||||
|
"@types/node": "*",
|
||||||
|
"typescript": ">=2.7"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/wasm": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||||
|
@ -2526,7 +2736,6 @@
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
@ -2589,6 +2798,11 @@
|
||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/v8-compile-cache-lib": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
@ -2791,6 +3005,14 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node-cron": "^3.0.11",
|
"@types/node-cron": "^3.0.11",
|
||||||
|
"axios": "^1.6.7",
|
||||||
"better-sqlite3": "^9.3.0",
|
"better-sqlite3": "^9.3.0",
|
||||||
"colorette": "^2.0.20",
|
"colorette": "^2.0.20",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
"joi": "^17.12.0",
|
"joi": "^17.12.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"typeorm": "^0.3.19",
|
"typeorm": "^0.3.19",
|
||||||
"underscore": "^1.13.6",
|
"underscore": "^1.13.6",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
|
|
@ -12,9 +12,9 @@ export function AdminHome() {
|
||||||
<PageHeader.Title>Partypack Admin Management Panel</PageHeader.Title>
|
<PageHeader.Title>Partypack Admin Management Panel</PageHeader.Title>
|
||||||
</PageHeader.TitleArea>
|
</PageHeader.TitleArea>
|
||||||
<PageHeader.Description>
|
<PageHeader.Description>
|
||||||
TEMP
|
Welcome! Please select a management page you'd like to visit:
|
||||||
<Button onClick={() => navigate("/admin/tracks")}>Tracks</Button>
|
<Button onClick={() => navigate("/admin/tracks")}>Tracks</Button>
|
||||||
<Button onClick={() => navigate("/admin/featured")}>Featured Tab Management</Button>
|
<Button onClick={() => navigate("/admin/featured")}>Discovery</Button>
|
||||||
</PageHeader.Description>
|
</PageHeader.Description>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -14,7 +14,7 @@ export function Home() {
|
||||||
Partypack was created by <a href="/credits">everyone listed here</a>. If you're interested in hosting your own instance of Partypack, be sure to check the <a href="https://github.com/McMistrzYT/Partypack">GitHub repo</a>.</Text>
|
Partypack was created by <a href="/credits">everyone listed here</a>. If you're interested in hosting your own instance of Partypack, be sure to check the <a href="https://github.com/McMistrzYT/Partypack">GitHub repo</a>.</Text>
|
||||||
<Heading>Quickstart Guide</Heading>
|
<Heading>Quickstart Guide</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
<b>Consider watching the easier to understand, visual guide available <a href="/tutorials/Quickstart.mp4">here</a>.</b><br />
|
<b>Consider watching the easier to understand, visual guide available in the Discord server.</b><br />
|
||||||
1. Join this instance's <a href="https://discord.gg/Rhd9Hq4D62">Discord server</a><br />
|
1. Join this instance's <a href="https://discord.gg/Rhd9Hq4D62">Discord server</a><br />
|
||||||
2. Click on the <SignInIcon size={16} /> icon in the top right<br />
|
2. Click on the <SignInIcon size={16} /> icon in the top right<br />
|
||||||
3. Log in using your Discord account<br />
|
3. Log in using your Discord account<br />
|
||||||
|
|
|
@ -270,7 +270,7 @@ export function Profile() {
|
||||||
}}>Delete draft</Button>
|
}}>Delete draft</Button>
|
||||||
</Song>;
|
</Song>;
|
||||||
})
|
})
|
||||||
: <Text>You have no bookmarked songs.</Text>
|
: <Text>You have no drafts.</Text>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
</Box> :
|
</Box> :
|
||||||
|
|
|
@ -91,7 +91,7 @@ export function TrackSubmission() {
|
||||||
<FormControl.Caption>You can use the #tools-and-resources channel to find useful resources on how to create MIDIs.</FormControl.Caption>
|
<FormControl.Caption>You can use the #tools-and-resources channel to find useful resources on how to create MIDIs.</FormControl.Caption>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={formControlStyle}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Audio File (.m4a, .mp3, .wav)</FormControl.Label>
|
<FormControl.Label>Audio File (.m4a, .mp3, .wav, .ogg)</FormControl.Label>
|
||||||
<TextInput type="file" />
|
<TextInput type="file" />
|
||||||
<FormControl.Caption>This will play in the background of your song. Make sure it was exported from REAPER.</FormControl.Caption>
|
<FormControl.Caption>This will play in the background of your song. Make sure it was exported from REAPER.</FormControl.Caption>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user