Discussions
How to form the URL once I get response from the talks/streams?
I wrote the following cloud function: (The code cleaning part is yet to be done, please ignore all the commented codes) Maybe the API_KEY is already exipired but that is not the concern here.
const cors = require('cors')({ origin: true });
const axios = require('axios');
const functions = require('firebase-functions');
const DID_API_URL = 'https://api.d-id.com/talks/streams';
const DID_API_KEY = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik53ek53TmV1R3ptcFZTQjNVZ0J4ZyJ9.eyJodHRwczovL2QtaWQuY29tL2ZlYXR1cmVzIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9zdHJpcGVfcHJvZHVjdF9pZCI6IiIsImh0dHBzOi8vZC1pZC5jb20vc3RyaXBlX2N1c3RvbWVyX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9zdHJpcGVfcHJvZHVjdF9uYW1lIjoidHJpYWwiLCJodHRwczovL2QtaWQuY29tL3N0cmlwZV9zdWJzY3JpcHRpb25faWQiOiIiLCJodHRwczovL2QtaWQuY29tL3N0cmlwZV9iaWxsaW5nX2ludGVydmFsIjoibW9udGgiLCJodHRwczovL2QtaWQuY29tL3N0cmlwZV9wbGFuX2dyb3VwIjoiZGVpZC10cmlhbCIsImh0dHBzOi8vZC1pZC5jb20vc3RyaXBlX3ByaWNlX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9zdHJpcGVfcHJpY2VfY3JlZGl0cyI6IiIsImh0dHBzOi8vZC1pZC5jb20vY2hhdF9zdHJpcGVfc3Vic2NyaXB0aW9uX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9jaGF0X3N0cmlwZV9wcmljZV9jcmVkaXRzIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9jaGF0X3N0cmlwZV9wcmljZV9pZCI6IiIsImh0dHBzOi8vZC1pZC5jb20vcHJvdmlkZXIiOiJnb29nbGUtb2F1dGgyIiwiaHR0cHM6Ly9kLWlkLmNvbS9pc19uZXciOmZhbHNlLCJodHRwczovL2QtaWQuY29tL2FwaV9rZXlfbW9kaWZpZWRfYXQiOiIyMDI1LTAxLTE0VDE3OjE5OjQ1LjExMVoiLCJodHRwczovL2QtaWQuY29tL29yZ19pZCI6IiIsImh0dHBzOi8vZC1pZC5jb20vYXBwc192aXNpdGVkIjpbIlN0dWRpbyJdLCJodHRwczovL2QtaWQuY29tL2N4X2xvZ2ljX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9jcmVhdGlvbl90aW1lc3RhbXAiOiIyMDI1LTAxLTE0VDExOjAzOjMzLjIxNloiLCJodHRwczovL2QtaWQuY29tL2FwaV9nYXRld2F5X2tleV9pZCI6Imdtb3hzZnMyMzQiLCJodHRwczovL2QtaWQuY29tL3VzYWdlX2lkZW50aWZpZXJfa2V5IjoibHpOdGtFMWE2QmJzNzQySEt2VkVHIiwiaHR0cHM6Ly9kLWlkLmNvbS9oYXNoX2tleSI6IjFXc3VYQTFfWC1mb1IwNGl2TkhtSSIsImh0dHBzOi8vZC1pZC5jb20vcHJpbWFyeSI6dHJ1ZSwiaHR0cHM6Ly9kLWlkLmNvbS9lbWFpbCI6Im1vbmEuZW50d2lja2x1bmdAZ21haWwuY29tIiwiaHR0cHM6Ly9kLWlkLmNvbS9jb3VudHJ5X2NvZGUiOiJERSIsImh0dHBzOi8vZC1pZC5jb20vcGF5bWVudF9wcm92aWRlciI6InN0cmlwZSIsImlzcyI6Imh0dHBzOi8vYXV0aC5kLWlkLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwNjcwMTU1MDUxMjM2NDI4NjkyOSIsImF1ZCI6WyJodHRwczovL2QtaWQudXMuYXV0aDAuY29tL2FwaS92Mi8iLCJodHRwczovL2QtaWQudXMuYXV0aDAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTczNzEwNDc2MCwiZXhwIjoxNzM3MTkxMTYwLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHJlYWQ6Y3VycmVudF91c2VyIHVwZGF0ZTpjdXJyZW50X3VzZXJfbWV0YWRhdGEgb2ZmbGluZV9hY2Nlc3MiLCJhenAiOiJHenJOSTFPcmU5Rk0zRWVEUmYzbTN6M1RTdzBKbFJZcSJ9.s-gocYPoorHfbGtIA-BcESPN0hGbe_IfvvH1N96aIFSZPTBRb2F8LdMeEhl5yBNoCSmrHCCEHohU_cl4A43CJYgaQ1FiBlvigTjQUV5LQpfHS2qg_kVbGC9Cv4ijxM_VPf2cF0MMJPDDArGuolc1tQ23LLJeWWzyN2YqCPIpNWl22JHYOGyjxok35HZW8Qfmx3E2aHnwHkFF4MBa0O2QhN-Igy0wBoVxxEWbJJbsVBpcBBqrQULHFXXznKnX4TEtvtRAk0kJhy2A3ed11OuZ4jplhfcBNmrqmhsk9ozDXQBraD5pcXwqfjJukv6mtm11lioXsTbWo1SP6sos2dS4Zw';
let startStreamResponse;
let talkResponse;
exports.dIdTextToVideoStream = functions.https.onRequest((req, res) => {
cors(req, res, async() => {
if (req.method !== 'POST') {
return res.status(405).send({ message: 'Only POST method is allowed.' });
}
const { imageUrl, text } = req.body;
console.log("imageUrl received from req.body", imageUrl);
console.log("text received from req.body", text);
if (!imageUrl || !text) {
return res.status(400).send({ message: 'Image URL and text are required.' });
}
console.log("Successfully received the imageUrl and text:");
console.log("imageUrl", imageUrl);
console.log("text", text);
const headers = {
accept: 'application/json',
'content-type': 'application/json',
authorization: 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik53ek53TmV1R3ptcFZTQjNVZ0J4ZyJ9.eyJodHRwczovL2QtaWQuY29tL2ZlYXR1cmVzIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9zdHJpcGVfcHJvZHVjdF9pZCI6IiIsImh0dHBzOi8vZC1pZC5jb20vc3RyaXBlX2N1c3RvbWVyX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9zdHJpcGVfcHJvZHVjdF9uYW1lIjoidHJpYWwiLCJodHRwczovL2QtaWQuY29tL3N0cmlwZV9zdWJzY3JpcHRpb25faWQiOiIiLCJodHRwczovL2QtaWQuY29tL3N0cmlwZV9iaWxsaW5nX2ludGVydmFsIjoibW9udGgiLCJodHRwczovL2QtaWQuY29tL3N0cmlwZV9wbGFuX2dyb3VwIjoiZGVpZC10cmlhbCIsImh0dHBzOi8vZC1pZC5jb20vc3RyaXBlX3ByaWNlX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9zdHJpcGVfcHJpY2VfY3JlZGl0cyI6IiIsImh0dHBzOi8vZC1pZC5jb20vY2hhdF9zdHJpcGVfc3Vic2NyaXB0aW9uX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9jaGF0X3N0cmlwZV9wcmljZV9jcmVkaXRzIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9jaGF0X3N0cmlwZV9wcmljZV9pZCI6IiIsImh0dHBzOi8vZC1pZC5jb20vcHJvdmlkZXIiOiJnb29nbGUtb2F1dGgyIiwiaHR0cHM6Ly9kLWlkLmNvbS9pc19uZXciOmZhbHNlLCJodHRwczovL2QtaWQuY29tL2FwaV9rZXlfbW9kaWZpZWRfYXQiOiIyMDI1LTAxLTE0VDE3OjE5OjQ1LjExMVoiLCJodHRwczovL2QtaWQuY29tL29yZ19pZCI6IiIsImh0dHBzOi8vZC1pZC5jb20vYXBwc192aXNpdGVkIjpbIlN0dWRpbyJdLCJodHRwczovL2QtaWQuY29tL2N4X2xvZ2ljX2lkIjoiIiwiaHR0cHM6Ly9kLWlkLmNvbS9jcmVhdGlvbl90aW1lc3RhbXAiOiIyMDI1LTAxLTE0VDExOjAzOjMzLjIxNloiLCJodHRwczovL2QtaWQuY29tL2FwaV9nYXRld2F5X2tleV9pZCI6Imdtb3hzZnMyMzQiLCJodHRwczovL2QtaWQuY29tL3VzYWdlX2lkZW50aWZpZXJfa2V5IjoibHpOdGtFMWE2QmJzNzQySEt2VkVHIiwiaHR0cHM6Ly9kLWlkLmNvbS9oYXNoX2tleSI6IjFXc3VYQTFfWC1mb1IwNGl2TkhtSSIsImh0dHBzOi8vZC1pZC5jb20vcHJpbWFyeSI6dHJ1ZSwiaHR0cHM6Ly9kLWlkLmNvbS9lbWFpbCI6Im1vbmEuZW50d2lja2x1bmdAZ21haWwuY29tIiwiaHR0cHM6Ly9kLWlkLmNvbS9jb3VudHJ5X2NvZGUiOiJERSIsImh0dHBzOi8vZC1pZC5jb20vcGF5bWVudF9wcm92aWRlciI6InN0cmlwZSIsImlzcyI6Imh0dHBzOi8vYXV0aC5kLWlkLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwNjcwMTU1MDUxMjM2NDI4NjkyOSIsImF1ZCI6WyJodHRwczovL2QtaWQudXMuYXV0aDAuY29tL2FwaS92Mi8iLCJodHRwczovL2QtaWQudXMuYXV0aDAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTczNzEwNzQ3NCwiZXhwIjoxNzM3MTkzODc0LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHJlYWQ6Y3VycmVudF91c2VyIHVwZGF0ZTpjdXJyZW50X3VzZXJfbWV0YWRhdGEgb2ZmbGluZV9hY2Nlc3MiLCJhenAiOiJHenJOSTFPcmU5Rk0zRWVEUmYzbTN6M1RTdzBKbFJZcSJ9.Og6H9fjKzIjhZOCVoPtUotJ6Rf1flR9_FQx1ooSCj6wkfiNHYVApV3TcnvM2I1rxrPQJklNnkARFz7bSzK2eiDu88U00Ob9AyBTPBTTbKpv4Yhn4GIVMUBzbzeb1fUQAINIamjKdx-PVpQVqw0JkSr1KiH3ue9IffVMS2ESIGpDNSFVyu6kBHI3Jk9BegSruPUZCVHHg90VKGm00Y5hq4dEMQXyfXJAliL7ezu35b448UGObZ9a44bGYsUZesAbMh-awZdm2gHr3pMdE7smNdHPrqYkZ36X-Df-grS6jeJR3hlzPm-ZKMqPa0UL1zC1-8hB7lDDgKHm85od3uma_Yg'
};
const options = {
method: 'POST',
url: 'https://api.d-id.com/talks/streams',
headers: headers,
data: {
stream_warmup: 'false',
compatibility_mode: 'on',
source_url: imageUrl
}
};
try {
// Step 1: Request to start a new stream
console.log("will initiate the stream response now..............................");
await axios
.request(options)
.then(res => {
//console.log("this is the response that I get from the stream", res);
startStreamResponse = res.data; // Assign res.data to startStreamResponse
//console.log(startStreamResponse); // Optionally, let's log it
})
.catch(err => console.error("Unable to get the startStreamResponse", err));
//console.log("Step 01: Verifying if the streamResponse has been received succcessfully", startStreamResponse);
const { id: streamId, offer, ice_servers: iceServers, session_id: sessionId } = await startStreamResponse;
console.log("streamId", streamId);
console.log("Session Id", sessionId);
console.log("Step 1 is done completely. Now let's process Step 2....");
// Step 2: Create WebRTC session and generate answer (server-side WebRTC)
const { RTCPeerConnection } = require('wrtc');
const peerConnection = new RTCPeerConnection({ iceServers });
await peerConnection.setRemoteDescription(offer);
console.log("set remote SDP Ok");
const answer = await peerConnection.createAnswer();
console.log("create local SDP Ok");
//debug the both sdp
console.log(offer.sdp); // Log the SDP to see the offered codecs and parameters
console.log(answer.sdp);// Log the answer SDP to verify what you're sending back
await peerConnection.setLocalDescription(answer);
console.log("set local SDP Ok");
const webRTCoptions = {
method: 'POST',
url: `https://api.d-id.com/talks/streams/${streamId}/sdp`,
headers: headers,
data: {session_id: sessionId, answer: {type: 'answer', sdp: answer.sdp}}
};
await axios
.request(webRTCoptions)
.then(res => {
//console.log("this is the response that I get from the stream", res);
// startStreamResponse = res.data; // Assign res.data to startStreamResponse
console.log(res.data); // Optionally, let's log it
})
.catch(err => console.error("Unable to establish the peer connection", err));
console.log("The process of Step 2 which establishing a peer connection is done. Let's start Step 3 now");
const talkPayload = {
method: 'POST',
url: `https://api.d-id.com/talks/streams/${streamId}`,
headers: headers,
data: {
script: {type: 'text', input: text, provider: {type: 'microsoft', voice_id: 'en-US-ChristopherNeural'}, ssml: 'false'},
config: {fluent: true, pad_audio: '0.0', result_format: 'mp4', stitch: true},
audio_optimization: '2',
session_id: sessionId
}
};
await axios
.request(talkPayload)
.then(res => {
//console.log("this is the response that I get from the stream", res);
talkResponse = res.data; // Assign res.data to startStreamResponse
console.log(res.data); // Optionally, let's log it
})
.catch(err => console.error("Unable to request for talks/streams", err));
const result = await talkResponse;
return res.status(200).send({
result_url: result //use this information in the frontend recruiting. You will find both the video and the text here
});
} catch (error) {
console.error('Error handling stream:', error);
res.status(500).send({ message: 'Internal server error', error: error.message });
}
})
})
I get the status and the video_id as the response: { status: 'started', video_id: 'tlk_RkaRtdG3ZOkMwQi-bLfSc' }
The first question is, am I getting the correct response? If yes, then what should I do with the video_id? Do I need to make another request like "Get a specific talk"?
Also, from "Get a specific talk", I receive the request_url successfully:
But this URL is not directly usable in the HTML elements and if I approach this solution where I intend to download the video in the firebase storage:
async downloadAndUploadVideo(url: string): Promise {
if (!url) {
throw new Error('Please provide a valid URL.');
}
const fileName = `didVideos/${new Date().getTime()}_video.mp4`;
const fileRef = this.storage.ref(fileName);
// Fetch the video as a Blob
const videoBlob = await this.http.get(url, { responseType: 'blob' }).toPromise();
// Upload the Blob to Firebase Storage
const uploadTask = this.storage.upload(fileName, videoBlob);
return new Promise((resolve, reject) => {
uploadTask.snapshotChanges().pipe(
finalize(async () => {
try {
const downloadURL = await fileRef.getDownloadURL().toPromise();
resolve(downloadURL); // Return the public URL of the uploaded video
} catch (error) {
reject(error);
}
})
).subscribe();
});
}
executing this line: const videoBlob = await this.http.get(url, { responseType: 'blob' }).toPromise(); fails and ends with an error. What is that I am doing wrong here?