@@ -32,10 +32,6 @@ const (
3232 exitCodeProcessDoneMinValue = - 1
3333)
3434
35- // ErrRecordingFinalizing is returned when attempting to access a recording that is
36- // currently being finalized (remuxed to add duration metadata).
37- var ErrRecordingFinalizing = errors .New ("recording is being finalized" )
38-
3935// FFmpegRecorder encapsulates an FFmpeg recording session with platform-specific screen capture.
4036// It manages the lifecycle of a single FFmpeg process and provides thread-safe operations.
4137type FFmpegRecorder struct {
@@ -237,14 +233,6 @@ func (fr *FFmpegRecorder) Stop(ctx context.Context) error {
237233 return fr .finalizeRecording (ctx )
238234}
239235
240- // WaitForFinalization blocks until finalization completes and returns the result.
241- // If finalization hasn't started, it will be triggered. If already complete, returns
242- // the cached result immediately. This is useful for callers like the download handler
243- // that need to wait for finalization before accessing the recording.
244- func (fr * FFmpegRecorder ) WaitForFinalization (ctx context.Context ) error {
245- return fr .finalizeRecording (ctx )
246- }
247-
248236// ForceStop immediately terminates the recording process.
249237func (fr * FFmpegRecorder ) ForceStop (ctx context.Context ) error {
250238 log := logger .FromContext (ctx )
@@ -378,22 +366,25 @@ func (fr *FFmpegRecorder) Metadata() *RecordingMetadata {
378366}
379367
380368// Recording returns the recording file as an io.ReadCloser.
381- // Returns ErrRecordingFinalizing if the recording is currently being finalized.
369+ // If the recording has exited but finalization is pending, this method blocks
370+ // until finalization completes.
382371func (fr * FFmpegRecorder ) Recording (ctx context.Context ) (io.ReadCloser , * RecordingMetadata , error ) {
383372 fr .mu .Lock ()
384373 if fr .deleted {
385374 fr .mu .Unlock ()
386375 return nil , nil , fmt .Errorf ("recording deleted: %w" , os .ErrNotExist )
387376 }
388- // Block access while finalization is pending or in progress.
389- // This covers both the race window (after process exit, before finalization starts)
390- // and during active finalization.
391- if fr .exitCode >= exitCodeProcessDoneMinValue && ! fr .finalizeComplete {
392- fr .mu .Unlock ()
393- return nil , nil , ErrRecordingFinalizing
394- }
377+ // If recording has exited but finalization is pending or in progress, wait for it.
378+ // This hides finalization as an implementation detail from callers.
379+ needsFinalization := fr .exitCode >= exitCodeProcessDoneMinValue && ! fr .finalizeComplete
395380 fr .mu .Unlock ()
396381
382+ if needsFinalization {
383+ if err := fr .finalizeRecording (ctx ); err != nil {
384+ return nil , nil , fmt .Errorf ("failed to finalize recording: %w" , err )
385+ }
386+ }
387+
397388 file , err := os .Open (fr .outputPath )
398389 if err != nil {
399390 return nil , nil , fmt .Errorf ("failed to open recording file: %w" , err )
@@ -416,18 +407,29 @@ func (fr *FFmpegRecorder) Recording(ctx context.Context) (io.ReadCloser, *Record
416407}
417408
418409// Delete removes the recording file from disk.
419- // Returns ErrRecordingFinalizing if the recording is currently being finalized.
410+ // If the recording has exited but finalization is pending, this method blocks
411+ // until finalization completes before deleting.
420412func (fr * FFmpegRecorder ) Delete (ctx context.Context ) error {
421413 fr .mu .Lock ()
422- defer fr .mu .Unlock ()
423414 if fr .deleted {
415+ fr .mu .Unlock ()
424416 return nil // already deleted
425417 }
426- // Block deletion while finalization is pending or in progress.
427- // This covers both the race window (after process exit, before finalization starts)
428- // and during active finalization.
429- if fr .exitCode >= exitCodeProcessDoneMinValue && ! fr .finalizeComplete {
430- return ErrRecordingFinalizing
418+ // If recording has exited but finalization is pending or in progress, wait for it.
419+ // We need to wait because deleting during finalization would corrupt the remux operation.
420+ needsFinalization := fr .exitCode >= exitCodeProcessDoneMinValue && ! fr .finalizeComplete
421+ fr .mu .Unlock ()
422+
423+ if needsFinalization {
424+ // Wait for finalization to complete. We don't fail the delete if finalization
425+ // fails - the caller wants the file gone regardless.
426+ _ = fr .finalizeRecording (ctx )
427+ }
428+
429+ fr .mu .Lock ()
430+ defer fr .mu .Unlock ()
431+ if fr .deleted {
432+ return nil // deleted while we were waiting
431433 }
432434 if err := os .Remove (fr .outputPath ); err != nil && ! os .IsNotExist (err ) {
433435 return fmt .Errorf ("failed to delete recording file: %w" , err )
0 commit comments