@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.
- package/dist/cjs/FileMessager.js +306 -83
- package/dist/cjs/FileMessagerClient.js +149 -43
- package/dist/cjs/FileTide.js +116 -17
- package/dist/cjs/HUDComponents/ChunkCapacitor.js +35 -0
- package/dist/cjs/HUDComponents/ConsoleProgressBarManager.js +5 -1
- package/dist/cjs/HUDManagers/FileTransferManager.js +133 -19
- package/dist/cjs/HUDManagers/FileTransferReceiverManager.js +1 -1
- package/dist/cjs/HUDManagers/FileTransferRecoveryManager.js +94 -0
- package/dist/cjs/HUDManagers/FileUtilityManager.js +118 -61
- package/package.json +3 -3
|
@@ -4,6 +4,7 @@ function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a
|
|
|
4
4
|
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
|
|
5
5
|
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
|
|
6
6
|
const fs = require("fs");
|
|
7
|
+
const FileTransferRecoveryManager = require("./FileTransferRecoveryManager.js");
|
|
7
8
|
var _FileTransferManager_brand = /*#__PURE__*/new WeakSet();
|
|
8
9
|
class FileTransferManager {
|
|
9
10
|
constructor({
|
|
@@ -17,25 +18,135 @@ class FileTransferManager {
|
|
|
17
18
|
this.maxRetries = maxRetries;
|
|
18
19
|
this.chunkSize = chunkSize;
|
|
19
20
|
}
|
|
20
|
-
sendFile(file, {
|
|
21
|
+
async sendFile(file, {
|
|
21
22
|
fileDetails,
|
|
23
|
+
originDetails,
|
|
22
24
|
onSendChunk,
|
|
23
25
|
onComplete,
|
|
24
26
|
onProgress,
|
|
25
27
|
onStart
|
|
26
|
-
}) {
|
|
28
|
+
}, clientTide) {
|
|
27
29
|
const transferId = _assertClassBrand(_FileTransferManager_brand, this, _generateTransferId).call(this, fileDetails);
|
|
28
|
-
return _assertClassBrand(_FileTransferManager_brand, this, _startTransfer).call(this, file, transferId, fileDetails, onSendChunk, onComplete, onProgress, onStart);
|
|
30
|
+
return await _assertClassBrand(_FileTransferManager_brand, this, _startTransfer).call(this, file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, clientTide);
|
|
31
|
+
}
|
|
32
|
+
async sendLargeFile(fileHandle, {
|
|
33
|
+
fileDetails,
|
|
34
|
+
originDetails,
|
|
35
|
+
recipientID,
|
|
36
|
+
onSendChunk,
|
|
37
|
+
onComplete,
|
|
38
|
+
onProgress,
|
|
39
|
+
onStart,
|
|
40
|
+
onTiding
|
|
41
|
+
}, clientTide) {
|
|
42
|
+
const transferId = _assertClassBrand(_FileTransferManager_brand, this, _generateTransferId).call(this, fileDetails);
|
|
43
|
+
const recoveredTransferState = (await clientTide.emitEventWithResponse(originDetails.senderID, "get-client-transfer-state", "sent-current-transfer-state", {
|
|
44
|
+
recipientID,
|
|
45
|
+
senderID: originDetails.senderID,
|
|
46
|
+
filePath: fileDetails.destinationPath
|
|
47
|
+
}, 30000)) || {
|
|
48
|
+
transferredSize: 0,
|
|
49
|
+
lastChunkSent: 0
|
|
50
|
+
};
|
|
51
|
+
const totalChunks = Math.ceil(fileDetails.size / this.chunkSize);
|
|
52
|
+
this.activeTransfers.set(transferId, {
|
|
53
|
+
totalChunks,
|
|
54
|
+
transferredSize: recoveredTransferState.transferredSize,
|
|
55
|
+
completedChunks: recoveredTransferState.lastChunkSent,
|
|
56
|
+
activeChunks: 0,
|
|
57
|
+
failedChunks: new Map(),
|
|
58
|
+
currentChunk: recoveredTransferState.lastChunkSent,
|
|
59
|
+
size: fileDetails.size
|
|
60
|
+
});
|
|
61
|
+
const updateProgress = () => {
|
|
62
|
+
const transferState = this.activeTransfers.get(transferId);
|
|
63
|
+
if (onProgress) {
|
|
64
|
+
onProgress(transferState.completedChunks / transferState.totalChunks * 100, this.chunkSize);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const exponentialBackoff = retryCount => Math.min(1000 * 2 ** retryCount, 10000);
|
|
68
|
+
let nextExpectedChunk = recoveredTransferState.lastChunkSent;
|
|
69
|
+
const chunkBuffer = new Map();
|
|
70
|
+
const sendChunk = async chunkIndex => {
|
|
71
|
+
const start = chunkIndex * this.chunkSize;
|
|
72
|
+
const end = Math.min(start + this.chunkSize, fileDetails.size);
|
|
73
|
+
const {
|
|
74
|
+
buffer
|
|
75
|
+
} = await fileHandle.read(Buffer.alloc(end - start), 0, end - start, start);
|
|
76
|
+
const transferState = this.activeTransfers.get(transferId);
|
|
77
|
+
chunkBuffer.set(chunkIndex, buffer);
|
|
78
|
+
while (chunkBuffer.has(nextExpectedChunk)) {
|
|
79
|
+
const nextChunkBuffer = chunkBuffer.get(nextExpectedChunk);
|
|
80
|
+
await onSendChunk(transferId, nextExpectedChunk, this.chunkSize, nextChunkBuffer, totalChunks, this.activeTransfers.get(transferId).completedChunks + 1);
|
|
81
|
+
chunkBuffer.delete(nextExpectedChunk);
|
|
82
|
+
nextExpectedChunk++;
|
|
83
|
+
transferState.transferredSize += this.chunkSize;
|
|
84
|
+
transferState.completedChunks++;
|
|
85
|
+
updateProgress();
|
|
86
|
+
}
|
|
87
|
+
if (transferState.completedChunks === transferState.totalChunks) {
|
|
88
|
+
return onComplete(transferId).then(() => {
|
|
89
|
+
this.activeTransfers.delete(transferId);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const sendChunkBatch = async (startChunkIndex, batchSize = 100, miniBatchSize = 10) => {
|
|
94
|
+
for (let i = 0; i < batchSize && startChunkIndex + i < totalChunks; i += miniBatchSize) {
|
|
95
|
+
const miniBatchPromises = [];
|
|
96
|
+
for (let j = 0; j < miniBatchSize && startChunkIndex + i + j < totalChunks; j++) {
|
|
97
|
+
const chunkIndex = startChunkIndex + i + j;
|
|
98
|
+
miniBatchPromises.push(sendChunk(chunkIndex));
|
|
99
|
+
}
|
|
100
|
+
await Promise.all(miniBatchPromises);
|
|
101
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const transferState = this.activeTransfers.get(transferId);
|
|
105
|
+
let currentBatchStart = transferState.currentChunk;
|
|
106
|
+
const keepAliveInterval = setInterval(() => {
|
|
107
|
+
if (onTiding) onTiding();
|
|
108
|
+
}, 5000);
|
|
109
|
+
try {
|
|
110
|
+
let adaptiveBatchSize = 100;
|
|
111
|
+
while (currentBatchStart < totalChunks) {
|
|
112
|
+
if (transferState.activeChunks < this.parallelChunks) {
|
|
113
|
+
adaptiveBatchSize = Math.min(adaptiveBatchSize + 10, 100);
|
|
114
|
+
} else {
|
|
115
|
+
adaptiveBatchSize = Math.max(adaptiveBatchSize - 10, 10);
|
|
116
|
+
}
|
|
117
|
+
await sendChunkBatch(currentBatchStart, adaptiveBatchSize);
|
|
118
|
+
currentBatchStart += adaptiveBatchSize;
|
|
119
|
+
await new Promise(resolve => setTimeout(resolve, 30));
|
|
120
|
+
}
|
|
121
|
+
} finally {
|
|
122
|
+
clearInterval(keepAliveInterval);
|
|
123
|
+
}
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
fileHandle.on("close", resolve);
|
|
126
|
+
fileHandle.on("error", error => {
|
|
127
|
+
this.activeTransfers.delete(transferId);
|
|
128
|
+
reject(error);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
29
131
|
}
|
|
30
132
|
}
|
|
31
|
-
function _startTransfer(file, transferId, fileDetails, onSendChunk, onComplete, onProgress, onStart) {
|
|
133
|
+
async function _startTransfer(file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, clientTide) {
|
|
134
|
+
const recoveredTransferState = (await clientTide.emitEventWithResponse(originDetails.senderID, "get-client-transfer-state", "sent-current-transfer-state", {
|
|
135
|
+
recipientID: originDetails.recipientID,
|
|
136
|
+
senderID: originDetails.senderID,
|
|
137
|
+
filePath: fileDetails.destinationPath
|
|
138
|
+
}, 30000)) || {
|
|
139
|
+
transferredSize: 0,
|
|
140
|
+
lastChunkSent: 0
|
|
141
|
+
};
|
|
32
142
|
const totalChunks = Math.ceil(file.length / this.chunkSize);
|
|
33
143
|
this.activeTransfers.set(transferId, {
|
|
34
144
|
totalChunks,
|
|
35
|
-
|
|
145
|
+
transferredSize: recoveredTransferState.transferredSize,
|
|
146
|
+
completedChunks: recoveredTransferState.lastChunkSent,
|
|
36
147
|
activeChunks: 0,
|
|
37
148
|
failedChunks: new Map(),
|
|
38
|
-
currentChunk:
|
|
149
|
+
currentChunk: recoveredTransferState.lastChunkSent,
|
|
39
150
|
size: fileDetails.size
|
|
40
151
|
});
|
|
41
152
|
const updateProgress = () => {
|
|
@@ -44,10 +155,13 @@ function _startTransfer(file, transferId, fileDetails, onSendChunk, onComplete,
|
|
|
44
155
|
onProgress(transferState.completedChunks / transferState.totalChunks * 100, this.chunkSize);
|
|
45
156
|
}
|
|
46
157
|
};
|
|
158
|
+
const exponentialBackoff = retryCount => Math.min(1000 * 2 ** retryCount, 10000);
|
|
47
159
|
return new Promise((resolve, reject) => {
|
|
48
|
-
const sendChunk = chunkIndex => {
|
|
160
|
+
const sendChunk = async chunkIndex => {
|
|
49
161
|
const transferState = this.activeTransfers.get(transferId);
|
|
50
|
-
if (chunkIndex >= transferState.totalChunks)
|
|
162
|
+
if (chunkIndex >= transferState.totalChunks) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
51
165
|
const start = chunkIndex * this.chunkSize;
|
|
52
166
|
const end = Math.min(start + this.chunkSize, file.length);
|
|
53
167
|
const chunk = file.slice(start, end);
|
|
@@ -60,7 +174,8 @@ function _startTransfer(file, transferId, fileDetails, onSendChunk, onComplete,
|
|
|
60
174
|
});
|
|
61
175
|
}
|
|
62
176
|
transferState.activeChunks++;
|
|
63
|
-
|
|
177
|
+
try {
|
|
178
|
+
await onSendChunk(transferId, chunkIndex, this.chunkSize, chunk, transferState.totalChunks, transferState.completedChunks + 1);
|
|
64
179
|
transferState.completedChunks++;
|
|
65
180
|
transferState.activeChunks--;
|
|
66
181
|
updateProgress();
|
|
@@ -68,23 +183,22 @@ function _startTransfer(file, transferId, fileDetails, onSendChunk, onComplete,
|
|
|
68
183
|
sendChunk(transferState.currentChunk++);
|
|
69
184
|
}
|
|
70
185
|
if (transferState.completedChunks === transferState.totalChunks) {
|
|
71
|
-
onComplete(transferId)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}).catch(reject);
|
|
186
|
+
await onComplete(transferId);
|
|
187
|
+
this.activeTransfers.delete(transferId);
|
|
188
|
+
resolve();
|
|
75
189
|
}
|
|
76
|
-
}
|
|
77
|
-
console.error(`Did not send chunk ${chunkIndex}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(`Did not send chunk ${chunkIndex} ~ ${error.message}`);
|
|
78
192
|
transferState.activeChunks--;
|
|
79
|
-
|
|
193
|
+
const retryCount = (transferState.failedChunks.get(chunkIndex) || 0) + 1;
|
|
194
|
+
if (retryCount > this.maxRetries) {
|
|
80
195
|
this.activeTransfers.delete(transferId);
|
|
81
196
|
reject(new Error(`Did not send chunk ${chunkIndex} after ${this.maxRetries} retries.`));
|
|
82
197
|
} else {
|
|
83
|
-
const retryCount = (transferState.failedChunks.get(chunkIndex) || 0) + 1;
|
|
84
198
|
transferState.failedChunks.set(chunkIndex, retryCount);
|
|
85
|
-
sendChunk(chunkIndex);
|
|
199
|
+
setTimeout(() => sendChunk(chunkIndex), exponentialBackoff(retryCount));
|
|
86
200
|
}
|
|
87
|
-
}
|
|
201
|
+
}
|
|
88
202
|
};
|
|
89
203
|
const transferState = this.activeTransfers.get(transferId);
|
|
90
204
|
for (let i = 0; i < Math.min(this.parallelChunks, transferState.totalChunks); i++) {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
class FileTransferRecoveryManager {
|
|
6
|
+
constructor(storagePath = `${process.cwd()}/transfer_states`) {
|
|
7
|
+
if (FileTransferRecoveryManager.instance) {
|
|
8
|
+
return FileTransferRecoveryManager.instance;
|
|
9
|
+
}
|
|
10
|
+
this.storagePath = storagePath;
|
|
11
|
+
this.memoryStorage = new Map();
|
|
12
|
+
this.ensureStorageDirectory();
|
|
13
|
+
FileTransferRecoveryManager.instance = this;
|
|
14
|
+
}
|
|
15
|
+
ensureStorageDirectory() {
|
|
16
|
+
if (!fs.existsSync(this.storagePath)) {
|
|
17
|
+
fs.mkdirSync(this.storagePath, {
|
|
18
|
+
recursive: true
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
sanitizeFileName(fileName) {
|
|
23
|
+
return fileName.replace(/[^a-zA-Z0-9-_\.]/g, "-").replace(/-+/g, "-").trim();
|
|
24
|
+
}
|
|
25
|
+
generateKey(clientName, senderName, filePath) {
|
|
26
|
+
const normalizedPath = this.sanitizeFileName(filePath);
|
|
27
|
+
return `${clientName}_${senderName}_${normalizedPath}.json`;
|
|
28
|
+
}
|
|
29
|
+
getStorageFilePath(clientName, senderName, filePath) {
|
|
30
|
+
return path.join(this.storagePath, this.generateKey(clientName, senderName, filePath));
|
|
31
|
+
}
|
|
32
|
+
saveTransferState(clientName, senderName, filePath, chunkIndex, transferredSize) {
|
|
33
|
+
const key = this.generateKey(clientName, senderName, filePath);
|
|
34
|
+
const transferState = {
|
|
35
|
+
transferredSize,
|
|
36
|
+
senderName,
|
|
37
|
+
clientName,
|
|
38
|
+
filePath,
|
|
39
|
+
lastChunkSent: chunkIndex
|
|
40
|
+
};
|
|
41
|
+
this.memoryStorage.set(key, transferState);
|
|
42
|
+
}
|
|
43
|
+
saveToDisk(clientName, senderName, filePath) {
|
|
44
|
+
const key = this.generateKey(clientName, senderName, filePath);
|
|
45
|
+
const storageFile = this.getStorageFilePath(clientName, senderName, filePath);
|
|
46
|
+
const transferState = this.memoryStorage.get(key);
|
|
47
|
+
if (transferState) {
|
|
48
|
+
try {
|
|
49
|
+
fs.writeFileSync(storageFile, JSON.stringify(transferState, null, 2));
|
|
50
|
+
console.log(`Saved transfer state to disk: ${storageFile}`);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`Error saving transfer state for ${key}: ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getTransferState(clientName, senderName, filePath) {
|
|
57
|
+
const key = this.generateKey(clientName, senderName, filePath);
|
|
58
|
+
if (this.memoryStorage.has(key)) {
|
|
59
|
+
return this.memoryStorage.get(key);
|
|
60
|
+
}
|
|
61
|
+
const storageFile = this.getStorageFilePath(clientName, senderName, filePath);
|
|
62
|
+
if (fs.existsSync(storageFile)) {
|
|
63
|
+
const transferState = JSON.parse(fs.readFileSync(storageFile, "utf-8"));
|
|
64
|
+
return transferState;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
getLastChunk(clientName, senderName, filePath) {
|
|
69
|
+
const transferState = this.getTransferState(clientName, senderName, filePath);
|
|
70
|
+
return transferState ? transferState.lastChunkSent : null;
|
|
71
|
+
}
|
|
72
|
+
clearTransferState(clientName, senderName, filePath) {
|
|
73
|
+
const key = this.generateKey(clientName, senderName, filePath);
|
|
74
|
+
this.memoryStorage.delete(key);
|
|
75
|
+
const storageFile = this.getStorageFilePath(clientName, senderName, filePath);
|
|
76
|
+
if (fs.existsSync(storageFile)) {
|
|
77
|
+
fs.unlinkSync(storageFile);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
flushToDisk() {
|
|
81
|
+
console.log(this.memoryStorage);
|
|
82
|
+
for (const [key, state] of this.memoryStorage.entries()) {
|
|
83
|
+
const {
|
|
84
|
+
clientName,
|
|
85
|
+
senderName,
|
|
86
|
+
filePath
|
|
87
|
+
} = state;
|
|
88
|
+
console.log("Saving current state ~ ", state);
|
|
89
|
+
this.saveToDisk(clientName, senderName, filePath);
|
|
90
|
+
}
|
|
91
|
+
;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
module.exports = new FileTransferRecoveryManager();
|
|
@@ -67,15 +67,18 @@ class FileUtilityManager {
|
|
|
67
67
|
/**
|
|
68
68
|
* Function to list all paths across the system using wildcard '*'.
|
|
69
69
|
* @param {number} depth - The maximum depth limit (from the root). Defaults to 1.
|
|
70
|
+
* @param {Function} onDirectory - Callback that outputs each directory's contents on the go.
|
|
70
71
|
* @returns {Promise<Array>} - A promise that resolves to an array of file and directory metadata across the system.
|
|
71
72
|
*/
|
|
72
|
-
static async listAllPathsInOS(depth = 1) {
|
|
73
|
+
static async listAllPathsInOS(depth = 1, onDirectory, onFile) {
|
|
73
74
|
const rootPaths = ["/", "C:/"];
|
|
74
75
|
let allResults = [];
|
|
75
76
|
const rootPromises = rootPaths.map(async rootPath => {
|
|
76
77
|
if (await this.exists(rootPath)) {
|
|
77
|
-
const subResult = await this.listPaths(rootPath, depth);
|
|
78
|
-
|
|
78
|
+
const subResult = await this.listPaths(rootPath, depth, 0, onDirectory);
|
|
79
|
+
if (depth <= 3) {
|
|
80
|
+
allResults.push(...subResult.results);
|
|
81
|
+
}
|
|
79
82
|
}
|
|
80
83
|
});
|
|
81
84
|
await Promise.all(rootPromises);
|
|
@@ -83,85 +86,114 @@ class FileUtilityManager {
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
* Function to list all paths and their metadata recursively.
|
|
90
|
+
* @param {string} dir - The directory path to scan.
|
|
91
|
+
* @param {number} maxDepth - The maximum depth limit (from the root). Defaults to 1.
|
|
92
|
+
* @param {number} currentDepth - The current depth level. Starts at 0 (for the root).
|
|
93
|
+
* @param {Function} onDirectory - Callback that outputs each directory's contents on the go.
|
|
94
|
+
* @returns {Promise<object>} - A promise that resolves to an object containing a list of files/directories with metadata.
|
|
95
|
+
*/
|
|
96
|
+
static async listPaths(dir, maxDepth = 1, currentDepth = 0, onDirectory, onContent) {
|
|
93
97
|
let results = [];
|
|
94
|
-
if (
|
|
98
|
+
if (currentDepth > maxDepth) {
|
|
95
99
|
return {
|
|
96
100
|
totalItems: 0,
|
|
97
101
|
totalSize: 0,
|
|
98
102
|
results
|
|
99
103
|
};
|
|
100
104
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
105
|
+
try {
|
|
106
|
+
const list = await fsAsync.readdir(dir, {
|
|
107
|
+
withFileTypes: true
|
|
108
|
+
});
|
|
109
|
+
const dirStats = await fsAsync.stat(dir);
|
|
110
|
+
const dirMetadata = {
|
|
111
|
+
totalItems: 0,
|
|
112
|
+
totalSize: 0,
|
|
113
|
+
createdTime: dirStats.birthtime,
|
|
114
|
+
lastModified: dirStats.mtime,
|
|
115
|
+
type: "directory",
|
|
116
|
+
path: dir
|
|
117
|
+
};
|
|
118
|
+
results.push(dirMetadata);
|
|
119
|
+
const processEntries = list.map(async file => {
|
|
120
|
+
const filePath = path.join(dir, file.name);
|
|
121
|
+
try {
|
|
122
|
+
if (file.isDirectory()) {
|
|
123
|
+
console.log(`[FileTide] Depth >> ${currentDepth} | Including directory ~ ${filePath}`);
|
|
124
|
+
const subDirResult = await this.listPaths(filePath, maxDepth, currentDepth + 1, onDirectory);
|
|
125
|
+
dirMetadata.totalItems += subDirResult.totalItems;
|
|
126
|
+
dirMetadata.totalSize += subDirResult.totalSize;
|
|
127
|
+
results.push(...subDirResult.results);
|
|
128
|
+
if (onDirectory) {
|
|
129
|
+
onDirectory(subDirResult.totalItems, subDirResult.results);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
console.log(`[FileTide] Depth >> ${currentDepth} | Including file ~ ${filePath}`);
|
|
133
|
+
const fileStats = await fsAsync.stat(filePath);
|
|
134
|
+
const fileMetadata = {
|
|
135
|
+
createdTime: fileStats.birthtime,
|
|
136
|
+
lastModified: fileStats.mtime,
|
|
137
|
+
size: fileStats.size,
|
|
138
|
+
path: filePath,
|
|
139
|
+
type: "file"
|
|
140
|
+
};
|
|
141
|
+
dirMetadata.totalItems += 1;
|
|
142
|
+
dirMetadata.totalSize += fileStats.size;
|
|
143
|
+
results.push(fileMetadata);
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error.code === "ENOENT") {
|
|
147
|
+
console.log(`[FileTide] Warning ~ File or directory not found - ${filePath}`);
|
|
148
|
+
} else {
|
|
149
|
+
console.log(`[FileTide] Error processing path ~ ${filePath} ~ `, error);
|
|
150
|
+
}
|
|
134
151
|
}
|
|
135
|
-
}
|
|
136
|
-
|
|
152
|
+
});
|
|
153
|
+
const concurrencyLimit = 5;
|
|
154
|
+
for (let i = 0; i < processEntries.length; i += concurrencyLimit) {
|
|
155
|
+
await Promise.all(processEntries.slice(i, i + concurrencyLimit));
|
|
137
156
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
return {
|
|
158
|
+
totalItems: dirMetadata.totalItems,
|
|
159
|
+
totalSize: dirMetadata.totalSize,
|
|
160
|
+
results
|
|
161
|
+
};
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error.code === 'ENOENT') {
|
|
164
|
+
console.log(`[FileTide] Warning ~ Directory not found - ${dir}`);
|
|
165
|
+
} else {
|
|
166
|
+
console.log(`[FileTide] Error processing directory ~ ${dir} ~ `, error);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
totalItems: 0,
|
|
170
|
+
totalSize: 0,
|
|
171
|
+
results
|
|
172
|
+
};
|
|
173
|
+
}
|
|
145
174
|
}
|
|
146
175
|
|
|
147
176
|
/**
|
|
148
177
|
* Allows for listing files in a specific directory or all files in the system '*'.
|
|
149
178
|
* @param {Array} directories - Array of directories to scan or '*' for entire system.
|
|
150
179
|
* @param {number} depth - The maximum depth limit (from the root). Defaults to 1.
|
|
180
|
+
* @param {Funtion} onDirectory - Callback that outputs each directory's contents on the go.
|
|
151
181
|
* @returns {Promise<Array>} - A promise that resolves to an array of file and directory metadata from specified directories or the entire system.
|
|
152
182
|
*/
|
|
153
|
-
static async listFiles(directories, depth = 1) {
|
|
183
|
+
static async listFiles(directories, depth = 1, onDirectory) {
|
|
154
184
|
let fileList = [];
|
|
155
185
|
if (directories.includes("*")) {
|
|
156
|
-
fileList = await this.listAllPathsInOS(depth);
|
|
186
|
+
fileList = await this.listAllPathsInOS(depth, onDirectory);
|
|
157
187
|
} else {
|
|
158
188
|
const dirPromises = directories.map(async dir => {
|
|
159
189
|
const currentDir = this.getTidePath(dir);
|
|
160
190
|
if (await this.exists(currentDir)) {
|
|
161
|
-
const subResult = await this.listPaths(currentDir, depth);
|
|
162
|
-
|
|
191
|
+
const subResult = await this.listPaths(currentDir, depth, 0, onDirectory);
|
|
192
|
+
if (depth <= 3) {
|
|
193
|
+
fileList.push(...subResult.results);
|
|
194
|
+
}
|
|
163
195
|
} else {
|
|
164
|
-
console.warn(`Directory does not exist
|
|
196
|
+
console.warn(`Directory does not exist ~ ${currentDir}`);
|
|
165
197
|
}
|
|
166
198
|
});
|
|
167
199
|
await Promise.all(dirPromises);
|
|
@@ -286,15 +318,28 @@ class FileUtilityManager {
|
|
|
286
318
|
console.log(`Directory at path ${dirPath} not found.`);
|
|
287
319
|
return [];
|
|
288
320
|
}
|
|
321
|
+
const largeFileThreshold = 2 * 1024 * 1024 * 1024;
|
|
289
322
|
const filesData = [];
|
|
290
323
|
async function readDirectory(currentPath) {
|
|
291
324
|
const files = await FileUtilityManager.filterFilesAndDirectories(currentPath, filterContent);
|
|
292
325
|
for (const file of files) {
|
|
293
326
|
const stats = await fs.promises.stat(file.path);
|
|
294
327
|
if (stats.isFile()) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
328
|
+
if (stats.size > largeFileThreshold) {
|
|
329
|
+
const currentFilePath = FileUtilityManager.getTidePath(file.path);
|
|
330
|
+
console.log(`Detected large file at ${currentFilePath}, size ~ ${stats.size} bytes.`);
|
|
331
|
+
filesData.push({
|
|
332
|
+
fileName: path.basename(currentFilePath),
|
|
333
|
+
filePath: currentFilePath,
|
|
334
|
+
fileSize: stats.size,
|
|
335
|
+
largeFile: true,
|
|
336
|
+
type: "file"
|
|
337
|
+
});
|
|
338
|
+
} else if (stats.size <= largeFileThreshold) {
|
|
339
|
+
const fileData = await FileUtilityManager.getFileData(file.path);
|
|
340
|
+
if (fileData) {
|
|
341
|
+
filesData.push(fileData);
|
|
342
|
+
}
|
|
298
343
|
}
|
|
299
344
|
} else if (stats.isDirectory()) {
|
|
300
345
|
await readDirectory(file.path);
|
|
@@ -351,10 +396,22 @@ class FileUtilityManager {
|
|
|
351
396
|
console.log(`Path at ${currentInputPath} not found.`);
|
|
352
397
|
return null;
|
|
353
398
|
}
|
|
399
|
+
const largeFileThreshold = 2 * 1024 * 1024 * 1024;
|
|
354
400
|
const stats = fs.statSync(currentInputPath);
|
|
355
401
|
if (stats.isFile()) {
|
|
402
|
+
if (stats.size > largeFileThreshold) {
|
|
403
|
+
console.log(`Detected large file at ${currentInputPath}, size ~ ${stats.size} bytes.`);
|
|
404
|
+
return {
|
|
405
|
+
fileName: path.basename(currentInputPath),
|
|
406
|
+
filePath: currentInputPath,
|
|
407
|
+
fileSize: stats.size,
|
|
408
|
+
largeFile: true,
|
|
409
|
+
type: "file"
|
|
410
|
+
};
|
|
411
|
+
}
|
|
356
412
|
return {
|
|
357
413
|
content: await this.getFileData(currentInputPath),
|
|
414
|
+
largeFile: false,
|
|
358
415
|
type: "file"
|
|
359
416
|
};
|
|
360
417
|
} else if (stats.isDirectory()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trap_stevo/filetide",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.41",
|
|
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": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"license": "See License in LICENSE.md",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@trap_stevo/iotide": "^0.0.37",
|
|
33
|
-
"@trap_stevo/iotide-client": "^0.0.
|
|
34
|
-
"@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.
|
|
33
|
+
"@trap_stevo/iotide-client": "^0.0.23",
|
|
34
|
+
"@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.42",
|
|
35
35
|
"chalk": "^4.1.2",
|
|
36
36
|
"readline": "^1.3.0"
|
|
37
37
|
},
|