wip v2
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
|
||||
// import { serveDir } from "jsr:@std/http/file-server"
|
||||
|
||||
import { brotli } from "jsr:@deno-library/compress";
|
||||
|
||||
|
||||
const memoryCache = true;
|
||||
const filepathResponseCache: Map<string, Response> = new Map();
|
||||
@@ -30,9 +32,15 @@ async function serveFile(filename: string) {
|
||||
|
||||
|
||||
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('Content-Encoding', 'br');
|
||||
|
||||
|
||||
if (filename.endsWith('.js')) {
|
||||
newResponse.headers.set('content-type', 'application/javascript')
|
||||
@@ -89,6 +97,15 @@ interface HelloMessage {
|
||||
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 {
|
||||
// socket:WebSocket;
|
||||
@@ -96,6 +113,7 @@ interface HelloMessage {
|
||||
// }
|
||||
|
||||
// const peerStates:Map<string, PeerState> = new Map();
|
||||
const bootstrapPeers: Set<string> = new Set();
|
||||
const userPeers: Map<string, Set<string>> = new Map();
|
||||
const peerSockets: Map<string, WebSocket> = 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) {
|
||||
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}`);
|
||||
|
||||
bootstrapPeers.delete(peerID);
|
||||
peerSockets.delete(peerID);
|
||||
deletePeerFromUserPeers(peerID);
|
||||
});
|
||||
@@ -303,6 +347,7 @@ async function main() {
|
||||
|
||||
messageDispatch.set('ping', pingHandler);
|
||||
messageDispatch.set('hello', helloHandler);
|
||||
messageDispatch.set('hello2', hello2Handler);
|
||||
messageDispatch.set('peer_message', peerMessageHandler);
|
||||
|
||||
Deno.serve({
|
||||
|
||||
@@ -3,7 +3,7 @@ They can delete your data and the value of your data [like this](https://twitter
|
||||
|
||||
|
||||
# 0. Some ranting and a blueprint for the future.
|
||||
I'm going to outline a few things that I think are really bad patterns wiht current software and creators ecosystems. Them I'm gonna tell you what I think we should do about it.
|
||||
I'm going to outline a few things that I think are really bad patterns with current software and creators ecosystems. Then I'm gonna tell you what I think we should do about it.
|
||||
|
||||
# 1. You don't own anything
|
||||
For a long time, i've been increasingly frustrated with software that I have to use on a daily basis. This is due to a number of factors:
|
||||
@@ -28,7 +28,7 @@ Today even your phone is incredibly fast and could search hundreds of megs of te
|
||||
|
||||
For downloadable software, the pitch was that instead of getting a single version of the software, you would now get all updates as long as you continued to subscribe, and that ongoing paymets would fund ongoing development and innovation.
|
||||
|
||||
If you tended to upgrade to new versions, that *seems* to be pretty much equivalent, but there are a couple of hidden problems that eventually came to dominate this model. Firstly, for things like Photoshop, the pace of innovation pretty much plataued. Yes there are aamzing new AI tools in the most recent versions of PS, but if you don't use those new tools and are just a traditional user, the appliaction hasn't really changed in a decade. Secondly, Even if the price were equivalent, if you stop paying, then you lose access. So that's not equivalent at all.
|
||||
If you tended to upgrade to new versions, that *seems* to be pretty much equivalent, but there are a couple of hidden problems that eventually came to dominate this model. Firstly, for things like Photoshop, the pace of innovation pretty much plataued. Yes there are amazing new AI tools in the most recent versions of PS, but if you don't use those new tools and are just a traditional user, the appliaction hasn't really changed in a decade. Secondly, Even if the price were equivalent, if you stop paying, then you lose access. So that's not equivalent at all.
|
||||
|
||||
Ultimately, this wasn't about delivering software innovation. It was about maximizing profits for the companies that make the software. If you need to keep subscribing forever, they can turn what used to be a one-time $200 purchase, into a lifetime of payment for new features, regardless if you want them or not.
|
||||
|
||||
@@ -48,7 +48,7 @@ In the olden days, you could either listen to the radio, or go to a store and bu
|
||||
## Modern day
|
||||
Today we can listen to pretty much any track we like from any artist instantly by using a streaming service like Spotify or Apple Music or by finding music on YouTube. This is truly like a superpower and completely mindblowing compared to how we used to listen to music!
|
||||
|
||||
The downside is that in order to provide this service, all the music has to be uploaded to a central service that a company owns, and the company charges a fee to stream the music to users, giving the rest to the musicians. The free that the streaming services have been charging has been increasing ever since they launched meaning that over time, the wealth is being moved from the musicians who actually create the music to the companies who stream it and now have a monopoly on those streaming services. They also gatekeep who can get onto the services.
|
||||
The downside is that in order to provide this service, all the music has to be uploaded to a central service that a company owns, and the company charges a fee to stream the music to users, giving the some small part to the musicians. The fee that the streaming services have been charging has been increasing ever since they launched meaning that over time, the wealth is being moved from the musicians who actually create the music to the companies who stream it and now have a monopoly on those streaming services. They also gatekeep who can get onto the services.
|
||||
|
||||
```javascript
|
||||
function foo() {
|
||||
|
||||
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 { openDatabase, getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db";
|
||||
|
||||
// import {PeerConnection} from "webRTC";
|
||||
|
||||
// declare let WebTorrent: any;
|
||||
|
||||
@@ -279,6 +280,88 @@ interface PeerMessage {
|
||||
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 {
|
||||
websocket: WebSocket | null = null;
|
||||
userID = "";
|
||||
@@ -304,12 +387,19 @@ class wsConnection {
|
||||
this.messageHandlers.set('pong', this.pongHandler);
|
||||
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_response', this.getPostIdsForUserResponseHandler.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('send_webrtc_offer', this.sendWebRTCOfferHandler.bind(this));
|
||||
this.peerMessageHandlers.set('send_webrtc_offer_response', this.getPostIdsForUserResponseHandler.bind(this));
|
||||
|
||||
|
||||
|
||||
window.addEventListener('beforeunload', () => this.disconnect());
|
||||
|
||||
this.connect();
|
||||
@@ -340,6 +430,10 @@ class wsConnection {
|
||||
pongHandler(data: any) {
|
||||
}
|
||||
|
||||
async sendWebRTCOfferHandler(data:any) {
|
||||
|
||||
}
|
||||
|
||||
async getPostIdsForUserResponseHandler(data: any) {
|
||||
// log(`getPostsForUserResponse: ${data}`)
|
||||
|
||||
@@ -1020,6 +1114,8 @@ class App {
|
||||
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() {
|
||||
let id = localStorage.getItem("peer_id");
|
||||
|
||||
@@ -1540,7 +1636,36 @@ class App {
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
@@ -1579,11 +1704,6 @@ class App {
|
||||
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')) {
|
||||
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 = [
|
||||
'/static/index.html',
|
||||
'/static/main.css',
|
||||
'/static/main.js',
|
||||
'/static/main2.js',
|
||||
'/static/lib/marked.min.js',
|
||||
'/static/lib/qrcode.min.js',
|
||||
'/static/db.js',
|
||||
'/static/PeerManager.js',
|
||||
'/static/IDUtils.js',
|
||||
'/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">
|
||||
{
|
||||
"imports": {
|
||||
"db": "/static/db.js"
|
||||
"db": "/static/db.js",
|
||||
"IDUtils": "/static/IDUtils.js",
|
||||
"PeerManager": "/static/PeerManager.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<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/qrcode.min.js" as="script">
|
||||
|
||||
<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/qrcode.min.js"></script>
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ hr {
|
||||
|
||||
#log {
|
||||
font-family: monospace;
|
||||
text-wrap: nowrap;
|
||||
text-wrap: wrap;
|
||||
font-size: 10px;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
|
||||
@@ -191,6 +191,69 @@ async function compressString(input) {
|
||||
// Convert the compressed data to a Uint8Array
|
||||
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 {
|
||||
constructor(userID, peerID, IDsToSync) {
|
||||
this.websocket = null;
|
||||
@@ -232,10 +295,13 @@ class wsConnection {
|
||||
this.messageHandlers.set('hello', this.helloResponseHandler.bind(this));
|
||||
this.messageHandlers.set('pong', this.pongHandler);
|
||||
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_response', this.getPostIdsForUserResponseHandler.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('send_webrtc_offer', this.sendWebRTCOfferHandler.bind(this));
|
||||
this.peerMessageHandlers.set('send_webrtc_offer_response', this.getPostIdsForUserResponseHandler.bind(this));
|
||||
window.addEventListener('beforeunload', () => this.disconnect());
|
||||
this.connect();
|
||||
}
|
||||
@@ -260,6 +326,8 @@ class wsConnection {
|
||||
}
|
||||
pongHandler(data) {
|
||||
}
|
||||
async sendWebRTCOfferHandler(data) {
|
||||
}
|
||||
async getPostIdsForUserResponseHandler(data) {
|
||||
// log(`getPostsForUserResponse: ${data}`)
|
||||
let message = data.message;
|
||||
@@ -765,6 +833,7 @@ class App {
|
||||
this.websocket?.broadcastNewPost(userID, post);
|
||||
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() {
|
||||
let id = localStorage.getItem("peer_id");
|
||||
if (!id) {
|
||||
@@ -1167,6 +1236,31 @@ class App {
|
||||
let db = await openDatabase(this.userID);
|
||||
}
|
||||
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');
|
||||
// Get initial state and route from URL and user agent etc
|
||||
// Set local state (userid etc) based on that.
|
||||
@@ -1191,10 +1285,6 @@ class App {
|
||||
await this.initDB();
|
||||
this.connectURL = `${document.location.origin}/connect/${this.userID}`;
|
||||
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')) {
|
||||
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 = [
|
||||
'/static/index.html',
|
||||
'/static/main.css',
|
||||
'/static/main.js',
|
||||
'/static/main2.js',
|
||||
'/static/lib/marked.min.js',
|
||||
'/static/lib/qrcode.min.js',
|
||||
'/static/db.js',
|
||||
'/static/PeerManager.js',
|
||||
'/static/IDUtils.js',
|
||||
'/static/favicon.ico'
|
||||
];
|
||||
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";
|
||||
class PeerManager {
|
||||
// import { wsConnection } from "./main";
|
||||
export class PeerManager {
|
||||
connect(peerID) {
|
||||
// Connect to the peer that has the peer id 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 = {
|
||||
iceServers: [
|
||||
@@ -17,80 +71,75 @@ PeerConnection.config = {
|
||||
{ urls: "stun:stun4.l.google.com" },
|
||||
],
|
||||
};
|
||||
const config = {
|
||||
iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
||||
};
|
||||
let polite = true;
|
||||
// const config = {
|
||||
// iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
|
||||
// };
|
||||
// let polite = true;
|
||||
// const signaler = new SignalingChannel();
|
||||
const signaler = {};
|
||||
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 } }) => {
|
||||
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);
|
||||
}
|
||||
};
|
||||
// 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);
|
||||
// }
|
||||
// };
|
||||
//# 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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"}
|
||||
163
webRTC.js
163
webRTC.js
@@ -1,96 +1,83 @@
|
||||
"use strict";
|
||||
class PeerManager {
|
||||
connect(peerID) {
|
||||
// Connect to the peer that has the peer id peerID
|
||||
}
|
||||
disconnect(peerID) {
|
||||
// https://web.dev/articles/webrtc-basics
|
||||
|
||||
// handles JSON.stringify/parse
|
||||
|
||||
class SignalingChannel {
|
||||
send(message) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
class PeerConnection {
|
||||
}
|
||||
PeerConnection.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 = {};
|
||||
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);
|
||||
|
||||
|
||||
function createPeerConnection(targetPeer) {
|
||||
|
||||
const signaling = new SignalingChannel();
|
||||
|
||||
// const constraints = {audio: true, video: true};
|
||||
const configuration = { iceServers: [{ urls: 'stun:stunserver2024.stunprotocol.org' }] };
|
||||
const peerConnection = new RTCPeerConnection(configuration);
|
||||
const sendChannel = peerConnection.createDataChannel('default');
|
||||
|
||||
|
||||
// Send any ice candidates to the other peer.
|
||||
peerConnection.onicecandidate = ({ candidate }) => signaling.send({ candidate });
|
||||
|
||||
// Let the "negotiationneeded" event trigger offer generation.
|
||||
peerConnection.onnegotiationneeded = async () => {
|
||||
try {
|
||||
await peerConnection.setLocalDescription(await peerConnection.createOffer());
|
||||
// Send the offer to the other peer.
|
||||
signaling.send({ desc: peerConnection.localDescription });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
// 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 });
|
||||
|
||||
// // Once remote track media arrives, show it in remote video element.
|
||||
// peerConnection.ontrack = (event) => {
|
||||
// // Don't set srcObject again if it is already set.
|
||||
// if (remoteView.srcObject) return;
|
||||
// remoteView.srcObject = event.streams[0];
|
||||
// };
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=webRTC.js.map
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user