magnetk 2.2.4 → 2.2.5
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/cli.js +4 -2
- package/client.js +165 -10
- package/download.js +95 -0
- package/magnetk.js +21 -5
- package/package.json +1 -1
- package/share.js +62 -0
package/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Magnetk CLI
|
|
@@ -20,7 +20,9 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
20
20
|
process.exit(0);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const client = new MagnetkClient(
|
|
23
|
+
const client = new MagnetkClient({
|
|
24
|
+
dhtEnabled: true // Enable DHT discovery
|
|
25
|
+
});
|
|
24
26
|
|
|
25
27
|
async function run() {
|
|
26
28
|
try {
|
package/client.js
CHANGED
|
@@ -34,16 +34,57 @@ export const CONNECTION_TYPE = {
|
|
|
34
34
|
UNKNOWN: 'UNKNOWN'
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
const DEFAULT_CONFIG_CONTENT = `# Magnetk P2P Configuration
|
|
38
|
+
# Auto-generated by Magnetk JS SDK. Edit as needed.
|
|
39
|
+
# All values are optional - defaults are used when not specified
|
|
40
|
+
|
|
41
|
+
# ===== Network Binding =====
|
|
42
|
+
BIND_ADDRESS=0.0.0.0
|
|
43
|
+
DISCOVERY_PORT=4001
|
|
44
|
+
TRANSPORT_PORT=4002
|
|
45
|
+
LOCALHOST_MODE=false
|
|
46
|
+
|
|
47
|
+
# ===== Relay =====
|
|
48
|
+
RELAY_ADDRESS=0.0.0.0:4001
|
|
49
|
+
|
|
50
|
+
# Multi-relay: comma-separated for failover/scaling
|
|
51
|
+
# RELAY_ADDRESSES=
|
|
52
|
+
|
|
53
|
+
# ===== DHT (Decentralized Discovery) =====
|
|
54
|
+
DHT_ENABLED=false
|
|
55
|
+
DHT_MODE=auto
|
|
56
|
+
# Default: Public IPFS bootstrap nodes
|
|
57
|
+
DHT_BOOTSTRAP_PEERS=/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN,/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa,/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb,/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt,/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
|
58
|
+
|
|
59
|
+
# ===== Discovery Priority =====
|
|
60
|
+
# Order: dht, relay, direct (comma-separated)
|
|
61
|
+
DISCOVERY_PRIORITY=dht,relay,direct
|
|
62
|
+
|
|
63
|
+
# ===== Chunking =====
|
|
64
|
+
CHUNK_SIZE_MB=1
|
|
65
|
+
MAX_PARALLEL_CHUNKS=10
|
|
66
|
+
`;
|
|
67
|
+
|
|
37
68
|
export class MagnetkClient extends EventEmitter {
|
|
38
69
|
constructor(config = {}) {
|
|
39
70
|
super();
|
|
40
71
|
this.config = {
|
|
72
|
+
// Relay settings (supports multiple relays for failover)
|
|
41
73
|
relayUrl: '69.169.109.243',
|
|
42
74
|
relayPort: 4003,
|
|
75
|
+
relays: [], // Array of {host, port} for multi-relay failover
|
|
43
76
|
seedPort: 4002,
|
|
44
77
|
enableSTUN: true,
|
|
45
78
|
seederPath: null,
|
|
46
79
|
configPath: null,
|
|
80
|
+
|
|
81
|
+
// DHT settings
|
|
82
|
+
dhtEnabled: false, // Enable DHT discovery
|
|
83
|
+
dhtBootstrapPeers: [], // Custom bootstrap peer multiaddrs
|
|
84
|
+
discoveryPriority: ['dht', 'relay', 'direct'], // Discovery order
|
|
85
|
+
|
|
86
|
+
// Auto-config: create settings.conf if missing
|
|
87
|
+
autoConfig: true,
|
|
47
88
|
...config
|
|
48
89
|
};
|
|
49
90
|
this.processes = [];
|
|
@@ -178,6 +219,32 @@ export class MagnetkClient extends EventEmitter {
|
|
|
178
219
|
}
|
|
179
220
|
}
|
|
180
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Look up seeds with multi-relay failover.
|
|
224
|
+
* Tries each relay in order until one succeeds.
|
|
225
|
+
* @param {string} fileHash
|
|
226
|
+
* @returns {Promise<Object>}
|
|
227
|
+
*/
|
|
228
|
+
async lookupPeerWithFailover(fileHash) {
|
|
229
|
+
// Build relay list: primary + configured relays
|
|
230
|
+
const relays = [
|
|
231
|
+
{ host: this.config.relayUrl, port: this.config.relayPort },
|
|
232
|
+
...this.config.relays
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
let lastError = null;
|
|
236
|
+
for (const relay of relays) {
|
|
237
|
+
try {
|
|
238
|
+
const result = await this.lookupPeer(relay.host, relay.port, fileHash);
|
|
239
|
+
if (result && result.found) return result;
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.log(`[Magnetk-JS] Relay ${relay.host}:${relay.port} failed: ${e.message}`);
|
|
242
|
+
lastError = e;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
throw lastError || new Error('All relays failed');
|
|
246
|
+
}
|
|
247
|
+
|
|
181
248
|
/**
|
|
182
249
|
* Seeds a file and returns a Magnetk link.
|
|
183
250
|
* @param {string} filePath
|
|
@@ -190,7 +257,6 @@ export class MagnetkClient extends EventEmitter {
|
|
|
190
257
|
}
|
|
191
258
|
|
|
192
259
|
this.emit('validating-relay');
|
|
193
|
-
// Simple reachability check (optional but good for UX)
|
|
194
260
|
|
|
195
261
|
const fileStats = fs.statSync(absolutePath);
|
|
196
262
|
this.emit('hashing', { percent: 0 });
|
|
@@ -221,12 +287,26 @@ export class MagnetkClient extends EventEmitter {
|
|
|
221
287
|
}
|
|
222
288
|
|
|
223
289
|
const args = [
|
|
224
|
-
'-relay', relayMultiaddr,
|
|
225
290
|
'-file', absolutePath,
|
|
226
291
|
'-port', this.config.seedPort.toString(),
|
|
227
292
|
'-config', configPath
|
|
228
293
|
];
|
|
229
294
|
|
|
295
|
+
// Add relay if available
|
|
296
|
+
if (relayPeerId || this.config.relayUrl) {
|
|
297
|
+
args.push('-relay', relayMultiaddr);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Add DHT if enabled
|
|
301
|
+
if (this.config.dhtEnabled) {
|
|
302
|
+
args.push('-dht');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Add bootstrap peers if configured
|
|
306
|
+
if (this.config.dhtBootstrapPeers && this.config.dhtBootstrapPeers.length > 0) {
|
|
307
|
+
args.push('-bootstrap', this.config.dhtBootstrapPeers.join(','));
|
|
308
|
+
}
|
|
309
|
+
|
|
230
310
|
console.log(`[Magnetk-JS] Spawning seeder: ${binPath} ${args.join(' ')}`);
|
|
231
311
|
|
|
232
312
|
return new Promise((resolve, reject) => {
|
|
@@ -251,10 +331,13 @@ export class MagnetkClient extends EventEmitter {
|
|
|
251
331
|
|
|
252
332
|
seeder.stderr.on('data', (data) => {
|
|
253
333
|
const msg = data.toString();
|
|
254
|
-
console.log(`[Seeder Stderr] ${msg}`);
|
|
334
|
+
console.log(`[Seeder Stderr] ${msg}`);
|
|
255
335
|
if (msg.includes('Connected to relay')) {
|
|
256
336
|
this.emit('relay-connected', { host: this.config.relayUrl, port: 4001 });
|
|
257
337
|
}
|
|
338
|
+
if (msg.includes('DHT') && msg.includes('announced')) {
|
|
339
|
+
this.emit('dht-announced');
|
|
340
|
+
}
|
|
258
341
|
});
|
|
259
342
|
|
|
260
343
|
seeder.on('error', (err) => {
|
|
@@ -320,7 +403,7 @@ export class MagnetkClient extends EventEmitter {
|
|
|
320
403
|
});
|
|
321
404
|
}
|
|
322
405
|
}
|
|
323
|
-
addressesToTry.push(
|
|
406
|
+
addressesToTry.push(`127.0.0.1:${this.config.seedPort}`);
|
|
324
407
|
|
|
325
408
|
const uniqueAddrs = [...new Set(addressesToTry)];
|
|
326
409
|
let socket;
|
|
@@ -392,11 +475,66 @@ export class MagnetkClient extends EventEmitter {
|
|
|
392
475
|
}
|
|
393
476
|
|
|
394
477
|
/**
|
|
395
|
-
* Finds available seeders for a hash.
|
|
478
|
+
* Finds available seeders for a hash (with multi-relay failover).
|
|
396
479
|
*/
|
|
397
480
|
async lookupSeeds(fileHash) {
|
|
398
|
-
|
|
399
|
-
|
|
481
|
+
try {
|
|
482
|
+
const result = await this.lookupPeerWithFailover(fileHash);
|
|
483
|
+
return result.found ? result.seeds : [];
|
|
484
|
+
} catch (e) {
|
|
485
|
+
return [];
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ═══════════════════════════════════════════════════
|
|
490
|
+
// MINIMAL-CODE API (for SDK users writing less code)
|
|
491
|
+
// ═══════════════════════════════════════════════════
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Share a file with one function call.
|
|
495
|
+
* Handles hashing, seeding, and returns the magnet link.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* const link = await magnetk.share('./my-file.zip');
|
|
499
|
+
* console.log('Share this link:', link);
|
|
500
|
+
*
|
|
501
|
+
* @param {string} filePath - Path to the file to share
|
|
502
|
+
* @returns {Promise<string>} Magnet link
|
|
503
|
+
*/
|
|
504
|
+
async share(filePath) {
|
|
505
|
+
return this.seed(filePath);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Download a file with one function call.
|
|
510
|
+
* Handles discovery, downloading, and verification.
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* await magnetk.get('magnetk:?xt=urn:sha256:abc...', './downloads/');
|
|
514
|
+
*
|
|
515
|
+
* @param {string} magnetURI - Magnet link to download
|
|
516
|
+
* @param {string} [outputPath] - Output directory or file path (default: current directory)
|
|
517
|
+
* @returns {Promise<string>} Path to the downloaded file
|
|
518
|
+
*/
|
|
519
|
+
async get(magnetURI, outputPath) {
|
|
520
|
+
const ml = typeof magnetURI === 'string' ? MagnetkLink.parse(magnetURI) : magnetURI;
|
|
521
|
+
const output = outputPath || `./${ml.fileName}`;
|
|
522
|
+
await this.download(magnetURI, output);
|
|
523
|
+
return output;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Hash a file and return its SHA-256 hash.
|
|
528
|
+
* Useful when users want to pre-compute hashes.
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* const hash = await magnetk.hash('./my-file.zip');
|
|
532
|
+
*
|
|
533
|
+
* @param {string} filePath
|
|
534
|
+
* @returns {Promise<string>} Hex-encoded SHA-256 hash
|
|
535
|
+
*/
|
|
536
|
+
async hash(filePath) {
|
|
537
|
+
return this._calculateHash(path.resolve(filePath));
|
|
400
538
|
}
|
|
401
539
|
|
|
402
540
|
_calculateHash(filePath) {
|
|
@@ -433,7 +571,7 @@ export class MagnetkClient extends EventEmitter {
|
|
|
433
571
|
if (fs.existsSync(p)) return p;
|
|
434
572
|
}
|
|
435
573
|
|
|
436
|
-
return binName; // Fallback to PATH
|
|
574
|
+
return binName; // Fallback to PATH
|
|
437
575
|
}
|
|
438
576
|
|
|
439
577
|
_resolveConfigPath() {
|
|
@@ -441,14 +579,31 @@ export class MagnetkClient extends EventEmitter {
|
|
|
441
579
|
if (this.config.configPath) return this.config.configPath;
|
|
442
580
|
|
|
443
581
|
// 2. Check local dev environment
|
|
444
|
-
const
|
|
582
|
+
const searchPaths = [
|
|
445
583
|
path.join(process.cwd(), 'config', 'settings.conf'),
|
|
446
584
|
path.join(process.cwd(), '..', '..', '..', 'config', 'settings.conf'),
|
|
447
585
|
path.join(process.cwd(), '..', '..', 'config', 'settings.conf')
|
|
448
586
|
];
|
|
449
|
-
for (const p of
|
|
587
|
+
for (const p of searchPaths) {
|
|
450
588
|
if (fs.existsSync(p)) return p;
|
|
451
589
|
}
|
|
590
|
+
|
|
591
|
+
// 3. Auto-create settings.conf in current directory
|
|
592
|
+
if (this.config.autoConfig) {
|
|
593
|
+
const autoPath = path.join(process.cwd(), 'config', 'settings.conf');
|
|
594
|
+
try {
|
|
595
|
+
const configDir = path.dirname(autoPath);
|
|
596
|
+
if (!fs.existsSync(configDir)) {
|
|
597
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
598
|
+
}
|
|
599
|
+
fs.writeFileSync(autoPath, DEFAULT_CONFIG_CONTENT);
|
|
600
|
+
console.log(`[Magnetk-JS] Created default config at ${autoPath}`);
|
|
601
|
+
return autoPath;
|
|
602
|
+
} catch (e) {
|
|
603
|
+
console.warn(`[Magnetk-JS] Could not auto-create config: ${e.message}`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
452
607
|
return ''; // Let seeder use default
|
|
453
608
|
}
|
|
454
609
|
}
|
package/download.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { MagnetkClient, CONNECTION_TYPE } from 'magnetk';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
// Parse command line arguments
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
if (args.length < 1) {
|
|
8
|
+
console.error('Usage: node download.js <magnetk_link>');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const magnetURI = args[0];
|
|
13
|
+
|
|
14
|
+
// Determine Desktop Path
|
|
15
|
+
const desktopPath = path.join(os.homedir(), '/OneDrive/Desktop');
|
|
16
|
+
|
|
17
|
+
async function download() {
|
|
18
|
+
const client = new MagnetkClient({
|
|
19
|
+
dhtEnabled: true, // Enable DHT discovery
|
|
20
|
+
relayUrl: '69.169.109.243',
|
|
21
|
+
relayPort: 4003,
|
|
22
|
+
seedPort: 4005
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Extract filename from URI roughly to show intention
|
|
26
|
+
// In real download, client.download handles parsing
|
|
27
|
+
let intendedFilename = 'downloaded_file';
|
|
28
|
+
try {
|
|
29
|
+
const urlParams = new URL(magnetURI.replace('magnetk:', 'http://dummy.com')).searchParams;
|
|
30
|
+
if (urlParams.has('dn')) intendedFilename = urlParams.get('dn');
|
|
31
|
+
} catch (e) { }
|
|
32
|
+
|
|
33
|
+
const outputPath = path.join(desktopPath, intendedFilename);
|
|
34
|
+
|
|
35
|
+
console.log(`[Download] Initiating download for: ${magnetURI}`);
|
|
36
|
+
console.log(`[Download] Target: ${outputPath}`);
|
|
37
|
+
|
|
38
|
+
// Check Public Identity
|
|
39
|
+
try {
|
|
40
|
+
console.log('[Download] Resolving Public Identity (STUN)...');
|
|
41
|
+
const identity = await client.getPublicIdentity();
|
|
42
|
+
console.log(`[Download] Public Identity: ${identity.ip}:${identity.port}`);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.warn(`[Download] STUN lookup failed: ${e.message}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Progress Tracking
|
|
48
|
+
client.on('download-start', (info) => console.log(`[Download] Started: ${info.fileName}`));
|
|
49
|
+
|
|
50
|
+
let lastPercent = -1;
|
|
51
|
+
client.on('progress', (p) => {
|
|
52
|
+
// Show progress bar-like output
|
|
53
|
+
const percent = Math.floor(p.percent);
|
|
54
|
+
if (percent > lastPercent) {
|
|
55
|
+
const barWidth = 20;
|
|
56
|
+
const filled = Math.floor((percent / 100) * barWidth);
|
|
57
|
+
const empty = barWidth - filled;
|
|
58
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
59
|
+
|
|
60
|
+
process.stdout.write(`\r[Download] [${bar}] ${p.percent.toFixed(1)}% | ${p.speed} KB/s | ${p.bytesDownloaded} / ${p.totalBytes} bytes`);
|
|
61
|
+
lastPercent = percent;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
client.on('connection-type', (c) => {
|
|
66
|
+
let typeStr = c.type;
|
|
67
|
+
if (c.type === CONNECTION_TYPE.DIRECT) typeStr += ' (P2P Direct)';
|
|
68
|
+
if (c.type === CONNECTION_TYPE.LOCAL) typeStr += ' (Local Network)';
|
|
69
|
+
if (c.type === CONNECTION_TYPE.RELAYED) typeStr += ' (Via Relay)';
|
|
70
|
+
console.log(`\n[Download] Connection established: ${typeStr}`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
client.on('error', (err) => console.error(`\n[Download] Error: ${err.message}`));
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
// Use new simplified API
|
|
77
|
+
await client.get(magnetURI, outputPath);
|
|
78
|
+
console.log('\n\n==================================================');
|
|
79
|
+
console.log(' DOWNLOAD COMPLETE!');
|
|
80
|
+
console.log('==================================================');
|
|
81
|
+
console.log(`File saved to: ${outputPath}`);
|
|
82
|
+
|
|
83
|
+
// Show stats
|
|
84
|
+
const stats = client.lastDownloadStats;
|
|
85
|
+
console.log('Final Stats:');
|
|
86
|
+
console.log(`- Connection Type: ${stats.connectionType}`);
|
|
87
|
+
console.log(`- Remote Address: ${stats.remoteAddr}`);
|
|
88
|
+
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error('\n[Download] Failed:', err);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
download();
|
package/magnetk.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Magnetk Link Parser and Generator for Magnetk P2P
|
|
3
|
+
* Supports DHT discovery and multi-relay configuration.
|
|
3
4
|
*/
|
|
4
5
|
export class MagnetkLink {
|
|
5
|
-
constructor(fileHash, fileName, fileSize, seedId, relayAddr) {
|
|
6
|
+
constructor(fileHash, fileName, fileSize, seedId, relayAddr, dhtEnabled = false) {
|
|
6
7
|
this.fileHash = fileHash;
|
|
7
8
|
this.fileName = fileName;
|
|
8
9
|
this.fileSize = fileSize;
|
|
9
10
|
this.seedId = seedId;
|
|
10
11
|
this.relayAddr = relayAddr;
|
|
12
|
+
this.dhtEnabled = dhtEnabled;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Parses a magnetk URI string.
|
|
15
|
-
* Format: magnetk:?xt=urn:sha256:<hash>&dn=<name>&xl=<size>&xs=<seed_id>&relay=<relay_addr
|
|
17
|
+
* Format: magnetk:?xt=urn:sha256:<hash>&dn=<name>&xl=<size>&xs=<seed_id>&relay=<relay_addr>&dht=true
|
|
16
18
|
* @param {string} uri
|
|
17
19
|
* @returns {MagnetkLink}
|
|
18
20
|
*/
|
|
@@ -21,7 +23,7 @@ export class MagnetkLink {
|
|
|
21
23
|
throw new Error('Invalid magnet link: must start with magnetk:?');
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
const params = new URLSearchParams(uri.slice(9));
|
|
26
|
+
const params = new URLSearchParams(uri.slice(9));
|
|
25
27
|
const xt = params.get('xt');
|
|
26
28
|
if (!xt || !xt.startsWith('urn:sha256:')) {
|
|
27
29
|
throw new Error('Invalid magnet link: missing xt (urn:sha256:...)');
|
|
@@ -32,8 +34,15 @@ export class MagnetkLink {
|
|
|
32
34
|
const fileSize = parseInt(params.get('xl') || '0', 10);
|
|
33
35
|
const seedId = params.get('xs') || '';
|
|
34
36
|
const relayAddr = params.get('relay') || '';
|
|
37
|
+
const dhtParam = (params.get('dht') || '').toLowerCase();
|
|
38
|
+
const dhtEnabled = dhtParam === 'true' || dhtParam === '1';
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
// Must have at least one discovery method
|
|
41
|
+
if (!relayAddr && !dhtEnabled) {
|
|
42
|
+
throw new Error('Magnet link needs either relay address or dht=true for discovery');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return new MagnetkLink(fileHash, fileName, fileSize, seedId, relayAddr, dhtEnabled);
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
/**
|
|
@@ -46,8 +55,15 @@ export class MagnetkLink {
|
|
|
46
55
|
if (this.fileName) parts.push(`dn=${encodeURIComponent(this.fileName)}`);
|
|
47
56
|
if (this.fileSize > 0) parts.push(`xl=${this.fileSize}`);
|
|
48
57
|
if (this.seedId) parts.push(`xs=${encodeURIComponent(this.seedId)}`);
|
|
49
|
-
if (this.relayAddr) parts.push(`relay=${this.relayAddr}`);
|
|
58
|
+
if (this.relayAddr) parts.push(`relay=${this.relayAddr}`);
|
|
59
|
+
if (this.dhtEnabled) parts.push('dht=true');
|
|
50
60
|
|
|
51
61
|
return `magnetk:?${parts.join('&')}`;
|
|
52
62
|
}
|
|
63
|
+
|
|
64
|
+
/** @returns {boolean} */
|
|
65
|
+
hasRelay() { return !!this.relayAddr; }
|
|
66
|
+
|
|
67
|
+
/** @returns {boolean} */
|
|
68
|
+
hasDHT() { return this.dhtEnabled; }
|
|
53
69
|
}
|
package/package.json
CHANGED
package/share.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MagnetkClient } from 'magnetk';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import 'dotenv/config';
|
|
4
|
+
|
|
5
|
+
const filePath = "./app.txt";
|
|
6
|
+
|
|
7
|
+
async function share() {
|
|
8
|
+
const client = new MagnetkClient({
|
|
9
|
+
relayUrl: '69.169.109.243', // Default to local for dev, change to public IPs for prod
|
|
10
|
+
relayPort: 4003,
|
|
11
|
+
seederPath: process.env.MAGNETK_SEEDER_PATH,
|
|
12
|
+
seedPort: 4005 // Default seed port
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
console.log(`[Share] Preparing to share: ${filePath}`);
|
|
16
|
+
|
|
17
|
+
// Check Public Identity
|
|
18
|
+
try {
|
|
19
|
+
console.log('[Share] Resolving Public Identity (STUN)...');
|
|
20
|
+
const identity = await client.getPublicIdentity();
|
|
21
|
+
console.log(`[Share] Public Identity: ${identity.ip}:${identity.port}`);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.warn(`[Share] STUN lookup failed: ${e.message}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Event listeners for feedback
|
|
27
|
+
client.on('validating-relay', () => console.log('[Share] Connecting to relay...'));
|
|
28
|
+
client.on('hashing', (p) => {
|
|
29
|
+
if (p.percent % 20 === 0 || p.percent === 100) {
|
|
30
|
+
console.log(`[Share] Hashing file... ${p.percent}%`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
client.on('relay-connected', (info) => console.log(`[Share] Connected to Relay at ${info.host}`));
|
|
34
|
+
client.on('seeding', (info) => {
|
|
35
|
+
console.log(`[Share] Seeding active: ${info.fileName} (${info.fileSize} bytes)`);
|
|
36
|
+
console.log(`[Share] File Hash: ${info.hash}`);
|
|
37
|
+
});
|
|
38
|
+
client.on('error', (err) => console.error(`[Share] Error: ${err.message}`));
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// Use new simplified API
|
|
42
|
+
const link = await client.share(filePath);
|
|
43
|
+
|
|
44
|
+
console.log('\n==================================================');
|
|
45
|
+
console.log(' FILE SHARED SUCCESSFULLY!');
|
|
46
|
+
console.log('==================================================');
|
|
47
|
+
console.log('\nmagnet link:');
|
|
48
|
+
console.log(link);
|
|
49
|
+
console.log('\nTo download, run:');
|
|
50
|
+
console.log(`node download.js "${link}"`);
|
|
51
|
+
console.log('\n[Share] Seeder running in background. Press Ctrl+C to stop.');
|
|
52
|
+
|
|
53
|
+
// Keep process alive
|
|
54
|
+
await client.keepSeeding();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error('[Share] Failed:', err);
|
|
57
|
+
client.stop();
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
share();
|