@@ -12,6 +12,50 @@ import { LameOptions } from "./lame-options";
1212
1313type ProgressKind = "encode" | "decode" ;
1414
15+ function parseEncodeProgressLine ( content : string ) : {
16+ progress ?: number ;
17+ eta ?: string ;
18+ } | null {
19+ const progressMatch = content . match ( / \( ( ( [ 0 - 9 ] ) | ( [ 0 - 9 ] { 2 } ) | ( 1 0 0 ) ) % \) \| / ) ;
20+ if ( ! progressMatch ) {
21+ return null ;
22+ }
23+
24+ const etaMatch = content . match ( / [ 0 - 9 ] { 1 , 2 } : [ 0 - 9 ] [ 0 - 9 ] / ) ;
25+
26+ /* c8 ignore next */
27+ const progress = Number ( progressMatch [ 1 ] ) ;
28+ const eta = etaMatch ? etaMatch [ 0 ] . trim ( ) : undefined ;
29+
30+ return { progress, eta } ;
31+ }
32+
33+ function parseDecodeProgressLine ( content : string ) : number | null {
34+ const progressMatch = content . match ( / [ 0 - 9 ] { 1 , 10 } \/ [ 0 - 9 ] { 1 , 10 } / ) ;
35+ if ( ! progressMatch ) {
36+ return null ;
37+ }
38+
39+ const [ current , total ] = progressMatch [ 0 ] . split ( "/" ) . map ( Number ) ;
40+ if ( ! Number . isFinite ( current ) || ! Number . isFinite ( total ) || total === 0 ) {
41+ return NaN ;
42+ }
43+
44+ return Math . floor ( ( current / total ) * 100 ) ;
45+ }
46+
47+ function normalizeCliMessage ( content : string ) : string | null {
48+ if (
49+ content . startsWith ( "lame: " ) ||
50+ content . startsWith ( "Warning: " ) ||
51+ content . includes ( "Error " )
52+ ) {
53+ return content . startsWith ( "lame: " ) ? content : `lame: ${ content } ` ;
54+ }
55+
56+ return null ;
57+ }
58+
1559/**
1660 * Thin wrapper around the LAME CLI that manages temp files, progress events,
1761 * and output handling while delegating the heavy lifting to the binary.
@@ -28,7 +72,8 @@ class Lame {
2872 new EventEmitter ( ) as LameProgressEmitter ;
2973
3074 private readonly options : LameOptionsBag ;
31- private readonly args : Array < string | number > ;
75+ private readonly builder : LameOptions ;
76+ private readonly args : string [ ] ;
3277
3378 private filePath ?: string ;
3479 private fileBuffer ?: Buffer ;
@@ -43,7 +88,8 @@ class Lame {
4388
4489 constructor ( options : LameOptionsBag ) {
4590 this . options = options ;
46- this . args = new LameOptions ( this . options ) . getArguments ( ) ;
91+ this . builder = new LameOptions ( this . options ) ;
92+ this . args = this . builder . getArguments ( ) ;
4793 this . lamePath = resolveLameBinary ( ) ;
4894 this . tempPath = join ( tmpdir ( ) , "node-lame" ) ;
4995 }
@@ -155,16 +201,23 @@ class Lame {
155201 */
156202 private async spawnLameAndTrackProgress (
157203 inputFilePath : string ,
158- baseArgs : Array < string | number > ,
204+ baseArgs : string [ ] ,
159205 type : ProgressKind ,
160206 ) : Promise < this> {
161- const args = [ ...baseArgs , "--disptime" , "1" ] . map ( ( value ) =>
162- String ( value ) ,
163- ) ;
207+ const args = [ ...baseArgs ] ;
208+
209+ if (
210+ this . builder . shouldUseDefaultDisptime ( ) &&
211+ ! args . includes ( "--disptime" )
212+ ) {
213+ args . push ( "--disptime" , "1" ) ;
214+ }
215+
216+ const normalizedArgs = args . map ( ( value ) => String ( value ) ) ;
164217
165218 const { outputPath, bufferOutput } =
166219 await this . prepareOutputTarget ( type ) ;
167- const spawnArgs = [ inputFilePath , outputPath , ...args ] ;
220+ const spawnArgs = [ inputFilePath , outputPath , ...normalizedArgs ] ;
168221
169222 this . status = {
170223 started : true ,
@@ -194,68 +247,46 @@ class Lame {
194247 this . status . progress ,
195248 this . status . eta ,
196249 ] ) ;
197- } else if (
198- type === "encode" &&
199- / \( ( ( [ 0 - 9 ] ) | ( [ 0 - 9 ] { 2 } ) | ( 1 0 0 ) ) % \) \| / . test ( content )
200- ) {
201- const progressMatch = content . match (
202- / \( ( ( [ 0 - 9 ] ) | ( [ 0 - 9 ] { 2 } ) | ( 1 0 0 ) ) % \) \| / ,
203- ) ;
204- const etaMatch = content . match ( / [ 0 - 9 ] { 1 , 2 } : [ 0 - 9 ] [ 0 - 9 ] / ) ;
205-
206- const progress =
207- progressMatch && progressMatch [ 1 ]
208- ? Number ( progressMatch [ 1 ] )
209- : undefined ;
210- const eta = etaMatch ? etaMatch [ 0 ] . trim ( ) : undefined ;
211-
212- if (
213- progress !== undefined &&
214- progress > this . status . progress
215- ) {
216- this . status . progress = progress ;
217- }
218-
219- if ( eta ) {
220- this . status . eta = eta ;
221- }
250+ } else if ( type === "encode" ) {
251+ const parsed = parseEncodeProgressLine ( content ) ;
252+ if ( parsed ) {
253+ if (
254+ parsed . progress !== undefined &&
255+ parsed . progress > this . status . progress
256+ ) {
257+ this . status . progress = parsed . progress ;
258+ }
222259
223- this . emitter . emit ( "progress" , [
224- this . status . progress ,
225- this . status . eta ,
226- ] ) ;
227- } else if (
228- type === "decode" &&
229- / [ 0 - 9 ] { 1 , 10 } \/ [ 0 - 9 ] { 1 , 10 } / . test ( content )
230- ) {
231- const progressMatch = content . match (
232- / [ 0 - 9 ] { 1 , 10 } \/ [ 0 - 9 ] { 1 , 10 } / ,
233- ) ;
260+ if ( parsed . eta ) {
261+ this . status . eta = parsed . eta ;
262+ }
234263
235- if ( progressMatch ) {
236- const [ current , total ] = progressMatch [ 0 ]
237- . split ( "/" )
238- . map ( Number ) ;
239- const progress = Math . floor ( ( current / total ) * 100 ) ;
264+ this . emitter . emit ( "progress" , [
265+ this . status . progress ,
266+ this . status . eta ,
267+ ] ) ;
268+ return ;
269+ }
270+ }
240271
241- if ( ! Number . isNaN ( progress ) ) {
242- this . status . progress = progress ;
272+ if ( type === "decode" ) {
273+ const parsed = parseDecodeProgressLine ( content ) ;
274+ if ( parsed !== null ) {
275+ if ( ! Number . isNaN ( parsed ) ) {
276+ this . status . progress = parsed ;
243277 }
244278
245279 this . emitter . emit ( "progress" , [
246280 this . status . progress ,
247281 this . status . eta ,
248282 ] ) ;
283+ return ;
249284 }
250- } else if (
251- content . startsWith ( "lame: " ) ||
252- content . startsWith ( "Warning: " ) ||
253- content . includes ( "Error " )
254- ) {
255- const message = content . startsWith ( "lame: " )
256- ? content
257- : `lame: ${ content } ` ;
258- this . emitter . emit ( "error" , new Error ( message ) ) ;
285+ }
286+
287+ const normalized = normalizeCliMessage ( content ) ;
288+ if ( normalized ) {
289+ this . emitter . emit ( "error" , new Error ( normalized ) ) ;
259290 }
260291 } ;
261292
@@ -366,9 +397,11 @@ class Lame {
366397
367398 private async ensureOutputDirectoryExists ( filePath : string ) {
368399 const dir = dirname ( filePath ) ;
369- if ( dir ) {
370- await mkdir ( dir , { recursive : true } ) ;
400+ if ( ! dir || dir === "." ) {
401+ return ;
371402 }
403+
404+ await mkdir ( dir , { recursive : true } ) ;
372405 }
373406
374407 private async generateTempFilePath (
@@ -398,4 +431,9 @@ class Lame {
398431 }
399432}
400433
401- export { Lame } ;
434+ export {
435+ Lame ,
436+ normalizeCliMessage ,
437+ parseDecodeProgressLine ,
438+ parseEncodeProgressLine ,
439+ } ;
0 commit comments