11import fs from 'fs/promises' ;
22import path from 'path' ;
33
4- import { SingleBar , MultiBar , Presets } from 'cli-progress' ;
4+ import { SingleBar , Presets } from 'cli-progress' ;
55import { glob } from 'glob' ;
66
77import { IBatchOptions , IBatchStats } from '@subzilla/types' ;
@@ -10,25 +10,14 @@ import SubtitleProcessor from './SubtitleProcessor';
1010
1111export default class BatchProcessor {
1212 private processor : SubtitleProcessor ;
13- private multiBar : MultiBar ;
14- private mainProgressBar ! : SingleBar ;
15- private directoryBars : Map < string , SingleBar > ;
13+ private progressBar : SingleBar | null = null ;
1614 private startTime : number = 0 ;
1715 private stats : IBatchStats ;
1816 private shouldStop : boolean = false ;
17+ private currentFile : string = '' ;
1918
2019 constructor ( ) {
2120 this . processor = new SubtitleProcessor ( ) ;
22- this . multiBar = new MultiBar (
23- {
24- format : '{bar} {percentage}% | {value}/{total} | {title}' ,
25- hideCursor : true ,
26- clearOnComplete : false ,
27- } ,
28- Presets . shades_classic ,
29- ) ;
30-
31- this . directoryBars = new Map ( ) ;
3221 this . stats = this . initializeStats ( ) ;
3322 }
3423
@@ -62,7 +51,9 @@ export default class BatchProcessor {
6251 }
6352
6453 this . stats . total = files . length ;
65- console . log ( `🔍 Found ${ files . length } files in ${ this . countDirectories ( files ) } directories...` ) ;
54+ const dirCount = this . countDirectories ( files ) ;
55+
56+ console . log ( `🔍 Found ${ files . length } files in ${ dirCount } directories\n` ) ;
6657
6758 // Create output directory if specified
6859 if ( options . common . outputDir ) {
@@ -74,10 +65,19 @@ export default class BatchProcessor {
7465
7566 this . stats . directoriesProcessed = Object . keys ( filesByDir ) . length ;
7667
77- // Initialize main progress bar
78- this . mainProgressBar = this . multiBar . create ( files . length , 0 , {
79- title : 'Total Progress' ,
80- } ) ;
68+ // Initialize progress bar
69+ this . progressBar = new SingleBar (
70+ {
71+ format : ' {bar} {percentage}% | {value}/{total} files | {status}' ,
72+ hideCursor : true ,
73+ clearOnComplete : false ,
74+ barCompleteChar : '█' ,
75+ barIncompleteChar : '░' ,
76+ } ,
77+ Presets . shades_classic ,
78+ ) ;
79+
80+ this . progressBar . start ( files . length , 0 , { status : 'Starting...' } ) ;
8181
8282 // Process directories
8383 if ( options . batch . parallel ) {
@@ -88,17 +88,18 @@ export default class BatchProcessor {
8888
8989 // Finalize statistics
9090 this . stats . timeTaken = ( Date . now ( ) - this . startTime ) / 1000 ;
91- this . stats . averageTimePerFile = this . stats . timeTaken / ( this . stats . successful + this . stats . failed ) ;
91+ this . stats . averageTimePerFile = this . stats . timeTaken / ( this . stats . successful + this . stats . failed ) || 0 ;
9292
93- // Stop progress bars
94- this . multiBar . stop ( ) ;
93+ // Stop progress bar
94+ this . progressBar . update ( { status : 'Complete!' } ) ;
95+ this . progressBar . stop ( ) ;
9596
9697 // Print summary
9798 this . printSummary ( ) ;
9899
99100 return this . stats ;
100101 } catch ( error ) {
101- this . multiBar . stop ( ) ;
102+ this . progressBar ? .stop ( ) ;
102103 throw error ;
103104 }
104105 }
@@ -209,7 +210,7 @@ export default class BatchProcessor {
209210 options : IBatchOptions ,
210211 ) : Promise < void > {
211212 const directories = Object . entries ( filesByDir ) ;
212- const chunks = this . chunkArray ( directories , options . batch . chunkSize || 3 ) ; // Use configured chunk size
213+ const chunks = this . chunkArray ( directories , options . batch . chunkSize || 3 ) ;
213214
214215 for ( const chunk of chunks ) {
215216 if ( this . shouldStop ) break ;
@@ -229,13 +230,6 @@ export default class BatchProcessor {
229230 private async processDirectory ( dir : string , files : string [ ] , options : IBatchOptions ) : Promise < void > {
230231 if ( this . shouldStop ) return ;
231232
232- // Create directory progress bar
233- const dirBar = this . multiBar . create ( files . length , 0 , {
234- title : `Processing ${ path . basename ( dir ) } ` ,
235- } ) ;
236-
237- this . directoryBars . set ( dir , dirBar ) ;
238-
239233 // Create output directory structure if needed
240234 if ( options . batch . preserveStructure && options . common . outputDir ) {
241235 const relativePath = path . relative ( process . cwd ( ) , dir ) ;
@@ -258,20 +252,22 @@ export default class BatchProcessor {
258252 await this . processFile ( file , dir , options ) ;
259253 }
260254 }
261-
262- // Remove directory progress bar
263- this . directoryBars . delete ( dir ) ;
264255 }
265256
266257 private async processFile ( file : string , dir : string , options : IBatchOptions ) : Promise < void > {
267258 const outputPath = this . getOutputPath ( file , dir , options ) ;
268259 const dirStats = this . stats . filesByDirectory [ dir ] ;
260+ const fileName = path . basename ( file ) ;
269261
270262 dirStats . total ++ ;
263+ this . currentFile = fileName ;
264+
265+ // Update progress bar with current file
266+ this . progressBar ?. update ( { status : this . truncateFileName ( fileName , 40 ) } ) ;
271267
272268 let attempts = 0 ;
273269 const retryCount = options . common . retryCount || 0 ;
274- const maxAttempts = retryCount + 1 ; // Initial attempt + retry count
270+ const maxAttempts = retryCount + 1 ;
275271 const retryDelay = options . common . retryDelay || 1000 ;
276272
277273 try {
@@ -290,19 +286,17 @@ export default class BatchProcessor {
290286 dirStats . successful ++ ;
291287 this . stats . successful ++ ;
292288
293- return ; // Success - exit early
289+ return ;
294290 } catch ( error ) {
295291 attempts ++ ;
296292
297293 if ( attempts < maxAttempts ) {
298- // Log retry attempt
299- console . log ( `🔄 Retrying ${ file } (attempt ${ attempts } /${ retryCount } )...` ) ;
294+ this . progressBar ?. update ( { status : `Retrying ${ fileName } (${ attempts } /${ retryCount } )` } ) ;
300295 await this . delay ( retryDelay ) ;
301296
302297 continue ;
303298 }
304299
305- // All retries exhausted
306300 dirStats . failed ++ ;
307301 this . stats . failed ++ ;
308302 this . stats . errors . push ( {
@@ -317,12 +311,20 @@ export default class BatchProcessor {
317311 }
318312 }
319313 } finally {
320- // Only increment progress bars once, after all retries are complete
321- this . mainProgressBar . increment ( ) ;
322- this . directoryBars . get ( dir ) ?. increment ( ) ;
314+ this . progressBar ?. increment ( ) ;
323315 }
324316 }
325317
318+ private truncateFileName ( fileName : string , maxLength : number ) : string {
319+ if ( fileName . length <= maxLength ) return fileName ;
320+
321+ const ext = path . extname ( fileName ) ;
322+ const name = path . basename ( fileName , ext ) ;
323+ const availableLength = maxLength - ext . length - 3 ; // 3 for "..."
324+
325+ return name . substring ( 0 , availableLength ) + '...' + ext ;
326+ }
327+
326328 private getOutputPath ( file : string , dir : string , options : IBatchOptions ) : string | undefined {
327329 if ( ! options . common . outputDir ) return undefined ;
328330
@@ -362,31 +364,29 @@ export default class BatchProcessor {
362364 }
363365
364366 private printSummary ( ) : void {
365- console . log ( '\n📊 Batch Processing Summary:' ) ;
366- console . log ( '━━━━━━━━━━━━━━━━━━━━━━━━━━' ) ;
367- console . log ( `Total files processed: ${ this . stats . total } ` ) ;
368- console . log ( `Directories processed: ${ this . stats . directoriesProcessed } ` ) ;
369- console . log ( `✅ Successfully converted: ${ this . stats . successful } ` ) ;
370- console . log ( `❌ Failed: ${ this . stats . failed } ` ) ;
371- console . log ( `⏭️ Skipped: ${ this . stats . skipped } ` ) ;
372- console . log ( `⏱️ Total time: ${ this . stats . timeTaken . toFixed ( 2 ) } s` ) ;
373- console . log ( `⚡ Average time per file: ${ this . stats . averageTimePerFile . toFixed ( 2 ) } s` ) ;
374-
375- console . log ( '\n📂 Directory Statistics:' ) ;
376- console . log ( '━━━━━━━━━━━━━━━━━━━━' ) ;
377- // Object.entries(this.stats.filesByDirectory).forEach(([dir, stats]) => {
378- // console.log(`\n${dir}:`);
379- // console.log(` Total: ${stats.total}`);
380- // console.log(` ✅ Success: ${stats.successful}`);
381- // console.log(` ❌ Failed: ${stats.failed}`);
382- // console.log(` ⏭️ Skipped: ${stats.skipped}`);
383- // });
367+ console . log ( '\n' ) ;
368+ console . log ( '📊 Batch Processing Summary' ) ;
369+ console . log ( '───────────────────────────' ) ;
370+ console . log ( ` Total: ${ this . stats . total } files in ${ this . stats . directoriesProcessed } directories` ) ;
371+ console . log ( ` ✓ Success: ${ this . stats . successful } ` ) ;
372+
373+ if ( this . stats . failed > 0 ) {
374+ console . log ( ` ✗ Failed: ${ this . stats . failed } ` ) ;
375+ }
376+
377+ if ( this . stats . skipped > 0 ) {
378+ console . log ( ` ⊘ Skipped: ${ this . stats . skipped } ` ) ;
379+ }
380+
381+ console . log (
382+ ` ⏱ Time: ${ this . stats . timeTaken . toFixed ( 2 ) } s (${ this . stats . averageTimePerFile . toFixed ( 2 ) } s/file)` ,
383+ ) ;
384384
385385 if ( this . stats . errors . length > 0 ) {
386386 console . log ( '\n❌ Errors:' ) ;
387- console . log ( '━━━━━━━━━ ' ) ;
387+ console . log ( '─────────────────────────── ' ) ;
388388 this . stats . errors . forEach ( ( { file, error } ) => {
389- console . log ( `${ file } : ${ error } ` ) ;
389+ console . log ( ` ${ path . basename ( file ) } : ${ error } ` ) ;
390390 } ) ;
391391 }
392392 }
0 commit comments