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
}
func (c *playbackCommandQueue) Clear() {
c.mutex.Lock()
c.queue = nil
c.mutex.Unlock()
}
func (c *playbackCommandQueue) Stop() {
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
playbackCommand{Type: cmdStop})
+7 -8
View File
@@ -3,7 +3,6 @@ package backend
import (
"context"
"errors"
"fmt"
"log"
"math/rand"
"time"
@@ -157,15 +156,15 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error {
case player.Stopped:
// nothing
case player.Playing:
if err := p.Pause(); err != nil {
return fmt.Errorf("failed to pause: %v", err)
}
needToUnpause = true
fallthrough
case player.Paused:
p.pendingPlayerChangeTimePos = stat.TimePos
p.pendingPlayerChange = true
}
if err := p.player.Stop(true); err != nil {
log.Printf("failed to stop player: %v", err)
}
oldVol := p.player.GetVolume()
if _, isMPV := p.player.(*mpv.Player); !isMPV {
@@ -293,7 +292,7 @@ func (p *playbackEngine) IsSeeking() bool {
}
func (p *playbackEngine) Stop() error {
return p.player.Stop()
return p.player.Stop(false)
}
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 {
if insertQueueMode == Replace {
p.player.Stop()
p.player.Stop(false)
p.nowPlayingIdx = -1
p.playQueue = nil
}
@@ -358,7 +357,7 @@ func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueu
func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) {
if insertMode == Replace {
p.player.Stop()
p.player.Stop(false)
p.nowPlayingIdx = -1
p.playQueue = nil
}
@@ -384,7 +383,7 @@ func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, ins
// Stop playback and clear the play queue.
func (p *playbackEngine) StopAndClearPlayQueue() {
changed := len(p.playQueue) > 0
p.player.Stop()
p.player.Stop(false)
p.playQueue = nil
p.nowPlayingIdx = -1
if changed {
+1
View File
@@ -139,6 +139,7 @@ func (p *PlaybackManager) CurrentRemotePlayer() *RemotePlaybackDevice {
}
func (p *PlaybackManager) SetRemotePlayer(rp *RemotePlaybackDevice) error {
p.cmdQueue.Clear()
if rp == nil {
if err := p.engine.SetPlayer(p.localPlayer); err != nil {
return err
+82 -15
View File
@@ -41,7 +41,8 @@ type proxyMapEntry struct {
type DLNAPlayer struct {
player.BasePlayerCallbackImpl
destroyed bool
destroyed bool
cancelRequest context.CancelFunc
avTransport *avtransport.Client
renderControl *renderingcontrol.Client
@@ -122,15 +123,31 @@ func NewDLNAPlayer(device *device.MediaRenderer) (*DLNAPlayer, 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 {
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
}
func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata, startTime float64) error {
if d.destroyed {
return nil
}
d.ensureSetupProxy()
d.metaLock.Lock()
@@ -179,17 +196,25 @@ func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadat
}
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 {
return err
}
if err := d.avTransport.Play(context.Background()); err != nil {
if err := d.avTransport.Play(ctx); err != nil {
return err
}
return nil
}
func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadata) error {
if d.destroyed {
return nil
}
var media *avtransport.MediaItem
d.metaLock.Lock()
d.nextTrackMeta = meta
@@ -203,7 +228,11 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
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 {
d.metaLock.Lock()
d.failedToSetNext = true
@@ -214,19 +243,23 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
}
func (d *DLNAPlayer) Continue() error {
if d.state == playing {
if d.destroyed || d.state == playing {
return nil
}
ctx, cancel := context.WithCancel(context.Background())
d.cancelRequest = cancel
defer cancel()
if d.pendingSeek {
d.pendingSeek = false
err := d.avTransport.Seek(context.Background(), int(d.pendingSeekSecs))
err := d.avTransport.Seek(ctx, int(d.pendingSeekSecs))
if err != nil {
return err
}
}
if err := d.avTransport.Play(context.Background()); err != nil {
if err := d.avTransport.Play(ctx); err != nil {
return err
}
d.metaLock.Lock()
@@ -240,11 +273,14 @@ func (d *DLNAPlayer) Continue() error {
}
func (d *DLNAPlayer) Pause() error {
if d.state != playing {
if d.destroyed || d.state != playing {
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
}
d.setTrackChangeTimer(0)
@@ -254,12 +290,29 @@ func (d *DLNAPlayer) Pause() error {
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 {
case stopped:
return nil
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
}
fallthrough
@@ -276,6 +329,10 @@ func (d *DLNAPlayer) Stop() error {
}
func (d *DLNAPlayer) SeekSeconds(secs float64) error {
if d.destroyed {
return nil
}
if d.state == paused {
d.pendingSeek = true
d.pendingSeekSecs = secs
@@ -309,7 +366,9 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error {
func (d *DLNAPlayer) sendSeekCmd(secs float64) error {
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
return err
}
@@ -346,8 +405,16 @@ func (d *DLNAPlayer) curPlayPos() time.Duration {
func (d *DLNAPlayer) Destroy() {
d.destroyed = true
d.setTrackChangeTimer(0)
if d.cancelRequest != nil {
d.cancelRequest()
}
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
}
func (j *JukeboxPlayer) Stop() error {
func (j *JukeboxPlayer) Stop(_ bool) error {
if j.state == stopped {
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.
func (p *Player) Stop() error {
func (p *Player) Stop(_ bool) error {
if !p.initialized {
return ErrUnitialized
}
+4 -2
View File
@@ -1,6 +1,8 @@
package player
import "github.com/dweymouth/supersonic/backend/mediaprovider"
import (
"github.com/dweymouth/supersonic/backend/mediaprovider"
)
type URLPlayer interface {
BasePlayer
@@ -17,7 +19,7 @@ type TrackPlayer interface {
type BasePlayer interface {
Continue() error
Pause() error
Stop() error
Stop(force bool) error
SeekSeconds(secs float64) error
IsSeeking() bool