numerous-whale-53652
09/18/2025, 1:10 PMhttps://${storageAccountName}.<http://blob.core.windows.net|blob.core.windows.net>,
sharedKeyCredential
);
const containerClient = blobServiceClient.getContainerClient(containerName);
// List all blobs that start with the room name pattern
const prefix = `livekit/record/${roomName}`;
const existingFiles: string[] = [];
for await (const blob of containerClient.listBlobsFlat({ prefix })) {
existingFiles.push(blob.name);
}
// Extract version numbers from existing files
const versions: number[] = [];
const basePattern = `livekit/record/${roomName}`;
for (const fileName of existingFiles) {
if (fileName === ${basePattern}.mp4) {
// This is the base file (no version number)
versions.push(0);
} else {
_// Try to extract version number from pattern: roomName_X.mp4_
_const match = fileName.match(new RegExp(^${basePattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}_([0-9]+)\\.mp4$));_
if (match) {
versions.push(parseInt(match[1], 10));
}
}
}
// Determine the next version number
let nextVersion = 0;
if (versions.length > 0) {
const maxVersion = Math.max(...versions);
nextVersion = maxVersion + 1;
}
// console.log(Room: ${roomName}, Found versions: [${versions.join(', ')}], Next version: ${nextVersion});
// Generate filename based on version
if (nextVersion === 0) {
return `livekit/record/${roomName}.mp4`;
} else if (nextVersion === 1) {
_return `livekit/record/${roomName}_1.mp4`;_
} else {
_return `livekit/record/${roomName}_${nextVersion}.mp4`;_
}
} catch (error) {
console.error('Error checking existing files:', error);
// Fallback to base filename if there's an error
return `livekit/record/${roomName}.mp4`;
}
}
*/
export async function *GET*(_req_: NextRequest) {
try {
const roomName = req.nextUrl.searchParams.get('roomName');
if (roomName === null) {
return new NextResponse('Missing roomName parameter', { status: 403 });
}
const {
LIVEKIT_API_KEY,
LIVEKIT_API_SECRET,
LIVEKIT_URL,
} = process.env;
// S3 configuration
const S3_BUCKET_NAME = 'XXXXXXXXXXXXX';
const S3_REGION = 'XXXXXXXXXXXXX';
// Debug logging
console.log(Attempting to start recording for room: ${roomName});
console.log(LiveKit URL: ${LIVEKIT_URL});
// Use the same URL conversion as the original working code
const hostURL = new URL(LIVEKIT_URL!);
hostURL.protocol = 'https:';
console.log(Egress URL: ${hostURL.origin});
const egressClient = new EgressClient(hostURL.origin, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
// Check if room exists first
try {
const roomClient = new RoomServiceClient(LIVEKIT_URL!, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
const rooms = await roomClient.listRooms([roomName]);
if (rooms.length === 0) {
console.log(Room ${roomName} does not exist);
return new NextResponse('Room does not exist. Please ensure participants have joined the room before starting recording.', { status: 404 });
}
console.log(Room ${roomName} found with ${rooms[0].numParticipants} participants);
} catch (roomError) {
console.error('Error checking room existence:', roomError);
// Continue anyway, let egress handle it
}
const existingEgresses = await egressClient.listEgress({ roomName });
if (existingEgresses.length > 0 && existingEgresses.some((e) => e.status < 2)) {
return new NextResponse('Meeting is already being recorded', { status: 409 });
}
// Generate simple filename for S3 upload
const fileName = `livekit/record/${roomName}.mp4`;
console.log(Generated filename: ${fileName});
const fileOutput = new EncodedFileOutput({
filepath: fileName,
output: {
case: 's3',
value: new S3Upload({
_// accessKey: AWS_ACCESS_KEY_ID!,_
_// secret: AWS_SECRET_ACCESS_KEY!,_
region: S3_REGION,
bucket: S3_BUCKET_NAME,
}),
},
});
console.log('File output configuration:', JSON.stringify({
filepath: fileName,
outputType: 's3',
region: S3_REGION,
bucket: S3_BUCKET_NAME
}, null, 2));
// Use the exact method signature from your original working code
console.log('Starting room composite egress...');
await egressClient.startRoomCompositeEgress(
roomName,
{
file: fileOutput,
},
{
layout: 'speaker',
},
);
console.log('Recording started successfully');
// Construct the URL of the saved file in S3
const fileUrl = `https://${S3_BUCKET_NAME}.s3.${S3_REGION}.amazonaws.com/${fileName}`;
console.log(Recording started. File will be saved to: ${fileUrl});
// save the meeting record
try {
const response = await fetch(${process.env.PYTHON_DISPATCHER_URL}/recordings, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
room_name: roomName,
recording_url: fileUrl,
}),
});
if (!response.ok) {
console.error(Failed to save recording link. Status: ${response.status});
}
} catch (saveError) {
console.error(Error saving recording link for room: ${roomName}, saveError);
}
return new NextResponse(null, { status: 200 });
} catch (error) {
if (error instanceof Error) {
console.error('Error starting recording:', error.message);
return new NextResponse(error.message, { status: 500 });
}
console.error('Unknown error starting recording:', error);
return new NextResponse('Internal server error', { status: 500 });
}
}tall-airline-16756
09/18/2025, 1:10 PMtall-airline-16756
09/18/2025, 1:11 PM