Discussions

Ask a Question
Back to All

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:

https://d-id-talks-prod.s3.us-west-2.amazonaws.com/auth0%7C6788f3f2e9af31fe0698596f/tlk_iAHllnBFkMvl9XRU3NCLi/1737363918777.mp4?AWSAccessKeyId=AKIA5CUMPJBIK65W6FGA&Expires=1737450324&Signature=eGLk%2Fa4nbEWi42ZXzbI3WiK7Kfg%3D

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?