@@ -8,6 +8,7 @@ import type {
88 LameOptionsBag ,
99 LameProgressEmitter ,
1010 LameStatus ,
11+ LameStreamMode ,
1112 LameStreamOptions ,
1213} from "../types" ;
1314import { LameOptions } from "./lame-options" ;
@@ -20,53 +21,45 @@ import type { ProgressKind } from "./lame-process";
2021
2122type StreamKind = ProgressKind ;
2223
23- interface LameStreamConfig extends LameStreamOptions {
24+ type LameStreamConfig = Omit < LameStreamOptions , "output" > & {
2425 binaryPath ?: string ;
25- }
26+ } ;
2627
27- class LameCodecStream extends Duplex {
28+ class LameStream extends Duplex {
2829 private readonly emitter : LameProgressEmitter ;
2930 private readonly status : LameStatus ;
30- private readonly kind : StreamKind ;
31- private readonly child : ChildProcessWithoutNullStreams ;
3231 private readonly builder : LameOptions ;
32+ private readonly binaryPath ?: string ;
33+ private readonly kind : StreamKind ;
34+
35+ private child ?: ChildProcessWithoutNullStreams ;
3336
3437 private isStdoutPaused = false ;
3538 private hasErrored = false ;
3639 private finished = false ;
3740
38- constructor ( kind : StreamKind , options : LameStreamConfig ) {
41+ constructor ( options : LameStreamConfig ) {
3942 super ( { allowHalfOpen : false } ) ;
4043
41- const { binaryPath, ...cliOptions } = options ;
44+ const { binaryPath, mode, ...cliOptions } = options ;
45+ if ( ! isValidStreamMode ( mode ) ) {
46+ throw new Error (
47+ 'lame: LameStream requires a mode of either "encode" or "decode"' ,
48+ ) ;
49+ }
50+
4251 const normalizedOptions : LameOptionsBag = {
43- ...( cliOptions as Omit < LameStreamOptions , "output" > ) ,
52+ ...( cliOptions as Omit < LameStreamOptions , "output" | "mode" > ) ,
4453 output : "stream" ,
4554 } as LameOptionsBag ;
4655
47- this . kind = kind ;
56+ this . binaryPath = binaryPath ;
4857 this . status = createInitialStatus ( ) ;
4958 this . emitter = new EventEmitter ( ) as LameProgressEmitter ;
5059 this . builder = new LameOptions ( normalizedOptions ) ;
60+ this . kind = mode ;
5161
52- const spawnArgs = buildLameSpawnArgs ( this . builder , this . kind , "-" , "-" ) ;
53-
54- this . child = spawnLameProcess ( {
55- binaryPath,
56- spawnArgs,
57- kind : this . kind ,
58- status : this . status ,
59- emitter : this . emitter ,
60- progressSources : [ "stderr" ] ,
61- completeOnTag : true ,
62- onError : ( error ) => this . emitStreamError ( error ) ,
63- onStdoutData : ( chunk ) => this . forwardStdout ( chunk ) ,
64- onStdoutEnd : ( ) => this . push ( null ) ,
65- onStdoutError : ( error ) => this . emitStreamError ( error ) ,
66- onStderrError : ( error ) => this . emitStreamError ( error ) ,
67- onStdinError : ( error ) => this . emitStreamError ( error ) ,
68- onSuccess : ( ) => this . handleSuccessfulClose ( ) ,
69- } ) ;
62+ this . initialize ( this . kind ) ;
7063 }
7164
7265 public getEmitter ( ) : LameProgressEmitter {
@@ -78,6 +71,10 @@ class LameCodecStream extends Duplex {
7871 }
7972
8073 public override _read ( ) : void {
74+ if ( ! this . child ) {
75+ return ;
76+ }
77+
8178 if ( this . isStdoutPaused && ! this . child . stdout . readableEnded ) {
8279 this . isStdoutPaused = false ;
8380 this . child . stdout . resume ( ) ;
@@ -89,18 +86,24 @@ class LameCodecStream extends Duplex {
8986 encoding : BufferEncoding ,
9087 callback : ( error ?: Error | null ) => void ,
9188 ) : void {
92- if ( this . finished || this . child . stdin . destroyed ) {
89+ const child = this . child ;
90+ if ( ! child ) {
91+ callback ( new Error ( "lame: Stream mode is not initialized yet" ) ) ;
92+ return ;
93+ }
94+
95+ if ( this . finished || child . stdin . destroyed ) {
9396 callback ( new Error ( "lame: Stream has already finished" ) ) ;
9497 return ;
9598 }
9699
97100 try {
98- const flushed = this . child . stdin . write ( chunk , encoding ) ;
101+ const flushed = child . stdin . write ( chunk , encoding ) ;
99102 if ( ! flushed ) {
100103 const cleanup = ( ) => {
101- this . child . stdin . off ( "drain" , onDrain ) ;
102- this . child . stdin . off ( "error" , onError ) ;
103- this . child . stdin . off ( "close" , onClose ) ;
104+ child . stdin . off ( "drain" , onDrain ) ;
105+ child . stdin . off ( "error" , onError ) ;
106+ child . stdin . off ( "close" , onClose ) ;
104107 } ;
105108
106109 const onDrain = ( ) => {
@@ -116,9 +119,9 @@ class LameCodecStream extends Duplex {
116119 callback ( new Error ( "lame: Input stream closed before drain" ) ) ;
117120 } ;
118121
119- this . child . stdin . once ( "drain" , onDrain ) ;
120- this . child . stdin . once ( "error" , onError ) ;
121- this . child . stdin . once ( "close" , onClose ) ;
122+ child . stdin . once ( "drain" , onDrain ) ;
123+ child . stdin . once ( "error" , onError ) ;
124+ child . stdin . once ( "close" , onClose ) ;
122125 return ;
123126 }
124127 } catch ( error ) {
@@ -130,8 +133,14 @@ class LameCodecStream extends Duplex {
130133 }
131134
132135 public override _final ( callback : ( error ?: Error | null ) => void ) : void {
136+ const child = this . child ;
137+ if ( ! child ) {
138+ callback ( new Error ( "lame: Stream mode is not initialized yet" ) ) ;
139+ return ;
140+ }
141+
133142 try {
134- this . child . stdin . end ( ) ;
143+ child . stdin . end ( ) ;
135144 } catch ( error ) {
136145 callback ( error as Error ) ;
137146 return ;
@@ -144,9 +153,16 @@ class LameCodecStream extends Duplex {
144153 error : Error | null ,
145154 callback : ( error ?: Error | null ) => void ,
146155 ) : void {
156+ const child = this . child ;
157+
158+ if ( ! child ) {
159+ callback ( error ?? null ) ;
160+ return ;
161+ }
162+
147163 try {
148- if ( ! this . child . killed ) {
149- this . child . kill ( ) ;
164+ if ( ! child . killed ) {
165+ child . kill ( ) ;
150166 }
151167 } catch ( killError ) {
152168 callback ( killError as Error ) ;
@@ -157,13 +173,38 @@ class LameCodecStream extends Duplex {
157173 callback ( error ?? null ) ;
158174 }
159175
176+ private initialize ( kind : StreamKind ) : void {
177+ if ( this . child ) {
178+ return ;
179+ }
180+
181+ const spawnArgs = buildLameSpawnArgs ( this . builder , kind , "-" , "-" ) ;
182+
183+ this . child = spawnLameProcess ( {
184+ binaryPath : this . binaryPath ,
185+ spawnArgs,
186+ kind,
187+ status : this . status ,
188+ emitter : this . emitter ,
189+ progressSources : [ "stderr" ] ,
190+ completeOnTag : true ,
191+ onError : ( error ) => this . emitStreamError ( error ) ,
192+ onStdoutData : ( chunk ) => this . forwardStdout ( chunk ) ,
193+ onStdoutEnd : ( ) => this . push ( null ) ,
194+ onStdoutError : ( error ) => this . emitStreamError ( error ) ,
195+ onStderrError : ( error ) => this . emitStreamError ( error ) ,
196+ onStdinError : ( error ) => this . emitStreamError ( error ) ,
197+ onSuccess : ( ) => this . handleSuccessfulClose ( ) ,
198+ } ) ;
199+ }
200+
160201 private forwardStdout ( chunk : Buffer ) {
161202 if ( this . hasErrored || this . finished ) {
162203 return ;
163204 }
164205
165206 const shouldContinue = this . push ( chunk ) ;
166- if ( ! shouldContinue ) {
207+ if ( ! shouldContinue && this . child ) {
167208 this . isStdoutPaused = true ;
168209 this . child . stdout . pause ( ) ;
169210 }
@@ -188,8 +229,9 @@ class LameCodecStream extends Duplex {
188229 this . status . finished = true ;
189230 this . cleanupChildListeners ( ) ;
190231 this . emitter . emit ( "error" , error ) ;
232+
191233 try {
192- if ( ! this . child . killed ) {
234+ if ( this . child && ! this . child . killed ) {
193235 this . child . kill ( ) ;
194236 }
195237 } catch {
@@ -200,24 +242,20 @@ class LameCodecStream extends Duplex {
200242 }
201243
202244 private cleanupChildListeners ( ) {
245+ if ( ! this . child ) {
246+ return ;
247+ }
248+
203249 this . child . stdout . removeAllListeners ( ) ;
204250 this . child . stderr . removeAllListeners ( ) ;
205251 this . child . stdin . removeAllListeners ( ) ;
206252 this . child . removeAllListeners ( ) ;
207253 }
208254}
209255
210- const createLameEncoderStream = ( options : LameStreamConfig ) => {
211- return new LameCodecStream ( "encode" , options ) ;
256+ const isValidStreamMode = ( value : unknown ) : value is LameStreamMode => {
257+ return value === "encode" || value === "decode" ;
212258} ;
213259
214- const createLameDecoderStream = ( options : LameStreamConfig ) => {
215- return new LameCodecStream ( "decode" , options ) ;
216- } ;
217-
218- export {
219- LameCodecStream ,
220- createLameDecoderStream ,
221- createLameEncoderStream ,
222- } ;
260+ export { LameStream } ;
223261export type { LameStreamConfig } ;
0 commit comments