Clubhouse своими руками: для iOS, Android, Web и даже Unity

Kate

Administrator
Команда форума
Меньше чем за год новая социальная сеть Clubhouse набрала больше 6 миллионов участников — и всё продолжает расти. В чём же секрет такой популярности?
Clubhouse — принципиально новый формат социальных сетей, такого ещё не было. Во время пандемии людям стало не хватать живого общения, поэтому в Clubhouse всё общение происходит голосом, через так называемые комнаты. Никаких привычных нам сообщений и диалогов. Всё общение происходит в реальном времени.
Комнаты могут быть закрытыми “для своих” или открытыми для всех желающих. Создатель (модератор) комнаты сам выбирает, кто может говорить, а присоединившиеся участники могут поднять руку, чтобы выразить желание что-то сказать, и если модератор захочет, включит желающему микрофон.
Однако попасть в Clubhouse могут не все. В настоящий момент официальное приложение доступно лишь владельцам айфонов, а зарегистрироваться можно только по приглашению от уже зарегистрированного пользователя. Но что, если у меня андроид? Или я хочу сидеть в соцсетях с компьютера?
Можно, конечно, терпеливо ждать, когда они решат выпустить версию для андроидов и десктопную версию, а можно поступить креативно – написать свой Clubhouse, с блэкджеком записью разговоров и поддержкой любых платформ. А Voximplant поможет с этим.
В этой статье я покажу, как можно создать комнату-конференцию наподобие Clubhouse для Web SDK, в качестве клиента я буду использовать браузер.
Обратите внимание, что Voximplant поддерживает множество SDK, начиная с Web, iOS, Android и заканчивая Flutter, React Native и даже Unity. То есть если вы пишете игру на Unity, есть возможность встроить аудио- или даже видеочат прямо в неё.

Настройки VoxEngine​

Создаём комнату. Заходим в свой аккаунт Voximplant (или регистрируем новый) и создаём там приложение, нового пользователя для авторизации и начинаем писать сценарий.
Сначала подключим модуль конференций Voximplant и обработаем отключение участников:
require(Modules.Conference)
let owner = null;
const onEndpointDisconnected = (event)=>{
const members = conference.getList();
if(members.length === 1){
VoxEngine.terminate();
return;
}
}
Затем обработаем проверку разрешений:
const checkPermissions = ({call,headers}) =>{
return new Promise((resolve)=>{
setTimeout(()=>{resolve(true)},500);
});
}
Создание новой конференции и подключение нового участника к комнате:
let logURL = ''; // for debug reason
let conference = null;
VoxEngine.addEventListener(AppEvents.Started, event => {
logURL = event.logURL;
conference = VoxEngine.createConference({hd_audio:true});
})
VoxEngine.addEventListener(AppEvents.CallAlerting, async event => {
const permissions = await checkPermissions(event);
if(permissions) {
event.call.addEventListener(CallEvents.Disconnected, onEndpointDisconnected);
event.call.answer();
conference.add({
call: event.call,
displayName: event.headers['X-Name'],
mode: "FORWARD",
direction: "BOTH",
scheme: event.scheme
});
if(conference.getList().length === 1){
owner = conference.getList()[0].id();
conference.getList()[0].getCall().sendMessage('owner');
}
conference.get(owner).getCall()
.sendMessage(conference.getList().length);)
} else {
event.call.hangup({'X-Reason':'DENIED'});
}
});
На этом с настройкой VoxEngine всё.

Настройка клиента​

Далее перейдём к клиентской части. Мы используем Web SDK, так что клиент будет представлять собой HTML-страницу. Кнопки управления конференцией скроем до инициализации SDK:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.hidden {
display: none !important;
}
</style>
<meta charset="UTF-8">
<title>The demo</title>
</head>
<body>
<div id="btns" class="hidden">
<p id="myname">Avi</p>
<button id="viewer">Join as listener</button>
<button id="speaker">Join as speaker</button>
<button id="leave" disabled>Leave</button>
</div>
<div id="audio"></div>
<h3>Current speakers <span id="countSpeakers">0</span></h3>
<div id="endpoints"></div>
</body>
<script src="*****"></script>
Далее самое интересное. Пишем сценарий в теге <script>. Для начала инициализируем наш SDK, авторизуемся и отобразим кнопки:
<script>
// sdk init
const sdk = VoxImplant.getInstance();
let user = 'user*****';
const init = async () => {
await sdk.init({ showDebugInfo: true, serverIp: 'url*****' });
await sdk.connect();
await sdk.login(`${user}@app**.acc**.voximplant.com`, 'pass*****');
}
init().then(() => {
document.getElementById('btns').classList.remove('hidden');
});
Задаём нужные константы для конференции. Имя пользователя для участника комнаты я возьму из тега с id=”myname”. Затем определим, когда видны определённые кнопки:
let currentCall;
let currentRole;
let countSpeakers = 0;
const confNumber = 'Test room';
const speakerBtn = document.getElementById('speaker');
const viewerBtn = document.getElementById('viewer');
const leaveBtn = document.getElementById('leave');
document.getElementById('myname').innerText = myName;
let setRole = (role) => {
currentRole = role;
if(role === 'speaker') {
speakerBtn.disabled = true;
leaveBtn.disabled = false;
viewerBtn.disabled = false;
}
if(role === 'viewer') {
speakerBtn.disabled = false;
leaveBtn.disabled = false;
viewerBtn.disabled = true;
}
if(role === 'start') {
speakerBtn.disabled = false;
viewerBtn.disabled = false;
leaveBtn.disabled = true;
}
}
Обработаем завершение конференции:
let endCall = () => {
if(currentCall && currentCall.state() !== 'ENDED') {
document.getElementById('endpoints').innerText = '';
currentCall.hangup();
setRole('start');
}
}
Обработаем добавление нового участника в комнату и удаление из нее:
let onEndpointAdded = (e) => {
console.warn('Endpoint added', e.endpoint.id);
const nameTable = document.getElementById('endpoints');
let p = document.createElement('p');
p.id = e.endpoint.id;
p.innerText = `Name: ${e.endpoint.displayName}, id: ${e.endpoint.id}`;
nameTable.append(p);
document.getElementById('countSpeakers').innerText = countSpeakers + 1;
e.endpoint.addEventListener(VoxImplant.EndpointEvents.RemoteMediaAdded, (ev)=> {
console.warn('RemoteMediaAdded', ev.mediaRenderer);
const nodeCall = document.getElementById('audio');
ev.mediaRenderer.render(nodeCall);
})
e.endpoint.addEventListener(VoxImplant.EndpointEvents.RemoteMediaRemoved, (ev)=>{
console.warn(`Endpoint ${e.endpoint.id} media removed ${ev.mediaRenderer}`);
})
// ENDPOINT REMOVED
e.endpoint.addEventListener(VoxImplant.EndpointEvents.Removed, (ev)=>{
console.warn(`Endpoint ${e.endpoint.id} removed`);
let removeP = document.getElementById(e.endpoint.id);
nameTable.removeChild(removeP);
})
}
Обрабатываем действие по кнопке выхода из комнаты и другие события:
const setCall = () => {
leaveBtn.onclick = endCall;
currentCall.addEventListener(VoxImplant.CallEvents.EndpointAdded, onEndpointAdded);
currentCall.addEventListener(VoxImplant.CallEvents.MessageReceived, (e) => {
console.warn('MessageReceived', e.text);
});
//handle connection
currentCall.addEventListener(VoxImplant.CallEvents.Connected, () => {
console.warn(`Call connected successfully`);
});
//other call event listeners
currentCall.addEventListener(VoxImplant.CallEvents.Disconnected, () => {
console.warn(`Call disconnected`);
endCall();
});
currentCall.addEventListener(VoxImplant.CallEvents.Failed, (e) => {
console.warn(`Call failed`);
endCall();
});
}
И напоследок обработаем кнопки, которые задают роль участника комнаты (говорящий или слушатель):
speakerBtn.onclick = async () => {
document.getElementById('endpoints').innerText = '';
if(currentCall) {
document.getElementById('endpoints').innerText = '';
await currentCall.hangup();
}
setTimeout(() => {
setRole('speaker');
currentCall = sdk.callConference({
number: confNumber,
extraHeaders: {'X-Name': myName}
});
setCall();
}, 300)
}
viewerBtn.onclick = async () => {
if(currentCall) {
document.getElementById('endpoints').innerText = '';
await currentCall.hangup();
}
setTimeout(() => {
setRole('viewer');
currentCall = sdk.joinAsViewer(confNumber);
setCall();
}, 300)
}
</script>
</html>
Готово. На выходе получаем комнату-аудиоконференцию, в которой участники могут быть говорящими или просто слушателями, управление ролями происходит с помощью кнопок, а ниже виден список говорящих с их именами и ID.
Остаётся только сделать базу данных пользователей и комнат, красивый выделяющийся интерфейс — и ваш собственный конкурент Clubhouse для любой платформы готов!

Источник статьи: https://habr.com/ru/company/Voximplant/blog/545090/
 
Сверху