Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @cpsiaki @LinaBell @liebeskind
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 11 additions & 9 deletions server/controllers/media/AddMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Request, Response } from "express";

export default async function AddMedia(req: Request, res: Response) {
const credentials = getCredentials(req.query);
const { interactiveNonce, profileId, urlSlug, visitorId } = credentials;

const { videos, type }: { videos: Video[] | string[]; type: "catalog" | "queue" } = req.body;
const [isAdmin, jukeboxAsset] = await Promise.all([checkIsAdmin(credentials), getDroppedAsset(credentials)]);

Expand All @@ -26,23 +28,23 @@ export default async function AddMedia(req: Request, res: Response) {
analytics.push({
analyticName: "addsToCatalog",
incrementBy: videos.length,
uniqueKey: credentials.urlSlug,
urlSlug: credentials.urlSlug,
uniqueKey: urlSlug,
urlSlug,
});
} else if (type === "queue") {
analytics.push({
analyticName: "addsToQueue",
incrementBy: videos.length,
profileId: credentials.profileId,
uniqueKey: credentials.profileId,
profileId,
uniqueKey: profileId,
});
}
let firstVideo = null;
if (jukeboxAsset.dataObject.queue.length === 0 && type === "queue" && jukeboxAsset.dataObject.nowPlaying === "-1") {
firstVideo = jukeboxAsset.dataObject.catalog.find((video: Video) => video.id.videoId === videos[0]);
if (firstVideo) {
const mediaLink = `https://www.youtube.com/watch?v=${firstVideo.id.videoId}`;
analytics.push({ analyticName: "plays", urlSlug: credentials.urlSlug, uniqueKey: credentials.urlSlug });
analytics.push({ analyticName: "plays", urlSlug, uniqueKey: urlSlug });
promises.push(
jukeboxAsset.updateMediaType({
mediaLink,
Expand All @@ -55,7 +57,7 @@ export default async function AddMedia(req: Request, res: Response) {
syncUserMedia: true, // Make it so everyone has the video synced instead of it playing from the beginning when they approach.
}),
);
const world = World.create(credentials.urlSlug, { credentials });
const world = World.create(urlSlug, { credentials });
world
.triggerParticle({
name: "musicNote_float",
Expand Down Expand Up @@ -91,9 +93,9 @@ export default async function AddMedia(req: Request, res: Response) {
redisObj.publish(`${process.env.INTERACTIVE_KEY}_JUKEBOX`, {
assetId: jukeboxAsset.id,
videos: firstVideo ? [firstVideo.id.videoId, ...videos] : videos,
interactiveNonce: credentials.interactiveNonce,
urlSlug: credentials.urlSlug,
visitorId: credentials.visitorId,
interactiveNonce,
urlSlug,
visitorId,
kind: type === "catalog" ? "addedToCatalog" : "addedToQueue",
event: "mediaAction",
});
Expand Down
8 changes: 5 additions & 3 deletions server/controllers/media/GetJukeboxDataObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { checkIsAdmin } from "../../middleware/isAdmin.js";
export default async function GetJukeboxDataObject(req: Request, res: Response) {
try {
const credentials = getCredentials(req.query);
const { profileId, urlSlug } = credentials;

const jukeboxAsset = await getDroppedAsset(credentials);
if (jukeboxAsset.error) {
return res.status(404).json({ message: "Asset not found" });
Expand All @@ -18,9 +20,9 @@ export default async function GetJukeboxDataObject(req: Request, res: Response)
analytics: [
{
analyticName: "views",
profileId: credentials.profileId,
uniqueKey: credentials.profileId,
urlSlug: credentials.urlSlug,
profileId,
uniqueKey: profileId,
urlSlug,
},
],
},
Expand Down
10 changes: 8 additions & 2 deletions server/controllers/media/NextSong.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export default async function NextSong(req: Request, res: Response) {
} else {
credentials = getCredentials(req.query);
}
const { urlSlug } = credentials;

const jukeboxAsset = await getDroppedAsset(credentials);
if (jukeboxAsset.error) {
return res.status(404).json({ message: "Asset not found" });
Expand All @@ -34,6 +36,7 @@ export default async function NextSong(req: Request, res: Response) {
let nowPlaying = "-1" as "-1" | Video;
const promises = [];
const analytics = [];

try {
// Lock the asset dataObject before attempting to find the next available song
// This would reduce YouTube API quota usage
Expand All @@ -46,17 +49,19 @@ export default async function NextSong(req: Request, res: Response) {
},
},
);

if (queue.length > 0) {
const [nextSong, index] = await findNextAvailableSong(queue, jukeboxAsset.dataObject.catalog);
nowPlaying = nextSong;

if (nowPlaying) {
remainingQueue = queue.slice(index + 1);
const videoId = nowPlaying.id.videoId;
// const videoTitle = nowPlaying.snippet.title;

const mediaLink = `https://www.youtube.com/watch?v=${videoId}`;

const world = World.create(credentials.urlSlug, { credentials });
const world = World.create(urlSlug, { credentials });
world
.triggerParticle({
name: "musicNote_float",
Expand All @@ -68,6 +73,7 @@ export default async function NextSong(req: Request, res: Response) {
})
.then()
.catch(() => console.error("Cannot trigger particle"));

promises.push(
jukeboxAsset.updateMediaType({
mediaLink,
Expand All @@ -80,7 +86,7 @@ export default async function NextSong(req: Request, res: Response) {
syncUserMedia: true, // Make it so everyone has the video synced instead of it playing from the beginning when they approach.
}),
);
analytics.push({ analyticName: "plays", urlSlug: credentials.urlSlug, uniqueKey: credentials.urlSlug });
analytics.push({ analyticName: "plays", urlSlug, uniqueKey: urlSlug });
} else {
promises.push(jukeboxAsset.updateMediaType({ mediaType: "none" }));
}
Expand Down
12 changes: 8 additions & 4 deletions server/controllers/media/RemoveMedia.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import redisObj from "../../redis-sse/index.js";
import { Credentials, Video } from "../../types/index.js";
import { getDroppedAsset } from "../../utils/index.js";
import { Video } from "../../types/index.js";
import { getCredentials, getDroppedAsset } from "../../utils/index.js";
import { Request, Response } from "express";

export default async function RemoveMedia(req: Request, res: Response) {
const { assetId, interactivePublicKey, interactiveNonce, urlSlug, visitorId } = req.query as Credentials;
const credentials = getCredentials(req.query);
const { interactiveNonce, urlSlug, visitorId } = credentials;

const { videoIds, type }: { videoIds: string[]; type: "catalog" | "queue" } = req.body;
const credentials = { assetId, interactivePublicKey, interactiveNonce, urlSlug, visitorId };
const jukeboxAsset = await getDroppedAsset(credentials);

if (jukeboxAsset.error) {
return res.status(404).json({ message: "Asset not found" });
}

const timeFactor = new Date(Math.round(new Date().getTime() / 10000) * 10000);
const lockId = `${jukeboxAsset.id}_${timeFactor}`;

try {
const jukeboxUpdate: {
catalog?: Video[];
Expand All @@ -29,6 +31,7 @@ export default async function RemoveMedia(req: Request, res: Response) {
} else if (type === "queue") {
jukeboxUpdate.queue = jukeboxAsset.dataObject.queue.filter((videoId: string) => !videoIds.includes(videoId));
}

await jukeboxAsset.updateDataObject(
{
...jukeboxAsset.dataObject,
Expand All @@ -41,6 +44,7 @@ export default async function RemoveMedia(req: Request, res: Response) {
},
},
);

redisObj.publish(`${process.env.INTERACTIVE_KEY}_JUKEBOX`, {
assetId: jukeboxAsset.id,
videos: videoIds,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const handleCheckInteractiveCredentials = async (
// if key matches proceed with check using jwt created by topiaInit
const user = User.create({ credentials });
await user.checkInteractiveCredentials();

return res.json({ success: true });
} catch (error) {
return errorHandler({
Expand Down
10 changes: 5 additions & 5 deletions server/controllers/status/isAdminCheck.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Credentials } from "../../types/index.js";
import { errorHandler, getVisitor } from "../../utils/index.js";
import { errorHandler, getCredentials, getVisitor } from "../../utils/index.js";
import { Request, Response } from "express";

export default async function isAdminCheck(req: Request, res: Response) {
try {
const { interactivePublicKey, interactiveNonce, urlSlug, visitorId } = req.query as Credentials;
const visitor = await getVisitor({ interactivePublicKey, interactiveNonce, urlSlug, visitorId });
const credentials = getCredentials(req.query);
const visitor = await getVisitor(credentials);
if (!visitor) {
return res.status(404).json({ message: "Visitor not found" });
} else if (visitor.isAdmin) {
Expand All @@ -18,7 +17,8 @@ export default async function isAdminCheck(req: Request, res: Response) {
error,
functionName: "isAdminCheck",
message: "Error in Admin Check",
req, res
req,
res,
});
}
}
14 changes: 6 additions & 8 deletions server/middleware/isAdmin.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { Credentials } from "../types/index.js";
import { getVisitor } from "../utils/index.js";
import { getCredentials, getVisitor } from "../utils/index.js";
import { Request, Response, NextFunction } from "express";

const checkIsAdmin = async (credentials) => {
const { interactivePublicKey, interactiveNonce, urlSlug, visitorId } = credentials;
const visitor = await getVisitor({ interactivePublicKey, interactiveNonce, urlSlug, visitorId });
const visitor = await getVisitor(credentials);
if (!visitor.isAdmin) {
return false;
}
return true
}
return true;
};

async function isAdmin(req: Request, res: Response, next: NextFunction) {
const { interactivePublicKey, interactiveNonce, urlSlug, visitorId } = req.query as Credentials;
const isAdmin = await checkIsAdmin({ interactivePublicKey, interactiveNonce, urlSlug, visitorId });
const credentials = getCredentials(req.query);
const isAdmin = await checkIsAdmin(credentials);
if (!isAdmin) {
return res.status(401).json({ message: "Unauthorized" });
}
Expand Down
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"dependencies": {
"@googleapis/youtube": "^14.0.0",
"@rtsdk/topia": "^0.12.6",
"@rtsdk/topia": "^0.15.4",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
Expand Down
8 changes: 1 addition & 7 deletions server/utils/droppedAssets/getDroppedAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ export const getDroppedAsset = async (credentials: Credentials) => {
const { assetId, interactivePublicKey, interactiveNonce, urlSlug, visitorId } = credentials;
if (!assetId || !interactivePublicKey || !interactiveNonce || !urlSlug || !visitorId) throw "Invalid credentials";

const droppedAsset = await DroppedAsset.get(assetId, urlSlug, {
credentials: {
interactiveNonce,
interactivePublicKey,
visitorId,
},
});
const droppedAsset = await DroppedAsset.get(assetId, urlSlug, { credentials });

if (!droppedAsset) throw "Dropped asset not found";

Expand Down
14 changes: 4 additions & 10 deletions server/utils/visitors/getVisitor.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { Visitor } from "../topiaInit.js"
import { errorHandler } from "../errorHandler.js"
import { Visitor } from "../topiaInit.js";
import { errorHandler } from "../errorHandler.js";
import { Credentials } from "../../types/index.js";

export const getVisitor = async (credentials: Credentials) => {
try {
const { interactivePublicKey, interactiveNonce, urlSlug, visitorId } = credentials;
const { urlSlug, visitorId } = credentials;

const visitor = await Visitor.get(parseInt(visitorId), urlSlug, {
credentials: {
interactiveNonce,
interactivePublicKey,
visitorId: parseInt(visitorId),
},
});
const visitor = await Visitor.get(visitorId, urlSlug, { credentials });
// @ts-ignore
if (!visitor || !visitor.username) throw "Not in world";

Expand Down
Loading