@trap_stevo/filetide 0.0.38 → 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,13 @@ const {
5
5
  } = require("@trap_stevo/iotide-client");
6
6
  const chalk = require("chalk");
7
7
  const path = require("path");
8
+ const fs = require("fs");
8
9
  const {
9
10
  FileNetUtilityManager,
10
11
  significantMessageColors,
11
12
  errorMessageColors
12
13
  } = require("./HUDManagers/FileNetUtilityManager");
14
+ const FileTransferRecoveryManager = require("./HUDManagers/FileTransferRecoveryManager");
13
15
  const {
14
16
  FileMessagerConfigManager
15
17
  } = require("./HUDManagers/FileMessagerConfigManager");
@@ -19,7 +21,6 @@ const {
19
21
  const {
20
22
  FileUtilityManager
21
23
  } = require("./HUDManagers/FileUtilityManager");
22
- const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
23
24
  class FileMessagerClient {
24
25
  constructor(options = {}, transportOptions = {
25
26
  parallelChunks: 3,
@@ -27,25 +28,27 @@ class FileMessagerClient {
27
28
  }) {
28
29
  this.clientOptions = FileMessagerConfigManager.getClientOptions(options);
29
30
  this.transporter = new FileTransferManager(transportOptions);
30
- this.progressBarManager = new ConsoleProgressBarManager([], {
31
- completionMessageColor: chalk.blue.bold
32
- });
33
31
  this.activeTransferTypes = new Map();
34
32
  this.clientTide = new HUDIoTide();
35
33
  this.clients = new Map();
36
34
  }
37
- createClientInstance(clientID, clientOptions, options = {}) {
35
+ createClientInstance(clientID, clientOptions, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
38
36
  this.clientTide.createIO(clientID, clientOptions.url, {
39
37
  transports: ["websocket"],
40
38
  ...options
39
+ }, headers, authURL, authHeaders, useAuthentication, queryAuthToken, onConnect, () => {
40
+ FileTransferRecoveryManager.flushToDisk();
41
+ if (onDisconnect) {
42
+ onDisconnect();
43
+ }
41
44
  });
42
45
  FileNetUtilityManager.outputGradient(`\n[FileTide ~ File Messager] ~ Created client with ID ${clientID} and socket ${clientID}!`, significantMessageColors);
43
46
  return clientID;
44
47
  }
45
- joinRoom(userID, roomName, options = {}) {
48
+ joinRoom(userID, roomName, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
46
49
  const socketName = this.createClientInstance(userID, {
47
50
  url: this.clientOptions.url
48
- }, options);
51
+ }, options, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
49
52
  this.clientTide.joinChannel(socketName, roomName, userID, () => {
50
53
  FileNetUtilityManager.outputGradient(`\n[FileTide ~ File Messager] ~ Client ~ ${userID} joined room ${roomName} successfully!`, significantMessageColors);
51
54
  this.clients.set(userID, {
@@ -111,8 +114,11 @@ class FileMessagerClient {
111
114
  * @param {Array} filesData - Array of file data objects from the directory.
112
115
  * @param {string} baseDirectory - The base directory path that contains the files.
113
116
  * @param {string} destinationPath - The path to which the files should be sent.
117
+ * @param {Number} tideSize - The tide size in sending files (500)
118
+ * @param {Number} minTideSize - The minimum tide size in sending files (10)
119
+ * @param {Number} maxTideSize - The maximum tide size in sending files (2000)
114
120
  */
115
- async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
121
+ async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath, tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
116
122
  try {
117
123
  const baseDirectoryName = path.basename(baseDirectory);
118
124
  const adjustedDestinationPath = FileUtilityManager.normalizePath(path.join(destinationPath, baseDirectoryName));
@@ -131,6 +137,9 @@ class FileMessagerClient {
131
137
  const sendFilePromises = filesData.map(async (fileInfo, index) => {
132
138
  const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
133
139
  const fileDestinationPath = FileUtilityManager.normalizePath(path.join(adjustedDestinationPath, relativeFilePath));
140
+ if (fileInfo.largeFile) {
141
+ return await this.sendDirectoryLargeFile(clientID, recipientId, fileInfo.fileName, fileInfo.filePath, fileDestinationPath, fileInfo.fileSize, index, tideSize, minTideSize, maxTideSize);
142
+ }
134
143
  return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
135
144
  });
136
145
  Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer directory: ", error));
@@ -138,29 +147,28 @@ class FileMessagerClient {
138
147
  FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused directory transfer: ${error}`, errorMessageColors);
139
148
  }
140
149
  }
150
+ async sendDirectoryLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destinationPath = null, fileSize, index = 0, tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
151
+ let initiatingTransfer = false;
152
+ if (index === 0) {
153
+ initiatingTransfer = true;
154
+ }
155
+ return await this.sendLargeFile(clientID, recipientId, fileName, filePath, destinationPath, fileSize, tideSize, minTideSize, maxTideSize, initiatingTransfer, true);
156
+ }
141
157
  sendDirectoryFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
142
158
  if (!file) {
143
159
  return;
144
160
  }
145
161
  if (index === 0) {
146
- this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
147
- this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
148
- this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
149
162
  this.clientTide.emitEvent(clientID, "transfer-start", {
150
163
  fileName,
151
164
  senderID: clientID,
152
165
  recipientID: recipientId,
153
- path: directoryPath || filePath
166
+ path: directoryPath || filePath,
167
+ fileSize
154
168
  });
155
169
  }
156
170
  this.transporter.sendFile(file, {
157
- onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
158
- if (chunkIndex === 0) {
159
- this.progressBarManager.addTask({
160
- name: fileName,
161
- size: fileSize
162
- });
163
- }
171
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks) => {
164
172
  return new Promise((resolve, reject) => {
165
173
  this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
166
174
  senderID: clientID,
@@ -169,6 +177,7 @@ class FileMessagerClient {
169
177
  fileChunk: chunkData,
170
178
  totalChunks,
171
179
  chunkIndex,
180
+ chunkSize,
172
181
  filePath,
173
182
  fileSize
174
183
  });
@@ -186,19 +195,124 @@ class FileMessagerClient {
186
195
  resolve();
187
196
  });
188
197
  },
189
- onProgress: progress => {
190
- this.progressBarManager.updateTaskProgress(fileName, 512 * 1024);
191
- },
192
198
  originDetails: {
193
199
  recipientID: recipientId,
194
200
  senderID: clientID
195
201
  },
202
+ recipientID: recipientId,
196
203
  fileDetails: {
204
+ destinationPath: filePath,
197
205
  name: fileName,
198
206
  path: filePath,
199
207
  size: fileSize
200
208
  }
201
- }).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
209
+ }, this.clientTide).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
210
+ }
211
+ async sendLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destination = process.cwd(), fileSize, tideSize = 500, minTideSize = 10, maxTideSize = 2000, initiatingTransfer = true, inDirectory = false) {
212
+ const destinationPath = FileUtilityManager.normalizePath(path.join(destination, fileName));
213
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer large file ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
214
+ const incomingType = this.activeTransferTypes.get(filePath);
215
+ if (!inDirectory && initiatingTransfer) {
216
+ const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
217
+ if (incomingType) {
218
+ this.activeTransferTypes.delete(filePath);
219
+ }
220
+ if (!transferAllowed) {
221
+ return;
222
+ }
223
+ this.clientTide.emitEvent(clientID, "transfer-start", {
224
+ fileName,
225
+ senderID: clientID,
226
+ recipientID: recipientId,
227
+ path: destination,
228
+ fileSize
229
+ });
230
+ } else if (inDirectory && !initiatingTransfer) {
231
+ if (incomingType) {
232
+ this.activeTransferTypes.delete(filePath);
233
+ }
234
+ }
235
+ const fileHandle = await fs.promises.open(filePath, "r");
236
+ const fileDetails = {
237
+ destinationPath,
238
+ name: fileName,
239
+ path: filePath,
240
+ size: fileSize
241
+ };
242
+ try {
243
+ let lastSendTime = Date.now();
244
+ const maxBatchSize = maxTideSize;
245
+ const minBatchSize = minTideSize;
246
+ let batchSize = tideSize > maxBatchSize || tideSize > minBatchSize ? tideSize : tideSize <= minBatchSize ? minBatchSize : 10;
247
+ const chunkBatch = [];
248
+ await this.transporter.sendLargeFile(fileHandle, {
249
+ recipientID: recipientId,
250
+ originDetails: {
251
+ recipientID: recipientId,
252
+ senderID: clientID
253
+ },
254
+ fileDetails,
255
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
256
+ return new Promise(resolve => {
257
+ chunkBatch.push({
258
+ senderID: clientID,
259
+ recipientId,
260
+ fileName,
261
+ fileChunk: chunkData,
262
+ completedChunks,
263
+ totalChunks,
264
+ chunkIndex,
265
+ chunkSize,
266
+ filePath: destination,
267
+ fileSize
268
+ });
269
+ if (chunkBatch.length >= batchSize) {
270
+ const now = Date.now();
271
+ const rtt = now - lastSendTime;
272
+ lastSendTime = now;
273
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
274
+ chunkBatch
275
+ });
276
+ chunkBatch.length = 0;
277
+ if (rtt >= 200 && batchSize < maxBatchSize) {
278
+ batchSize = Math.min(batchSize + 50, maxBatchSize);
279
+ } else if (rtt < 200 && batchSize > minBatchSize) {
280
+ batchSize = Math.max(batchSize - 50, minBatchSize);
281
+ }
282
+ }
283
+ resolve();
284
+ });
285
+ },
286
+ onComplete: transferId => {
287
+ return new Promise(resolve => {
288
+ if (chunkBatch.length > 0) {
289
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
290
+ chunkBatch
291
+ });
292
+ chunkBatch.length = 0;
293
+ }
294
+ this.clientTide.emitEvent(clientID, "transfer-complete", {
295
+ senderID: clientID,
296
+ recipientId,
297
+ fileName,
298
+ filePath: destination
299
+ });
300
+ resolve();
301
+ });
302
+ },
303
+ onTiding: () => {
304
+ this.clientTide.emitEvent(clientID, "client-tiding", {
305
+ senderID: clientID,
306
+ message: "Transfer ongoing..."
307
+ });
308
+ }
309
+ }, this.clientTide);
310
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Large file ~ ${fileName} transfer complete!`, significantMessageColors);
311
+ } catch (error) {
312
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Error transferring large file: ${error.message}`, errorMessageColors);
313
+ } finally {
314
+ await fileHandle.close();
315
+ }
202
316
  }
203
317
  async sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize) {
204
318
  if (!file) {
@@ -214,31 +328,25 @@ class FileMessagerClient {
214
328
  if (!transferAllowed) {
215
329
  return;
216
330
  }
217
- this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
218
- this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
219
- this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
220
331
  this.clientTide.emitEvent(clientID, "transfer-start", {
221
332
  fileName,
222
333
  senderID: clientID,
223
334
  recipientID: recipientId,
224
- path: filePath
335
+ path: filePath,
336
+ fileSize
225
337
  });
226
- this.transporter.sendFile(file, {
227
- onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
228
- if (chunkIndex === 0) {
229
- this.progressBarManager.addTask({
230
- name: fileName,
231
- size: fileSize
232
- });
233
- }
338
+ await this.transporter.sendFile(file, {
339
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
234
340
  return new Promise((resolve, reject) => {
235
341
  this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
236
342
  senderID: clientID,
237
343
  recipientId,
238
344
  fileName,
239
345
  fileChunk: chunkData,
346
+ completedChunks,
240
347
  totalChunks,
241
348
  chunkIndex,
349
+ chunkSize,
242
350
  filePath,
243
351
  fileSize
244
352
  });
@@ -256,19 +364,17 @@ class FileMessagerClient {
256
364
  resolve();
257
365
  });
258
366
  },
259
- onProgress: progress => {
260
- this.progressBarManager.updateTaskProgress(fileName, 512 * 1024);
261
- },
262
367
  originDetails: {
263
368
  recipientID: recipientId,
264
369
  senderID: clientID
265
370
  },
266
371
  fileDetails: {
372
+ destinationPath,
267
373
  name: fileName,
268
374
  path: filePath,
269
375
  size: fileSize
270
376
  }
271
- }).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
377
+ }, this.clientTide).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer ~ ${error}`, errorMessageColors));
272
378
  }
273
379
  }
274
380
  ;
@@ -12,6 +12,10 @@ const {
12
12
  significantMessageColors,
13
13
  errorMessageColors
14
14
  } = require("./HUDManagers/FileNetUtilityManager.js");
15
+ const {
16
+ FileTransferReceiverManager
17
+ } = require("./HUDManagers/FileTransferReceiverManager.js");
18
+ const FileTransferRecoveryManager = require("./HUDManagers/FileTransferRecoveryManager.js");
15
19
  const {
16
20
  FileNetConfigManager
17
21
  } = require("./HUDManagers/FileNetConfigManager.js");
@@ -19,6 +23,7 @@ const {
19
23
  FileUtilityManager
20
24
  } = require("./HUDManagers/FileUtilityManager.js");
21
25
  const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
26
+ const ChunkCapacitor = require("./HUDComponents/ChunkCapacitor.js");
22
27
  const ConsoleTable = require("./HUDComponents/ConsoleTable.js");
23
28
  const FileMessagerClient = require("./FileMessagerClient");
24
29
  const FileMessager = require("./FileMessager");
@@ -72,13 +77,20 @@ class FileTide {
72
77
  * @param {Function} onTransferProgress - Callback for when an current transfers progress
73
78
  * @param {Boolean} [enableTransferBarrier=true] - Whether to enable the transfer barrier
74
79
  * @param {Object} [options = {}] - Additional options
80
+ * @param {Object} [headers = {}] - Additional connection headers
81
+ * @param {Function} onConnect - Callback for when the client connects to the desired server
82
+ * @param {Function} onDisconnect - Callback for when the client disconnects from the desired server
83
+ * @param {String} authURL - The destination of the connection authentication functionality
84
+ * @param {Object} [authHeaders = {}] - Additional connection authentication headers
85
+ * @param {Boolean} [useAuthentication=true] - Whether to enable connection authentication
86
+ * @param {Boolean} [queryAuthToken=false] - Whether to enable connection authentication tokens
75
87
  */
76
- launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}) {
88
+ launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
77
89
  if (this.clients.has(userID)) {
78
90
  this.stopMessager(userID);
79
91
  }
80
92
  const newClient = new FileMessagerClient(clientOptions);
81
- newClient.joinRoom(userID, roomName, connectionOptions);
93
+ newClient.joinRoom(userID, roomName, connectionOptions, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
82
94
  this.transferBarrier = enableTransferBarrier;
83
95
  this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">CurrentUser"), FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents")]);
84
96
  this.clients.set(userID, newClient);
@@ -101,6 +113,9 @@ class FileTide {
101
113
  getTidePath(inputPath) {
102
114
  return FileUtilityManager.getTidePath(inputPath);
103
115
  }
116
+ saveTransferStates() {
117
+ FileTransferRecoveryManager.flushToDisk();
118
+ }
104
119
  filterFilesAndDirectories(dirPath, filterContent = [], callback) {
105
120
  FileUtilityManager.filterFilesAndDirectories(dirPath, filterContent, (files, error) => {
106
121
  if (callback) {
@@ -117,8 +132,8 @@ class FileTide {
117
132
  }
118
133
  clientMessager.listFiles(clientID, recipientID, depth);
119
134
  }
120
- async listFiles(directories, depth = 1) {
121
- return await FileUtilityManager.listFiles(directories, depth);
135
+ async listFiles(directories, depth = 1, onDirectory) {
136
+ return await FileUtilityManager.listFiles(directories, depth, onDirectory);
122
137
  }
123
138
  clearShorePolicies(clientID, shores = []) {
124
139
  let clientShorePolicies = this.clientShorePolicies.get(clientID);
@@ -150,8 +165,11 @@ class FileTide {
150
165
  * @param {Buffer} fileData - The path of the data to send
151
166
  * @param {String} destinationPath - The destination path on the device
152
167
  * @param {Array} filterDirectoryContent - List of paths to filter
168
+ * @param {Number} tideSize - The tide size in sending files (500)
169
+ * @param {Number} minTideSize - The minimum tide size in sending files (10)
170
+ * @param {Number} maxTideSize - The maximum tide size in sending files (2000)
153
171
  */
154
- async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = []) {
172
+ async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = [], tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
155
173
  const clientID = this.currentUserID;
156
174
  const clientMessager = this.clients.get(clientID);
157
175
  if (!clientMessager) {
@@ -164,13 +182,18 @@ class FileTide {
164
182
  return;
165
183
  }
166
184
  if (fileDetails.type === "file") {
185
+ if (fileDetails.largeFile) {
186
+ FileTide.outputGradient(`[${clientID}] Sending large file ~ ${fileDetails.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
187
+ await clientMessager.sendLargeFile(clientID, recipientID, fileDetails.fileName, fileDetails.filePath, destinationPath, fileDetails.fileSize, tideSize, minTideSize, maxTideSize);
188
+ return;
189
+ }
167
190
  FileTide.outputGradient(`[${clientID}] Sending file ~ ${fileDetails.content.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
168
191
  clientMessager.sendFile(clientID, recipientID, fileDetails.content.fileName, fileDetails.content.fileData, destinationPath, fileDetails.content.size);
169
192
  return;
170
193
  }
171
194
  if (fileDetails.type === "directory") {
172
195
  FileTide.outputGradient(`[${clientID}] Sending ${fileDetails.content.length} files in directory ~ ${path.basename(filePath)} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
173
- clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath);
196
+ clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath, tideSize, minTideSize, maxTideSize);
174
197
  return;
175
198
  }
176
199
  FileTide.outputGradient(`[FileTide] ~ No active client ~ ${recipientID} found.`, errorMessageColors);
@@ -238,7 +261,8 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
238
261
  onTransferRequested,
239
262
  onTransferStatus,
240
263
  onListFiles,
241
- onCurrentOnlineClients
264
+ onCurrentOnlineClients,
265
+ onClientTiding
242
266
  } = options;
243
267
  const cliTable = new ConsoleTable({
244
268
  padding: 2,
@@ -266,7 +290,7 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
266
290
  return content;
267
291
  }
268
292
  });
269
- let activeTransfers = new Map();
293
+ const fileTransferReceiver = new FileTransferReceiverManager();
270
294
  client.clientTide.onEvent(userID, "current-online-clients", data => {
271
295
  const currentClients = {};
272
296
  Object.keys(data).forEach(key => {
@@ -284,6 +308,11 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
284
308
  onCurrentOnlineClients(currentClients, true);
285
309
  }
286
310
  });
311
+ client.clientTide.onEvent(userID, "current-client-tiding", data => {
312
+ if (onClientTiding) {
313
+ onClientTiding(data.message, significantMessageColors);
314
+ }
315
+ });
287
316
  client.clientTide.onEvent(userID, "current-clients", data => {
288
317
  const currentClients = {};
289
318
  Object.keys(data).forEach(key => {
@@ -299,6 +328,23 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
299
328
  onCurrentOnlineClients(currentClients, false);
300
329
  }
301
330
  });
331
+ client.clientTide.onEvent(userID, "save-current-transfer-states", async data => {
332
+ this.saveTransferStates();
333
+ return;
334
+ });
335
+ client.clientTide.onEvent(userID, "current-transfer-state", async data => {
336
+ const {
337
+ senderID,
338
+ filePath
339
+ } = data;
340
+ const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, senderID, filePath);
341
+ client.clientTide.emitEvent(userID, "send-current-transfer-state", {
342
+ recipientID: userID,
343
+ senderID,
344
+ transferState: currentTransferState
345
+ });
346
+ return;
347
+ });
302
348
  client.clientTide.onEvent(userID, "queue-incoming-transfer", async data => {
303
349
  if (this.transferBarrier && onIncomingTransfer) {
304
350
  onIncomingTransfer(data, status => {
@@ -317,6 +363,16 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
317
363
  return;
318
364
  });
319
365
  client.clientTide.onEvent(userID, "incoming-transfer", async data => {
366
+ const {
367
+ currentSender
368
+ } = data;
369
+ if (currentSender && currentSender === userID) {
370
+ _FileTide.outputGradient(`[${userID}] Transfer successfully sent to ${data.recipientID}`, significantMessageColors);
371
+ this.progressBarManager.completionMessage = `Successfully sent to ${data.recipientID}!`;
372
+ this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
373
+ this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
374
+ return;
375
+ }
320
376
  _FileTide.outputGradient(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`, significantMessageColors);
321
377
  if (onTransferStart) {
322
378
  onTransferStart(data);
@@ -325,14 +381,10 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
325
381
  this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
326
382
  this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
327
383
  });
328
- client.clientTide.onEvent(userID, "incoming-file", data => {
384
+ client.clientTide.onEvent(userID, "incoming-file", async data => {
329
385
  try {
330
386
  _FileTide.outputGradient(`[${userID}] Preparing to receive file : ${data.fileName}`, significantMessageColors);
331
- activeTransfers.set(data.fileName, {
332
- receivedChunks: [],
333
- totalChunks: data.totalChunks,
334
- fileInfo: data
335
- });
387
+ await fileTransferReceiver.initializeTransfer(data);
336
388
  const saveDir = FileUtilityManager.getTidePath(data.path);
337
389
  const verifiedSender = data.senderID && data.senderID !== userID;
338
390
  if (verifiedSender) {
@@ -342,7 +394,7 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
342
394
  });
343
395
  }
344
396
  if (onIncomingFile) {
345
- onIncomingFile(verifiedSender, data, activeTransfers);
397
+ onIncomingFile(verifiedSender, data, fileTransferReceiver.activeTransfers);
346
398
  }
347
399
  if (verifiedSender && !fs.existsSync(saveDir)) {
348
400
  fs.mkdirSync(saveDir, {
@@ -366,22 +418,35 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
366
418
  onTransferBarrier(data);
367
419
  }
368
420
  });
369
- client.clientTide.onEvent(userID, "transfer-progress", data => {
421
+ client.clientTide.onEvent(userID, "transfer-progress", async data => {
370
422
  if (!data || data.fileChunk === undefined || data.fileChunk === null) {
371
423
  return;
372
424
  }
373
- const transferState = activeTransfers.get(data.fileName);
374
- if (!transferState) {
375
- return;
376
- }
377
- transferState.receivedChunks[data.chunkIndex] = Buffer.from(data.fileChunk);
378
- const progress = (data.chunkIndex + 1) / transferState.totalChunks * 100;
379
- this.progressBarManager.updateTaskProgress(data.fileName, 512 * 1024);
425
+ await fileTransferReceiver.handleTransferProgress(data);
426
+ this.progressBarManager.updateTaskProgress(data.fileName, data.chunkSize || 512 * 1024);
427
+ const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, data.senderName, data.path) || {
428
+ transferredSize: 0
429
+ };
430
+ currentTransferState.transferredSize += data.chunkSize || 512 * 1024;
431
+ FileTransferRecoveryManager.saveTransferState(userID, data.senderName, data.path, data.chunkIndex, currentTransferState.transferredSize);
380
432
  if (onTransferProgress) {
381
433
  onTransferProgress(data);
382
434
  }
383
435
  });
384
436
  client.clientTide.onEvent(userID, "transfer-status", data => {
437
+ const {
438
+ chunkIndex,
439
+ chunkSize,
440
+ fileName,
441
+ fileSize
442
+ } = data;
443
+ if (chunkIndex === 0) {
444
+ this.progressBarManager.addTask({
445
+ name: fileName,
446
+ size: fileSize
447
+ });
448
+ }
449
+ this.progressBarManager.updateTaskProgress(fileName, chunkSize);
385
450
  if (onTransferStatus) {
386
451
  onTransferStatus(data);
387
452
  }
@@ -391,22 +456,25 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
391
456
  const savePath = FileUtilityManager.getTidePath(path.join(data.filePath, data.fileName));
392
457
  try {
393
458
  _FileTide.outputGradient(`[${userID}] File transfer complete : ${data.fileName}`, significantMessageColors);
394
- const transferState = activeTransfers.get(data.fileName);
459
+ const transferState = fileTransferReceiver.activeTransfers.get(data.fileName);
395
460
  if (!transferState) {
396
461
  return;
397
462
  }
398
- const fullFile = Buffer.concat(transferState.receivedChunks);
399
- fs.writeFileSync(savePath, fullFile);
400
- _FileTide.outputGradient(`[${userID}] File saved at : ${savePath}`, significantMessageColors);
401
- activeTransfers.delete(data.fileName);
402
- if (onTransferComplete) {
403
- onTransferComplete(data, activeTransfers, true);
404
- }
463
+ fileTransferReceiver.completeTransfer(data, (transferData, transfers, success, error) => {
464
+ if (success) {
465
+ FileTransferRecoveryManager.clearTransferState(userID, data.senderID, data.filePath);
466
+ _FileTide.outputGradient(`[${userID}] File saved at : ${savePath}\n`, significantMessageColors);
467
+ } else if (!success) {
468
+ _FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n\t${error}\n`, errorMessageColors);
469
+ }
470
+ if (onTransferComplete) {
471
+ onTransferComplete(data, fileTransferReceiver.activeTransfers, success);
472
+ }
473
+ });
405
474
  } catch (error) {
406
475
  _FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n${error}`, errorMessageColors);
407
- activeTransfers.delete(data.fileName);
408
476
  if (onTransferComplete) {
409
- onTransferComplete(data, activeTransfers, false);
477
+ onTransferComplete(data, fileTransferReceiver.activeTransfers, false);
410
478
  }
411
479
  }
412
480
  });
@@ -420,17 +488,29 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
420
488
  return;
421
489
  });
422
490
  client.clientTide.onEvent(userID, "list-files", async data => {
423
- _FileTide.outputGradient(`[${userID}] Sending file list!`, significantMessageColors);
491
+ _FileTide.outputGradient(`[${userID}] Sending file list to ${data.senderID}...!`, significantMessageColors);
424
492
  const currentClientShorePolicies = this.getShorePolicies(userID);
425
- const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1);
426
- client.clientTide.emitEvent(userID, "send-file-list-to-client", {
427
- senderID: data.recipientID,
428
- recipientID: data.senderID,
429
- listedFiles
493
+ const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1, (totalItems, directoryContents) => {
494
+ const capacitor = new ChunkCapacitor(directoryContents, 100);
495
+ const contents = capacitor.getAllChunks();
496
+ for (let files of contents) {
497
+ client.clientTide.emitEvent(userID, "send-file-list-to-client", {
498
+ senderID: data.recipientID,
499
+ recipientID: data.senderID,
500
+ totalItems: files.length,
501
+ listedFiles: files
502
+ });
503
+ }
430
504
  });
505
+ setTimeout(() => {
506
+ _FileTide.outputGradient(`[${userID}] Sent file list to ${data.senderID}!`, significantMessageColors);
507
+ }, 1000);
508
+ });
509
+ client.clientTide.onEvent(userID, "error", async error => {
510
+ _FileTide.outputGradient(`[${userID} | FileTide Error] ${error}`, errorMessageColors);
431
511
  });
432
512
  client.clientTide.onEvent(userID, "client-file-list", async data => {
433
- _FileTide.outputGradient(`[${userID}] Listed ${data.senderID}'s files!`, significantMessageColors);
513
+ _FileTide.outputGradient(`[${userID}] Received ${data.totalItems} directory contents from ${data.senderID}'s file list!`, significantMessageColors);
434
514
  if (onListFiles) {
435
515
  onListFiles(data);
436
516
  }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ class ChunkCapacitor {
4
+ constructor(list, chunkSize = 100) {
5
+ this.list = list;
6
+ this.chunkSize = chunkSize;
7
+ this.chunks = this.createChunks();
8
+ }
9
+ createChunks() {
10
+ const chunks = [];
11
+ for (let i = 0; i < this.list.length; i += this.chunkSize) {
12
+ const chunk = this.list.slice(i, i + this.chunkSize);
13
+ chunks.push(chunk);
14
+ }
15
+ return chunks;
16
+ }
17
+ getChunk(index) {
18
+ if (index < 0 || index >= this.chunks.length) {
19
+ console.log("Chunk index out of bounds.");
20
+ return null;
21
+ }
22
+ return this.chunks[index];
23
+ }
24
+ getTotalChunks() {
25
+ return this.chunks.length;
26
+ }
27
+ getTotalItems() {
28
+ return this.list.length;
29
+ }
30
+ getAllChunks() {
31
+ return this.chunks;
32
+ }
33
+ }
34
+ ;
35
+ module.exports = ChunkCapacitor;
@@ -123,7 +123,11 @@ class ConsoleProgressBarManager {
123
123
  newBar.speed = speed;
124
124
  const filledLength = Math.round(settings.barLength * (newBar.current / newBar.totalSize));
125
125
  const bar = settings.barColor(settings.filledChar.repeat(filledLength)) + settings.unfilledChar.repeat(settings.barLength - filledLength);
126
- process.stdout.write(`${settings.taskNameColor(newBar.taskName)} > ${settings.totalSizeColor(newBar.getFormattedSize(newBar.totalSize))} | ${settings.sizeTransferredColor(newBar.getFormattedSize(newBar.current))} ${settings.speedColor(`${speedData.current} ${speedData.unit}/s`)} | ${settings.taskETAColor(this.convertSeconds(timeLeft.toFixed(2)))} [${bar}] ${settings.percentageColor(percentage.toFixed(2) + "%")}`);
126
+ try {
127
+ process.stdout.write(`${settings.taskNameColor(newBar.taskName)} > ${settings.totalSizeColor(newBar.getFormattedSize(newBar.totalSize))} | ${settings.sizeTransferredColor(newBar.getFormattedSize(newBar.current))} ${settings.speedColor(`${speedData.current} ${speedData.unit}/s`)} | ${settings.taskETAColor(this.convertSeconds(timeLeft.toFixed(2)))} [${bar}] ${settings.percentageColor(percentage.toFixed(2) + "%")}`);
128
+ } catch (error) {
129
+ console.log(error);
130
+ }
127
131
  },
128
132
  update: amount => {
129
133
  newBar.current += amount;