wip v2
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
// import { serveDir } from "jsr:@std/http/file-server"
|
// import { serveDir } from "jsr:@std/http/file-server"
|
||||||
|
|
||||||
|
import { brotli } from "jsr:@deno-library/compress";
|
||||||
|
|
||||||
|
|
||||||
const memoryCache = true;
|
const memoryCache = true;
|
||||||
const filepathResponseCache: Map<string, Response> = new Map();
|
const filepathResponseCache: Map<string, Response> = new Map();
|
||||||
@@ -30,9 +32,15 @@ async function serveFile(filename: string) {
|
|||||||
|
|
||||||
|
|
||||||
const file = await Deno.readFile("../" + filename);
|
const file = await Deno.readFile("../" + filename);
|
||||||
const newResponse = new Response(file);
|
|
||||||
|
const compressed = await brotli.compress(file);
|
||||||
|
|
||||||
|
const newResponse = await new Response(compressed);
|
||||||
|
// const newResponse = await new Response(file);
|
||||||
|
|
||||||
newResponse.headers.set('Cache-Control', 'no-transform');
|
newResponse.headers.set('Cache-Control', 'no-transform');
|
||||||
|
newResponse.headers.set('Content-Encoding', 'br');
|
||||||
|
|
||||||
|
|
||||||
if (filename.endsWith('.js')) {
|
if (filename.endsWith('.js')) {
|
||||||
newResponse.headers.set('content-type', 'application/javascript')
|
newResponse.headers.set('content-type', 'application/javascript')
|
||||||
@@ -89,6 +97,15 @@ interface HelloMessage {
|
|||||||
known_users: string[]
|
known_users: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Hello2Message {
|
||||||
|
type: string
|
||||||
|
user_id: string
|
||||||
|
user_name: string
|
||||||
|
peer_id: string
|
||||||
|
peer_name: string
|
||||||
|
is_bootstrap_peer: boolean
|
||||||
|
// peer_description:RTCSessionDescription
|
||||||
|
}
|
||||||
|
|
||||||
// interface PeerState {
|
// interface PeerState {
|
||||||
// socket:WebSocket;
|
// socket:WebSocket;
|
||||||
@@ -96,6 +113,7 @@ interface HelloMessage {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// const peerStates:Map<string, PeerState> = new Map();
|
// const peerStates:Map<string, PeerState> = new Map();
|
||||||
|
const bootstrapPeers: Set<string> = new Set();
|
||||||
const userPeers: Map<string, Set<string>> = new Map();
|
const userPeers: Map<string, Set<string>> = new Map();
|
||||||
const peerSockets: Map<string, WebSocket> = new Map();
|
const peerSockets: Map<string, WebSocket> = new Map();
|
||||||
const socketPeers: Map<WebSocket, string> = new Map();
|
const socketPeers: Map<WebSocket, string> = new Map();
|
||||||
@@ -104,6 +122,31 @@ const socketPeers: Map<WebSocket, string> = new Map();
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
function hello2Handler(m:Hello2Message, socket:WebSocket) {
|
||||||
|
// bootstrapPeers.add(m.)
|
||||||
|
console.log(`[>hello2]: peer ${colorID(m.peer_id)}:${m.peer_name}, user ${colorID(m.user_id)}:${m.user_name}`);
|
||||||
|
|
||||||
|
if (m.is_bootstrap_peer) {
|
||||||
|
bootstrapPeers.add(m.peer_id);
|
||||||
|
console.log(`This is a bootstrap peer. bootstrap peers:${[...bootstrapPeers.values()]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userPeers.has(m.user_id)) {
|
||||||
|
userPeers.set(m.user_id, new Set());
|
||||||
|
}
|
||||||
|
userPeers.get(m.user_id)?.add(m.peer_id);
|
||||||
|
peerSockets.set(m.peer_id, socket); // TODO:MAYBEBUG - what happens with multiple windows each with their own websocket?
|
||||||
|
socketPeers.set(socket, m.peer_id);
|
||||||
|
|
||||||
|
if (!m.is_bootstrap_peer) {
|
||||||
|
|
||||||
|
return JSON.stringify({ type: 'hello2', bootstrapPeers: [...bootstrapPeers.values()] });
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.stringify({type:'hello2', message:'hello2 bootstrap peer!'});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function helloHandler(m: HelloMessage, socket: WebSocket) {
|
function helloHandler(m: HelloMessage, socket: WebSocket) {
|
||||||
console.log(`Received hello from peer ${colorID(m.peer_id)}:${m.peer_name}, user ${colorID(m.user_id)}:${m.user_name}`);
|
console.log(`Received hello from peer ${colorID(m.peer_id)}:${m.peer_name}, user ${colorID(m.user_id)}:${m.user_name}`);
|
||||||
|
|
||||||
@@ -234,6 +277,7 @@ function connectWebsocket(request: Request) {
|
|||||||
}
|
}
|
||||||
console.log("Websocket close:", colorID(peerID), `code:${event.code} reason:${event.reason} wasClean: ${event.wasClean}`);
|
console.log("Websocket close:", colorID(peerID), `code:${event.code} reason:${event.reason} wasClean: ${event.wasClean}`);
|
||||||
|
|
||||||
|
bootstrapPeers.delete(peerID);
|
||||||
peerSockets.delete(peerID);
|
peerSockets.delete(peerID);
|
||||||
deletePeerFromUserPeers(peerID);
|
deletePeerFromUserPeers(peerID);
|
||||||
});
|
});
|
||||||
@@ -303,6 +347,7 @@ async function main() {
|
|||||||
|
|
||||||
messageDispatch.set('ping', pingHandler);
|
messageDispatch.set('ping', pingHandler);
|
||||||
messageDispatch.set('hello', helloHandler);
|
messageDispatch.set('hello', helloHandler);
|
||||||
|
messageDispatch.set('hello2', hello2Handler);
|
||||||
messageDispatch.set('peer_message', peerMessageHandler);
|
messageDispatch.set('peer_message', peerMessageHandler);
|
||||||
|
|
||||||
Deno.serve({
|
Deno.serve({
|
||||||
|
|||||||
13
src/IDUtils.ts
Normal file
13
src/IDUtils.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
function uuidv4() {
|
||||||
|
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c: any) =>
|
||||||
|
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateID() {
|
||||||
|
if (self.crypto.hasOwnProperty("randomUUID")) {
|
||||||
|
return self.crypto.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return uuidv4();
|
||||||
|
}
|
||||||
544
src/PeerManager.ts
Normal file
544
src/PeerManager.ts
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
// connect to WS server, send info, connecto to bootstrap peer
|
||||||
|
// once connected to bootstrap peer,
|
||||||
|
|
||||||
|
// Goal, connect to bootstrap peer, ask bootstrap peer for peers that have posts from users that we care about. get peers, connect to those peers, sync.
|
||||||
|
// how? do "perfect negotiation" with bootstrap peer. All logic here moves to BP.
|
||||||
|
|
||||||
|
import { generateID } from "IDUtils";
|
||||||
|
|
||||||
|
// Use a broadcast channel to only have one peer manager for multiple tabs,
|
||||||
|
// then we won't need to have a session ID as all queries for a peerID will be coming from the same peer manager
|
||||||
|
|
||||||
|
export class PeerManager {
|
||||||
|
routingTable: Map<string, string>;
|
||||||
|
|
||||||
|
private peers: Map<string, PeerConnection>;
|
||||||
|
private signaler: Signaler;
|
||||||
|
searchQueryFunctions: Map<string, Function> = new Map();
|
||||||
|
RPC_remote: Map<string, Function> = new Map();
|
||||||
|
rpc: { [key: string]: Function } = {};
|
||||||
|
isBootstrapPeer: boolean = false;
|
||||||
|
bootstrapPeerConnection: PeerConnection | null = null;
|
||||||
|
|
||||||
|
async onConnected(bootstrapPeerID: string) {
|
||||||
|
this.bootstrapPeerConnection = await this.connect(bootstrapPeerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(userID: string, peerID: string, isBootstrapPeer: boolean) {
|
||||||
|
this.isBootstrapPeer = isBootstrapPeer;
|
||||||
|
this.peers = new Map();
|
||||||
|
this.routingTable = new Map();
|
||||||
|
|
||||||
|
|
||||||
|
this.signaler = new Signaler(userID, peerID, isBootstrapPeer, this.onConnected.bind(this));
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
let dummyPeer = new PeerConnection(this, "dummy_peer", this.signaler);
|
||||||
|
this.peers.set("dummy_peer", dummyPeer);
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(remotePeerID: string) {
|
||||||
|
// Connect to the peer that has the peer id peerID
|
||||||
|
let peerConnection = new PeerConnection(this, remotePeerID, this.signaler);
|
||||||
|
await peerConnection.connect();
|
||||||
|
this.peers.set(remotePeerID, peerConnection);
|
||||||
|
return peerConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect(remotePeerID: string) {
|
||||||
|
let peer = this.peers.get(remotePeerID);
|
||||||
|
|
||||||
|
if (!peer) {
|
||||||
|
console.log(`PeerManager.disconnect: couln't find peer ${remotePeerID}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await peer.disconnect();
|
||||||
|
this.peers.delete(remotePeerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
async call(peerID: string, functionName: string, args: any) {
|
||||||
|
let peer = this.peers.get(peerID);
|
||||||
|
|
||||||
|
if (!peer) {
|
||||||
|
console.log(`Can't find peer ${peerID}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await peer.call(functionName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
callFromRemote(functionName: string, args: any) {
|
||||||
|
let func = this.RPC_remote.get(functionName);
|
||||||
|
|
||||||
|
if (!func) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerRPC(functionName: string, func: Function) {
|
||||||
|
this.rpc[functionName] = (peerID: string, args: any) => {
|
||||||
|
return this.call(peerID, functionName, args);
|
||||||
|
};
|
||||||
|
this.RPC_remote.set(functionName, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSearchQuery(searchType: string, queryFunction: Function) {
|
||||||
|
this.searchQueryFunctions.set(searchType, queryFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(type: string, message: any) {
|
||||||
|
let promises = [];
|
||||||
|
for (let peer of this.peers.values()) {
|
||||||
|
promises.push(peer.call(type, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Promise.allSettled(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(remotePeerID: string, message: any) {
|
||||||
|
console.log(remotePeerID, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function log(...args: any[]): void {
|
||||||
|
for (let arg of args) {
|
||||||
|
console.log("[LOG]", arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
type: string;
|
||||||
|
from_peer: string;
|
||||||
|
to_peer: string;
|
||||||
|
from_username: string;
|
||||||
|
from_peername: string;
|
||||||
|
peer_message: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially this wil be the bootstrap peer, We'll keep a connection to it and it will keep a list of all connected peers.
|
||||||
|
// Eventually we will replace this with connecting via other peers.
|
||||||
|
class Signaler {
|
||||||
|
websocket: WebSocket;
|
||||||
|
|
||||||
|
sessionID: string;
|
||||||
|
userID: string;
|
||||||
|
peerID: string;
|
||||||
|
bootstrapPeerID: string = "";
|
||||||
|
private isBootstrapPeer: boolean = false;
|
||||||
|
private onConnected: Function;
|
||||||
|
|
||||||
|
constructor(userID: string, peerID: string, isBootstrapPeer: boolean, onConnected: Function) {
|
||||||
|
this.onConnected = onConnected;
|
||||||
|
this.isBootstrapPeer = isBootstrapPeer;
|
||||||
|
this.sessionID = generateID();
|
||||||
|
this.userID = userID;
|
||||||
|
this.peerID = peerID;
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.websocket = new WebSocket(
|
||||||
|
`wss://${window.location.hostname}:${window.location.port}/ws`,
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.websocket.onopen = async (event) => {
|
||||||
|
log("signaler:ws:onopen");
|
||||||
|
await this.sendHello2();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.websocket.onmessage = this.onMessage.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPeerMessage(remotePeerID: string, peerMessage: { type: string; description: RTCSessionDescription; }) {
|
||||||
|
this.send({
|
||||||
|
type: "peer_message",
|
||||||
|
from: this.peerID,
|
||||||
|
to: remotePeerID,
|
||||||
|
from_username: "blah user",
|
||||||
|
from_peername: "blah peer",
|
||||||
|
message: peerMessage
|
||||||
|
})
|
||||||
|
|
||||||
|
// let responseMessage = { type: "peer_message",
|
||||||
|
// from: app.peerID,
|
||||||
|
// to: data.from,
|
||||||
|
// from_username: app.username,
|
||||||
|
// from_peername: app.peername,
|
||||||
|
// message: { type: "get_posts_for_user", post_ids: postIds, user_id: message.user_id } }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async sendHello2() {
|
||||||
|
this.send({
|
||||||
|
type: "hello2",
|
||||||
|
user_id: this.userID,
|
||||||
|
// user_name: app.username,
|
||||||
|
peer_id: this.peerID,
|
||||||
|
session_id: this.sessionID,
|
||||||
|
// peer_name: app.peername,
|
||||||
|
is_bootstrap_peer: this.isBootstrapPeer,
|
||||||
|
// peer_description: this.rtcPeerDescription
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(event: MessageEvent) {
|
||||||
|
let messageJSON = event.data;
|
||||||
|
let message: any = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
message = JSON.parse(messageJSON);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("->signaler:", message);
|
||||||
|
|
||||||
|
if (message.type === "hello2") {
|
||||||
|
|
||||||
|
if (!this.isBootstrapPeer) {
|
||||||
|
this.bootstrapPeerID = message.bootstrapPeers[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onConnected(this.bootstrapPeerID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send(message: any) {
|
||||||
|
let messageJSON = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageJSON = JSON.stringify(message);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("<-signaler:", message);
|
||||||
|
|
||||||
|
this.websocket.send(messageJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendPeerMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
class PeerConnection {
|
||||||
|
private remotePeerID: string;
|
||||||
|
private signaler: Signaler;
|
||||||
|
private peerManager: PeerManager;
|
||||||
|
private dataChannel: RTCDataChannel | null = null;
|
||||||
|
private messageHandlers: Map<string, Function> = new Map();
|
||||||
|
|
||||||
|
// private makingOffer:boolean = false;
|
||||||
|
// private ignoreOffer:boolean = false;
|
||||||
|
|
||||||
|
rtcPeer: RTCPeerConnection | null = null;
|
||||||
|
|
||||||
|
static config = {
|
||||||
|
iceServers: [
|
||||||
|
{ urls: "stun:ddln.app" },
|
||||||
|
// { urls: "turn:ddln.app", username: "a", credential: "b" },
|
||||||
|
{ urls: "stun:stun.l.google.com" }, // keeping this for now as my STUN server is not return ipv6
|
||||||
|
// { urls: "stun:stun1.l.google.com" },
|
||||||
|
// { urls: "stun:stun2.l.google.com" },
|
||||||
|
// { urls: "stun:stun3.l.google.com" },
|
||||||
|
// { urls: "stun:stun4.l.google.com" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
pendingRPCs: Map<
|
||||||
|
string,
|
||||||
|
{ resolve: Function; reject: Function; functionName: string }
|
||||||
|
> = new Map();
|
||||||
|
messageSuperlog: boolean = true;
|
||||||
|
|
||||||
|
async RPCHandler(message: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
peerManager: PeerManager,
|
||||||
|
remotePeerID: string,
|
||||||
|
signaler: Signaler,
|
||||||
|
) {
|
||||||
|
this.peerManager = peerManager;
|
||||||
|
this.remotePeerID = remotePeerID;
|
||||||
|
this.signaler = signaler;
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
this.rtcPeer = new RTCPeerConnection(PeerConnection.config);
|
||||||
|
this.dataChannel = this.rtcPeer.createDataChannel("ddln_main");
|
||||||
|
|
||||||
|
if (this.rtcPeer === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.rtcPeer.onicecandidate = ({ candidate }) => this.signaler.send(JSON.stringify({ candidate }));
|
||||||
|
this.rtcPeer.onicecandidate = ({ candidate }) => console.log(candidate);
|
||||||
|
|
||||||
|
|
||||||
|
this.rtcPeer.onnegotiationneeded = async (event) => {
|
||||||
|
log("on negotiation needed fired");
|
||||||
|
|
||||||
|
if (!this.rtcPeer) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
let makingOffer = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
makingOffer = true;
|
||||||
|
await this.rtcPeer.setLocalDescription();
|
||||||
|
|
||||||
|
if (!this.rtcPeer.localDescription) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.signaler.sendPeerMessage(this.remotePeerID, { type: "rtcDescription", description: this.rtcPeer.localDescription });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
makingOffer = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
}
|
||||||
|
|
||||||
|
send(message: any) {
|
||||||
|
this.messageSuperlog && console.log("<-", message.type, message);
|
||||||
|
|
||||||
|
let messageJSON = JSON.stringify(message);
|
||||||
|
// this.dataChannel?.send();
|
||||||
|
|
||||||
|
this.onMessage(messageJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
call(functionName: string, args: any) {
|
||||||
|
let transactionID = generateID(); // make this faster as we will only ever have a small number of in-flight queries on a peer
|
||||||
|
// Think about a timeout here to auto reject it after a while.
|
||||||
|
let promise = new Promise((resolve, reject) => {
|
||||||
|
this.pendingRPCs.set(transactionID, { resolve, reject, functionName });
|
||||||
|
// setTimeout(() => reject("bad"), 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
let message = {
|
||||||
|
type: "rpc_call",
|
||||||
|
transaction_id: transactionID,
|
||||||
|
function_name: functionName,
|
||||||
|
args: args,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.send(message);
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(messageJSON: any) {
|
||||||
|
|
||||||
|
let message: any = {};
|
||||||
|
try {
|
||||||
|
message = JSON.parse(messageJSON);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PeerConnection.onMessage:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messageSuperlog && console.log("->", message.type, message);
|
||||||
|
let type = message.type;
|
||||||
|
|
||||||
|
if (type === "rpc_response") {
|
||||||
|
let pendingRPC = this.pendingRPCs.get(message.transaction_id);
|
||||||
|
|
||||||
|
if (!pendingRPC) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingRPC.resolve(message.response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "rpc_call") {
|
||||||
|
|
||||||
|
let response = this.peerManager.callFromRemote(message.function_name, message.args);
|
||||||
|
|
||||||
|
let responseMessage = { type: 'rpc_response', transaction_id: message.transaction_id, response: response };
|
||||||
|
|
||||||
|
this.send(responseMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.peerManger.onMessage(this.remotePeerID, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// export class PeerConnection2 {
|
||||||
|
|
||||||
|
// id: string;
|
||||||
|
|
||||||
|
// private makingOffer:boolean = false;
|
||||||
|
// private ignoreOffer:boolean = false;
|
||||||
|
|
||||||
|
// rtcPeer: RTCPeerConnection;
|
||||||
|
|
||||||
|
// signaler:WebSocket;
|
||||||
|
|
||||||
|
// static config = {
|
||||||
|
// iceServers: [
|
||||||
|
// { urls: "stun:stun.l.google.com" },
|
||||||
|
// { urls: "stun:stun1.l.google.com" },
|
||||||
|
// { urls: "stun:stun2.l.google.com" },
|
||||||
|
// { urls: "stun:stun3.l.google.com" },
|
||||||
|
// { urls: "stun:stun4.l.google.com" },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
|
||||||
|
// constructor(remotePeerID: string, signaler:WebSocket) {
|
||||||
|
// this.id = remotePeerID;
|
||||||
|
|
||||||
|
// this.rtcPeer = new RTCPeerConnection(PeerConnection2.config);
|
||||||
|
|
||||||
|
// this.signaler = signaler;;
|
||||||
|
|
||||||
|
// this.rtcPeer.onnegotiationneeded = async () => {
|
||||||
|
// try {
|
||||||
|
// this.makingOffer = true;
|
||||||
|
// await this.rtcPeer.setLocalDescription();
|
||||||
|
// signaler.send(JSON.stringify({ description: this.rtcPeer.localDescription }));
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error(err);
|
||||||
|
// } finally {
|
||||||
|
// this.makingOffer = false;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// this.rtcPeer.onicecandidate = ({ candidate }) => signaler.send(JSON.stringify({ candidate }));
|
||||||
|
|
||||||
|
// this.ignoreOffer = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// onSignallerMessage = async ({ data: { description, candidate } }: MessageEvent) => {
|
||||||
|
// try {
|
||||||
|
// if (description) {
|
||||||
|
// // const offerCollision =
|
||||||
|
// // description.type === "offer" &&
|
||||||
|
// // (this.makingOffer || this.rtcPeer.signalingState !== "stable");
|
||||||
|
|
||||||
|
// // this.ignoreOffer = !polite && offerCollision;
|
||||||
|
// if (this.ignoreOffer) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// await this.rtcPeer.setRemoteDescription(description);
|
||||||
|
// if (description.type === "offer") {
|
||||||
|
// await this.rtcPeer.setLocalDescription();
|
||||||
|
// this.signaler.send(JSON.stringify({ description: this.rtcPeer.localDescription }));
|
||||||
|
// }
|
||||||
|
// } else if (candidate) {
|
||||||
|
// try {
|
||||||
|
// await this.rtcPeer.addIceCandidate(candidate);
|
||||||
|
// } catch (err) {
|
||||||
|
// if (!this.ignoreOffer) {
|
||||||
|
// throw err;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error(err);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // const config = {
|
||||||
|
// // iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // let polite = true;
|
||||||
|
|
||||||
|
// // const signaler = new SignalingChannel();
|
||||||
|
// // const signaler: any = {}
|
||||||
|
// // const rtcPeer = new RTCPeerConnection(config);
|
||||||
|
|
||||||
|
// // // const constraints = { audio: true, video: true };
|
||||||
|
// // const selfVideo = document.querySelector("video.selfview");
|
||||||
|
// // const remoteVideo = document.querySelector("video.remoteview");
|
||||||
|
|
||||||
|
// // async function start() {
|
||||||
|
// // try {
|
||||||
|
// // const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
|
||||||
|
// // for (const track of stream.getTracks()) {
|
||||||
|
// // pc.addTrack(track, stream);
|
||||||
|
// // }
|
||||||
|
// // selfVideo.srcObject = stream;
|
||||||
|
// // } catch (err) {
|
||||||
|
// // console.error(err);
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // rtcPeer.ontrack = ({ track, streams }) => {
|
||||||
|
// // track.onunmute = () => {
|
||||||
|
// // if (remoteVideo.srcObject) {
|
||||||
|
// // return;
|
||||||
|
// // }
|
||||||
|
// // remoteVideo.srcObject = streams[0];
|
||||||
|
// // };
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // makingOffer = false;
|
||||||
|
|
||||||
|
// // rtcPeer.onnegotiationneeded = async () => {
|
||||||
|
// // try {
|
||||||
|
// // // makingOffer = true;
|
||||||
|
// // await rtcPeer.setLocalDescription();
|
||||||
|
// // signaler.send({ description: rtcPeer.localDescription });
|
||||||
|
// // } catch (err) {
|
||||||
|
// // console.error(err);
|
||||||
|
// // } finally {
|
||||||
|
// // makingOffer = false;
|
||||||
|
// // }
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // rtcPeer.onicecandidate = ({ candidate }) => signaler.send({ candidate });
|
||||||
|
|
||||||
|
// // let ignoreOffer = false;
|
||||||
|
|
||||||
|
// // signaler.onmessage = async ({ data: { description, candidate } }: MessageEvent) => {
|
||||||
|
// // try {
|
||||||
|
// // if (description) {
|
||||||
|
// // const offerCollision =
|
||||||
|
// // description.type === "offer" &&
|
||||||
|
// // // (makingOffer || rtcPeer.signalingState !== "stable");
|
||||||
|
|
||||||
|
// // ignoreOffer = !polite && offerCollision;
|
||||||
|
// // if (ignoreOffer) {
|
||||||
|
// // return;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // await rtcPeer.setRemoteDescription(description);
|
||||||
|
// // if (description.type === "offer") {
|
||||||
|
// // await rtcPeer.setLocalDescription();
|
||||||
|
// // signaler.send({ description: rtcPeer.localDescription });
|
||||||
|
// // }
|
||||||
|
// // } else if (candidate) {
|
||||||
|
// // try {
|
||||||
|
// // await rtcPeer.addIceCandidate(candidate);
|
||||||
|
// // } catch (err) {
|
||||||
|
// // if (!ignoreOffer) {
|
||||||
|
// // throw err;
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // } catch (err) {
|
||||||
|
// // console.error(err);
|
||||||
|
// // }
|
||||||
|
// // };
|
||||||
28
src/log.ts
Normal file
28
src/log.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
let logLines: string[] = [];
|
||||||
|
let logLength = 30;
|
||||||
|
let logVisible = false;
|
||||||
|
|
||||||
|
export function setLogVisibility(visible:boolean) {
|
||||||
|
logVisible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderLog() {
|
||||||
|
if (!logVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let log = document.getElementById("log");
|
||||||
|
if (!log) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
log.innerText = logLines.join("\n");
|
||||||
|
}
|
||||||
|
export function log(message: string) {
|
||||||
|
console.log(message);
|
||||||
|
logLines.push(`${new Date().toLocaleTimeString()}: ${message}`);
|
||||||
|
if (logLines.length > logLength) {
|
||||||
|
logLines = logLines.slice(logLines.length - logLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLog();
|
||||||
|
}
|
||||||
130
src/main.ts
130
src/main.ts
@@ -33,6 +33,7 @@ Restruucture the app around the data. App/WS split is messy. Clean it up.
|
|||||||
// import * as ForceGraph3D from "3d-force-graph";
|
// import * as ForceGraph3D from "3d-force-graph";
|
||||||
import { openDatabase, getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db";
|
import { openDatabase, getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db";
|
||||||
|
|
||||||
|
// import {PeerConnection} from "webRTC";
|
||||||
|
|
||||||
// declare let WebTorrent: any;
|
// declare let WebTorrent: any;
|
||||||
|
|
||||||
@@ -279,6 +280,88 @@ interface PeerMessage {
|
|||||||
message: any;
|
message: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// class Signaler {
|
||||||
|
// websocket: WebSocket | null = null;
|
||||||
|
|
||||||
|
// websocketPingInterval: number = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// connect() {
|
||||||
|
// if (this.websocket?.readyState === WebSocket.OPEN) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// window.clearInterval(this.websocketPingInterval);
|
||||||
|
// if (this.websocket) { this.websocket.close() };
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// this.websocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/ws`);
|
||||||
|
// } catch (error: any) {
|
||||||
|
// console.log(error.message);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// this.websocket.onopen = async (event) => {
|
||||||
|
// log("ws:connected");
|
||||||
|
// await this.sendHello();
|
||||||
|
|
||||||
|
// // If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
|
||||||
|
// let helloRefreshIntervalPeriod = 120;
|
||||||
|
// if (app.isHeadless) {
|
||||||
|
// console.log("wsConnection: Setting hello refresh interval to ", helloRefreshIntervalPeriod)
|
||||||
|
// this.helloRefreshInterval = window.setInterval(() => {
|
||||||
|
// console.log("wsConnection: Hello refresh.")
|
||||||
|
|
||||||
|
// if (!navigator.onLine) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.sendHello();
|
||||||
|
// }, helloRefreshIntervalPeriod * 1000);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// this.websocketPingInterval = window.setInterval(() => {
|
||||||
|
// if (!navigator.onLine) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.send({ type: "ping", peer_id: this.peerID, peer_name: app.peername, user_id: app.userID, user_name: app.username });
|
||||||
|
// }, 10_000)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// this.websocket.onclose = (event) => {
|
||||||
|
// log("ws:disconnected");
|
||||||
|
// // this.retry *= 2;
|
||||||
|
// log(`Retrying in ${this.retry} seconds`);
|
||||||
|
// window.setTimeout(() => { this.connect(); }, this.retry * 1000);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// this.websocket.onmessage = (event) => {
|
||||||
|
// // log('ws:<-' + event.data.slice(0, 240));
|
||||||
|
// let data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
// let { type } = data;
|
||||||
|
|
||||||
|
// let handler = this.messageHandlers.get(type);
|
||||||
|
// if (!handler) {
|
||||||
|
// console.warn(`Got a message we can't handle:`, type);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// handler(data);
|
||||||
|
|
||||||
|
// };
|
||||||
|
|
||||||
|
// this.websocket.onerror = (event) => {
|
||||||
|
// log('ws:error: ' + event);
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// disconnect() {
|
||||||
|
// this.websocket?.close();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
class wsConnection {
|
class wsConnection {
|
||||||
websocket: WebSocket | null = null;
|
websocket: WebSocket | null = null;
|
||||||
userID = "";
|
userID = "";
|
||||||
@@ -304,12 +387,19 @@ class wsConnection {
|
|||||||
this.messageHandlers.set('pong', this.pongHandler);
|
this.messageHandlers.set('pong', this.pongHandler);
|
||||||
this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
|
this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
this.peerMessageHandlers.set('get_post_ids_for_user', this.getPostIdsForUserHandler.bind(this));
|
this.peerMessageHandlers.set('get_post_ids_for_user', this.getPostIdsForUserHandler.bind(this));
|
||||||
this.peerMessageHandlers.set('get_post_ids_for_user_response', this.getPostIdsForUserResponseHandler.bind(this));
|
this.peerMessageHandlers.set('get_post_ids_for_user_response', this.getPostIdsForUserResponseHandler.bind(this));
|
||||||
|
|
||||||
this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
|
this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
|
||||||
this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserReponseHandler.bind(this));
|
this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserReponseHandler.bind(this));
|
||||||
|
|
||||||
|
this.peerMessageHandlers.set('send_webrtc_offer', this.sendWebRTCOfferHandler.bind(this));
|
||||||
|
this.peerMessageHandlers.set('send_webrtc_offer_response', this.getPostIdsForUserResponseHandler.bind(this));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener('beforeunload', () => this.disconnect());
|
window.addEventListener('beforeunload', () => this.disconnect());
|
||||||
|
|
||||||
this.connect();
|
this.connect();
|
||||||
@@ -340,6 +430,10 @@ class wsConnection {
|
|||||||
pongHandler(data: any) {
|
pongHandler(data: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendWebRTCOfferHandler(data:any) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
async getPostIdsForUserResponseHandler(data: any) {
|
async getPostIdsForUserResponseHandler(data: any) {
|
||||||
// log(`getPostsForUserResponse: ${data}`)
|
// log(`getPostsForUserResponse: ${data}`)
|
||||||
|
|
||||||
@@ -1020,6 +1114,8 @@ class App {
|
|||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// What happens with multiple tabs/connections from the same peer ID? I don't think this works, we also need a session ID for each connection
|
||||||
getPeerID() {
|
getPeerID() {
|
||||||
let id = localStorage.getItem("peer_id");
|
let id = localStorage.getItem("peer_id");
|
||||||
|
|
||||||
@@ -1540,7 +1636,36 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async main() {
|
async main() {
|
||||||
|
let urlParams = (new URL(window.location.href)).searchParams;
|
||||||
|
if (urlParams.has('log')) {
|
||||||
|
this.showInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
let peer:RTCPeerConnection|null = null;
|
||||||
|
// if (window.RTCPeerConnection) {
|
||||||
|
peer = new RTCPeerConnection({
|
||||||
|
iceServers: [
|
||||||
|
{ urls: "stun:ddln.app"},
|
||||||
|
{ urls: "turn:ddln.app", username:"a", credential:"b"},
|
||||||
|
{ urls: "stun:stun.l.google.com" }, // keeping this for now as my STUN server is not return ipv6
|
||||||
|
// { urls: "stun:stun1.l.google.com" },
|
||||||
|
// { urls: "stun:stun2.l.google.com" },
|
||||||
|
// { urls: "stun:stun3.l.google.com" },
|
||||||
|
// { urls: "stun:stun4.l.google.com" },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.createDataChannel('boop');
|
||||||
|
|
||||||
|
peer.onicecandidate = ({ candidate }) => {log(`WRTC:${candidate?.address} ${candidate?.protocol} ${candidate?.type} ${(candidate as any)?.url}`)};
|
||||||
|
peer.onnegotiationneeded = (event) => {console.log("on negotiation needed: ", event)}
|
||||||
|
|
||||||
|
peer.createOffer().then((description)=>{
|
||||||
|
peer.setLocalDescription(description)
|
||||||
|
log("RTC: " + description.sdp + description.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
// await this.exportPostsForUser('b38b623c-c3fa-4351-9cab-50233c99fa4e');
|
// await this.exportPostsForUser('b38b623c-c3fa-4351-9cab-50233c99fa4e');
|
||||||
|
|
||||||
@@ -1579,11 +1704,6 @@ class App {
|
|||||||
document.getElementById('connectURL')!.innerHTML = `<a href="${this.connectURL}">connect</a>`;
|
document.getElementById('connectURL')!.innerHTML = `<a href="${this.connectURL}">connect</a>`;
|
||||||
|
|
||||||
|
|
||||||
let urlParams = (new URL(window.location.href)).searchParams;
|
|
||||||
if (urlParams.has('log')) {
|
|
||||||
this.showInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (urlParams.has('headless')) {
|
if (urlParams.has('headless')) {
|
||||||
this.isHeadless = true;
|
this.isHeadless = true;
|
||||||
}
|
}
|
||||||
|
|||||||
2114
src/main2.ts
Normal file
2114
src/main2.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,12 @@ const cacheName = "dandelion_cache_v1";
|
|||||||
const contentToCache = [
|
const contentToCache = [
|
||||||
'/static/index.html',
|
'/static/index.html',
|
||||||
'/static/main.css',
|
'/static/main.css',
|
||||||
'/static/main.js',
|
'/static/main2.js',
|
||||||
'/static/lib/marked.min.js',
|
'/static/lib/marked.min.js',
|
||||||
'/static/lib/qrcode.min.js',
|
'/static/lib/qrcode.min.js',
|
||||||
'/static/db.js',
|
'/static/db.js',
|
||||||
|
'/static/PeerManager.js',
|
||||||
|
'/static/IDUtils.js',
|
||||||
'/static/favicon.ico'
|
'/static/favicon.ico'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
110
src/webRTC.ts
110
src/webRTC.ts
@@ -1,110 +0,0 @@
|
|||||||
class PeerManager {
|
|
||||||
connect(peerID: string) {
|
|
||||||
// Connect to the peer that has the peer id peerID
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(peerID: string) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PeerConnection {
|
|
||||||
static config = {
|
|
||||||
iceServers: [
|
|
||||||
{ urls: "stun:stun.l.google.com" },
|
|
||||||
{ urls: "stun:stun1.l.google.com" },
|
|
||||||
{ urls: "stun:stun2.l.google.com" },
|
|
||||||
{ urls: "stun:stun3.l.google.com" },
|
|
||||||
{ urls: "stun:stun4.l.google.com" },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
|
||||||
};
|
|
||||||
|
|
||||||
let polite = true;
|
|
||||||
|
|
||||||
// const signaler = new SignalingChannel();
|
|
||||||
const signaler: any = {}
|
|
||||||
const pc = new RTCPeerConnection(config);
|
|
||||||
|
|
||||||
|
|
||||||
const constraints = { audio: true, video: true };
|
|
||||||
const selfVideo = document.querySelector("video.selfview");
|
|
||||||
const remoteVideo = document.querySelector("video.remoteview");
|
|
||||||
|
|
||||||
async function start() {
|
|
||||||
try {
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
||||||
|
|
||||||
for (const track of stream.getTracks()) {
|
|
||||||
pc.addTrack(track, stream);
|
|
||||||
}
|
|
||||||
// selfVideo.srcObject = stream;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pc.ontrack = ({ track, streams }) => {
|
|
||||||
track.onunmute = () => {
|
|
||||||
// if (remoteVideo.srcObject) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// remoteVideo.srcObject = streams[0];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let makingOffer = false;
|
|
||||||
|
|
||||||
pc.onnegotiationneeded = async () => {
|
|
||||||
try {
|
|
||||||
makingOffer = true;
|
|
||||||
await pc.setLocalDescription();
|
|
||||||
signaler.send({ description: pc.localDescription });
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
} finally {
|
|
||||||
makingOffer = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pc.onicecandidate = ({ candidate }) => signaler.send({ candidate });
|
|
||||||
|
|
||||||
let ignoreOffer = false;
|
|
||||||
|
|
||||||
signaler.onmessage = async ({ data: { description, candidate } }: MessageEvent) => {
|
|
||||||
try {
|
|
||||||
if (description) {
|
|
||||||
const offerCollision =
|
|
||||||
description.type === "offer" &&
|
|
||||||
(makingOffer || pc.signalingState !== "stable");
|
|
||||||
|
|
||||||
ignoreOffer = !polite && offerCollision;
|
|
||||||
if (ignoreOffer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await pc.setRemoteDescription(description);
|
|
||||||
if (description.type === "offer") {
|
|
||||||
await pc.setLocalDescription();
|
|
||||||
signaler.send({ description: pc.localDescription });
|
|
||||||
}
|
|
||||||
} else if (candidate) {
|
|
||||||
try {
|
|
||||||
await pc.addIceCandidate(candidate);
|
|
||||||
} catch (err) {
|
|
||||||
if (!ignoreOffer) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -10,19 +10,21 @@
|
|||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"db": "/static/db.js"
|
"db": "/static/db.js",
|
||||||
|
"IDUtils": "/static/IDUtils.js",
|
||||||
|
"PeerManager": "/static/PeerManager.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<link rel="preload" href="/static/main.css" as="style">
|
<link rel="preload" href="/static/main.css" as="style">
|
||||||
<link rel="preload" href="/static/main.js" as="script">
|
<link rel="preload" href="/static/main2.js" as="script">
|
||||||
<link rel="preload" href="/static/lib/marked.min.js" as="script">
|
<link rel="preload" href="/static/lib/marked.min.js" as="script">
|
||||||
<link rel="preload" href="/static/lib/qrcode.min.js" as="script">
|
<link rel="preload" href="/static/lib/qrcode.min.js" as="script">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/main.css">
|
<link rel="stylesheet" href="/static/main.css">
|
||||||
|
|
||||||
<script type="module" src="/static/main.js"></script>
|
<script type="module" src="/static/main2.js"></script>
|
||||||
<script src="/static/lib/marked.min.js"></script>
|
<script src="/static/lib/marked.min.js"></script>
|
||||||
<script src="/static/lib/qrcode.min.js"></script>
|
<script src="/static/lib/qrcode.min.js"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ hr {
|
|||||||
|
|
||||||
#log {
|
#log {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
text-wrap: nowrap;
|
text-wrap: wrap;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -191,6 +191,69 @@ async function compressString(input) {
|
|||||||
// Convert the compressed data to a Uint8Array
|
// Convert the compressed data to a Uint8Array
|
||||||
return new Uint8Array(compressedArray);
|
return new Uint8Array(compressedArray);
|
||||||
}
|
}
|
||||||
|
// class Signaler {
|
||||||
|
// websocket: WebSocket | null = null;
|
||||||
|
// websocketPingInterval: number = 0;
|
||||||
|
// connect() {
|
||||||
|
// if (this.websocket?.readyState === WebSocket.OPEN) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// window.clearInterval(this.websocketPingInterval);
|
||||||
|
// if (this.websocket) { this.websocket.close() };
|
||||||
|
// try {
|
||||||
|
// this.websocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/ws`);
|
||||||
|
// } catch (error: any) {
|
||||||
|
// console.log(error.message);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.websocket.onopen = async (event) => {
|
||||||
|
// log("ws:connected");
|
||||||
|
// await this.sendHello();
|
||||||
|
// // If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
|
||||||
|
// let helloRefreshIntervalPeriod = 120;
|
||||||
|
// if (app.isHeadless) {
|
||||||
|
// console.log("wsConnection: Setting hello refresh interval to ", helloRefreshIntervalPeriod)
|
||||||
|
// this.helloRefreshInterval = window.setInterval(() => {
|
||||||
|
// console.log("wsConnection: Hello refresh.")
|
||||||
|
// if (!navigator.onLine) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.sendHello();
|
||||||
|
// }, helloRefreshIntervalPeriod * 1000);
|
||||||
|
// }
|
||||||
|
// this.websocketPingInterval = window.setInterval(() => {
|
||||||
|
// if (!navigator.onLine) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.send({ type: "ping", peer_id: this.peerID, peer_name: app.peername, user_id: app.userID, user_name: app.username });
|
||||||
|
// }, 10_000)
|
||||||
|
// };
|
||||||
|
// this.websocket.onclose = (event) => {
|
||||||
|
// log("ws:disconnected");
|
||||||
|
// // this.retry *= 2;
|
||||||
|
// log(`Retrying in ${this.retry} seconds`);
|
||||||
|
// window.setTimeout(() => { this.connect(); }, this.retry * 1000);
|
||||||
|
// };
|
||||||
|
// this.websocket.onmessage = (event) => {
|
||||||
|
// // log('ws:<-' + event.data.slice(0, 240));
|
||||||
|
// let data = JSON.parse(event.data);
|
||||||
|
// let { type } = data;
|
||||||
|
// let handler = this.messageHandlers.get(type);
|
||||||
|
// if (!handler) {
|
||||||
|
// console.warn(`Got a message we can't handle:`, type);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// handler(data);
|
||||||
|
// };
|
||||||
|
// this.websocket.onerror = (event) => {
|
||||||
|
// log('ws:error: ' + event);
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// disconnect() {
|
||||||
|
// this.websocket?.close();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
class wsConnection {
|
class wsConnection {
|
||||||
constructor(userID, peerID, IDsToSync) {
|
constructor(userID, peerID, IDsToSync) {
|
||||||
this.websocket = null;
|
this.websocket = null;
|
||||||
@@ -232,10 +295,13 @@ class wsConnection {
|
|||||||
this.messageHandlers.set('hello', this.helloResponseHandler.bind(this));
|
this.messageHandlers.set('hello', this.helloResponseHandler.bind(this));
|
||||||
this.messageHandlers.set('pong', this.pongHandler);
|
this.messageHandlers.set('pong', this.pongHandler);
|
||||||
this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
|
this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
|
||||||
|
//
|
||||||
this.peerMessageHandlers.set('get_post_ids_for_user', this.getPostIdsForUserHandler.bind(this));
|
this.peerMessageHandlers.set('get_post_ids_for_user', this.getPostIdsForUserHandler.bind(this));
|
||||||
this.peerMessageHandlers.set('get_post_ids_for_user_response', this.getPostIdsForUserResponseHandler.bind(this));
|
this.peerMessageHandlers.set('get_post_ids_for_user_response', this.getPostIdsForUserResponseHandler.bind(this));
|
||||||
this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
|
this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
|
||||||
this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserReponseHandler.bind(this));
|
this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserReponseHandler.bind(this));
|
||||||
|
this.peerMessageHandlers.set('send_webrtc_offer', this.sendWebRTCOfferHandler.bind(this));
|
||||||
|
this.peerMessageHandlers.set('send_webrtc_offer_response', this.getPostIdsForUserResponseHandler.bind(this));
|
||||||
window.addEventListener('beforeunload', () => this.disconnect());
|
window.addEventListener('beforeunload', () => this.disconnect());
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
@@ -260,6 +326,8 @@ class wsConnection {
|
|||||||
}
|
}
|
||||||
pongHandler(data) {
|
pongHandler(data) {
|
||||||
}
|
}
|
||||||
|
async sendWebRTCOfferHandler(data) {
|
||||||
|
}
|
||||||
async getPostIdsForUserResponseHandler(data) {
|
async getPostIdsForUserResponseHandler(data) {
|
||||||
// log(`getPostsForUserResponse: ${data}`)
|
// log(`getPostsForUserResponse: ${data}`)
|
||||||
let message = data.message;
|
let message = data.message;
|
||||||
@@ -765,6 +833,7 @@ class App {
|
|||||||
this.websocket?.broadcastNewPost(userID, post);
|
this.websocket?.broadcastNewPost(userID, post);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
// What happens with multiple tabs/connections from the same peer ID? I don't think this works, we also need a session ID for each connection
|
||||||
getPeerID() {
|
getPeerID() {
|
||||||
let id = localStorage.getItem("peer_id");
|
let id = localStorage.getItem("peer_id");
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@@ -1167,6 +1236,31 @@ class App {
|
|||||||
let db = await openDatabase(this.userID);
|
let db = await openDatabase(this.userID);
|
||||||
}
|
}
|
||||||
async main() {
|
async main() {
|
||||||
|
let urlParams = (new URL(window.location.href)).searchParams;
|
||||||
|
if (urlParams.has('log')) {
|
||||||
|
this.showInfo();
|
||||||
|
}
|
||||||
|
let peer = null;
|
||||||
|
// if (window.RTCPeerConnection) {
|
||||||
|
peer = new RTCPeerConnection({
|
||||||
|
iceServers: [
|
||||||
|
{ urls: "stun:ddln.app" },
|
||||||
|
{ urls: "turn:ddln.app", username: "a", credential: "b" },
|
||||||
|
{ urls: "stun:stun.l.google.com" }, // keeping this for now as my STUN server is not return ipv6
|
||||||
|
// { urls: "stun:stun1.l.google.com" },
|
||||||
|
// { urls: "stun:stun2.l.google.com" },
|
||||||
|
// { urls: "stun:stun3.l.google.com" },
|
||||||
|
// { urls: "stun:stun4.l.google.com" },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
peer.createDataChannel('boop');
|
||||||
|
peer.onicecandidate = ({ candidate }) => { log(`WRTC:${candidate?.address} ${candidate?.protocol} ${candidate?.type} ${candidate?.url}`); };
|
||||||
|
peer.onnegotiationneeded = (event) => { console.log("on negotiation needed: ", event); };
|
||||||
|
peer.createOffer().then((description) => {
|
||||||
|
peer.setLocalDescription(description);
|
||||||
|
log("RTC: " + description.sdp + description.type);
|
||||||
|
});
|
||||||
|
// }
|
||||||
// await this.exportPostsForUser('b38b623c-c3fa-4351-9cab-50233c99fa4e');
|
// await this.exportPostsForUser('b38b623c-c3fa-4351-9cab-50233c99fa4e');
|
||||||
// Get initial state and route from URL and user agent etc
|
// Get initial state and route from URL and user agent etc
|
||||||
// Set local state (userid etc) based on that.
|
// Set local state (userid etc) based on that.
|
||||||
@@ -1191,10 +1285,6 @@ class App {
|
|||||||
await this.initDB();
|
await this.initDB();
|
||||||
this.connectURL = `${document.location.origin}/connect/${this.userID}`;
|
this.connectURL = `${document.location.origin}/connect/${this.userID}`;
|
||||||
document.getElementById('connectURL').innerHTML = `<a href="${this.connectURL}">connect</a>`;
|
document.getElementById('connectURL').innerHTML = `<a href="${this.connectURL}">connect</a>`;
|
||||||
let urlParams = (new URL(window.location.href)).searchParams;
|
|
||||||
if (urlParams.has('log')) {
|
|
||||||
this.showInfo();
|
|
||||||
}
|
|
||||||
if (urlParams.has('headless')) {
|
if (urlParams.has('headless')) {
|
||||||
this.isHeadless = true;
|
this.isHeadless = true;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,10 +5,12 @@ const cacheName = "dandelion_cache_v1";
|
|||||||
const contentToCache = [
|
const contentToCache = [
|
||||||
'/static/index.html',
|
'/static/index.html',
|
||||||
'/static/main.css',
|
'/static/main.css',
|
||||||
'/static/main.js',
|
'/static/main2.js',
|
||||||
'/static/lib/marked.min.js',
|
'/static/lib/marked.min.js',
|
||||||
'/static/lib/qrcode.min.js',
|
'/static/lib/qrcode.min.js',
|
||||||
'/static/db.js',
|
'/static/db.js',
|
||||||
|
'/static/PeerManager.js',
|
||||||
|
'/static/IDUtils.js',
|
||||||
'/static/favicon.ico'
|
'/static/favicon.ico'
|
||||||
];
|
];
|
||||||
self.addEventListener("install", (e) => {
|
self.addEventListener("install", (e) => {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"sw.js","sourceRoot":"","sources":["../src/sw.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"sw.js","sourceRoot":"","sources":["../src/sw.ts"],"names":[],"mappings":"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"}
|
||||||
205
static/webRTC.js
205
static/webRTC.js
@@ -1,12 +1,66 @@
|
|||||||
"use strict";
|
// import { wsConnection } from "./main";
|
||||||
class PeerManager {
|
export class PeerManager {
|
||||||
connect(peerID) {
|
connect(peerID) {
|
||||||
// Connect to the peer that has the peer id peerID
|
// Connect to the peer that has the peer id peerID
|
||||||
}
|
}
|
||||||
disconnect(peerID) {
|
disconnect(peerID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class PeerConnection {
|
export class PeerConnection {
|
||||||
|
constructor(remotePeerID, signaler) {
|
||||||
|
this.makingOffer = false;
|
||||||
|
this.ignoreOffer = false;
|
||||||
|
this.onSignallerMessage = async ({ data: { description, candidate } }) => {
|
||||||
|
try {
|
||||||
|
if (description) {
|
||||||
|
// const offerCollision =
|
||||||
|
// description.type === "offer" &&
|
||||||
|
// (this.makingOffer || this.rtcPeer.signalingState !== "stable");
|
||||||
|
// this.ignoreOffer = !polite && offerCollision;
|
||||||
|
if (this.ignoreOffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.rtcPeer.setRemoteDescription(description);
|
||||||
|
if (description.type === "offer") {
|
||||||
|
await this.rtcPeer.setLocalDescription();
|
||||||
|
this.signaler.send(JSON.stringify({ description: this.rtcPeer.localDescription }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (candidate) {
|
||||||
|
try {
|
||||||
|
await this.rtcPeer.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (!this.ignoreOffer) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.id = remotePeerID;
|
||||||
|
this.rtcPeer = new RTCPeerConnection(PeerConnection.config);
|
||||||
|
this.signaler = signaler;
|
||||||
|
;
|
||||||
|
this.rtcPeer.onnegotiationneeded = async () => {
|
||||||
|
try {
|
||||||
|
this.makingOffer = true;
|
||||||
|
await this.rtcPeer.setLocalDescription();
|
||||||
|
signaler.send(JSON.stringify({ description: this.rtcPeer.localDescription }));
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.makingOffer = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.rtcPeer.onicecandidate = ({ candidate }) => signaler.send(JSON.stringify({ candidate }));
|
||||||
|
this.ignoreOffer = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PeerConnection.config = {
|
PeerConnection.config = {
|
||||||
iceServers: [
|
iceServers: [
|
||||||
@@ -17,80 +71,75 @@ PeerConnection.config = {
|
|||||||
{ urls: "stun:stun4.l.google.com" },
|
{ urls: "stun:stun4.l.google.com" },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const config = {
|
// const config = {
|
||||||
iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
// iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
||||||
};
|
// };
|
||||||
let polite = true;
|
// let polite = true;
|
||||||
// const signaler = new SignalingChannel();
|
// const signaler = new SignalingChannel();
|
||||||
const signaler = {};
|
// const signaler: any = {}
|
||||||
const pc = new RTCPeerConnection(config);
|
// const rtcPeer = new RTCPeerConnection(config);
|
||||||
const constraints = { audio: true, video: true };
|
// // const constraints = { audio: true, video: true };
|
||||||
const selfVideo = document.querySelector("video.selfview");
|
// const selfVideo = document.querySelector("video.selfview");
|
||||||
const remoteVideo = document.querySelector("video.remoteview");
|
// const remoteVideo = document.querySelector("video.remoteview");
|
||||||
async function start() {
|
// async function start() {
|
||||||
try {
|
// try {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
// const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
for (const track of stream.getTracks()) {
|
// for (const track of stream.getTracks()) {
|
||||||
pc.addTrack(track, stream);
|
// pc.addTrack(track, stream);
|
||||||
}
|
// }
|
||||||
// selfVideo.srcObject = stream;
|
// selfVideo.srcObject = stream;
|
||||||
}
|
// } catch (err) {
|
||||||
catch (err) {
|
// console.error(err);
|
||||||
console.error(err);
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// rtcPeer.ontrack = ({ track, streams }) => {
|
||||||
pc.ontrack = ({ track, streams }) => {
|
// track.onunmute = () => {
|
||||||
track.onunmute = () => {
|
// if (remoteVideo.srcObject) {
|
||||||
// if (remoteVideo.srcObject) {
|
// return;
|
||||||
// return;
|
// }
|
||||||
// }
|
// remoteVideo.srcObject = streams[0];
|
||||||
// remoteVideo.srcObject = streams[0];
|
// };
|
||||||
};
|
// };
|
||||||
};
|
// makingOffer = false;
|
||||||
let makingOffer = false;
|
// rtcPeer.onnegotiationneeded = async () => {
|
||||||
pc.onnegotiationneeded = async () => {
|
// try {
|
||||||
try {
|
// // makingOffer = true;
|
||||||
makingOffer = true;
|
// await rtcPeer.setLocalDescription();
|
||||||
await pc.setLocalDescription();
|
// signaler.send({ description: rtcPeer.localDescription });
|
||||||
signaler.send({ description: pc.localDescription });
|
// } catch (err) {
|
||||||
}
|
// console.error(err);
|
||||||
catch (err) {
|
// } finally {
|
||||||
console.error(err);
|
// makingOffer = false;
|
||||||
}
|
// }
|
||||||
finally {
|
// };
|
||||||
makingOffer = false;
|
// rtcPeer.onicecandidate = ({ candidate }) => signaler.send({ candidate });
|
||||||
}
|
// let ignoreOffer = false;
|
||||||
};
|
// signaler.onmessage = async ({ data: { description, candidate } }: MessageEvent) => {
|
||||||
pc.onicecandidate = ({ candidate }) => signaler.send({ candidate });
|
// try {
|
||||||
let ignoreOffer = false;
|
// if (description) {
|
||||||
signaler.onmessage = async ({ data: { description, candidate } }) => {
|
// const offerCollision =
|
||||||
try {
|
// description.type === "offer" &&
|
||||||
if (description) {
|
// // (makingOffer || rtcPeer.signalingState !== "stable");
|
||||||
const offerCollision = description.type === "offer" &&
|
// ignoreOffer = !polite && offerCollision;
|
||||||
(makingOffer || pc.signalingState !== "stable");
|
// if (ignoreOffer) {
|
||||||
ignoreOffer = !polite && offerCollision;
|
// return;
|
||||||
if (ignoreOffer) {
|
// }
|
||||||
return;
|
// await rtcPeer.setRemoteDescription(description);
|
||||||
}
|
// if (description.type === "offer") {
|
||||||
await pc.setRemoteDescription(description);
|
// await rtcPeer.setLocalDescription();
|
||||||
if (description.type === "offer") {
|
// signaler.send({ description: rtcPeer.localDescription });
|
||||||
await pc.setLocalDescription();
|
// }
|
||||||
signaler.send({ description: pc.localDescription });
|
// } else if (candidate) {
|
||||||
}
|
// try {
|
||||||
}
|
// await rtcPeer.addIceCandidate(candidate);
|
||||||
else if (candidate) {
|
// } catch (err) {
|
||||||
try {
|
// if (!ignoreOffer) {
|
||||||
await pc.addIceCandidate(candidate);
|
// throw err;
|
||||||
}
|
// }
|
||||||
catch (err) {
|
// }
|
||||||
if (!ignoreOffer) {
|
// }
|
||||||
throw err;
|
// } catch (err) {
|
||||||
}
|
// console.error(err);
|
||||||
}
|
// }
|
||||||
}
|
// };
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=webRTC.js.map
|
//# sourceMappingURL=webRTC.js.map
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"webRTC.js","sourceRoot":"","sources":["../src/webRTC.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"webRTC.js","sourceRoot":"","sources":["../src/webRTC.ts"],"names":[],"mappings":"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"}
|
||||||
161
webRTC.js
161
webRTC.js
@@ -1,96 +1,83 @@
|
|||||||
"use strict";
|
// https://web.dev/articles/webrtc-basics
|
||||||
class PeerManager {
|
|
||||||
connect(peerID) {
|
// handles JSON.stringify/parse
|
||||||
// Connect to the peer that has the peer id peerID
|
|
||||||
}
|
class SignalingChannel {
|
||||||
disconnect(peerID) {
|
send(message) {
|
||||||
|
console.log(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class PeerConnection {
|
|
||||||
}
|
|
||||||
PeerConnection.config = {
|
function createPeerConnection(targetPeer) {
|
||||||
iceServers: [
|
|
||||||
{ urls: "stun:stun.l.google.com" },
|
const signaling = new SignalingChannel();
|
||||||
{ urls: "stun:stun1.l.google.com" },
|
|
||||||
{ urls: "stun:stun2.l.google.com" },
|
// const constraints = {audio: true, video: true};
|
||||||
{ urls: "stun:stun3.l.google.com" },
|
const configuration = { iceServers: [{ urls: 'stun:stunserver2024.stunprotocol.org' }] };
|
||||||
{ urls: "stun:stun4.l.google.com" },
|
const peerConnection = new RTCPeerConnection(configuration);
|
||||||
],
|
const sendChannel = peerConnection.createDataChannel('default');
|
||||||
};
|
|
||||||
const config = {
|
|
||||||
iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
// Send any ice candidates to the other peer.
|
||||||
};
|
peerConnection.onicecandidate = ({ candidate }) => signaling.send({ candidate });
|
||||||
let polite = true;
|
|
||||||
// const signaler = new SignalingChannel();
|
// Let the "negotiationneeded" event trigger offer generation.
|
||||||
const signaler = {};
|
peerConnection.onnegotiationneeded = async () => {
|
||||||
const pc = new RTCPeerConnection(config);
|
|
||||||
const constraints = { audio: true, video: true };
|
|
||||||
const selfVideo = document.querySelector("video.selfview");
|
|
||||||
const remoteVideo = document.querySelector("video.remoteview");
|
|
||||||
async function start() {
|
|
||||||
try {
|
try {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
await peerConnection.setLocalDescription(await peerConnection.createOffer());
|
||||||
for (const track of stream.getTracks()) {
|
// Send the offer to the other peer.
|
||||||
pc.addTrack(track, stream);
|
signaling.send({ desc: peerConnection.localDescription });
|
||||||
}
|
} catch (err) {
|
||||||
// selfVideo.srcObject = stream;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
pc.ontrack = ({ track, streams }) => {
|
|
||||||
track.onunmute = () => {
|
|
||||||
// if (remoteVideo.srcObject) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// remoteVideo.srcObject = streams[0];
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
let makingOffer = false;
|
// // Once remote track media arrives, show it in remote video element.
|
||||||
pc.onnegotiationneeded = async () => {
|
// peerConnection.ontrack = (event) => {
|
||||||
try {
|
// // Don't set srcObject again if it is already set.
|
||||||
makingOffer = true;
|
// if (remoteView.srcObject) return;
|
||||||
await pc.setLocalDescription();
|
// remoteView.srcObject = event.streams[0];
|
||||||
signaler.send({ description: pc.localDescription });
|
// };
|
||||||
|
|
||||||
|
// Call start() to initiate.
|
||||||
|
async function start() {
|
||||||
|
// try {
|
||||||
|
// Get local stream, show it in self-view, and add it to be sent.
|
||||||
|
// const stream =
|
||||||
|
// await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
// stream.getTracks().forEach((track) =>
|
||||||
|
// peerConnection.addTrack(track, stream));
|
||||||
|
// selfView.srcObject = stream;
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error(err);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
catch (err) {
|
|
||||||
|
signaling.onmessage = async ({ desc, candidate }) => {
|
||||||
|
try {
|
||||||
|
if (desc) {
|
||||||
|
// If you get an offer, you need to reply with an answer.
|
||||||
|
if (desc.type === 'offer') {
|
||||||
|
await peerConnection.setRemoteDescription(desc);
|
||||||
|
// const stream =
|
||||||
|
// await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
// stream.getTracks().forEach((track) =>
|
||||||
|
// peerConnection.addTrack(track, stream));
|
||||||
|
await peerConnection.setLocalDescription(await peerConnection.createAnswer());
|
||||||
|
signaling.send({ desc: peerConnection.localDescription });
|
||||||
|
} else if (desc.type === 'answer') {
|
||||||
|
await peerConnection.setRemoteDescription(desc);
|
||||||
|
} else {
|
||||||
|
console.log('Unsupported SDP type.');
|
||||||
|
}
|
||||||
|
} else if (candidate) {
|
||||||
|
await peerConnection.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
finally {
|
};
|
||||||
makingOffer = false;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
pc.onicecandidate = ({ candidate }) => signaler.send({ candidate });
|
|
||||||
let ignoreOffer = false;
|
|
||||||
signaler.onmessage = async ({ data: { description, candidate } }) => {
|
|
||||||
try {
|
|
||||||
if (description) {
|
|
||||||
const offerCollision = description.type === "offer" &&
|
|
||||||
(makingOffer || pc.signalingState !== "stable");
|
|
||||||
ignoreOffer = !polite && offerCollision;
|
|
||||||
if (ignoreOffer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await pc.setRemoteDescription(description);
|
|
||||||
if (description.type === "offer") {
|
|
||||||
await pc.setLocalDescription();
|
|
||||||
signaler.send({ description: pc.localDescription });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (candidate) {
|
|
||||||
try {
|
|
||||||
await pc.addIceCandidate(candidate);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (!ignoreOffer) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=webRTC.js.map
|
|
||||||
|
|||||||
Reference in New Issue
Block a user