@trap_stevo/filetide 0.0.25 → 0.0.27
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.
- package/dist/cjs/FileMessager.js +60 -16
- package/dist/cjs/FileMessagerClient.js +35 -7
- package/dist/cjs/FileTide.js +29 -4
- package/dist/cjs/HUDComponents/ConsoleProgressBarManager.js +264 -0
- package/dist/cjs/HUDManagers/FileTransferManager.js +0 -6
- package/dist/cjs/HUDManagers/FileUtilityManager.js +48 -7
- package/package.json +1 -1
package/dist/cjs/FileMessager.js
CHANGED
|
@@ -35,65 +35,104 @@ class FileMessager {
|
|
|
35
35
|
this.fileNet.on("transfer-start", this.handleFileTransferStart.bind(this));
|
|
36
36
|
this.fileNet.on("transfer-progress", this.handleFileTransferProgress.bind(this));
|
|
37
37
|
this.fileNet.on("transfer-complete", this.handleFileTransferComplete.bind(this));
|
|
38
|
+
this.fileNet.on("request-transfer-from-client", this.handleRequestTransferFromClient.bind(this));
|
|
38
39
|
this.fileNet.on("client-to-client-transfer", this.handleClientToClientTransfer.bind(this));
|
|
39
40
|
this.fileNet.on("client-offline", this.handleClientOffline.bind(this));
|
|
40
41
|
this.fileNet.on("clients-online", this.handleClientsOnline.bind(this));
|
|
41
42
|
this.fileNet.on("client-online", this.handleClientOnline.bind(this));
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
45
|
+
getClientFromID(clientID) {
|
|
46
|
+
const client = this.onlineClients.get(clientID);
|
|
47
|
+
if (!client) {
|
|
48
|
+
console.log(`Client ~ ${clientID} not found.`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
return client;
|
|
52
|
+
}
|
|
53
|
+
requestFromClient(senderID, clientID, filePath = process.cwd(), destinationPath) {
|
|
54
|
+
const client = this.getClientFromID(clientID);
|
|
55
|
+
if (!client) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.fileNet.emitToTide(client.tideID, "transfer-requested", {
|
|
59
|
+
senderID,
|
|
60
|
+
clientID,
|
|
61
|
+
path: filePath,
|
|
62
|
+
destinationPath
|
|
63
|
+
});
|
|
64
|
+
}
|
|
44
65
|
|
|
45
66
|
/**
|
|
46
67
|
* Send file chunk to a specific client
|
|
47
|
-
* @param {
|
|
48
|
-
* @param {
|
|
68
|
+
* @param {Object} senderData - Holds the ID (tide ID), and client name (userID) of the sender
|
|
69
|
+
* @param {Object} clientData - The ID (socket ID), and client name (userID) of the client
|
|
49
70
|
* @param {Buffer} fileChunk - The file chunk to send
|
|
50
71
|
* @param {String} fileName - The name of the file
|
|
51
72
|
* @param {Number} chunkIndex - The index of the current chunk
|
|
52
73
|
*/
|
|
53
|
-
sendFileChunkToClient(
|
|
54
|
-
const
|
|
74
|
+
sendFileChunkToClient(senderData, clientData, fileChunk, fileName, chunkIndex, totalChunks, filePath = process.cwd(), fileSize = 100) {
|
|
75
|
+
const senderClient = this.getClientFromID(senderData.id);
|
|
76
|
+
const client = this.getClientFromID(clientData.id);
|
|
55
77
|
if (!client) {
|
|
56
|
-
console.log(`Client ~ ${clientId} not found.`);
|
|
57
78
|
return;
|
|
58
79
|
}
|
|
59
80
|
if (chunkIndex === 0) {
|
|
60
81
|
this.fileNet.emitToTide(client.tideID, "incoming-file", {
|
|
61
|
-
senderID,
|
|
82
|
+
senderID: senderData.userID,
|
|
83
|
+
clientID: clientData.userID,
|
|
62
84
|
fileName,
|
|
63
85
|
totalChunks,
|
|
64
86
|
chunkIndex,
|
|
65
|
-
path: filePath
|
|
87
|
+
path: filePath,
|
|
88
|
+
fileSize
|
|
66
89
|
});
|
|
67
90
|
}
|
|
68
91
|
this.fileNet.emitToTide(client.tideID, "transfer-progress", {
|
|
69
|
-
senderID,
|
|
92
|
+
senderID: senderData.id,
|
|
70
93
|
fileName,
|
|
71
94
|
fileChunk,
|
|
72
95
|
chunkIndex,
|
|
73
|
-
path: filePath
|
|
96
|
+
path: filePath,
|
|
97
|
+
fileSize
|
|
74
98
|
});
|
|
75
|
-
console.log(`[FileTide ~ File Messager] ~ Sent chunk ${chunkIndex} of ${fileName} to client ~ ${
|
|
76
|
-
const senderClient = this.onlineClients.get(senderID);
|
|
99
|
+
console.log(`[FileTide ~ File Messager] ~ Sent chunk ${chunkIndex} of ${fileName} to client ~ ${clientData.userID}!`);
|
|
77
100
|
if (senderClient) {
|
|
78
101
|
this.fileNet.emitToTide(senderClient.tideID, "transfer-status", {
|
|
79
|
-
status: `Chunk ${chunkIndex} of ${fileName} sent to client ${
|
|
80
|
-
recipientID:
|
|
102
|
+
status: `Chunk ${chunkIndex} of ${fileName} sent to client ${clientData.userID}!`,
|
|
103
|
+
recipientID: clientData.userID,
|
|
81
104
|
success: true
|
|
82
105
|
});
|
|
83
106
|
}
|
|
84
107
|
}
|
|
108
|
+
handleRequestTransferFromClient(transferData) {
|
|
109
|
+
const {
|
|
110
|
+
senderID,
|
|
111
|
+
recipientId,
|
|
112
|
+
filePath,
|
|
113
|
+
destinationPath
|
|
114
|
+
} = transferData;
|
|
115
|
+
this.requestFromClient(senderID, recipientId, filePath, destinationPath);
|
|
116
|
+
}
|
|
85
117
|
handleClientToClientTransfer(transferData) {
|
|
86
118
|
const {
|
|
87
119
|
senderID,
|
|
88
120
|
recipientId,
|
|
89
121
|
fileName,
|
|
90
122
|
fileChunk,
|
|
123
|
+
fileSize,
|
|
91
124
|
filePath,
|
|
92
125
|
chunkIndex,
|
|
93
126
|
totalChunks
|
|
94
127
|
} = transferData;
|
|
95
128
|
console.log(`Transferring file ~ ${fileName} | chunk ${chunkIndex + 1} of ${totalChunks} to client (${recipientId})`);
|
|
96
|
-
this.sendFileChunkToClient(
|
|
129
|
+
this.sendFileChunkToClient({
|
|
130
|
+
userID: senderID,
|
|
131
|
+
id: FileNetClientManager.getOnlineClient(senderID).id
|
|
132
|
+
}, {
|
|
133
|
+
userID: recipientId,
|
|
134
|
+
id: FileNetClientManager.getOnlineClient(recipientId).id
|
|
135
|
+
}, fileChunk, fileName, chunkIndex, totalChunks, filePath, fileSize);
|
|
97
136
|
if (chunkIndex + 1 === totalChunks) {
|
|
98
137
|
this.fileNet.emit("transfer-complete", {
|
|
99
138
|
senderID,
|
|
@@ -104,8 +143,13 @@ class FileMessager {
|
|
|
104
143
|
console.log(`[FileTide ~ File Messager] ~ File transfer completed: ${fileName} from ${senderID} to ${recipientId}`);
|
|
105
144
|
}
|
|
106
145
|
}
|
|
107
|
-
handleFileTransferStart(
|
|
108
|
-
console.log(`
|
|
146
|
+
handleFileTransferStart(data, emitToChannel) {
|
|
147
|
+
console.log(`Alerting client of incoming transfer from ${data.senderID}...`);
|
|
148
|
+
const client = this.getClientFromID(data.recipientID);
|
|
149
|
+
if (!client) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.fileNet.emitToTide(client.tideID, "incoming-transfer", data);
|
|
109
153
|
}
|
|
110
154
|
handleFileTransferProgress(data, emitToChannel) {
|
|
111
155
|
console.log(`Progress: ${data.progress}`);
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
const {
|
|
4
4
|
HUDIoTide
|
|
5
5
|
} = require("@trap_stevo/iotide-client");
|
|
6
|
+
const chalk = require("chalk");
|
|
7
|
+
const path = require("path");
|
|
6
8
|
const {
|
|
7
9
|
FileMessagerConfigManager
|
|
8
10
|
} = require("./HUDManagers/FileMessagerConfigManager");
|
|
@@ -12,7 +14,7 @@ const {
|
|
|
12
14
|
const {
|
|
13
15
|
FileUtilityManager
|
|
14
16
|
} = require("./HUDManagers/FileUtilityManager");
|
|
15
|
-
const
|
|
17
|
+
const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
|
|
16
18
|
class FileMessagerClient {
|
|
17
19
|
constructor(options = {}, transportOptions = {
|
|
18
20
|
parallelChunks: 3,
|
|
@@ -20,6 +22,9 @@ class FileMessagerClient {
|
|
|
20
22
|
}) {
|
|
21
23
|
this.clientOptions = FileMessagerConfigManager.getClientOptions(options);
|
|
22
24
|
this.transporter = new FileTransferManager(transportOptions);
|
|
25
|
+
this.progressBarManager = new ConsoleProgressBarManager([], {
|
|
26
|
+
completionMessageColor: chalk.blue.bold
|
|
27
|
+
});
|
|
23
28
|
this.clientTide = new HUDIoTide();
|
|
24
29
|
this.clients = new Map();
|
|
25
30
|
}
|
|
@@ -54,19 +59,36 @@ class FileMessagerClient {
|
|
|
54
59
|
sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
|
|
55
60
|
const baseDirectoryName = path.basename(baseDirectory);
|
|
56
61
|
const adjustedDestinationPath = FileUtilityManager.normalizePath(path.join(destinationPath, baseDirectoryName));
|
|
57
|
-
const sendFilePromises = filesData.map(fileInfo => {
|
|
62
|
+
const sendFilePromises = filesData.map((fileInfo, index) => {
|
|
58
63
|
const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
|
|
59
64
|
const fileDestinationPath = FileUtilityManager.normalizePath(path.join(adjustedDestinationPath, relativeFilePath));
|
|
60
|
-
return this.sendFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath);
|
|
65
|
+
return this.sendFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
|
|
61
66
|
});
|
|
62
67
|
Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer files: ", error));
|
|
63
68
|
}
|
|
64
|
-
sendFile(clientID, recipientId, fileName, file, filePath = process.cwd()) {
|
|
69
|
+
sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
|
|
65
70
|
if (!file) {
|
|
66
71
|
return;
|
|
67
72
|
}
|
|
73
|
+
if (index === 0) {
|
|
74
|
+
this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
|
|
75
|
+
this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
|
|
76
|
+
this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
|
|
77
|
+
this.clientTide.emitEvent(clientID, "transfer-start", {
|
|
78
|
+
fileName,
|
|
79
|
+
senderID: clientID,
|
|
80
|
+
recipientID: recipientId,
|
|
81
|
+
path: directoryPath || filePath
|
|
82
|
+
});
|
|
83
|
+
}
|
|
68
84
|
this.transporter.sendFile(file, {
|
|
69
85
|
onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
|
|
86
|
+
if (chunkIndex === 0) {
|
|
87
|
+
this.progressBarManager.addTask({
|
|
88
|
+
name: fileName,
|
|
89
|
+
size: fileSize
|
|
90
|
+
});
|
|
91
|
+
}
|
|
70
92
|
return new Promise((resolve, reject) => {
|
|
71
93
|
this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
|
|
72
94
|
senderID: clientID,
|
|
@@ -75,7 +97,8 @@ class FileMessagerClient {
|
|
|
75
97
|
fileChunk: chunkData,
|
|
76
98
|
totalChunks,
|
|
77
99
|
chunkIndex,
|
|
78
|
-
filePath
|
|
100
|
+
filePath,
|
|
101
|
+
fileSize
|
|
79
102
|
});
|
|
80
103
|
resolve();
|
|
81
104
|
});
|
|
@@ -92,13 +115,18 @@ class FileMessagerClient {
|
|
|
92
115
|
});
|
|
93
116
|
},
|
|
94
117
|
onProgress: progress => {
|
|
95
|
-
console.log(`\n[FileTide ~ File Messager] ~ ${progress.toFixed(2)}% |>>| ${fileName}`);
|
|
118
|
+
//console.log(`\n[FileTide ~ File Messager] ~ ${progress.toFixed(2)}% |>>| ${fileName}`);
|
|
119
|
+
this.progressBarManager.updateTaskProgress(fileName, 512 * 1024);
|
|
120
|
+
},
|
|
121
|
+
originDetails: {
|
|
122
|
+
recipientID: recipientId,
|
|
123
|
+
senderID: clientID
|
|
96
124
|
},
|
|
97
125
|
fileDetails: {
|
|
98
126
|
name: fileName,
|
|
99
127
|
path: filePath
|
|
100
128
|
}
|
|
101
|
-
}).then(() => console.log(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`)).catch(error => console.error(
|
|
129
|
+
}).then(() => console.log(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`)).catch(error => console.error("Refused file transfer: ", error));
|
|
102
130
|
}
|
|
103
131
|
onFileChunkReceived(userID) {
|
|
104
132
|
this.clientTide.onEvent(userID, "transfer-progress", data => {
|
package/dist/cjs/FileTide.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
const {
|
|
13
13
|
FileUtilityManager
|
|
14
14
|
} = require("./HUDManagers/FileUtilityManager.js");
|
|
15
|
+
const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
|
|
15
16
|
const ConsoleTable = require("./HUDComponents/ConsoleTable.js");
|
|
16
17
|
const FileMessagerClient = require("./FileMessagerClient");
|
|
17
18
|
const FileMessager = require("./FileMessager");
|
|
@@ -23,6 +24,9 @@ class FileTide {
|
|
|
23
24
|
* Automatically checks and creates directories as needed.
|
|
24
25
|
*/
|
|
25
26
|
_classPrivateMethodInitSpec(this, _FileTide_brand);
|
|
27
|
+
this.progressBarManager = new ConsoleProgressBarManager([], {
|
|
28
|
+
completionMessageColor: chalk.blue.bold
|
|
29
|
+
});
|
|
26
30
|
this.clients = new Map();
|
|
27
31
|
return;
|
|
28
32
|
}
|
|
@@ -74,6 +78,9 @@ class FileTide {
|
|
|
74
78
|
getPathData(inputPath) {
|
|
75
79
|
return FileUtilityManager.getPathData(inputPath);
|
|
76
80
|
}
|
|
81
|
+
getTidePath(inputPath) {
|
|
82
|
+
return FileUtilityManager.getTidePath(inputPath);
|
|
83
|
+
}
|
|
77
84
|
|
|
78
85
|
/**
|
|
79
86
|
* Send a file directly to a device without it needing to create a client.
|
|
@@ -237,6 +244,12 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
237
244
|
cliTable.setData(currentClients);
|
|
238
245
|
console.log(`\n[FileTide ~ FileNet]\n\n`, cliTable.render(), "\n");
|
|
239
246
|
});
|
|
247
|
+
client.clientTide.onEvent(userID, "incoming-transfer", data => {
|
|
248
|
+
console.log(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`);
|
|
249
|
+
this.progressBarManager.completionMessage = `Successfully received transfer from ${recipientId}!`;
|
|
250
|
+
this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
|
|
251
|
+
this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
|
|
252
|
+
});
|
|
240
253
|
client.clientTide.onEvent(userID, "incoming-file", data => {
|
|
241
254
|
console.log(`[${userID}] Preparing to receive file : ${data.fileName}`);
|
|
242
255
|
activeTransfers.set(data.fileName, {
|
|
@@ -244,7 +257,11 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
244
257
|
totalChunks: data.totalChunks,
|
|
245
258
|
fileInfo: data
|
|
246
259
|
});
|
|
247
|
-
const saveDir = FileUtilityManager.
|
|
260
|
+
const saveDir = FileUtilityManager.getTidePath(data.path);
|
|
261
|
+
this.progressBarManager.addTask({
|
|
262
|
+
name: data.fileName,
|
|
263
|
+
size: data.fileSize
|
|
264
|
+
});
|
|
248
265
|
if (data.senderID && data.senderID !== userID && !fs.existsSync(saveDir)) {
|
|
249
266
|
fs.mkdirSync(saveDir, {
|
|
250
267
|
recursive: true
|
|
@@ -260,10 +277,15 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
260
277
|
if (!transferState) {
|
|
261
278
|
return;
|
|
262
279
|
}
|
|
263
|
-
|
|
280
|
+
|
|
281
|
+
//console.log(`[${userID}] Receiving chunk ${data.chunkIndex + 1}/${transferState.totalChunks} for file : ${data.fileName}`);
|
|
282
|
+
|
|
264
283
|
transferState.receivedChunks[data.chunkIndex] = Buffer.from(data.fileChunk);
|
|
265
284
|
const progress = (data.chunkIndex + 1) / transferState.totalChunks * 100;
|
|
266
|
-
|
|
285
|
+
|
|
286
|
+
//console.log(`[${userID}] Progress : ${progress.toFixed(2)}%`);
|
|
287
|
+
|
|
288
|
+
this.progressBarManager.updateTaskProgress(data.fileName, 512 * 1024);
|
|
267
289
|
if (onIncomingFile) {
|
|
268
290
|
onIncomingFile(data);
|
|
269
291
|
}
|
|
@@ -274,12 +296,15 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
274
296
|
if (!transferState) {
|
|
275
297
|
return;
|
|
276
298
|
}
|
|
277
|
-
const savePath = FileUtilityManager.
|
|
299
|
+
const savePath = FileUtilityManager.getTidePath(path.join(data.filePath, data.fileName));
|
|
278
300
|
const fullFile = Buffer.concat(transferState.receivedChunks);
|
|
279
301
|
fs.writeFileSync(savePath, fullFile);
|
|
280
302
|
console.log(`[${userID}] File saved at : ${savePath}`);
|
|
281
303
|
activeTransfers.delete(data.fileName);
|
|
282
304
|
});
|
|
305
|
+
client.clientTide.onEvent(userID, "transfer-request", data => {
|
|
306
|
+
console.log(`[${userID}] Transfer requested from: ${data.clientID}`);
|
|
307
|
+
});
|
|
283
308
|
return;
|
|
284
309
|
}
|
|
285
310
|
;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const readline = require("readline");
|
|
4
|
+
const chalk = require("chalk");
|
|
5
|
+
class ConsoleProgressBarManager {
|
|
6
|
+
constructor(tasks = [], options = {}) {
|
|
7
|
+
this.visibleBars = process.stdout.rows - 3;
|
|
8
|
+
this.currentPage = 0;
|
|
9
|
+
this.barsPerPage = this.visibleBars;
|
|
10
|
+
this.maxCompletionFeedSize = 5;
|
|
11
|
+
this.completionTimeout = 5000;
|
|
12
|
+
this.completionFeed = [];
|
|
13
|
+
this.completedTasksCount = 0;
|
|
14
|
+
this.totalTasks = tasks.length;
|
|
15
|
+
this.taskBars = [];
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.completionMessage = options.completionMessage || "Successfully processed all tasks!";
|
|
18
|
+
this.completionMessageColor = options.completionMessageColor || chalk.green;
|
|
19
|
+
this.allTasksCompleted = false;
|
|
20
|
+
this.taskBars = tasks.map((task, index) => this.createTaskBar(task, index));
|
|
21
|
+
}
|
|
22
|
+
convertSeconds(seconds) {
|
|
23
|
+
if (seconds === 0) return "0 s";
|
|
24
|
+
const units = [{
|
|
25
|
+
name: "y",
|
|
26
|
+
seconds: 60 * 60 * 24 * 365
|
|
27
|
+
}, {
|
|
28
|
+
name: "mo",
|
|
29
|
+
seconds: 60 * 60 * 24 * 30
|
|
30
|
+
}, {
|
|
31
|
+
name: "w",
|
|
32
|
+
seconds: 60 * 60 * 24 * 7
|
|
33
|
+
}, {
|
|
34
|
+
name: "d",
|
|
35
|
+
seconds: 60 * 60 * 24
|
|
36
|
+
}, {
|
|
37
|
+
name: "h",
|
|
38
|
+
seconds: 60 * 60
|
|
39
|
+
}, {
|
|
40
|
+
name: "m",
|
|
41
|
+
seconds: 60
|
|
42
|
+
}, {
|
|
43
|
+
name: "s",
|
|
44
|
+
seconds: 1
|
|
45
|
+
}];
|
|
46
|
+
let remainingSeconds = seconds;
|
|
47
|
+
let result = "";
|
|
48
|
+
for (const unit of units) {
|
|
49
|
+
const count = Math.floor(remainingSeconds / unit.seconds);
|
|
50
|
+
if (count > 0) {
|
|
51
|
+
result += `${count}${unit.name} `;
|
|
52
|
+
remainingSeconds %= unit.seconds;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result.trim();
|
|
56
|
+
}
|
|
57
|
+
createTaskBar(task, index) {
|
|
58
|
+
const defaultOptions = {
|
|
59
|
+
barLength: 40,
|
|
60
|
+
filledChar: ">",
|
|
61
|
+
unfilledChar: " ",
|
|
62
|
+
barColor: chalk.hex("#00CED1").bold,
|
|
63
|
+
taskNameColor: chalk.hex("#007B7F").bold,
|
|
64
|
+
taskCompletedColor: chalk.hex("#2ED573").bold,
|
|
65
|
+
sizeTransferredColor: chalk.hex("#5ECAFF"),
|
|
66
|
+
totalSizeColor: chalk.hex("#00FFFF"),
|
|
67
|
+
taskETAColor: chalk.hex("#00BFFF"),
|
|
68
|
+
speedColor: chalk.hex("#90E0EF"),
|
|
69
|
+
percentageColor: chalk.hex("#1A936F").bold,
|
|
70
|
+
dynamicUnits: true
|
|
71
|
+
};
|
|
72
|
+
const settings = {
|
|
73
|
+
...defaultOptions,
|
|
74
|
+
...this.options
|
|
75
|
+
};
|
|
76
|
+
const newBar = {
|
|
77
|
+
taskName: task.name,
|
|
78
|
+
totalSize: task.size,
|
|
79
|
+
current: 0,
|
|
80
|
+
startTime: Date.now(),
|
|
81
|
+
taskIndex: index,
|
|
82
|
+
completed: false,
|
|
83
|
+
getFormattedSize: size => {
|
|
84
|
+
if (!settings.dynamicUnits) return `${size.toFixed(1)} B`;
|
|
85
|
+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
86
|
+
let unitIndex = 0;
|
|
87
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
88
|
+
size /= 1024;
|
|
89
|
+
unitIndex++;
|
|
90
|
+
}
|
|
91
|
+
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
92
|
+
},
|
|
93
|
+
getUnitData: size => {
|
|
94
|
+
if (!settings.dynamicUnits) return {
|
|
95
|
+
current: size.toFixed(1),
|
|
96
|
+
unit: "B"
|
|
97
|
+
};
|
|
98
|
+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
99
|
+
let unitIndex = 0;
|
|
100
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
101
|
+
size /= 1024;
|
|
102
|
+
unitIndex++;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
current: size.toFixed(2),
|
|
106
|
+
unit: units[unitIndex]
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
redraw: () => {
|
|
110
|
+
readline.cursorTo(process.stdout, 0, newBar.taskIndex % this.barsPerPage);
|
|
111
|
+
process.stdout.clearLine();
|
|
112
|
+
if (newBar.completed) {
|
|
113
|
+
process.stdout.write(settings.taskCompletedColor(`${newBar.taskName}: Completed!`));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const percentage = newBar.current / newBar.totalSize * 100;
|
|
117
|
+
const elapsedTime = (Date.now() - newBar.startTime) / 1000;
|
|
118
|
+
const speed = newBar.current / elapsedTime;
|
|
119
|
+
const speedData = newBar.getUnitData(speed);
|
|
120
|
+
const timeLeft = (newBar.totalSize - newBar.current) / (speed || 1);
|
|
121
|
+
newBar.progress = percentage;
|
|
122
|
+
newBar.timeLeft = timeLeft;
|
|
123
|
+
newBar.speed = speed;
|
|
124
|
+
const filledLength = Math.round(settings.barLength * (newBar.current / newBar.totalSize));
|
|
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) + "%")}`);
|
|
127
|
+
},
|
|
128
|
+
update: amount => {
|
|
129
|
+
newBar.current += amount;
|
|
130
|
+
if (newBar.current > newBar.totalSize) {
|
|
131
|
+
newBar.current = newBar.totalSize;
|
|
132
|
+
}
|
|
133
|
+
const startIndex = this.currentPage * this.barsPerPage;
|
|
134
|
+
const endIndex = startIndex + this.barsPerPage;
|
|
135
|
+
if (newBar.taskIndex >= startIndex && newBar.taskIndex < endIndex) {
|
|
136
|
+
newBar.redraw();
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
set: amount => {
|
|
140
|
+
newBar.current = amount;
|
|
141
|
+
if (newBar.current > newBar.totalSize) {
|
|
142
|
+
newBar.current = newBar.totalSize;
|
|
143
|
+
}
|
|
144
|
+
const startIndex = this.currentPage * this.barsPerPage;
|
|
145
|
+
const endIndex = startIndex + this.barsPerPage;
|
|
146
|
+
if (newBar.taskIndex >= startIndex && newBar.taskIndex < endIndex) {
|
|
147
|
+
newBar.redraw();
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
complete: () => {
|
|
151
|
+
newBar.completed = true;
|
|
152
|
+
this.completedTasksCount++;
|
|
153
|
+
if (this.completionFeed.length >= this.maxCompletionFeedSize) {
|
|
154
|
+
this.completionFeed.shift();
|
|
155
|
+
}
|
|
156
|
+
this.completionFeed.push(newBar.taskName);
|
|
157
|
+
newBar.redraw();
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
this.completionFeed = this.completionFeed.filter(task => task !== newBar.taskName);
|
|
160
|
+
}, this.completionTimeout);
|
|
161
|
+
if (this.completedTasksCount === this.totalTasks) {
|
|
162
|
+
this.allTasksCompleted = true;
|
|
163
|
+
this.exitInputListener();
|
|
164
|
+
this.displayPage();
|
|
165
|
+
this.displayCompletionMessage();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
return newBar;
|
|
170
|
+
}
|
|
171
|
+
addTask(task) {
|
|
172
|
+
const index = this.taskBars.length;
|
|
173
|
+
const newTaskBar = this.createTaskBar(task, index);
|
|
174
|
+
this.taskBars.push(newTaskBar);
|
|
175
|
+
this.totalTasks++;
|
|
176
|
+
}
|
|
177
|
+
updateTaskProgress(taskName, amount) {
|
|
178
|
+
const taskBar = this.taskBars.find(bar => bar.taskName === taskName);
|
|
179
|
+
if (taskBar) {
|
|
180
|
+
taskBar.update(amount);
|
|
181
|
+
try {
|
|
182
|
+
if (taskBar.current >= taskBar.totalSize && taskBar.complete) {
|
|
183
|
+
taskBar.complete();
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {}
|
|
186
|
+
} else {
|
|
187
|
+
console.log(chalk.red(`Task ${taskName} not found.`));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
setTaskProgress(taskName, amount) {
|
|
191
|
+
const taskBar = this.taskBars.find(bar => bar.taskName === taskName);
|
|
192
|
+
if (taskBar) {
|
|
193
|
+
taskBar.set(amount);
|
|
194
|
+
if (taskBar.current >= taskBar.totalSize) {
|
|
195
|
+
taskBar.complete();
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
console.log(chalk.red(`Task ${taskName} not found.`));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
clearPage() {
|
|
202
|
+
for (let i = 0; i < this.barsPerPage + 2; i++) {
|
|
203
|
+
readline.cursorTo(process.stdout, 0, i);
|
|
204
|
+
process.stdout.clearLine();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
displayPage(customPagePrefix = "Page", bottomMessage, bottomMessageColor = chalk.gray) {
|
|
208
|
+
this.clearPage();
|
|
209
|
+
const startIndex = this.currentPage * this.barsPerPage;
|
|
210
|
+
const endIndex = startIndex + this.barsPerPage;
|
|
211
|
+
this.taskBars.slice(startIndex, endIndex).forEach(bar => {
|
|
212
|
+
bar.redraw();
|
|
213
|
+
});
|
|
214
|
+
if (!this.allTasksCompleted) {
|
|
215
|
+
this.redrawCompletionFeed(customPagePrefix, bottomMessage, bottomMessageColor);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
redrawCompletionFeed(customPagePrefix = "Page", bottomMessage, pageFooterMessageColor = chalk.blue, bottomMessageColor = chalk.gray, recentlyCompletedColor = chalk.green) {
|
|
219
|
+
const bottomPosition = this.barsPerPage;
|
|
220
|
+
readline.cursorTo(process.stdout, 0, bottomPosition);
|
|
221
|
+
process.stdout.clearLine();
|
|
222
|
+
process.stdout.write(pageFooterMessageColor(`${customPagePrefix} ${this.currentPage + 1}/${Math.max(1, Math.ceil(this.taskBars.length / this.barsPerPage))} | Completed: ${this.completedTasksCount}/${this.totalTasks}`));
|
|
223
|
+
if (this.completionFeed.length > 0) {
|
|
224
|
+
process.stdout.write(recentlyCompletedColor(` | Recently completed: ${this.completionFeed.join(", ")}`));
|
|
225
|
+
}
|
|
226
|
+
readline.cursorTo(process.stdout, 0, bottomPosition + 1);
|
|
227
|
+
process.stdout.clearLine();
|
|
228
|
+
if (bottomMessage) {
|
|
229
|
+
process.stdout.write(bottomMessageColor(bottomMessage));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
listenForInput(customPagePrefix = "Page", bottomMessage, bottomMessageColor = chalk.gray, recentlyCompletedColor = chalk.green) {
|
|
233
|
+
process.stdin.setRawMode(true);
|
|
234
|
+
process.stdin.resume();
|
|
235
|
+
process.stdin.setEncoding("utf-8");
|
|
236
|
+
this.inputListener = key => {
|
|
237
|
+
if (key === "\u0003") {
|
|
238
|
+
process.exit();
|
|
239
|
+
} else if (this.allTasksCompleted) {
|
|
240
|
+
this.exitInputListener();
|
|
241
|
+
} else if (key === "n" && this.currentPage < Math.ceil(this.taskBars.length / this.barsPerPage) - 1) {
|
|
242
|
+
this.currentPage++;
|
|
243
|
+
this.displayPage(customPagePrefix, bottomMessage, bottomMessageColor, recentlyCompletedColor);
|
|
244
|
+
} else if (key === "p" && this.currentPage > 0) {
|
|
245
|
+
this.currentPage--;
|
|
246
|
+
this.displayPage(customPagePrefix, bottomMessage, bottomMessageColor, recentlyCompletedColor);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
process.stdin.on("data", this.inputListener);
|
|
250
|
+
}
|
|
251
|
+
exitInputListener() {
|
|
252
|
+
process.stdin.removeListener("data", this.inputListener);
|
|
253
|
+
process.stdin.setRawMode(false);
|
|
254
|
+
process.stdin.resume();
|
|
255
|
+
}
|
|
256
|
+
displayCompletionMessage() {
|
|
257
|
+
const lastLine = Math.min(this.taskBars.length, this.barsPerPage);
|
|
258
|
+
readline.cursorTo(process.stdout, 0, lastLine);
|
|
259
|
+
process.stdout.clearLine();
|
|
260
|
+
console.log(this.completionMessageColor(this.completionMessage));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
;
|
|
264
|
+
module.exports = ConsoleProgressBarManager;
|
|
@@ -50,12 +50,6 @@ function _startTransfer(file, transferId, fileDetails, onSendChunk, onComplete,
|
|
|
50
50
|
const start = chunkIndex * this.chunkSize;
|
|
51
51
|
const end = Math.min(start + this.chunkSize, file.length);
|
|
52
52
|
const chunk = file.slice(start, end);
|
|
53
|
-
if (!fs.existsSync(fileDetails.path)) {
|
|
54
|
-
fs.mkdirSync(fileDetails.path, {
|
|
55
|
-
recursive: true
|
|
56
|
-
});
|
|
57
|
-
console.log(`[FileTide ~ Transporter] Created directory: ${fileDetails.path}`);
|
|
58
|
-
}
|
|
59
53
|
if (onStart && chunkIndex === 0) {
|
|
60
54
|
onStart({
|
|
61
55
|
name: fileDetails.name,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const path = require(
|
|
4
|
-
const fs = require(
|
|
5
|
-
const
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const filesPath = path.join(__dirname, "../files");
|
|
6
7
|
class FileUtilityManager {
|
|
7
8
|
/**
|
|
8
9
|
* Recursively calculates the total size of files in a directory.
|
|
@@ -23,6 +24,45 @@ class FileUtilityManager {
|
|
|
23
24
|
});
|
|
24
25
|
return totalSize;
|
|
25
26
|
}
|
|
27
|
+
static getTidePath(inputPath) {
|
|
28
|
+
const homeDir = os.homedir();
|
|
29
|
+
const platform = process.platform;
|
|
30
|
+
const folderMappings = {
|
|
31
|
+
">Downloads": path.join(homeDir, "Downloads"),
|
|
32
|
+
">Documents": path.join(homeDir, "Documents"),
|
|
33
|
+
">Desktop": path.join(homeDir, "Desktop"),
|
|
34
|
+
">AppData": platform === "win32" ? path.join(homeDir, "AppData", "Roaming") : path.join(homeDir, ".config"),
|
|
35
|
+
">LocalAppData": platform === "win32" ? path.join(homeDir, "AppData", "Local") : "/var/local",
|
|
36
|
+
">Music": path.join(homeDir, "Music"),
|
|
37
|
+
">Pictures": path.join(homeDir, "Pictures"),
|
|
38
|
+
">Videos": path.join(homeDir, "Videos"),
|
|
39
|
+
">Public": platform === "win32" ? path.join("C:", "Users", "Public") : "/usr/share",
|
|
40
|
+
">Templates": path.join(homeDir, "Templates"),
|
|
41
|
+
">Temp": platform === "win32" ? path.join("C:", "Windows", "Temp") : "/tmp",
|
|
42
|
+
">LogFiles": platform === "win32" ? path.join("C:", "Windows", "Logs") : "/var/log",
|
|
43
|
+
">SystemRoot": platform === "win32" ? path.join("C:", "Windows") : "/",
|
|
44
|
+
">Downloads": path.join(homeDir, "Downloads"),
|
|
45
|
+
">Cache": platform === "win32" ? path.join(homeDir, "AppData", "Local", "Cache") : path.join(homeDir, ".cache"),
|
|
46
|
+
">Config": platform === "win32" ? path.join(homeDir, "AppData", "Local", "Config") : "/etc",
|
|
47
|
+
">CurrentDirectory": process.cwd(),
|
|
48
|
+
">Home": homeDir
|
|
49
|
+
};
|
|
50
|
+
let resolvedPath = inputPath;
|
|
51
|
+
Object.keys(folderMappings).forEach(shortcut => {
|
|
52
|
+
if (resolvedPath.includes(shortcut)) {
|
|
53
|
+
const replacementPath = folderMappings[shortcut];
|
|
54
|
+
if (fs.existsSync(replacementPath)) {
|
|
55
|
+
resolvedPath = resolvedPath.replace(shortcut, replacementPath);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(`The directory for '${shortcut}' does not exist on this system.`);
|
|
58
|
+
resolvedPath = resolvedPath.replace(shortcut, homeDir);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
resolvedPath = this.osPath(resolvedPath);
|
|
63
|
+
resolvedPath = this.normalizePath(resolvedPath);
|
|
64
|
+
return resolvedPath;
|
|
65
|
+
}
|
|
26
66
|
|
|
27
67
|
/**
|
|
28
68
|
* Normalizes the input path to handle both single and double slashes.
|
|
@@ -48,7 +88,7 @@ class FileUtilityManager {
|
|
|
48
88
|
* @returns {Array} - An array of objects containing file data, name, and path.
|
|
49
89
|
*/
|
|
50
90
|
static getDirectoryData(inputDirPath) {
|
|
51
|
-
const dirPath = this.
|
|
91
|
+
const dirPath = this.getTidePath(inputDirPath);
|
|
52
92
|
if (!fs.existsSync(dirPath)) {
|
|
53
93
|
console.log(`Directory at path ${dirPath} not found.`);
|
|
54
94
|
return [];
|
|
@@ -76,10 +116,10 @@ class FileUtilityManager {
|
|
|
76
116
|
/**
|
|
77
117
|
* Gets file data from a given file path.
|
|
78
118
|
* @param {string} inputFilePath - The full path to the file.
|
|
79
|
-
* @returns {Object|null} - An object containing file data, name,
|
|
119
|
+
* @returns {Object|null} - An object containing file data, name, path, and size or null if the file doesn't exist.
|
|
80
120
|
*/
|
|
81
121
|
static getFileData(inputFilePath) {
|
|
82
|
-
const filePath = this.
|
|
122
|
+
const filePath = this.getTidePath(inputFilePath);
|
|
83
123
|
if (!fs.existsSync(filePath)) {
|
|
84
124
|
console.log(`File at path ${filePath} not found.`);
|
|
85
125
|
return null;
|
|
@@ -89,6 +129,7 @@ class FileUtilityManager {
|
|
|
89
129
|
});
|
|
90
130
|
const fileName = path.basename(filePath);
|
|
91
131
|
return {
|
|
132
|
+
size: fileData.length,
|
|
92
133
|
fileName,
|
|
93
134
|
fileData,
|
|
94
135
|
filePath
|
|
@@ -101,7 +142,7 @@ class FileUtilityManager {
|
|
|
101
142
|
* @returns {Object|Array|null} - File data if it's a file, an array of file data if it's a directory, or null if the path doesn't exist.
|
|
102
143
|
*/
|
|
103
144
|
static getPathData(inputPath) {
|
|
104
|
-
const currentInputPath = this.
|
|
145
|
+
const currentInputPath = this.getTidePath(inputPath);
|
|
105
146
|
if (!fs.existsSync(currentInputPath)) {
|
|
106
147
|
console.log(`Path at ${currentInputPath} not found.`);
|
|
107
148
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trap_stevo/filetide",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"description": "Revolutionizing real-time file transfer with seamless, instant communication across any device. Deliver files instantly, regardless of platform, and experience unparalleled speed and control in managing transfers. Elevate your file-sharing capabilities with a tool designed for precision, efficiency, and effortless connectivity.",
|
|
5
5
|
"main": "dist/cjs/FileTide.js",
|
|
6
6
|
"scripts": {
|