fix: changing channels quickly causes two to play

This commit is contained in:
Adriano Caloiaro 2026-04-19 20:53:34 -06:00
parent 8782746579
commit 36059f23ac
No known key found for this signature in database
4 changed files with 42 additions and 11 deletions

View file

@ -2,6 +2,7 @@ package app
import ( import (
"bytes" "bytes"
c "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -103,25 +104,36 @@ func Play(ctx *context.AppContext) {
} }
ctx.IsPlaying = true ctx.IsPlaying = true
streamCtx, cancel := c.WithCancel(c.Background())
ctx.StreamCancel = cancel
go func() { go func() {
client := &http.Client{} client := &http.Client{}
req, _ := http.NewRequest("GET", chn.Playlist, nil) req, _ := http.NewRequestWithContext(streamCtx, "GET", chn.Playlist, nil)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil || resp.StatusCode != 200 { 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 return
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { 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 return
} }
if streamURL, ok := difm.GetStreamURL(body, ctx); ok { if streamURL, ok := difm.GetStreamURL(body, ctx); ok {
select {
case <-streamCtx.Done():
return
default:
}
UpdateNowPlaying(ctx, chn) UpdateNowPlaying(ctx, chn)
difm.Stream(streamURL, ctx) difm.Stream(streamURL, ctx, streamCtx)
} }
}() }()
} }

View file

@ -1,6 +1,7 @@
package context package context
import ( import (
c "context"
"io" "io"
"time" "time"
@ -40,6 +41,7 @@ type AppContext struct {
Player *pulse.PlaybackStream Player *pulse.PlaybackStream
ShowStatus bool // The status pane will be visible when true ShowStatus bool // The status pane will be visible when true
StatusChannel chan components.StatusMessage StatusChannel chan components.StatusMessage
StreamCancel c.CancelFunc
View *views.ViewContext View *views.ViewContext
} }

View file

@ -219,20 +219,25 @@ func ListFavorites(ctx *context.AppContext) (favorites []components.FavoriteItem
} }
// Stream streams the provided URL using the given di.fm premium token // 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 client := http.DefaultClient
u := fmt.Sprintf("%s?%s", url, config.GetToken()) u := fmt.Sprintf("%s?%s", url, config.GetToken())
// Keep increasing playback latency by one second for every time that the player exits with EOF // Keep increasing playback latency by one second for every time that the player exits with EOF
// while ctx.IsPlaying // while ctx.IsPlaying
for playbackLatency := 1; ctx.IsPlaying; playbackLatency++ { for playbackLatency := 1; ; playbackLatency++ {
select {
case <-streamCtx.Done():
return
default:
}
ctx.SetStatusMessage("Buffering stream...") ctx.SetStatusMessage("Buffering stream...")
if ctx.Player != nil { if ctx.Player != nil {
ctx.Player.Close() ctx.Player.Close()
} }
rctx := c.Background() req, err := http.NewRequestWithContext(streamCtx, "GET", u, nil)
req, err := http.NewRequestWithContext(rctx, "GET", u, nil)
if err != nil { if err != nil {
ctx.SetStatusMessage("There was a problem streaming audio!") ctx.SetStatusMessage("There was a problem streaming audio!")
return return
@ -241,6 +246,9 @@ func Stream(url string, ctx *context.AppContext) {
resp, err := client.Do(req) resp, err := client.Do(req)
switch { switch {
case err != nil: case err != nil:
if streamCtx.Err() != nil {
return
}
ctx.SetStatusMessage(fmt.Sprintf("There was a problem streaming audio: %s", err.Error())) ctx.SetStatusMessage(fmt.Sprintf("There was a problem streaming audio: %s", err.Error()))
return return
case resp.StatusCode != 200: case resp.StatusCode != 200:
@ -254,14 +262,20 @@ func Stream(url string, ctx *context.AppContext) {
go func() { go func() {
for { for {
_, err = io.CopyN(audioStream, resp.Body, 512) _, copyErr := io.CopyN(audioStream, resp.Body, 512)
if err != nil { if copyErr != nil {
return 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) err = player.Play(ctx, audioStream, playbackLatency)
if err == nil { if err == nil {
return return

View file

@ -44,6 +44,9 @@ func Play(ctx *context.AppContext, stream io.ReadWriter, playbackLatency int) (e
func Stop(ctx *context.AppContext) { func Stop(ctx *context.AppContext) {
ctx.IsPlaying = false ctx.IsPlaying = false
if ctx.StreamCancel != nil {
ctx.StreamCancel()
}
if ctx.Player != nil { if ctx.Player != nil {
ctx.AudioStream.Close() ctx.AudioStream.Close()
ctx.Player.Close() ctx.Player.Close()