From 99bb02ce6aee5144efba01ad3b7b6010dc9f4819 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Sat, 31 Aug 2024 14:23:26 +0200 Subject: [PATCH] openvidu-recording: Add comments --- .../openvidu-recording-node/public/app.js | 3 +++ .../openvidu-recording-node/src/index.js | 16 ++++++++++++++++ .../openvidu-recording-node/src/s3.service.js | 2 ++ 3 files changed, 21 insertions(+) diff --git a/advanced-features/openvidu-recording-node/public/app.js b/advanced-features/openvidu-recording-node/public/app.js index 65b2c56f..d110eb48 100644 --- a/advanced-features/openvidu-recording-node/public/app.js +++ b/advanced-features/openvidu-recording-node/public/app.js @@ -41,6 +41,7 @@ async function joinRoom() { } }); + // When recording status changes... room.on(LivekitClient.RoomEvent.RecordingStatusChanged, async (isRecording) => { await updateRecordingInfo(isRecording); }); @@ -66,6 +67,7 @@ async function joinRoom() { const localVideoTrack = this.room.localParticipant.videoTrackPublications.values().next().value.track; addTrack(localVideoTrack, userName, true); + // Update recording info await updateRecordingInfo(room.isRecording); } catch (error) { console.log("There was an error connecting to the room:", error.message); @@ -126,6 +128,7 @@ document.addEventListener("DOMContentLoaded", async function () { generateFormValues(); } + // Remove recording video when the dialog is closed document.getElementById("recording-video-dialog").addEventListener("close", () => { const recordingVideo = document.getElementById("recording-video"); recordingVideo.src = ""; diff --git a/advanced-features/openvidu-recording-node/src/index.js b/advanced-features/openvidu-recording-node/src/index.js index 2aca4f03..f12435b4 100644 --- a/advanced-features/openvidu-recording-node/src/index.js +++ b/advanced-features/openvidu-recording-node/src/index.js @@ -68,17 +68,20 @@ app.post("/recordings/start", async (req, res) => { const activeEgresses = await getActiveEgressesByRoom(roomName); + // Check if there is already an active egress for this room if (activeEgresses.length > 0) { res.status(409).json({ errorMessage: "Recording already started for this room" }); return; } + // Use the EncodedFileOutput to save the recording to an MP4 file const fileOutput = new EncodedFileOutput({ fileType: EncodedFileType.MP4, filepath: `${RECORDINGS_PATH}{room_name}-{room_id}-{time}` }); try { + // Start a RoomCompositeEgress to record all participants in the room const egressInfo = await egressClient.startRoomCompositeEgress(roomName, { file: fileOutput }); const recording = { name: egressInfo.fileResults[0].filename.split("/").pop(), @@ -101,6 +104,7 @@ app.post("/recordings/stop", async (req, res) => { const activeEgresses = await getActiveEgressesByRoom(roomName); + // Check if there is an active egress for this room if (activeEgresses.length === 0) { res.status(409).json({ errorMessage: "Recording not started for this room" }); return; @@ -109,6 +113,7 @@ app.post("/recordings/stop", async (req, res) => { const egressId = activeEgresses[0].egressId; try { + // Stop the Egress to finish the recording const egressInfo = await egressClient.stopEgress(egressId); const file = egressInfo.fileResults[0]; const recording = { @@ -125,6 +130,7 @@ app.post("/recordings/stop", async (req, res) => { const getActiveEgressesByRoom = async (roomName) => { try { + // List all active egresses for the room return await egressClient.listEgress({ roomName, active: true }); } catch (error) { console.error("Error listing egresses.", error); @@ -140,6 +146,8 @@ app.get("/recordings", async (req, res) => { const keyStart = RECORDINGS_PATH + (roomName ? `${roomName}` + (roomId ? `-${roomId}` : "") : ""); const keyEnd = ".mp4.json"; const regex = new RegExp(`^${keyStart}.*${keyEnd}$`); + + // List all Egress metadata files in the recordings path that match the regex const payloadKeys = await s3Service.listObjects(RECORDINGS_PATH, regex); const recordings = await Promise.all(payloadKeys.map((payloadKey) => getRecordingInfo(payloadKey))); res.json({ recordings }); @@ -150,8 +158,10 @@ app.get("/recordings", async (req, res) => { }); const getRecordingInfo = async (payloadKey) => { + // Get the Egress metadata file as JSON const data = await s3Service.getObjectAsJson(payloadKey); + // Get the recording file size const recordingKey = payloadKey.replace(".json", ""); const size = await s3Service.getObjectSize(recordingKey); @@ -175,10 +185,15 @@ app.get("/recordings/:recordingName", async (req, res) => { } try { + // Get the recording file from S3 const { body, size } = await s3Service.getObject(key); + + // Set the response headers res.setHeader("Content-Type", "video/mp4"); res.setHeader("Content-Length", size); res.setHeader("Accept-Ranges", "bytes"); + + // Pipe the recording file to the response body.pipe(res).on("finish", () => res.end()); } catch (error) { console.error("Error getting recording.", error); @@ -197,6 +212,7 @@ app.delete("/recordings/:recordingName", async (req, res) => { } try { + // Delete the recording file and metadata file from S3 await Promise.all([s3Service.deleteObject(key), s3Service.deleteObject(`${key}.json`)]); res.json({ message: "Recording deleted" }); } catch (error) { diff --git a/advanced-features/openvidu-recording-node/src/s3.service.js b/advanced-features/openvidu-recording-node/src/s3.service.js index 52f1f239..f8424552 100644 --- a/advanced-features/openvidu-recording-node/src/s3.service.js +++ b/advanced-features/openvidu-recording-node/src/s3.service.js @@ -81,6 +81,8 @@ export class S3Service { }; const command = new ListObjectsV2Command(params); const { Contents: objects } = await this.run(command); + + // Filter objects by regex and return the keys return ( objects ?.filter((object) => regex.test(object.Key))