Skip to content
Open
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
52 changes: 52 additions & 0 deletions examples/demo/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import dotenv from 'dotenv';
import express from 'express';
import type { Express } from 'express';
import { EgressClient, EncodedFileOutput, S3Upload } from 'livekit-server-sdk';

dotenv.config({ path: ".env.local" });
const LIVEKIT_API_KEY = process.env.LIVEKIT_API_KEY;
const LIVEKIT_API_SECRET = process.env.LIVEKIT_API_SECRET;
const LIVEKIT_URL = process.env.LIVEKIT_URL;
const S3_ACCESS_KEY = process.env.S3_ACCESS_KEY;
const S3_SECRET = process.env.S3_SECRET;
const S3_BUCKET = process.env.S3_BUCKET;
const S3_ENDPOINT = process.env.S3_ENDPOINT;

const app = express();
app.use(express.json());

app.post("/api/start-recording", async (req, res) => {
const { roomName } = req.body;
// Upload the recording to S3 compatible storage
const fileOutput = new EncodedFileOutput({
filepath: "recording.mp4",
output: {
case: "s3",
value: new S3Upload({
accessKey: S3_ACCESS_KEY,
secret: S3_SECRET,
bucket: S3_BUCKET,
forcePathStyle: true,
endpoint: S3_ENDPOINT
}),
}
});
if(!LIVEKIT_URL) {
res.status(500).json({ error: "Server misconfigured" });
return;
}
const egressClient = new EgressClient(LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
const egressInfo = await egressClient.startRoomCompositeEgress(roomName, fileOutput);
const egressId = egressInfo.egressId;
res.json({
egressId: egressId
});
});

app.post("/api/stop-recording", async (req, res) => {
const { egressId } = req.body;
const egressClient = new EgressClient(LIVEKIT_URL!, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
await egressClient.stopEgress(egressId);
});

export const handler: Express = app;
41 changes: 41 additions & 0 deletions examples/demo/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ let currentRoom: Room | undefined;

let startTime: number;

let egressId: any;

const searchParams = new URLSearchParams(window.location.search);
const storedUrl = searchParams.get('url') ?? 'ws://localhost:7880';
const storedToken = searchParams.get('token') ?? '';
Expand Down Expand Up @@ -598,6 +600,38 @@ const appActions = {
});
}
},

toggleRecording: async () => {
if (!currentRoom)
return;
if (!currentRoom.isRecording) {
const roomName = currentRoom.name;
const response = await fetch("/api/start-recording", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ roomName })
});
if (!response.ok)
throw new Error("Failed to start recording!");
const data = await response.json();
egressId = data.egressId;
updateButtonsForPublishState();
}
else {
await fetch("/api/stop-recording", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ egressId })
});

updateButtonsForPublishState();
setButtonDisabled("start-recording-button", true);
}
}
};

declare global {
Expand Down Expand Up @@ -989,6 +1023,7 @@ function setButtonsForState(connected: boolean) {
'disconnect-room-button',
'flip-video-button',
'send-button',
'start-recording-button'
];
if (currentRoom && currentRoom.options.e2ee) {
connectedSet.push('toggle-e2ee-button', 'e2ee-ratchet-button');
Expand Down Expand Up @@ -1091,6 +1126,12 @@ function updateButtonsForPublishState() {
`${currentRoom.isE2EEEnabled ? 'Disable' : 'Enable'} E2EE`,
currentRoom.isE2EEEnabled,
);

setButtonState(
'start-recording-button',
`${currentRoom.isRecording ? 'Stop' : 'Start'} Recording`,
currentRoom.isRecording,
);
}

async function acquireDeviceList() {
Expand Down
8 changes: 8 additions & 0 deletions examples/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ <h2>Livekit Sample App</h2>
>
Open PiP
</button>
<button
id="start-recording-button"
class="btn btn-secondary mt-1"
disabled
type="button"
onclick="appActions.toggleRecording()">
Start Recording
</button>
<button
id="toggle-e2ee-button"
class="btn btn-secondary mt-1"
Expand Down
21 changes: 21 additions & 0 deletions examples/demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "livekit-demo",
"version": "1.0.0",
"description": "Example of Livekit Demo",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc & vite build"
},
"dependencies": {
"dotenv": "^16.5.0",
"express": "^5.1.0",
"livekit-server-sdk": "^2.12.0",
"vite": "^3.2.7",
"vite-plugin-mix": "^0.4.0"
},
"devDependencies": {
"@types/express": "^5.0.1",
"typescript": "^5.8.3"
}
}
10 changes: 10 additions & 0 deletions examples/demo/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'vite';
import mix from 'vite-plugin-mix';

export default defineConfig({
plugins: [
mix.default({
handler: './api.ts',
}),
],
});
Loading