multiple peers

This commit is contained in:
Klemek
2025-03-17 15:49:56 +01:00
parent 7365e76d2e
commit 4f7c14935e
3 changed files with 161 additions and 79 deletions
+2 -1
View File
@@ -13,7 +13,8 @@
* [x] Clean project * [x] Clean project
* [x] Better data upload by chunks and ensure consistancy * [x] Better data upload by chunks and ensure consistancy
* [x] Link sharing and better workflow * [x] Link sharing and better workflow
* [ ] Multiple peers * [x] Multiple peers
* [ ] Get peer data
* [ ] QRCode * [ ] QRCode
* [ ] DaisyUI redesign * [ ] DaisyUI redesign
* [ ] Multiple files * [ ] Multiple files
+12 -8
View File
@@ -29,8 +29,8 @@
</h1> </h1>
<br /> <br />
<div> <div>
<label>Local ID</label><br> <label>Status</label><br>
<input :value="localId" disabled /> <input :value="statusText" disabled />
<br> <br>
<br> <br>
<template v-if="serverIsReady"> <template v-if="serverIsReady">
@@ -39,16 +39,19 @@
<br> <br>
<br> <br>
</template> </template>
<template v-if="remoteId">
<label>Remote ID</label><br>
<input v-model="remoteId" disabled>
<br>
<br>
</template>
<template v-if="isServer"> <template v-if="isServer">
<input type="file" @change="onFileChange" :disabled="server.data" /> <input type="file" @change="onFileChange" :disabled="server.data" />
<br> <br>
<br>
<template v-for="client in server.clients">
<label>{{ client.id }} - {{ client.status }}</label><br>
<progress :value="client.sent" :max="downloadTotal" aria-busy="!client.connected"></progress>
<br>
<br>
</template> </template>
</template>
<template v-else>
<label v-if="fileName">{{ prettyFileSize }} - "{{ fileName }}"</label><br>
<template v-if="readyToDownload"> <template v-if="readyToDownload">
<input type="submit" @click.prevent="onDownload" value="Download" /> <input type="submit" @click.prevent="onDownload" value="Download" />
<br> <br>
@@ -59,6 +62,7 @@
<br> <br>
<br> <br>
</template> </template>
</template>
<template v-if="error"> <template v-if="error">
<span class="red">{{error}}</span> <span class="red">{{error}}</span>
<br> <br>
+138 -61
View File
@@ -33,17 +33,17 @@ const app = createApp({
fileSize: null, fileSize: null,
localId: null, localId: null,
remoteId: null,
connection: null, // TODO multiple connections
isServer: true, isServer: true,
server: { server: {
clients: [],
url: null, url: null,
data: null, // TODO multiple files data: null, // TODO multiple files
copied: false, copied: false,
}, },
client: { client: {
remoteId: null, remoteId: null,
connection: null,
connected: false,
downloadStart: null, downloadStart: null,
received: [], received: [],
buffer: null, // TODO multiple files buffer: null, // TODO multiple files
@@ -55,14 +55,11 @@ const app = createApp({
canConnect() { canConnect() {
return this.peer !== null && this.localId !== null; return this.peer !== null && this.localId !== null;
}, },
isConnected() {
return this.connection !== null;
},
readyToDownload() { readyToDownload() {
return this.client.buffer !== null && !this.client.downloadStart; return this.client.buffer !== null && !this.client.downloadStart;
}, },
serverIsReady() { serverIsReady() {
return this.server.data !== null; return this.canConnect && this.server.data !== null;
}, },
downloading() { downloading() {
return this.client.downloadStart !== null; return this.client.downloadStart !== null;
@@ -82,6 +79,45 @@ const app = createApp({
} }
return "Copy link"; return "Copy link";
}, },
statusText() {
if (this.error) {
return this.error;
}
if (!this.canConnect) {
return 'Acquiring ID...';
}
if (this.isServer) {
if (! this.server.data) {
return 'Waiting for file upload';
}
return 'Ready to send file';
}
if (! this.client.connected) {
return 'Connecting to peer...';
}
if (this.readyToDownload) {
return 'Ready to download';
}
if (! this.downloading) {
return 'Waiting for file info...';
}
if (this.downloadProgress > this.downloadTotal) {
return 'File downloaded';
}
return 'Downloading...';
},
prettyFileSize() {
let size = this.fileSize / 1000;
if (size < 1000) {
return `${size.toFixed(2)} KB`;
}
size /= 1000;
if (size < 1000) {
return `${size.toFixed(2)} MB`;
}
size /= 1000;
return `${size.toFixed(2)} GB`;
},
}, },
watch: {}, watch: {},
updated() { updated() {
@@ -133,12 +169,27 @@ const app = createApp({
this.peer.on("disconnected", this.onPeerDisconnected); this.peer.on("disconnected", this.onPeerDisconnected);
this.peer.on("error", this.onPeerError); this.peer.on("error", this.onPeerError);
}, },
initConnection(conn) { initServerConnection(conn) {
this.connection = conn; const index = this.server.clients.length;
this.connection.on("open", this.onConnectionOpen); this.server.clients.push({
this.connection.on("close", this.onConnectionClose); connection: conn,
this.connection.on("data", this.onConnectionData); id: conn.peer,
this.connection.on("error", this.onConnectionError); done: false,
sent: 0,
connected: false,
status: 'Connecting...',
});
conn.on("open", () => this.onServerConnectionOpen(index));
conn.on("close", () => this.onServerConnectionClose(index));
conn.on("data", (data) => this.onServerConnectionData(index, data));
conn.on("error", (err) => this.onServerConnectionError(index, err));
},
initClientConnection(conn) {
this.client.connection = conn;
conn.on("open", this.onClientConnectionOpen);
conn.on("close", this.onClientConnectionClose);
conn.on("data", this.onClientConnectionData);
conn.on("error", this.onClientConnectionError);
}, },
clientCreateStream() { clientCreateStream() {
try { try {
@@ -156,6 +207,11 @@ const app = createApp({
link.download = this.fileName; link.download = this.fileName;
link.click(); link.click();
}, },
clientOpenConnection() {
this.initClientConnection(
this.peer.connect(this.client.remoteId, { reliable: false }),
);
},
// PEER EVENTS // PEER EVENTS
onPeerOpen(id) { onPeerOpen(id) {
console.log("onPeerOpen", id); console.log("onPeerOpen", id);
@@ -164,17 +220,14 @@ const app = createApp({
if (this.isServer) { if (this.isServer) {
this.server.url = `${window.location.href}?s=${id}`; this.server.url = `${window.location.href}?s=${id}`;
} else { } else {
this.initConnection( this.clientOpenConnection();
this.peer.connect(this.client.remoteId, { reliable: false }),
);
this.remoteId = this.client.remoteId;
} }
}, },
onPeerConnection(conn) { onPeerConnection(conn) {
console.log("onPeerConnection", conn); console.log("onPeerConnection", conn);
// TODO multiple connections for server if (this.isServer) {
this.initConnection(conn); this.initServerConnection(conn);
this.remoteId = conn.peer; }
}, },
onPeerClose() { onPeerClose() {
console.log("onPeerClose"); console.log("onPeerClose");
@@ -190,15 +243,42 @@ const app = createApp({
this.peer = null; this.peer = null;
setTimeout(this.initPeer, 1000); setTimeout(this.initPeer, 1000);
}, },
// CONNECTION EVENTS // SERVER CONNECTION EVENTS
onConnectionOpen() { onServerConnectionOpen(index) {
console.log("onConnectionOpen"); console.log("onServerConnectionOpen", index);
if (this.isServer) { this.server.clients[index].connected = true;
this.sendServerInfo(); this.server.clients[index].status = 'Connected';
this.sendServerInfo(index);
},
onServerConnectionData(index, data) {
console.log("onServerConnectionData", index, data.type);
switch (data.type) {
case MESSAGE_TYPE.ClientSeek:
this.handleClientSeek(index, data);
break;
case MESSAGE_TYPE.ClientDone:
this.handleClientDone(index, data);
break;
default:
console.error("Invalid message type");
break;
} }
}, },
onConnectionData(data) { onServerConnectionClose(index) {
console.log("onConnectionData", data.type); console.log("onServerConnectionClose", index);
this.server.clients[index].status = 'Disconnected';
},
onServerConnectionError(index, err) {
console.log("onServerConnectionError", index, err);
this.server.clients[index].status = 'Error';
},
// CLIENT CONNECTION EVENTS
onClientConnectionOpen() {
console.log("onClientConnectionOpen");
this.client.connected = true;
},
onClientConnectionData(data) {
console.log("onClientConnectionData", data.type);
switch (data.type) { switch (data.type) {
case MESSAGE_TYPE.ServerInfo: case MESSAGE_TYPE.ServerInfo:
this.handleServerInfo(data); this.handleServerInfo(data);
@@ -209,33 +289,26 @@ const app = createApp({
case MESSAGE_TYPE.ServerDone: case MESSAGE_TYPE.ServerDone:
this.handleServerDone(data); this.handleServerDone(data);
break; break;
case MESSAGE_TYPE.ClientSeek:
this.handleClientSeek(data);
break;
case MESSAGE_TYPE.ClientDone:
this.handleClientDone(data);
break;
default: default:
console.error("Invalid message type"); console.error("Invalid message type");
break; break;
} }
}, },
onConnectionClose() { onClientConnectionClose() {
console.log("onConnectionClose"); console.log("onClientConnectionClose");
this.connection = null; this.client.connection = null;
// TODO handle conn close
}, },
onConnectionError(err) { onClientConnectionError(err) {
console.log("onConnectionError", err); console.log("onClientConnectionError", err);
// TODO handle error this.error = `Connection failed: ${err.type}. Reconnecting...`
throw err; setTimeout(this.clientOpenConnection, 1000);
}, },
// EXCHANGES // EXCHANGES
sendServerInfo() { sendServerInfo(index) {
this.connection.send({ this.server.clients[index].connection.send({
type: MESSAGE_TYPE.ServerInfo, type: MESSAGE_TYPE.ServerInfo,
fileName: this.server.data ? this.fileName : null, fileName: this.fileName,
fileSize: this.server.data ? this.fileSize : null, fileSize: this.fileSize,
}); });
}, },
handleServerInfo(data) { handleServerInfo(data) {
@@ -243,12 +316,13 @@ const app = createApp({
this.fileSize = data.fileSize; this.fileSize = data.fileSize;
this.clientCreateStream(); this.clientCreateStream();
}, },
sendServerChunk(index) { sendServerChunk(index, chunkIndex) {
this.connection.send({ this.server.clients[index].connection.send({
type: MESSAGE_TYPE.ServerChunk, type: MESSAGE_TYPE.ServerChunk,
index, index: chunkIndex,
bytes: this.server.data.slice(index, index + MAX_CHUNK_SIZE), bytes: this.server.data.slice(chunkIndex, chunkIndex + MAX_CHUNK_SIZE),
}); });
this.server.clients[index].sent += MAX_CHUNK_SIZE;
}, },
handleServerChunk(data) { handleServerChunk(data) {
new Uint8Array(this.client.buffer).set( new Uint8Array(this.client.buffer).set(
@@ -257,10 +331,11 @@ const app = createApp({
); );
this.client.received.push(data.index); this.client.received.push(data.index);
}, },
sendServerDone() { sendServerDone(index) {
this.connection.send({ this.server.clients[index].connection.send({
type: MESSAGE_TYPE.ServerDone, type: MESSAGE_TYPE.ServerDone,
}); });
}, },
handleServerDone() { handleServerDone() {
const indexes = []; const indexes = [];
@@ -277,30 +352,32 @@ const app = createApp({
} }
}, },
sendClientSeek(indexes = null) { sendClientSeek(indexes = null) {
this.connection.send({ this.client.connection.send({
type: MESSAGE_TYPE.ClientSeek, type: MESSAGE_TYPE.ClientSeek,
indexes, indexes,
}); });
}, },
handleClientSeek(data) { handleClientSeek(index, data) {
if (data.indexes) { if (data.indexes) {
data.indexes.forEach((index) => { data.indexes.forEach((chunkIndex) => {
setTimeout(() => this.sendServerChunk(index)); setTimeout(() => this.sendServerChunk(index, chunkIndex));
}); });
} else { } else {
for (let index = 0; index < this.fileSize; index += MAX_CHUNK_SIZE) { for (let chunkIndex = 0; chunkIndex < this.fileSize; chunkIndex += MAX_CHUNK_SIZE) {
setTimeout(() => this.sendServerChunk(index)); setTimeout(() => this.sendServerChunk(index, chunkIndex));
} }
} }
setTimeout(this.sendServerDone); setTimeout(() => this.sendServerDone(index));
}, },
sendClientDone() { sendClientDone() {
this.connection.send({ this.client.connection.send({
type: MESSAGE_TYPE.ClientDone, type: MESSAGE_TYPE.ClientDone,
}); });
}, },
handleClientDone() { handleClientDone(index) {
this.connection.close(); this.server.clients[index].connection.close();
this.server.clients[index].done = true;
this.server.clients[index].status = 'Done';
}, },
// UI EVENTS // UI EVENTS
onFileChange(event) { onFileChange(event) {