Files
mirotalk/server.js
T
2021-01-12 21:30:42 +01:00

245 lines
9.2 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
http://patorjk.com/software/taag/#p=display&f=ANSI%20Regular&t=Server
███████ ███████ ██████  ██  ██ ███████ ██████ 
██      ██      ██   ██ ██  ██ ██      ██   ██ 
███████ █████  ██████  ██  ██ █████  ██████  
     ██ ██     ██   ██  ██  ██  ██     ██   ██ 
███████ ███████ ██  ██   ████   ███████ ██  ██                 
*/
require("dotenv").config();
const express = require("express");
const path = require("path");
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server().listen(server);
const ngrok = require("ngrok");
var ngrokEnabled = process.env.NGROK_ENABLED;
var ngrokAuthToken = process.env.NGROK_AUTH_TOKEN;
var turnUrls = process.env.TURN_URLS;
var turnUsername = process.env.TURN_USERNAME;
var turnCredential = process.env.TURN_PASSWORD;
// use all static files from the www folder
app.use(express.static(path.join(__dirname, "www")));
// =====================================================
// expose server to external with https tunnel using ngrok
// =====================================================
async function ngrokstart() {
try {
await ngrok.authtoken(ngrokAuthToken);
await ngrok.connect(PORT);
const api = ngrok.getApi();
let data = await api.get("api/tunnels");
data = JSON.parse(data);
//console.log(data);
const pu0 = data.tunnels[0].public_url;
const pu1 = data.tunnels[1].public_url;
const tunelHttps = pu0.startsWith("https") ? pu0 : pu1;
console.log("ngrok-tunnel", { https: tunelHttps });
// https://www.iditect.com/how-to/55122741.html
} catch (e) {
console.error("[Error] ngrokstart", e);
}
}
/*
* You should probably use a different stun-turn server doing commercial stuff
* Also see: https://gist.github.com/zziuni/3741933 or https://www.twilio.com/docs/stun-turn
* Check the functionality of STUN/TURN servers: https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
*/
var ice_servers = [
{ urls: "stun:stun.l.google.com:19302" },
{
urls: turnUrls,
username: turnUsername,
credential: turnCredential,
},
];
// =====================================================
// Start Local Server with ngrok https tunel
// =====================================================
var PORT = process.env.PORT || 80;
server.listen(PORT, null, function () {
console.log(
`%c
███████╗██╗ ██████╗ ███╗ ██╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
██╔════╝██║██╔════╝ ████╗ ██║ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
███████╗██║██║ ███╗██╔██╗ ██║█████╗███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
╚════██║██║██║ ██║██║╚██╗██║╚════╝╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
███████║██║╚██████╔╝██║ ╚████║ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝ started...
`,
"font-family:monospace"
);
if (ngrokEnabled == "true") {
ngrokstart();
}
// init settings
console.log("settings", {
http: "http://localhost:" + PORT,
iceServers: ice_servers,
});
});
// All URL patterns should served with the same file.
app.get(["/", "/:room"], (req, res) =>
res.sendFile(path.join(__dirname, "www/index.html"))
);
var channels = {}; // collect channels
var sockets = {}; // collect sockets
/**
* Users will connect to the signaling server, after which they'll issue a "join"
* to join a particular channel. The signaling server keeps track of all sockets
* who are in a channel, and on join will send out 'addPeer' events to each pair
* of users in a channel. When clients receive the 'addPeer' even they'll begin
* setting up an RTCPeerConnection with one another. During this process they'll
* need to relay ICECandidate information to one another, as well as SessionDescription
* information. After all of that happens, they'll finally be able to complete
* the peer connection and will be in streaming audio/video between eachother.
*/
// =====================================================
// on peer connected
// =====================================================
io.sockets.on("connect", (socket) => {
console.log("[" + socket.id + "] --> connection accepted");
socket.channels = {};
sockets[socket.id] = socket;
// =====================================================
// on peer diconnected
// =====================================================
socket.on("disconnect", () => {
for (var channel in socket.channels) {
removePeerFrom(channel);
}
console.log("[" + socket.id + "] <--> disconnected");
delete sockets[socket.id];
});
// =====================================================
// on peer join
// =====================================================
socket.on("join", (config) => {
console.log("[" + socket.id + "] --> join ", config);
var channel = config.channel;
if (channel in socket.channels) {
console.log("[" + socket.id + "] [Warning] already joined", channel);
return;
}
if (!(channel in channels)) {
channels[channel] = {};
}
//
for (id in channels[channel]) {
// offer false
channels[channel][id].emit("addPeer", {
peer_id: socket.id,
should_create_offer: false,
iceServers: ice_servers,
});
// offer true
socket.emit("addPeer", {
peer_id: id,
should_create_offer: true,
iceServers: ice_servers,
});
console.log("[" + socket.id + "] emit add Peer [" + id + "]");
}
channels[channel][socket.id] = socket;
socket.channels[channel] = channel;
});
// =====================================================
// remove peers
// =====================================================
function removePeerFrom(channel) {
if (!(channel in socket.channels)) {
console.log("[" + socket.id + "] [Warning] not in ", channel);
return;
}
delete socket.channels[channel];
delete channels[channel][socket.id];
for (id in channels[channel]) {
channels[channel][id].emit("removePeer", { peer_id: socket.id });
socket.emit("removePeer", { peer_id: id });
console.log("[" + socket.id + "] emit remove Peer [" + id + "]");
}
}
// =====================================================
// relay ICE to peers
// =====================================================
socket.on("relayICE", (config) => {
let peer_id = config.peer_id;
let ice_candidate = config.ice_candidate;
/*
console.log(
"[" + socket.id + "] relay ICE-candidate to [" + peer_id + "] ",
{ address: config.ice_candidate.address }
); // ice_candidate
*/
if (peer_id in sockets) {
sockets[peer_id].emit("iceCandidate", {
peer_id: socket.id,
ice_candidate: ice_candidate,
});
}
});
// =====================================================
// relay SDP to peers
// =====================================================
socket.on("relaySDP", (config) => {
let peer_id = config.peer_id;
let session_description = config.session_description;
console.log(
"[" + socket.id + "] relay SessionDescription to [" + peer_id + "] ",
{ type: session_description.type }
); // session_description
if (peer_id in sockets) {
sockets[peer_id].emit("sessionDescription", {
peer_id: socket.id,
session_description: session_description,
});
}
});
// =====================================================
// handle peer message
// =====================================================
socket.on("msg", (config) => {
let peers = config.peers;
let msg = config.msg;
console.log("[" + socket.id + "] emit onMessage", {
msg: msg,
});
for (peer_id in peers) {
sockets[peer_id].emit("onMessage", {
msg: msg,
});
}
});
}); // end [sockets.on-connect]