Skip to content

Commit f7367fc

Browse files
committed
fix race conditions in media status updates and GUI actions
1 parent 25b5340 commit f7367fc

2 files changed

Lines changed: 38 additions & 17 deletions

File tree

castprotocol/v2/application/application.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ func (a *Application) Update() error {
410410
a.volumeReceiver = &recvStatus.Status.Volume
411411

412412
if a.application == nil || a.application.IsIdleScreen {
413+
// No app (or just the idle screen) means our media session is
414+
// gone. Clear the cached snapshot so callers see IDLE instead
415+
// of a stale last-known PLAYING state.
416+
a.media = nil
413417
return nil
414418
}
415419

@@ -425,6 +429,14 @@ func (a *Application) updateMediaStatus() error {
425429
if err != nil {
426430
return err
427431
}
432+
if len(mediaStatus.Status) == 0 {
433+
// The receiver reports no media session, e.g. the media
434+
// finished and the session was torn down between polls.
435+
// Clear the cached snapshot so callers see IDLE instead of
436+
// a stale last-known PLAYING state.
437+
a.media = nil
438+
return nil
439+
}
428440
for _, media := range mediaStatus.Status {
429441
a.media = &media
430442
a.volumeMedia = &media.Volume

internal/gui/actions.go

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,21 +1713,26 @@ out:
17131713
}
17141714

17151715
if nextURI == "" {
1716-
if screen.tvdata == nil {
1716+
// Stop/skip actions clear these fields from another
1717+
// goroutine before this watcher's context is cancelled,
1718+
// so snapshot both and skip the tick if either is gone.
1719+
tvdata := screen.tvdata
1720+
srv := screen.httpserver
1721+
if tvdata == nil || srv == nil {
17171722
continue
17181723
}
17191724

17201725
// No need to check for the error as this is something
17211726
// that we did in previous steps in our workflow
1722-
mPath, _ := url.Parse(screen.tvdata.MediaURL)
1723-
sPath, _ := url.Parse(screen.tvdata.SubtitlesURL)
1727+
mPath, _ := url.Parse(tvdata.MediaURL)
1728+
sPath, _ := url.Parse(tvdata.SubtitlesURL)
17241729

17251730
// Make sure we clean up after ourselves and avoid
17261731
// leaving any dangling handlers. Given the nextURI is ""
17271732
// we know that the previously playing media entry was
17281733
// replaced by the one in the NextURI entry.
1729-
screen.httpserver.RemoveHandler(mPath.Path)
1730-
screen.httpserver.RemoveHandler(sPath.Path)
1734+
srv.RemoveHandler(mPath.Path)
1735+
srv.RemoveHandler(sPath.Path)
17311736

17321737
_, mediaPath, err := getNextAutoPlayMediaOrError(screen)
17331738
if err != nil {
@@ -1869,17 +1874,20 @@ func skipToMediaPathAction(screen *FyneScreen, mediaPath string) {
18691874
// Set casting media type
18701875
screen.SetMediaType(mediaType)
18711876

1872-
// Get server address
1873-
whereToListen := screen.httpserver.GetAddr()
1877+
// Get server address. A concurrent stop action may have already
1878+
// cleared the field, in which case there is nothing to skip to.
1879+
server := screen.httpserver
1880+
if server == nil {
1881+
return
1882+
}
1883+
whereToListen := server.GetAddr()
18741884

18751885
var mediaURL string
18761886
var subtitleURL string
18771887

18781888
if transcode {
18791889
// TRANSCODING PATH: Stop server and restart with new file and transcode options
1880-
if screen.httpserver != nil {
1881-
screen.httpserver.StopServer()
1882-
}
1890+
server.StopServer()
18831891

18841892
// Get actual media duration from ffprobe (Chromecast can't report it for transcoded streams)
18851893
if duration, err := utils.DurationForMediaSeconds(screen.ffmpegPath, screen.mediafile); err == nil {
@@ -1901,15 +1909,16 @@ func skipToMediaPathAction(screen *FyneScreen, mediaPath string) {
19011909
}
19021910

19031911
// Create new HTTP server with transcoding
1904-
screen.httpserver = httphandlers.NewServer(whereToListen)
1912+
server = httphandlers.NewServer(whereToListen)
1913+
screen.httpserver = server
19051914
serverStarted := make(chan error)
19061915
var serverCTXStop context.CancelFunc
19071916
serverStoppedCTX, serverCTXStop = context.WithCancel(context.Background())
19081917
screen.serverStopCTX = serverStoppedCTX
19091918
screen.cancelServerStop = serverCTXStop
19101919

19111920
go func() {
1912-
screen.httpserver.StartSimpleServerWithTranscode(serverStarted, screen.mediafile, tcOpts)
1921+
server.StartSimpleServerWithTranscode(serverStarted, screen.mediafile, tcOpts)
19131922
serverCTXStop()
19141923
}()
19151924

@@ -1929,18 +1938,18 @@ func skipToMediaPathAction(screen *FyneScreen, mediaPath string) {
19291938
screen.mediaDuration = 0
19301939

19311940
// Get subtitle URL if needed (remove old handler first)
1932-
screen.httpserver.RemoveHandler("/subtitles.vtt")
1941+
server.RemoveHandler("/subtitles.vtt")
19331942
if screen.subsfile != "" {
19341943
ext := strings.ToLower(filepath.Ext(screen.subsfile))
19351944
switch ext {
19361945
case ".srt":
19371946
webvttData, err := utils.ConvertSRTtoWebVTT(screen.subsfile)
19381947
if err == nil {
1939-
screen.httpserver.AddHandler("/subtitles.vtt", nil, nil, webvttData)
1948+
server.AddHandler("/subtitles.vtt", nil, nil, webvttData)
19401949
subtitleURL = "http://" + whereToListen + "/subtitles.vtt"
19411950
}
19421951
case ".vtt":
1943-
screen.httpserver.AddHandler("/subtitles.vtt", nil, nil, screen.subsfile)
1952+
server.AddHandler("/subtitles.vtt", nil, nil, screen.subsfile)
19441953
subtitleURL = "http://" + whereToListen + "/subtitles.vtt"
19451954
}
19461955
}
@@ -1950,8 +1959,8 @@ func skipToMediaPathAction(screen *FyneScreen, mediaPath string) {
19501959
// URL uses ConvertFilename (encoded) for valid HTTP URL with special characters
19511960
oldHandlerPath := "/" + filepath.Base(oldMediaPath)
19521961
newHandlerPath := "/" + filepath.Base(screen.mediafile)
1953-
screen.httpserver.RemoveHandler(oldHandlerPath)
1954-
screen.httpserver.AddHandler(newHandlerPath, nil, nil, screen.mediafile)
1962+
server.RemoveHandler(oldHandlerPath)
1963+
server.AddHandler(newHandlerPath, nil, nil, screen.mediafile)
19551964

19561965
// Build media URL using URL-encoded filename (for special chars like brackets)
19571966
mediaURL = "http://" + whereToListen + "/" + utils.ConvertFilename(screen.mediafile)

0 commit comments

Comments
 (0)