Skip to content

Commit

Permalink
[mirotalksfu] - fix avatars and voices
Browse files Browse the repository at this point in the history
  • Loading branch information
miroslavpejic85 committed Nov 18, 2024
1 parent 0dd7a8a commit 54e706f
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 94 deletions.
10 changes: 5 additions & 5 deletions app/src/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ dev dependencies: {
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.6.38
* @version 1.6.39
*
*/

Expand Down Expand Up @@ -2246,13 +2246,13 @@ function startServer() {
}
});

// https://docs.heygen.com/reference/avatar-list
// https://docs.heygen.com/reference/list-avatars-v2
socket.on('getAvatarList', async ({}, cb) => {
if (!config.videoAI.enabled || !config.videoAI.apiKey)
return cb({ error: 'Video AI seems disabled, try later!' });

try {
const response = await axios.get(`${config.videoAI.basePath}/v1/avatar.list`, {
const response = await axios.get(`${config.videoAI.basePath}/v2/avatars`, {
headers: {
'Content-Type': 'application/json',
'X-Api-Key': config.videoAI.apiKey,
Expand All @@ -2270,13 +2270,13 @@ function startServer() {
}
});

// https://docs.heygen.com/reference/get-voices
// https://docs.heygen.com/reference/list-voices-v2
socket.on('getVoiceList', async ({}, cb) => {
if (!config.videoAI.enabled || !config.videoAI.apiKey)
return cb({ error: 'Video AI seems disabled, try later!' });

try {
const response = await axios.get(`${config.videoAI.basePath}/v1/voice.list`, {
const response = await axios.get(`${config.videoAI.basePath}/v2/voices`, {
headers: {
'Content-Type': 'application/json',
'X-Api-Key': config.videoAI.apiKey,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
"version": "1.6.38",
"version": "1.6.39",
"description": "WebRTC SFU browser-based video calls",
"main": "Server.js",
"scripts": {
Expand Down Expand Up @@ -79,7 +79,7 @@
"ngrok": "^5.0.0-beta.2",
"nodemailer": "^6.9.16",
"openai": "^4.72.0",
"qs": "6.13.0",
"qs": "6.13.1",
"socket.io": "4.8.1",
"swagger-ui-express": "5.0.1",
"uuid": "11.0.3"
Expand Down
4 changes: 2 additions & 2 deletions public/js/Room.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.6.38
* @version 1.6.39
*
*/

Expand Down Expand Up @@ -4616,7 +4616,7 @@ function showAbout() {
imageUrl: image.about,
customClass: { image: 'img-about' },
position: 'center',
title: 'WebRTC SFU v1.6.38',
title: 'WebRTC SFU v1.6.39',
html: `
<br />
<div id="about">
Expand Down
172 changes: 87 additions & 85 deletions public/js/RoomClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.6.38
* @version 1.6.39
*
*/

Expand Down Expand Up @@ -8330,75 +8330,65 @@ class RoomClient {

//console.log('AVATARS LISTS', completion.response.avatars);
completion.response.avatars.forEach((avatar) => {
avatar.avatar_states.forEach((avatarUi) => {
if (
!excludedIds.includes(avatarUi.id) &&
(showFreeAvatars ? freeAvatars.includes(avatarUi.pose_name) : true)
) {
const div = document.createElement('div');
div.style.float = 'left';
div.style.padding = '5px';
div.style.width = '100px';
div.style.height = '200px';
const img = document.createElement('img');
const hr = document.createElement('hr');
const label = document.createElement('label');
const textContent = document.createTextNode(avatarUi.pose_name);
label.appendChild(textContent);
//label.style.fontSize = '12px';
img.setAttribute('id', avatarUi.id);
img.setAttribute('class', 'avatarImg');
img.setAttribute('src', avatarUi.normal_thumbnail_medium);
img.setAttribute('width', '100%');
img.setAttribute('height', 'auto');
img.setAttribute('alt', avatarUi.pose_name);
img.setAttribute('style', 'cursor:pointer; padding: 2px; border-radius: 5px;');
img.setAttribute(
'avatarData',
avatarUi.id +
'|' +
avatar.name +
'|' +
avatarUi.default_voice.free.voice_id +
'|' +
avatarUi.video_url.grey,
);
img.onclick = () => {
const avatarImages = document.querySelectorAll('.avatarImg');
avatarImages.forEach((image) => {
image.style.border = 'none';
});
img.style.border = 'var(--border)';
const avatarData = img.getAttribute('avatarData');
const avatarDataArr = avatarData.split('|');
VideoAI.avatarId = avatarDataArr[0];
VideoAI.avatarName = avatarDataArr[1];
//VideoAI.avatarVoice = avatarDataArr[2] ? avatarDataArr[2] : ''; use the default one

avatarVideoAIPreview.setAttribute('src', avatarUi.video_url.grey);
avatarVideoAIPreview.play();

console.log('Avatar image click event', {
avatar,
avatarUi,
avatarDataArr,
});
};
div.append(img);
div.append(hr);
div.append(label);
avatarVideoAIcontainer.append(div);

// Show the first available free avatar
if (showFreeAvatars && avatarUi.pose_name === 'Kristin in Black Suit') {
avatarVideoAIPreview.setAttribute('src', avatarUi.video_url.grey);
avatarVideoAIPreview.playsInline = true;
avatarVideoAIPreview.autoplay = true;
avatarVideoAIPreview.controls = true;
avatarVideoAIPreview.volume = 0.5;
}
if (
!excludedIds.includes(avatar.avatar_id) &&
(showFreeAvatars ? freeAvatars.includes(avatar.avatar_name) : true)
) {
const div = document.createElement('div');
div.style.float = 'left';
div.style.padding = '5px';
div.style.width = '100px';
div.style.height = '200px';
const img = document.createElement('img');
const hr = document.createElement('hr');
const label = document.createElement('label');
const textContent = document.createTextNode(avatar.avatar_name);
label.appendChild(textContent);
//label.style.fontSize = '12px';
img.setAttribute('id', avatar.avatar_id);
img.setAttribute('class', 'avatarImg');
img.setAttribute('src', avatar.preview_image_url);
img.setAttribute('width', '100%');
img.setAttribute('height', 'auto');
img.setAttribute('alt', avatar.avatar_name);
img.setAttribute('style', 'cursor:pointer; padding: 2px; border-radius: 5px;');
img.setAttribute(
'avatarData',
avatar.avatar_id + '|' + avatar.avatar_name + '|' + avatar.preview_video_url,
);
img.onclick = () => {
const avatarImages = document.querySelectorAll('.avatarImg');
avatarImages.forEach((image) => {
image.style.border = 'none';
});
img.style.border = 'var(--border)';
const avatarData = img.getAttribute('avatarData');
const avatarDataArr = avatarData.split('|');
VideoAI.avatarId = avatarDataArr[0];
VideoAI.avatarName = avatarDataArr[1];

avatarVideoAIPreview.setAttribute('src', avatarDataArr[2]);
avatarVideoAIPreview.play();

console.log('Avatar image click event', {
avatar,
avatarDataArr,
});
};
div.append(img);
div.append(hr);
div.append(label);
avatarVideoAIcontainer.append(div);

// Show the first available free avatar
if (showFreeAvatars && avatar.avatar_name === 'Kristin in Black Suit') {
avatarVideoAIPreview.setAttribute('src', avatar.preview_video_url);
avatarVideoAIPreview.playsInline = true;
avatarVideoAIPreview.autoplay = true;
avatarVideoAIPreview.controls = true;
avatarVideoAIPreview.volume = 0.5;
}
});
}
});
})
.catch((err) => {
Expand All @@ -8413,40 +8403,52 @@ class RoomClient {
getVoiceList() {
this.socket
.request('getVoiceList')
.then(function (completion) {
.then((completion) => {
//console.log('VOICES LISTS', completion.response.voices);

// Ensure the response has the list of voices
const voiceList = completion?.response?.voices ?? [];
if (!voiceList.length) {
console.warn('No voices available in the response');
return;
}

const selectElement = document.getElementById('avatarVoiceIDs');
selectElement.innerHTML = '<option value="">Select Avatar Voice</option>'; // Reset options with default

// Sort the list alphabetically by language
const sortedList = completion.response.list.sort((a, b) => a.language.localeCompare(b.language));
const sortedList = voiceList.sort((a, b) => (a.language ?? '').localeCompare(b.language ?? ''));

sortedList.forEach((flag) => {
// console.log('flag', flag);
const { is_paid, voice_id, language, display_name, gender } = flag;
if (showFreeAvatars ? is_paid == false : true) {
// Populate the select element with options
sortedList.forEach((voice) => {
const { is_paid, voice_id, language, display_name, gender } = voice;
if (showFreeAvatars ? !is_paid : true) {
const option = document.createElement('option');
option.value = voice_id;
option.text = `${language}, ${display_name} (${gender})`; // You can customize the display text
option.textContent = `${language ?? 'Unknown'}, ${display_name ?? 'Unnamed'} (${gender ?? 'N/A'})`;
selectElement.appendChild(option);
}
});

// Event listener for changes on select element
// Event listener for changes on the select element
selectElement.addEventListener('change', (event) => {
const selectedVoiceID = event.target.value;
const selectedPreviewURL = completion.response.list.find(
(flag) => flag.voice_id === selectedVoiceID,
)?.preview?.movio;
VideoAI.avatarVoice = selectedVoiceID ? selectedVoiceID : null;
if (selectedPreviewURL) {
const selectedVoice = voiceList.find((voice) => voice.voice_id === selectedVoiceID);

VideoAI.avatarVoice = selectedVoiceID || null;

const previewAudioURL = selectedVoice?.preview_audio;
if (previewAudioURL) {
const avatarPreviewAudio = document.getElementById('avatarPreviewAudio');
avatarPreviewAudio.src = selectedPreviewURL;
avatarPreviewAudio.play();
avatarPreviewAudio.src = previewAudioURL;
avatarPreviewAudio.play().catch((err) => {
console.error('Error playing preview audio:', err);
});
}
});
})
.catch((err) => {
console.error('Video AI getVoiceList error:', err);
console.error('Video AI getVoiceList error', err);
});
}

Expand Down

0 comments on commit 54e706f

Please sign in to comment.