@@ -81,22 +81,23 @@ export async function runFFmpeg(args: string[]): Promise<{ success: boolean, out
8181 let currentSize = '0KB'
8282 let currentSpeed = '0x'
8383
84- // Progress bar
85- const progressBar = isMetadataExtraction
86- ? null
87- : logger . progress ( 100 , 'Preparing conversion...' )
84+ // Progress bar reference - only created once explicitly needed
85+ let progressBar : ReturnType < typeof logger . progress > | null = null
86+
87+ // Flag to prevent duplicate completion messages
88+ let progressCompleted = false
8889
8990 // Regular update interval
9091 const updateInterval = setInterval ( ( ) => {
91- if ( ! progressBar || ! bookDuration )
92+ if ( ! progressBar || ! bookDuration || progressCompleted )
9293 return
9394
9495 // Only apply time-based updates if we're actually making progress
9596 const now = Date . now ( )
9697 if ( now - lastOutputTime > 3000 && currentTimeMs > 0 ) {
9798 // It's been a while since the last FFmpeg update, show progress is still happening
9899 // Assume some minimal progress
99- const progressPercent = Math . max ( 0.1 , Math . min ( 99.9 , ( currentTimeMs / bookDuration ) * 100 ) )
100+ const progressPercent = Math . min ( 99 , ( currentTimeMs / bookDuration ) * 100 )
100101 progressBar . update ( progressPercent , `Converting ${ formatTime ( currentTimeMs ) } / ${ formatTime ( bookDuration ) } (${ currentSpeed } ) - Size: ${ currentSize } ` )
101102
102103 // Small time progress so user knows it's not frozen
@@ -105,24 +106,31 @@ export async function runFFmpeg(args: string[]): Promise<{ success: boolean, out
105106 } , 1000 )
106107
107108 function updateProgressFromOutput ( text : string ) {
108- if ( ! progressBar )
109- return
110-
111109 const progress = parseFFmpegProgress ( text )
112110
113111 // Get total duration once we know it
114112 if ( progress . totalMs && progress . totalMs > 0 && ! bookDuration ) {
115113 bookDuration = progress . totalMs
114+
115+ // Create progress bar when we first know the duration
116+ // This prevents creating it prematurely
117+ if ( ! isMetadataExtraction && ! progressBar && ! progressCompleted ) {
118+ progressBar = logger . progress ( 100 , 'Starting conversion...' )
119+ }
116120 }
117121
122+ // Skip further processing if no progress bar
123+ if ( ! progressBar || progressCompleted )
124+ return
125+
118126 // Update time position if provided
119127 if ( progress . timeMs !== undefined ) {
120128 currentTimeMs = progress . timeMs
121129 lastOutputTime = Date . now ( )
122130
123131 // Calculate progress percentage based on time
124132 if ( bookDuration ) {
125- const progressPercent = Math . max ( 0.1 , Math . min ( 99.9 , ( currentTimeMs / bookDuration ) * 100 ) )
133+ const progressPercent = Math . min ( 99 , ( currentTimeMs / bookDuration ) * 100 )
126134
127135 // Update size and speed if available
128136 if ( progress . size )
@@ -191,19 +199,31 @@ export async function runFFmpeg(args: string[]): Promise<{ success: boolean, out
191199 ffmpeg . on ( 'close' , ( code ) => {
192200 clearInterval ( updateInterval )
193201
194- if ( progressBar ) {
202+ if ( progressBar && ! progressCompleted ) {
203+ progressCompleted = true
204+
195205 if ( code === 0 ) {
196- progressBar . finish ( 'Conversion completed successfully!' )
206+ // This is where the duplicate message happens
207+ // Set to 100% first and only then finish with message
208+ progressBar . update ( 100 )
209+ const barRef = progressBar // Store reference to avoid null issues
210+ setTimeout ( ( ) => {
211+ if ( barRef )
212+ barRef . finish ( 'Conversion completed successfully!' )
213+ } , 50 )
197214 }
198215 else {
199216 progressBar . interrupt ( 'Conversion failed' , 'error' )
200217 }
201218 }
202219
203- resolve ( {
204- success : code === 0 ,
205- output,
206- } )
220+ // Give a small delay to allow the progress bar to finish properly
221+ setTimeout ( ( ) => {
222+ resolve ( {
223+ success : code === 0 ,
224+ output,
225+ } )
226+ } , 100 )
207227 } )
208228 } )
209229}
@@ -212,7 +232,12 @@ export async function runFFmpeg(args: string[]): Promise<{ success: boolean, out
212232 * Extracts metadata from an AAX file using FFmpeg
213233 */
214234export async function extractAAXMetadata ( filePath : string ) : Promise < string > {
215- logger . info ( `Extracting metadata from ${ filePath } ...` )
235+ // Shorten the filepath display if it's too long
236+ const displayPath = filePath . length > 70
237+ ? `...${ filePath . substring ( filePath . length - 67 ) } `
238+ : filePath
239+
240+ logger . debug ( `Extracting metadata from ${ displayPath } ...` )
216241
217242 // For metadata extraction we use a simple info message rather than a progress bar
218243 // since it's usually quick and doesn't provide meaningful progress information
0 commit comments