From 333b96dd3b43603eb1d5ec5d1bfb80f4c3435785 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 27 Jul 2025 17:39:48 -0700 Subject: [PATCH] switch back to using HTTP server to stream file to MPV - it doesn't idle after playing FIFO --- backend/playbackmanager.go | 1 - backend/util/filestreamer.go | 10 +---- backend/waveformimage.go | 84 ++++++------------------------------ 3 files changed, 14 insertions(+), 81 deletions(-) diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index a1a3a7a..67360c2 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -119,7 +119,6 @@ func (p *PlaybackManager) addOnTrackChangeHook() { case <-ctx.Done(): return default: - log.Println("updating waveform img, job step", job.step) img := job.Get() for _, cb := range p.onWaveformImgUpdate { cb(img) diff --git a/backend/util/filestreamer.go b/backend/util/filestreamer.go index 8f015fc..ac241fa 100644 --- a/backend/util/filestreamer.go +++ b/backend/util/filestreamer.go @@ -51,7 +51,6 @@ func (fs *FileStreamerServer) Serve() error { _ = fs.server.Serve(fs.listener) }() - log.Println("Serving and WAITING for done") <-fs.done // wait for the handler to finish ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) @@ -61,13 +60,9 @@ func (fs *FileStreamerServer) Serve() error { } // Handler that streams the file using chunked transfer encoding. -func (fs *FileStreamerServer) streamHandler(w http.ResponseWriter, r *http.Request) { - log.Println("FILE STREAMER REQUEST") +func (fs *FileStreamerServer) streamHandler(w http.ResponseWriter, _ *http.Request) { defer close(fs.done) // signal Serve() to shut down after this request - totalWrote := 0 - defer log.Println("File streamer wrote", totalWrote, "bytes") - file, err := os.Open(fs.Path) if err != nil { log.Println("File streamer failed to open source file") @@ -92,7 +87,7 @@ func (fs *FileStreamerServer) streamHandler(w http.ResponseWriter, r *http.Reque } if n > 0 { - written, err := w.Write(buf[:n]) + _, err := w.Write(buf[:n]) if err != nil { log.Printf("client write error: %v", err) break @@ -100,7 +95,6 @@ func (fs *FileStreamerServer) streamHandler(w http.ResponseWriter, r *http.Reque if canFlush { flusher.Flush() } - totalWrote += written } if n == 0 && complete { diff --git a/backend/waveformimage.go b/backend/waveformimage.go index 82bddb6..2f46bef 100644 --- a/backend/waveformimage.go +++ b/backend/waveformimage.go @@ -15,10 +15,10 @@ import ( "time" "github.com/dweymouth/supersonic/backend/mediaprovider" + "github.com/dweymouth/supersonic/backend/util" "github.com/go-audio/audio" "github.com/go-audio/wav" "github.com/supersonic-app/go-mpv" - "golang.org/x/sys/unix" ) type WaveformImageGenerator struct { @@ -135,9 +135,17 @@ func (w *WaveformImageGenerator) StartWaveformGeneration(item *mediaprovider.Tra // stream it to MPV via fifo so it doesn't possibly // terminate the conversion to WAV early encountering EOF if !fileDone() { - fifoPath := filepath.Join(filepath.Dir(path), filepath.Base(path)+"_fifo") - copyFileToFifo(ctx, job, path, fifoPath, fileDone) - path = fifoPath // MPV will read from the FIFO + srv, err := util.NewFileStreamerServer(path, fileDone) + if err != nil { + job.setError(err) + return + } + + path = srv.Addr() + + log.Println("streaming file to MPV at ", path) + go srv.Serve() + time.Sleep(10 * time.Millisecond) // make sure server has time to come up } // Start converting the file to WAV for analysis @@ -237,71 +245,6 @@ func generateWaveformImage(ctx context.Context, data *waveformData, job *Wavefor } } -func copyFileToFifo(ctx context.Context, job *WaveformImageJob, filePath, fifoPath string, fileDone func() bool) { - if err := unix.Mkfifo(fifoPath, 0600); err != nil { - job.setError(err) - return - } - - go func() { - fifo, err := os.OpenFile(fifoPath, os.O_WRONLY, os.ModeNamedPipe) - if err != nil { - job.setError(err) - return - } - file, err := os.Open(filePath) - if err != nil { - job.setError(err) - return - } - defer file.Close() - defer fifo.Close() - - buf := make([]byte, 4096) - offset := int64(0) - - for { - if e := ctx.Err(); e != nil { - job.setError(e) - return - } - - done := fileDone() - info, err := os.Stat(filePath) - if err != nil { - job.setError(err) - return - } - currentSize := info.Size() - - // If we've read everything available so far - if offset >= currentSize { - if done { - return // done - } - // wait a bit for more data to be written to file - time.Sleep(50 * time.Millisecond) - continue - } - - // Calculate how much is safe to read - toRead := currentSize - offset - 1 - if toRead > int64(cap(buf)) { - toRead = int64(cap(buf)) - } - - n, err := file.ReadAt(buf[:toRead], offset) - if n > 0 { - if _, werr := fifo.Write(buf[:n]); werr != nil { - job.setError(werr) - return - } - offset += int64(n) - } - } - }() -} - // assumes mono, 16 bit func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformData, millisecs int64, fileDone func() bool) error { f, err := os.Open(transcodeFile) @@ -379,8 +322,6 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat if err == io.EOF && !fileDone() { return errors.New("WAV read got premature EOF") } - } else if fileIsDone { - log.Println("read samples on done file") } if err != nil { return err @@ -401,7 +342,6 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat data.progress = curChunk chunkSamples = chunkSamples[:0] if curChunk >= 1024 { - doneReading = true break } }