@@ -99,7 +99,11 @@ class WebTorrentManager extends EventEmitter {
9999 this . logger . debug ( "🚀 Initializing shared WebTorrent client..." ) ;
100100
101101 try {
102- this . webTorrentClient = new WebTorrent ( ) ;
102+ this . webTorrentClient = new WebTorrent ( {
103+ maxConns : 100 , // Increase max connections (default: 55)
104+ downloadLimit : - 1 , // No download limit
105+ uploadLimit : - 1 // No upload limit (helps with reciprocity)
106+ } ) ;
103107
104108 // Set unlimited max listeners for the WebTorrent client (if method exists)
105109 if ( typeof this . webTorrentClient . setMaxListeners === 'function' ) {
@@ -209,110 +213,185 @@ class WebTorrentManager extends EventEmitter {
209213
210214 this . logger . debug ( `🔄 Adding torrent from magnet URI: ${ magnetUri . substring ( 0 , 100 ) } ...` ) ;
211215
212- // Use the callback form of add() for event-driven approach
213- var torrent = this . webTorrentClient . add ( magnetUri ) ;
214-
215- this . logger . info ( `📦 Torrent added: ${ torrent . name || 'Unknown' } , size: ${ torrent . length } bytes` ) ;
216- // Check file size after metadata is received
217- // torrent.on("ready", () => {
218- // this.logger.info(
219- // `✅ Torrent ready! File: ${torrent.name}, Size: ${torrent.length} bytes, Files: ${torrent.files.length}`
220- // );
221-
222- // if (torrent.files.length === 0) {
223- // const error = new Error("No files in torrent");
224- // this.logger.error("❌ No files in torrent", {
225- // name: torrent.name,
226- // infoHash: torrent.infoHash,
227- // magnetUri: magnetUri.substring(0, 100) + '...'
228- // });
229- // this.emit('download-error', { magnetUri, error });
230- // this.safeTorrentDestroy(torrent);
231- // return;
232- // }
233-
234- // // Check file size against maximum allowed size
235- // if (maxFileSizeBytes && torrent.length > maxFileSizeBytes) {
236- // const fileSizeMB = (torrent.length / (1024 * 1024)).toFixed(2);
237- // const maxSizeMB = (maxFileSizeBytes / (1024 * 1024)).toFixed(2);
238- // const error = new Error(
239- // `File size (${fileSizeMB} MB) exceeds maximum allowed size (${maxSizeMB} MB). Download cancelled.`
240- // );
241- // this.logger.warn(`⚠️ File too large: ${fileSizeMB}MB > ${maxSizeMB}MB`);
242- // this.emit('download-error', { magnetUri, error });
243- // this.safeTorrentDestroy(torrent);
244- // return;
245- // }
246-
247- // // Use the 'done' event which fires when all pieces are downloaded
248- // // This handles both fresh downloads and already-complete torrents
249- // torrent.on("done", () => {
250- // this.logger.info(`🎉 Torrent download complete: ${torrent.name}`);
251-
252- // const file = torrent.files[0]; // Get the first file
253- // const chunks: Buffer[] = [];
254-
255- // this.logger.debug(`📥 Reading file data: ${file.name} (${file.length} bytes)...`);
256-
257- // // Create a stream to read the file
258- // const stream = file.createReadStream();
259-
260- // stream.on("data", (chunk: Buffer) => {
261- // chunks.push(chunk);
262- // });
263-
264- // stream.on("end", () => {
265- // const buffer = Buffer.concat(chunks);
266- // this.logger.debug(
267- // `✅ File read completed! ${buffer.length} bytes`
268- // );
269-
270- // // Emit download complete event with buffer
271- // this.emit('download-complete', {
272- // magnetUri,
273- // buffer,
274- // name: torrent.name || 'Unknown',
275- // size: buffer.length
276- // });
277-
278- // // Destroy torrent to clean up
279- // this.safeTorrentDestroy(torrent);
280- // });
281-
282- // stream.on("error", (error: unknown) => {
283- // this.logger.error("❌ Stream error during file read:", {
284- // ...this.serializeError(error),
285- // fileName: file.name,
286- // fileLength: file.length
287- // });
288- // this.emit('download-error', { magnetUri, error });
289- // this.safeTorrentDestroy(torrent);
290- // });
291- // });
292- // });
293- torrent . on ( 'metadata' , ( ) => {
294- this . logger . info (
295- `✅ Metadata received! Torrent: ${ torrent . name } , Size: ${ torrent . length } bytes, Files: ${ torrent . files . length } `
216+ // Add the torrent
217+ const torrent = this . webTorrentClient . add ( magnetUri ) ;
218+
219+ this . logger . info ( `📦 Torrent added: ${ torrent . name || 'Unknown' } ` ) ;
220+
221+ // ANTI-CHOKING STRATEGY: Handle wire connections to express interest immediately
222+ torrent . on ( 'wire' , ( wire : any , addr : string ) => {
223+ this . logger . debug ( `🔌 Connected to peer: ${ addr } ` ) ;
224+
225+ // Express interest immediately when connected
226+ wire . interested ( ) ;
227+ this . logger . debug ( `✋ Expressed interest to peer: ${ addr } ` ) ;
228+
229+ // Handle choke events - re-express interest when choked
230+ wire . on ( 'choke' , ( ) => {
231+ this . logger . debug ( `🚫 Choked by peer: ${ addr } - re-expressing interest` ) ;
232+ if ( ! wire . amInterested ) {
233+ wire . interested ( ) ; // Request to be unchoked
234+ }
235+ } ) ;
236+
237+ // Log unchoke events
238+ wire . on ( 'unchoke' , ( ) => {
239+ this . logger . debug ( `✅ Unchoked by peer: ${ addr } - can download` ) ;
240+ } ) ;
241+
242+ // Log when peer is interested
243+ wire . on ( 'interested' , ( ) => {
244+ this . logger . debug ( `👀 Peer ${ addr } is interested in our data` ) ;
245+ } ) ;
246+
247+ // Log when peer is not interested
248+ wire . on ( 'uninterested' , ( ) => {
249+ this . logger . debug ( `😴 Peer ${ addr } is not interested in our data` ) ;
250+ } ) ;
251+ } ) ;
252+
253+ // Emit metadata event
254+ torrent . on ( 'metadata' , ( ) => {
255+ this . logger . info (
256+ `� Metadata received: ${ torrent . name } , Size: ${ ( torrent . length / 1024 / 1024 ) . toFixed ( 2 ) } MB, Files: ${ torrent . files . length } `
257+ ) ;
258+
259+ const metadataData : MetadataEvent = {
260+ name : torrent . name || 'Unknown' ,
261+ size : torrent . length ,
262+ magnetUri : magnetUri ,
263+ infoHash : torrent . infoHash || 'Unknown'
264+ } ;
265+
266+ this . emit ( 'metadata' , metadataData ) ;
267+
268+ setTimeout ( ( ) => {
269+ this . logger . debug ( `👥 Peers sharing this torrent: ${ torrent . numPeers } ` ) ;
270+ } , 1000 ) ;
271+ } ) ;
272+
273+ // Check file size after metadata is received
274+ torrent . once ( 'ready' , ( ) => {
275+ this . logger . info (
276+ `✅ Torrent ready! File: ${ torrent . name } , Size: ${ torrent . length } bytes, Files: ${ torrent . files . length } `
277+ ) ;
278+
279+ if ( torrent . files . length === 0 ) {
280+ const error = new Error ( "No files in torrent" ) ;
281+ this . logger . error ( "❌ No files in torrent" , {
282+ name : torrent . name ,
283+ infoHash : torrent . infoHash ,
284+ magnetUri : magnetUri . substring ( 0 , 100 ) + '...'
285+ } ) ;
286+ this . emit ( 'download-error' , { magnetUri, error } ) ;
287+ this . safeTorrentDestroy ( torrent ) ;
288+ return ;
289+ }
290+
291+ // Check file size against maximum allowed size
292+ if ( maxFileSizeBytes && torrent . length > maxFileSizeBytes ) {
293+ const fileSizeMB = ( torrent . length / ( 1024 * 1024 ) ) . toFixed ( 2 ) ;
294+ const maxSizeMB = ( maxFileSizeBytes / ( 1024 * 1024 ) ) . toFixed ( 2 ) ;
295+ const error = new Error (
296+ `File size (${ fileSizeMB } MB) exceeds maximum allowed size (${ maxSizeMB } MB). Download cancelled.`
296297 ) ;
298+ this . logger . warn ( `⚠️ File too large: ${ fileSizeMB } MB > ${ maxSizeMB } MB` ) ;
299+ this . emit ( 'download-error' , { magnetUri, error } ) ;
300+ this . safeTorrentDestroy ( torrent ) ;
301+ return ;
302+ }
303+
304+ // Use the 'done' event which fires when all pieces are downloaded
305+ torrent . once ( 'done' , ( ) => {
306+ this . logger . info ( `🎉 Torrent download complete: ${ torrent . name } ` ) ;
307+
308+ const file = torrent . files [ 0 ] ; // Get the first file
309+ const chunks : Buffer [ ] = [ ] ;
310+
311+ this . logger . debug ( `📥 Reading file data: ${ file . name } (${ file . length } bytes)...` ) ;
297312
298- setTimeout ( ( ) => {
299- this . logger . info ( `peers sharing this torrent: ${ torrent . numPeers } ` ) ;
300- } , 1000 ) ;
313+ // Create a stream to read the file
314+ const stream = file . createReadStream ( ) ;
315+
316+ stream . on ( 'data' , ( chunk : Buffer ) => {
317+ chunks . push ( chunk ) ;
318+ } ) ;
319+
320+ stream . on ( 'end' , ( ) => {
321+ const buffer = Buffer . concat ( chunks ) ;
322+ this . logger . info (
323+ `✅ File read completed! ${ buffer . length } bytes`
324+ ) ;
325+
326+ // Emit download complete event with buffer
327+ this . emit ( 'download-complete' , {
328+ magnetUri,
329+ buffer,
330+ name : torrent . name || 'Unknown' ,
331+ size : buffer . length
332+ } ) ;
333+
334+ // Destroy torrent to clean up
335+ this . safeTorrentDestroy ( torrent ) ;
336+ } ) ;
337+
338+ stream . on ( 'error' , ( error : unknown ) => {
339+ this . logger . error ( "❌ Stream error during file read:" , {
340+ ...this . serializeError ( error ) ,
341+ fileName : file . name ,
342+ fileLength : file . length
343+ } ) ;
344+ this . emit ( 'download-error' , { magnetUri, error } ) ;
345+ this . safeTorrentDestroy ( torrent ) ;
346+ } ) ;
301347 } ) ;
302- torrent . on ( 'upload' , ( bytes ) => {
303- this . logger . debug ( `⬆️ Uploaded ${ bytes } bytes` ) ;
348+ } ) ;
349+
350+ // Enhanced torrent error handling
351+ torrent . on ( 'error' , ( error : unknown ) => {
352+ this . logger . error ( `❌ WebTorrent torrent error:` , {
353+ ...this . serializeError ( error ) ,
354+ magnetUri : magnetUri . substring ( 0 , 100 ) + '...' ,
355+ infoHash : torrent . infoHash ,
356+ torrentName : torrent . name
304357 } ) ;
358+ this . emit ( 'download-error' , { magnetUri, error } ) ;
359+ } ) ;
305360
306- torrent . on ( 'wire' , ( wire , addr ) => {
307- this . logger . debug ( `⬆️ New peer connected: ${ addr } ` ) ;
361+ // Add torrent event listeners for debugging
362+ torrent . on ( 'warning' , ( warning : unknown ) => {
363+ this . logger . debug ( "⚠️ WebTorrent warning:" , {
364+ ...this . serializeError ( warning )
308365 } ) ;
366+ } ) ;
309367
310- torrent . on ( 'download ' , ( bytes ) => {
311- this . logger . debug ( `⬇️ Downloaded ${ bytes } bytes` ) ;
312- this . logger . debug ( `📦 Total downloaded: ${ torrent . downloaded } bytes` ) ;
313- this . logger . debug ( `⚡ Download speed: ${ torrent . downloadSpeed } bytes/sec` ) ;
314- this . logger . debug ( `📈 Progress: ${ torrent . progress } %` ) ;
368+ torrent . on ( 'noPeers ' , ( ) => {
369+ this . logger . warn ( "⚠️ No peers found for torrent" , {
370+ magnetUri : magnetUri . substring ( 0 , 100 ) + '...' ,
371+ infoHash : torrent . infoHash ,
372+ name : torrent . name
315373 } ) ;
374+ } ) ;
375+
376+ // Add download progress event emission
377+ torrent . on ( 'download' , ( _bytes : number ) => {
378+ const progressData : DownloadProgressEvent = {
379+ downloaded : torrent . downloaded ,
380+ downloadSpeed : torrent . downloadSpeed ,
381+ progress : torrent . progress ,
382+ name : torrent . name || 'Unknown' ,
383+ magnetUri : magnetUri
384+ } ;
385+
386+ this . logger . debug ( `📊 Download progress: ${ ( progressData . progress * 100 ) . toFixed ( 1 ) } % - ${ progressData . name } ` , {
387+ downloaded : `${ ( progressData . downloaded / ( 1024 * 1024 ) ) . toFixed ( 2 ) } MB` ,
388+ speed : `${ ( progressData . downloadSpeed / ( 1024 * 1024 ) ) . toFixed ( 2 ) } MB/s` ,
389+ progress : `${ ( progressData . progress * 100 ) . toFixed ( 1 ) } %`
390+ } ) ;
391+
392+ // Emit the download event that external code can listen to
393+ this . emit ( 'download' , progressData ) ;
394+ } ) ;
316395 }
317396
318397 /**
@@ -331,6 +410,31 @@ class WebTorrentManager extends EventEmitter {
331410 const magnetURI = torrent . magnetURI ;
332411 this . logger . debug ( `🧲 File seeded successfully: ${ filePath } ` ) ;
333412 this . logger . debug ( ` Magnet URI: ${ magnetURI } ` ) ;
413+
414+ // ANTI-CHOKING STRATEGY FOR SEEDING: Immediately unchoke all connected peers
415+ torrent . on ( 'wire' , ( wire : any , addr : string ) => {
416+ this . logger . debug ( `🔌 Peer connected to seeder: ${ addr } ` ) ;
417+
418+ // Immediately unchoke the peer
419+ wire . unchoke ( ) ;
420+ this . logger . debug ( `✅ Unchoked peer: ${ addr } ` ) ;
421+
422+ // Keep peers unchoked when they show interest
423+ wire . on ( 'interested' , ( ) => {
424+ this . logger . debug ( `👀 Peer ${ addr } interested - ensuring unchoked` ) ;
425+ wire . unchoke ( ) ;
426+ } ) ;
427+
428+ // Log choke/unchoke state changes
429+ wire . on ( 'choke' , ( ) => {
430+ this . logger . debug ( `🚫 We choked peer: ${ addr } ` ) ;
431+ } ) ;
432+
433+ wire . on ( 'unchoke' , ( ) => {
434+ this . logger . debug ( `✅ We unchoked peer: ${ addr } ` ) ;
435+ } ) ;
436+ } ) ;
437+
334438 resolve ( magnetURI ) ;
335439 } ) ;
336440 } catch ( error ) {
0 commit comments