keep hanging DLNA player from hanging Supersonic when quitting/switching players

This commit is contained in:
Drew Weymouth
2025-03-30 13:51:50 -07:00
parent 2701bb3f7f
commit 9001dc296f
7 changed files with 102 additions and 27 deletions
+6
View File
@@ -61,6 +61,12 @@ func (c *playbackCommandQueue) C() <-chan playbackCommand {
return c.nextChan return c.nextChan
} }
func (c *playbackCommandQueue) Clear() {
c.mutex.Lock()
c.queue = nil
c.mutex.Unlock()
}
func (c *playbackCommandQueue) Stop() { func (c *playbackCommandQueue) Stop() {
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop}, c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
playbackCommand{Type: cmdStop}) playbackCommand{Type: cmdStop})
+7 -8
View File
@@ -3,7 +3,6 @@ package backend
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"log" "log"
"math/rand" "math/rand"
"time" "time"
@@ -157,15 +156,15 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error {
case player.Stopped: case player.Stopped:
// nothing // nothing
case player.Playing: case player.Playing:
if err := p.Pause(); err != nil {
return fmt.Errorf("failed to pause: %v", err)
}
needToUnpause = true needToUnpause = true
fallthrough fallthrough
case player.Paused: case player.Paused:
p.pendingPlayerChangeTimePos = stat.TimePos p.pendingPlayerChangeTimePos = stat.TimePos
p.pendingPlayerChange = true p.pendingPlayerChange = true
} }
if err := p.player.Stop(true); err != nil {
log.Printf("failed to stop player: %v", err)
}
oldVol := p.player.GetVolume() oldVol := p.player.GetVolume()
if _, isMPV := p.player.(*mpv.Player); !isMPV { if _, isMPV := p.player.(*mpv.Player); !isMPV {
@@ -293,7 +292,7 @@ func (p *playbackEngine) IsSeeking() bool {
} }
func (p *playbackEngine) Stop() error { func (p *playbackEngine) Stop() error {
return p.player.Stop() return p.player.Stop(false)
} }
func (p *playbackEngine) Pause() error { func (p *playbackEngine) Pause() error {
@@ -332,7 +331,7 @@ func (p *playbackEngine) LoadTracks(tracks []*mediaprovider.Track, insertQueueMo
func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error { func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error {
if insertQueueMode == Replace { if insertQueueMode == Replace {
p.player.Stop() p.player.Stop(false)
p.nowPlayingIdx = -1 p.nowPlayingIdx = -1
p.playQueue = nil p.playQueue = nil
} }
@@ -358,7 +357,7 @@ func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueu
func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) { func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) {
if insertMode == Replace { if insertMode == Replace {
p.player.Stop() p.player.Stop(false)
p.nowPlayingIdx = -1 p.nowPlayingIdx = -1
p.playQueue = nil p.playQueue = nil
} }
@@ -384,7 +383,7 @@ func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, ins
// Stop playback and clear the play queue. // Stop playback and clear the play queue.
func (p *playbackEngine) StopAndClearPlayQueue() { func (p *playbackEngine) StopAndClearPlayQueue() {
changed := len(p.playQueue) > 0 changed := len(p.playQueue) > 0
p.player.Stop() p.player.Stop(false)
p.playQueue = nil p.playQueue = nil
p.nowPlayingIdx = -1 p.nowPlayingIdx = -1
if changed { if changed {
+1
View File
@@ -139,6 +139,7 @@ func (p *PlaybackManager) CurrentRemotePlayer() *RemotePlaybackDevice {
} }
func (p *PlaybackManager) SetRemotePlayer(rp *RemotePlaybackDevice) error { func (p *PlaybackManager) SetRemotePlayer(rp *RemotePlaybackDevice) error {
p.cmdQueue.Clear()
if rp == nil { if rp == nil {
if err := p.engine.SetPlayer(p.localPlayer); err != nil { if err := p.engine.SetPlayer(p.localPlayer); err != nil {
return err return err
+81 -14
View File
@@ -42,6 +42,7 @@ type DLNAPlayer struct {
player.BasePlayerCallbackImpl player.BasePlayerCallbackImpl
destroyed bool destroyed bool
cancelRequest context.CancelFunc
avTransport *avtransport.Client avTransport *avtransport.Client
renderControl *renderingcontrol.Client renderControl *renderingcontrol.Client
@@ -122,15 +123,31 @@ func NewDLNAPlayer(device *device.MediaRenderer) (*DLNAPlayer, error) {
} }
func (d *DLNAPlayer) SetVolume(vol int) error { func (d *DLNAPlayer) SetVolume(vol int) error {
return d.renderControl.SetVolume(context.Background(), vol) if d.destroyed {
return nil
}
ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
return d.renderControl.SetVolume(ctx, vol)
} }
func (d *DLNAPlayer) GetVolume() int { func (d *DLNAPlayer) GetVolume() int {
vol, _ := d.renderControl.GetVolume(context.Background()) if d.destroyed {
return 0
}
ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
vol, _ := d.renderControl.GetVolume(ctx)
return vol return vol
} }
func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata, startTime float64) error { func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata, startTime float64) error {
if d.destroyed {
return nil
}
d.ensureSetupProxy() d.ensureSetupProxy()
d.metaLock.Lock() d.metaLock.Lock()
@@ -179,17 +196,25 @@ func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadat
} }
func (d *DLNAPlayer) playAVTransportMedia(media *avtransport.MediaItem) error { func (d *DLNAPlayer) playAVTransportMedia(media *avtransport.MediaItem) error {
err := d.avTransport.SetAVTransportMedia(context.Background(), media) ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
err := d.avTransport.SetAVTransportMedia(ctx, media)
if err != nil { if err != nil {
return err return err
} }
if err := d.avTransport.Play(context.Background()); err != nil { if err := d.avTransport.Play(ctx); err != nil {
return err return err
} }
return nil return nil
} }
func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadata) error { func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadata) error {
if d.destroyed {
return nil
}
var media *avtransport.MediaItem var media *avtransport.MediaItem
d.metaLock.Lock() d.metaLock.Lock()
d.nextTrackMeta = meta d.nextTrackMeta = meta
@@ -203,7 +228,11 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
Title: meta.Name, Title: meta.Name,
} }
} }
err := d.avTransport.SetNextAVTransportMedia(context.Background(), media)
ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
err := d.avTransport.SetNextAVTransportMedia(ctx, media)
if err != nil { if err != nil {
d.metaLock.Lock() d.metaLock.Lock()
d.failedToSetNext = true d.failedToSetNext = true
@@ -214,19 +243,23 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
} }
func (d *DLNAPlayer) Continue() error { func (d *DLNAPlayer) Continue() error {
if d.state == playing { if d.destroyed || d.state == playing {
return nil return nil
} }
ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
if d.pendingSeek { if d.pendingSeek {
d.pendingSeek = false d.pendingSeek = false
err := d.avTransport.Seek(context.Background(), int(d.pendingSeekSecs)) err := d.avTransport.Seek(ctx, int(d.pendingSeekSecs))
if err != nil { if err != nil {
return err return err
} }
} }
if err := d.avTransport.Play(context.Background()); err != nil { if err := d.avTransport.Play(ctx); err != nil {
return err return err
} }
d.metaLock.Lock() d.metaLock.Lock()
@@ -240,11 +273,14 @@ func (d *DLNAPlayer) Continue() error {
} }
func (d *DLNAPlayer) Pause() error { func (d *DLNAPlayer) Pause() error {
if d.state != playing { if d.destroyed || d.state != playing {
return nil return nil
} }
if err := d.avTransport.Pause(context.Background()); err != nil { ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
if err := d.avTransport.Pause(ctx); err != nil {
return err return err
} }
d.setTrackChangeTimer(0) d.setTrackChangeTimer(0)
@@ -254,12 +290,29 @@ func (d *DLNAPlayer) Pause() error {
return nil return nil
} }
func (d *DLNAPlayer) Stop() error { func (d *DLNAPlayer) Stop(force bool) error {
if d.destroyed {
return nil
}
if force && d.cancelRequest != nil {
d.cancelRequest()
}
switch d.state { switch d.state {
case stopped: case stopped:
return nil return nil
case playing: case playing:
if err := d.avTransport.Pause(context.Background()); err != nil { var ctx context.Context
var cancel context.CancelFunc
if force {
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
} else {
ctx, cancel = context.WithCancel(context.Background())
}
d.cancelRequest = cancel
defer cancel()
if err := d.avTransport.Pause(ctx); err != nil {
return err return err
} }
fallthrough fallthrough
@@ -276,6 +329,10 @@ func (d *DLNAPlayer) Stop() error {
} }
func (d *DLNAPlayer) SeekSeconds(secs float64) error { func (d *DLNAPlayer) SeekSeconds(secs float64) error {
if d.destroyed {
return nil
}
if d.state == paused { if d.state == paused {
d.pendingSeek = true d.pendingSeek = true
d.pendingSeekSecs = secs d.pendingSeekSecs = secs
@@ -309,7 +366,9 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error {
func (d *DLNAPlayer) sendSeekCmd(secs float64) error { func (d *DLNAPlayer) sendSeekCmd(secs float64) error {
d.seeking = true d.seeking = true
if err := d.avTransport.Seek(context.Background(), int(secs)); err != nil { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := d.avTransport.Seek(ctx, int(secs)); err != nil {
d.seeking = false d.seeking = false
return err return err
} }
@@ -346,8 +405,16 @@ func (d *DLNAPlayer) curPlayPos() time.Duration {
func (d *DLNAPlayer) Destroy() { func (d *DLNAPlayer) Destroy() {
d.destroyed = true d.destroyed = true
d.setTrackChangeTimer(0)
if d.cancelRequest != nil {
d.cancelRequest()
}
if d.proxyServer != nil { if d.proxyServer != nil {
go d.proxyServer.Shutdown(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
go d.proxyServer.Shutdown(ctx)
d.proxyServer = nil
} }
} }
+1 -1
View File
@@ -68,7 +68,7 @@ func (j *JukeboxPlayer) Pause() error {
return nil return nil
} }
func (j *JukeboxPlayer) Stop() error { func (j *JukeboxPlayer) Stop(_ bool) error {
if j.state == stopped { if j.state == stopped {
return nil return nil
} }
+1 -1
View File
@@ -165,7 +165,7 @@ func (p *Player) PlayFile(url string, _ mediaprovider.MediaItemMetadata, startTi
} }
// Stops playback and clears the play queue. // Stops playback and clears the play queue.
func (p *Player) Stop() error { func (p *Player) Stop(_ bool) error {
if !p.initialized { if !p.initialized {
return ErrUnitialized return ErrUnitialized
} }
+4 -2
View File
@@ -1,6 +1,8 @@
package player package player
import "github.com/dweymouth/supersonic/backend/mediaprovider" import (
"github.com/dweymouth/supersonic/backend/mediaprovider"
)
type URLPlayer interface { type URLPlayer interface {
BasePlayer BasePlayer
@@ -17,7 +19,7 @@ type TrackPlayer interface {
type BasePlayer interface { type BasePlayer interface {
Continue() error Continue() error
Pause() error Pause() error
Stop() error Stop(force bool) error
SeekSeconds(secs float64) error SeekSeconds(secs float64) error
IsSeeking() bool IsSeeking() bool