From 36059f23ac5c429d4991813a73b881dd72d3bfbd Mon Sep 17 00:00:00 2001 From: Adriano Caloiaro Date: Sun, 19 Apr 2026 20:53:34 -0600 Subject: [PATCH] fix: changing channels quickly causes two to play --- app/app.go | 20 ++++++++++++++++---- context/context.go | 2 ++ difm/difm.go | 28 +++++++++++++++++++++------- player/player.go | 3 +++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/app.go b/app/app.go index a5f573e..6aae6bd 100644 --- a/app/app.go +++ b/app/app.go @@ -2,6 +2,7 @@ package app import ( "bytes" + c "context" "encoding/json" "errors" "fmt" @@ -103,25 +104,36 @@ func Play(ctx *context.AppContext) { } ctx.IsPlaying = true + streamCtx, cancel := c.WithCancel(c.Background()) + ctx.StreamCancel = cancel go func() { client := &http.Client{} - req, _ := http.NewRequest("GET", chn.Playlist, nil) + req, _ := http.NewRequestWithContext(streamCtx, "GET", chn.Playlist, nil) resp, err := client.Do(req) if err != nil || resp.StatusCode != 200 { - ctx.SetStatusMessage(fmt.Sprintf("Unable to stream channel: %s. Try again.", chn.Name)) + if streamCtx.Err() == nil { + ctx.SetStatusMessage(fmt.Sprintf("Unable to stream channel: %s. Try again.", chn.Name)) + } return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - ctx.SetStatusMessage(fmt.Sprintf("Unable to stream channel: %s. Try again.", chn.Name)) + if streamCtx.Err() == nil { + ctx.SetStatusMessage(fmt.Sprintf("Unable to stream channel: %s. Try again.", chn.Name)) + } return } if streamURL, ok := difm.GetStreamURL(body, ctx); ok { + select { + case <-streamCtx.Done(): + return + default: + } UpdateNowPlaying(ctx, chn) - difm.Stream(streamURL, ctx) + difm.Stream(streamURL, ctx, streamCtx) } }() } diff --git a/context/context.go b/context/context.go index 059c176..7849dd9 100644 --- a/context/context.go +++ b/context/context.go @@ -1,6 +1,7 @@ package context import ( + c "context" "io" "time" @@ -40,6 +41,7 @@ type AppContext struct { Player *pulse.PlaybackStream ShowStatus bool // The status pane will be visible when true StatusChannel chan components.StatusMessage + StreamCancel c.CancelFunc View *views.ViewContext } diff --git a/difm/difm.go b/difm/difm.go index 0c9a5ac..e611407 100644 --- a/difm/difm.go +++ b/difm/difm.go @@ -219,20 +219,25 @@ func ListFavorites(ctx *context.AppContext) (favorites []components.FavoriteItem } // Stream streams the provided URL using the given di.fm premium token -func Stream(url string, ctx *context.AppContext) { +func Stream(url string, ctx *context.AppContext, streamCtx c.Context) { client := http.DefaultClient u := fmt.Sprintf("%s?%s", url, config.GetToken()) // Keep increasing playback latency by one second for every time that the player exits with EOF // while ctx.IsPlaying - for playbackLatency := 1; ctx.IsPlaying; playbackLatency++ { + for playbackLatency := 1; ; playbackLatency++ { + select { + case <-streamCtx.Done(): + return + default: + } + ctx.SetStatusMessage("Buffering stream...") if ctx.Player != nil { ctx.Player.Close() } - rctx := c.Background() - req, err := http.NewRequestWithContext(rctx, "GET", u, nil) + req, err := http.NewRequestWithContext(streamCtx, "GET", u, nil) if err != nil { ctx.SetStatusMessage("There was a problem streaming audio!") return @@ -241,6 +246,9 @@ func Stream(url string, ctx *context.AppContext) { resp, err := client.Do(req) switch { case err != nil: + if streamCtx.Err() != nil { + return + } ctx.SetStatusMessage(fmt.Sprintf("There was a problem streaming audio: %s", err.Error())) return case resp.StatusCode != 200: @@ -254,14 +262,20 @@ func Stream(url string, ctx *context.AppContext) { go func() { for { - _, err = io.CopyN(audioStream, resp.Body, 512) - if err != nil { + _, copyErr := io.CopyN(audioStream, resp.Body, 512) + if copyErr != nil { return } } }() - time.Sleep(time.Duration(playbackLatency) * time.Second) + select { + case <-streamCtx.Done(): + resp.Body.Close() + return + case <-time.After(time.Duration(playbackLatency) * time.Second): + } + err = player.Play(ctx, audioStream, playbackLatency) if err == nil { return diff --git a/player/player.go b/player/player.go index 2ee03fd..d86798d 100644 --- a/player/player.go +++ b/player/player.go @@ -44,6 +44,9 @@ func Play(ctx *context.AppContext, stream io.ReadWriter, playbackLatency int) (e func Stop(ctx *context.AppContext) { ctx.IsPlaying = false + if ctx.StreamCancel != nil { + ctx.StreamCancel() + } if ctx.Player != nil { ctx.AudioStream.Close() ctx.Player.Close()