@trap_stevo/filetide 0.0.39 → 0.0.41

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,31 +28,35 @@ 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);
49
- this.clientTide.joinChannel(socketName, roomName, userID, () => {
50
- FileNetUtilityManager.outputGradient(`\n[FileTide ~ File Messager] ~ Client ~ ${userID} joined room ${roomName} successfully!`, significantMessageColors);
51
- this.clients.set(userID, {
52
- roomName
51
+ }, options, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
52
+ return () => {
53
+ this.clientTide.joinChannel(socketName, roomName, userID, () => {
54
+ FileNetUtilityManager.outputGradient(`\n[FileTide ~ File Messager] ~ Client ~ ${userID} joined room ${roomName} successfully!`, significantMessageColors);
55
+ this.clients.set(userID, {
56
+ roomName
57
+ });
53
58
  });
54
- });
59
+ };
55
60
  }
56
61
  async requestTransfer(clientID, senderID, filePath, destinationPath, filterContent = []) {
57
62
  const transferAllowed = await this.allowedTransfer(clientID, senderID, destinationPath, "N/A", "requestSend");
@@ -111,8 +116,11 @@ class FileMessagerClient {
111
116
  * @param {Array} filesData - Array of file data objects from the directory.
112
117
  * @param {string} baseDirectory - The base directory path that contains the files.
113
118
  * @param {string} destinationPath - The path to which the files should be sent.
119
+ * @param {Number} tideSize - The tide size in sending files (500)
120
+ * @param {Number} minTideSize - The minimum tide size in sending files (10)
121
+ * @param {Number} maxTideSize - The maximum tide size in sending files (2000)
114
122
  */
115
- async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
123
+ async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath, tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
116
124
  try {
117
125
  const baseDirectoryName = path.basename(baseDirectory);
118
126
  const adjustedDestinationPath = FileUtilityManager.normalizePath(path.join(destinationPath, baseDirectoryName));
@@ -131,6 +139,9 @@ class FileMessagerClient {
131
139
  const sendFilePromises = filesData.map(async (fileInfo, index) => {
132
140
  const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
133
141
  const fileDestinationPath = FileUtilityManager.normalizePath(path.join(adjustedDestinationPath, relativeFilePath));
142
+ if (fileInfo.largeFile) {
143
+ return await this.sendDirectoryLargeFile(clientID, recipientId, fileInfo.fileName, fileInfo.filePath, fileDestinationPath, fileInfo.fileSize, index, tideSize, minTideSize, maxTideSize);
144
+ }
134
145
  return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
135
146
  });
136
147
  Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer directory: ", error));
@@ -138,29 +149,28 @@ class FileMessagerClient {
138
149
  FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused directory transfer: ${error}`, errorMessageColors);
139
150
  }
140
151
  }
152
+ async sendDirectoryLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destinationPath = null, fileSize, index = 0, tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
153
+ let initiatingTransfer = false;
154
+ if (index === 0) {
155
+ initiatingTransfer = true;
156
+ }
157
+ return await this.sendLargeFile(clientID, recipientId, fileName, filePath, destinationPath, fileSize, tideSize, minTideSize, maxTideSize, initiatingTransfer, true);
158
+ }
141
159
  sendDirectoryFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
142
160
  if (!file) {
143
161
  return;
144
162
  }
145
163
  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
164
  this.clientTide.emitEvent(clientID, "transfer-start", {
150
165
  fileName,
151
166
  senderID: clientID,
152
167
  recipientID: recipientId,
153
- path: directoryPath || filePath
168
+ path: directoryPath || filePath,
169
+ fileSize
154
170
  });
155
171
  }
156
172
  this.transporter.sendFile(file, {
157
173
  onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks) => {
158
- if (chunkIndex === 0) {
159
- this.progressBarManager.addTask({
160
- name: fileName,
161
- size: fileSize
162
- });
163
- }
164
174
  return new Promise((resolve, reject) => {
165
175
  this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
166
176
  senderID: clientID,
@@ -187,19 +197,124 @@ class FileMessagerClient {
187
197
  resolve();
188
198
  });
189
199
  },
190
- onProgress: (progress, chunkSize) => {
191
- this.progressBarManager.updateTaskProgress(fileName, chunkSize);
192
- },
193
200
  originDetails: {
194
201
  recipientID: recipientId,
195
202
  senderID: clientID
196
203
  },
204
+ recipientID: recipientId,
197
205
  fileDetails: {
206
+ destinationPath: filePath,
198
207
  name: fileName,
199
208
  path: filePath,
200
209
  size: fileSize
201
210
  }
202
- }).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
211
+ }, 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));
212
+ }
213
+ async sendLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destination = process.cwd(), fileSize, tideSize = 500, minTideSize = 10, maxTideSize = 2000, initiatingTransfer = true, inDirectory = false) {
214
+ const destinationPath = FileUtilityManager.normalizePath(path.join(destination, fileName));
215
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer large file ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
216
+ const incomingType = this.activeTransferTypes.get(filePath);
217
+ if (!inDirectory && initiatingTransfer) {
218
+ const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
219
+ if (incomingType) {
220
+ this.activeTransferTypes.delete(filePath);
221
+ }
222
+ if (!transferAllowed) {
223
+ return;
224
+ }
225
+ this.clientTide.emitEvent(clientID, "transfer-start", {
226
+ fileName,
227
+ senderID: clientID,
228
+ recipientID: recipientId,
229
+ path: destination,
230
+ fileSize
231
+ });
232
+ } else if (inDirectory && !initiatingTransfer) {
233
+ if (incomingType) {
234
+ this.activeTransferTypes.delete(filePath);
235
+ }
236
+ }
237
+ const fileHandle = await fs.promises.open(filePath, "r");
238
+ const fileDetails = {
239
+ destinationPath,
240
+ name: fileName,
241
+ path: filePath,
242
+ size: fileSize
243
+ };
244
+ try {
245
+ let lastSendTime = Date.now();
246
+ const maxBatchSize = maxTideSize;
247
+ const minBatchSize = minTideSize;
248
+ let batchSize = tideSize > maxBatchSize || tideSize > minBatchSize ? tideSize : tideSize <= minBatchSize ? minBatchSize : 10;
249
+ const chunkBatch = [];
250
+ await this.transporter.sendLargeFile(fileHandle, {
251
+ recipientID: recipientId,
252
+ originDetails: {
253
+ recipientID: recipientId,
254
+ senderID: clientID
255
+ },
256
+ fileDetails,
257
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
258
+ return new Promise(resolve => {
259
+ chunkBatch.push({
260
+ senderID: clientID,
261
+ recipientId,
262
+ fileName,
263
+ fileChunk: chunkData,
264
+ completedChunks,
265
+ totalChunks,
266
+ chunkIndex,
267
+ chunkSize,
268
+ filePath: destination,
269
+ fileSize
270
+ });
271
+ if (chunkBatch.length >= batchSize) {
272
+ const now = Date.now();
273
+ const rtt = now - lastSendTime;
274
+ lastSendTime = now;
275
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
276
+ chunkBatch
277
+ });
278
+ chunkBatch.length = 0;
279
+ if (rtt >= 200 && batchSize < maxBatchSize) {
280
+ batchSize = Math.min(batchSize + 50, maxBatchSize);
281
+ } else if (rtt < 200 && batchSize > minBatchSize) {
282
+ batchSize = Math.max(batchSize - 50, minBatchSize);
283
+ }
284
+ }
285
+ resolve();
286
+ });
287
+ },
288
+ onComplete: transferId => {
289
+ return new Promise(resolve => {
290
+ if (chunkBatch.length > 0) {
291
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
292
+ chunkBatch
293
+ });
294
+ chunkBatch.length = 0;
295
+ }
296
+ this.clientTide.emitEvent(clientID, "transfer-complete", {
297
+ senderID: clientID,
298
+ recipientId,
299
+ fileName,
300
+ filePath: destination
301
+ });
302
+ resolve();
303
+ });
304
+ },
305
+ onTiding: () => {
306
+ this.clientTide.emitEvent(clientID, "client-tiding", {
307
+ senderID: clientID,
308
+ message: "Transfer ongoing..."
309
+ });
310
+ }
311
+ }, this.clientTide);
312
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Large file ~ ${fileName} transfer complete!`, significantMessageColors);
313
+ } catch (error) {
314
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Error transferring large file: ${error.message}`, errorMessageColors);
315
+ } finally {
316
+ await fileHandle.close();
317
+ }
203
318
  }
204
319
  async sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize) {
205
320
  if (!file) {
@@ -215,29 +330,22 @@ class FileMessagerClient {
215
330
  if (!transferAllowed) {
216
331
  return;
217
332
  }
218
- this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
219
- this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
220
- this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
221
333
  this.clientTide.emitEvent(clientID, "transfer-start", {
222
334
  fileName,
223
335
  senderID: clientID,
224
336
  recipientID: recipientId,
225
- path: filePath
337
+ path: filePath,
338
+ fileSize
226
339
  });
227
- this.transporter.sendFile(file, {
228
- onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks) => {
229
- if (chunkIndex === 0) {
230
- this.progressBarManager.addTask({
231
- name: fileName,
232
- size: fileSize
233
- });
234
- }
340
+ await this.transporter.sendFile(file, {
341
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
235
342
  return new Promise((resolve, reject) => {
236
343
  this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
237
344
  senderID: clientID,
238
345
  recipientId,
239
346
  fileName,
240
347
  fileChunk: chunkData,
348
+ completedChunks,
241
349
  totalChunks,
242
350
  chunkIndex,
243
351
  chunkSize,
@@ -258,19 +366,17 @@ class FileMessagerClient {
258
366
  resolve();
259
367
  });
260
368
  },
261
- onProgress: (progress, chunkSize) => {
262
- this.progressBarManager.updateTaskProgress(fileName, chunkSize);
263
- },
264
369
  originDetails: {
265
370
  recipientID: recipientId,
266
371
  senderID: clientID
267
372
  },
268
373
  fileDetails: {
374
+ destinationPath,
269
375
  name: fileName,
270
376
  path: filePath,
271
377
  size: fileSize
272
378
  }
273
- }).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
379
+ }, 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));
274
380
  }
275
381
  }
276
382
  ;
@@ -15,6 +15,7 @@ const {
15
15
  const {
16
16
  FileTransferReceiverManager
17
17
  } = require("./HUDManagers/FileTransferReceiverManager.js");
18
+ const FileTransferRecoveryManager = require("./HUDManagers/FileTransferRecoveryManager.js");
18
19
  const {
19
20
  FileNetConfigManager
20
21
  } = require("./HUDManagers/FileNetConfigManager.js");
@@ -22,6 +23,7 @@ const {
22
23
  FileUtilityManager
23
24
  } = require("./HUDManagers/FileUtilityManager.js");
24
25
  const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
26
+ const ChunkCapacitor = require("./HUDComponents/ChunkCapacitor.js");
25
27
  const ConsoleTable = require("./HUDComponents/ConsoleTable.js");
26
28
  const FileMessagerClient = require("./FileMessagerClient");
27
29
  const FileMessager = require("./FileMessager");
@@ -75,20 +77,27 @@ class FileTide {
75
77
  * @param {Function} onTransferProgress - Callback for when an current transfers progress
76
78
  * @param {Boolean} [enableTransferBarrier=true] - Whether to enable the transfer barrier
77
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
78
87
  */
79
- 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) {
80
89
  if (this.clients.has(userID)) {
81
90
  this.stopMessager(userID);
82
91
  }
83
92
  const newClient = new FileMessagerClient(clientOptions);
84
- newClient.joinRoom(userID, roomName, connectionOptions);
93
+ const enterRoom = newClient.joinRoom(userID, roomName, connectionOptions, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
85
94
  this.transferBarrier = enableTransferBarrier;
86
95
  this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">CurrentUser"), FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents")]);
87
96
  this.clients.set(userID, newClient);
88
97
  FileTide.outputGradient(`[FileTide] ~ Messager launched successfully for client ~ ${userID}!`, significantMessageColors);
89
98
  _assertClassBrand(_FileTide_brand, this, _setupFileEventListeners).call(this, userID, newClient, onTransferProgress, onIncomingTransfer, onIncomingFile, onTransferBarrier, options);
90
99
  if (onLaunch) {
91
- onLaunch(newClient);
100
+ onLaunch(newClient, enterRoom);
92
101
  }
93
102
  return;
94
103
  }
@@ -104,6 +113,9 @@ class FileTide {
104
113
  getTidePath(inputPath) {
105
114
  return FileUtilityManager.getTidePath(inputPath);
106
115
  }
116
+ saveTransferStates() {
117
+ FileTransferRecoveryManager.flushToDisk();
118
+ }
107
119
  filterFilesAndDirectories(dirPath, filterContent = [], callback) {
108
120
  FileUtilityManager.filterFilesAndDirectories(dirPath, filterContent, (files, error) => {
109
121
  if (callback) {
@@ -120,8 +132,8 @@ class FileTide {
120
132
  }
121
133
  clientMessager.listFiles(clientID, recipientID, depth);
122
134
  }
123
- async listFiles(directories, depth = 1) {
124
- return await FileUtilityManager.listFiles(directories, depth);
135
+ async listFiles(directories, depth = 1, onDirectory) {
136
+ return await FileUtilityManager.listFiles(directories, depth, onDirectory);
125
137
  }
126
138
  clearShorePolicies(clientID, shores = []) {
127
139
  let clientShorePolicies = this.clientShorePolicies.get(clientID);
@@ -153,8 +165,11 @@ class FileTide {
153
165
  * @param {Buffer} fileData - The path of the data to send
154
166
  * @param {String} destinationPath - The destination path on the device
155
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)
156
171
  */
157
- async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = []) {
172
+ async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = [], tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
158
173
  const clientID = this.currentUserID;
159
174
  const clientMessager = this.clients.get(clientID);
160
175
  if (!clientMessager) {
@@ -167,13 +182,18 @@ class FileTide {
167
182
  return;
168
183
  }
169
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
+ }
170
190
  FileTide.outputGradient(`[${clientID}] Sending file ~ ${fileDetails.content.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
171
191
  clientMessager.sendFile(clientID, recipientID, fileDetails.content.fileName, fileDetails.content.fileData, destinationPath, fileDetails.content.size);
172
192
  return;
173
193
  }
174
194
  if (fileDetails.type === "directory") {
175
195
  FileTide.outputGradient(`[${clientID}] Sending ${fileDetails.content.length} files in directory ~ ${path.basename(filePath)} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
176
- clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath);
196
+ clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath, tideSize, minTideSize, maxTideSize);
177
197
  return;
178
198
  }
179
199
  FileTide.outputGradient(`[FileTide] ~ No active client ~ ${recipientID} found.`, errorMessageColors);
@@ -215,6 +235,10 @@ class FileTide {
215
235
  stopMessager(userID) {
216
236
  if (this.clients.has(userID)) {
217
237
  FileTide.outputGradient(`Stopping messager for user ${userID}...`, significantMessageColors);
238
+ const connectionOrigin = this.clients.get(userID);
239
+ if (connectionOrigin) {
240
+ connectionOrigin.clientTide.closeSocket(userID);
241
+ }
218
242
  this.clients.delete(userID);
219
243
  FileTide.outputGradient(`Stopped messager for user ${userID}.`, significantMessageColors);
220
244
  return;
@@ -241,7 +265,9 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
241
265
  onTransferRequested,
242
266
  onTransferStatus,
243
267
  onListFiles,
244
- onCurrentOnlineClients
268
+ onCurrentOnlineClients,
269
+ onClientTiding,
270
+ onCurrentClientIDAlreadyOnline
245
271
  } = options;
246
272
  const cliTable = new ConsoleTable({
247
273
  padding: 2,
@@ -287,6 +313,11 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
287
313
  onCurrentOnlineClients(currentClients, true);
288
314
  }
289
315
  });
316
+ client.clientTide.onEvent(userID, "current-client-tiding", data => {
317
+ if (onClientTiding) {
318
+ onClientTiding(data.message, significantMessageColors);
319
+ }
320
+ });
290
321
  client.clientTide.onEvent(userID, "current-clients", data => {
291
322
  const currentClients = {};
292
323
  Object.keys(data).forEach(key => {
@@ -302,6 +333,33 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
302
333
  onCurrentOnlineClients(currentClients, false);
303
334
  }
304
335
  });
336
+ client.clientTide.onEvent(userID, "current-client-id-already-online", clientID => {
337
+ if (userID === clientID) {
338
+ return;
339
+ }
340
+ _FileTide.outputGradient(`[FileTide ~ FileNet] ~ Client ~ ${userID} already online.`, significantMessageColors);
341
+ if (onCurrentClientIDAlreadyOnline) {
342
+ onCurrentClientIDAlreadyOnline(clientID);
343
+ }
344
+ return;
345
+ });
346
+ client.clientTide.onEvent(userID, "save-current-transfer-states", async data => {
347
+ this.saveTransferStates();
348
+ return;
349
+ });
350
+ client.clientTide.onEvent(userID, "current-transfer-state", async data => {
351
+ const {
352
+ senderID,
353
+ filePath
354
+ } = data;
355
+ const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, senderID, filePath);
356
+ client.clientTide.emitEvent(userID, "send-current-transfer-state", {
357
+ recipientID: userID,
358
+ senderID,
359
+ transferState: currentTransferState
360
+ });
361
+ return;
362
+ });
305
363
  client.clientTide.onEvent(userID, "queue-incoming-transfer", async data => {
306
364
  if (this.transferBarrier && onIncomingTransfer) {
307
365
  onIncomingTransfer(data, status => {
@@ -320,6 +378,16 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
320
378
  return;
321
379
  });
322
380
  client.clientTide.onEvent(userID, "incoming-transfer", async data => {
381
+ const {
382
+ currentSender
383
+ } = data;
384
+ if (currentSender && currentSender === userID) {
385
+ _FileTide.outputGradient(`[${userID}] Transfer successfully sent to ${data.recipientID}`, significantMessageColors);
386
+ this.progressBarManager.completionMessage = `Successfully sent to ${data.recipientID}!`;
387
+ this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
388
+ this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
389
+ return;
390
+ }
323
391
  _FileTide.outputGradient(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`, significantMessageColors);
324
392
  if (onTransferStart) {
325
393
  onTransferStart(data);
@@ -371,11 +439,29 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
371
439
  }
372
440
  await fileTransferReceiver.handleTransferProgress(data);
373
441
  this.progressBarManager.updateTaskProgress(data.fileName, data.chunkSize || 512 * 1024);
442
+ const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, data.senderName, data.path) || {
443
+ transferredSize: 0
444
+ };
445
+ currentTransferState.transferredSize += data.chunkSize || 512 * 1024;
446
+ FileTransferRecoveryManager.saveTransferState(userID, data.senderName, data.path, data.chunkIndex, currentTransferState.transferredSize);
374
447
  if (onTransferProgress) {
375
448
  onTransferProgress(data);
376
449
  }
377
450
  });
378
451
  client.clientTide.onEvent(userID, "transfer-status", data => {
452
+ const {
453
+ chunkIndex,
454
+ chunkSize,
455
+ fileName,
456
+ fileSize
457
+ } = data;
458
+ if (chunkIndex === 0) {
459
+ this.progressBarManager.addTask({
460
+ name: fileName,
461
+ size: fileSize
462
+ });
463
+ }
464
+ this.progressBarManager.updateTaskProgress(fileName, chunkSize);
379
465
  if (onTransferStatus) {
380
466
  onTransferStatus(data);
381
467
  }
@@ -389,11 +475,12 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
389
475
  if (!transferState) {
390
476
  return;
391
477
  }
392
- fileTransferReceiver.completeTransfer(data, (transferData, transfers, success) => {
478
+ fileTransferReceiver.completeTransfer(data, (transferData, transfers, success, error) => {
393
479
  if (success) {
480
+ FileTransferRecoveryManager.clearTransferState(userID, data.senderID, data.filePath);
394
481
  _FileTide.outputGradient(`[${userID}] File saved at : ${savePath}\n`, significantMessageColors);
395
482
  } else if (!success) {
396
- _FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n`, errorMessageColors);
483
+ _FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n\t${error}\n`, errorMessageColors);
397
484
  }
398
485
  if (onTransferComplete) {
399
486
  onTransferComplete(data, fileTransferReceiver.activeTransfers, success);
@@ -416,17 +503,29 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
416
503
  return;
417
504
  });
418
505
  client.clientTide.onEvent(userID, "list-files", async data => {
419
- _FileTide.outputGradient(`[${userID}] Sending file list!`, significantMessageColors);
506
+ _FileTide.outputGradient(`[${userID}] Sending file list to ${data.senderID}...!`, significantMessageColors);
420
507
  const currentClientShorePolicies = this.getShorePolicies(userID);
421
- const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1);
422
- client.clientTide.emitEvent(userID, "send-file-list-to-client", {
423
- senderID: data.recipientID,
424
- recipientID: data.senderID,
425
- listedFiles
508
+ const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1, (totalItems, directoryContents) => {
509
+ const capacitor = new ChunkCapacitor(directoryContents, 100);
510
+ const contents = capacitor.getAllChunks();
511
+ for (let files of contents) {
512
+ client.clientTide.emitEvent(userID, "send-file-list-to-client", {
513
+ senderID: data.recipientID,
514
+ recipientID: data.senderID,
515
+ totalItems: files.length,
516
+ listedFiles: files
517
+ });
518
+ }
426
519
  });
520
+ setTimeout(() => {
521
+ _FileTide.outputGradient(`[${userID}] Sent file list to ${data.senderID}!`, significantMessageColors);
522
+ }, 1000);
523
+ });
524
+ client.clientTide.onEvent(userID, "error", async error => {
525
+ _FileTide.outputGradient(`[${userID} | FileTide Error] ${error}`, errorMessageColors);
427
526
  });
428
527
  client.clientTide.onEvent(userID, "client-file-list", async data => {
429
- _FileTide.outputGradient(`[${userID}] Listed ${data.senderID}'s files!`, significantMessageColors);
528
+ _FileTide.outputGradient(`[${userID}] Received ${data.totalItems} directory contents from ${data.senderID}'s file list!`, significantMessageColors);
430
529
  if (onListFiles) {
431
530
  onListFiles(data);
432
531
  }
@@ -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;