From 436df0f1996c28b5494547071960a05b07546d9c Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 16 Mar 2025 08:54:18 -0700 Subject: [PATCH] reorganize; add stopwatch to dlna player for play time tracking --- backend/playbackengine.go | 2 + backend/player/dlna/dlnaplayer.go | 80 ++++++++++++------------------- backend/util/util.go | 29 +++++++++++ 3 files changed, 61 insertions(+), 50 deletions(-) diff --git a/backend/playbackengine.go b/backend/playbackengine.go index 9ae9a5d..3cd5854 100644 --- a/backend/playbackengine.go +++ b/backend/playbackengine.go @@ -163,6 +163,8 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) { if needToUnpause { p.PlayTrackAt(p.nowPlayingIdx) + // TODO - find a better way to wait until the players are ready + time.Sleep(200 * time.Millisecond) p.SeekSeconds(p.pendingPlayerChangeTimePos) p.pendingPlayerChange = false } diff --git a/backend/player/dlna/dlnaplayer.go b/backend/player/dlna/dlnaplayer.go index b9410ac..49279c4 100644 --- a/backend/player/dlna/dlnaplayer.go +++ b/backend/player/dlna/dlnaplayer.go @@ -18,6 +18,7 @@ import ( "github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/player" + "github.com/dweymouth/supersonic/backend/util" "github.com/supersonic-app/go-upnpcast/device" "github.com/supersonic-app/go-upnpcast/services/avtransport" "github.com/supersonic-app/go-upnpcast/services/renderingcontrol" @@ -43,8 +44,8 @@ type DLNAPlayer struct { curTrackMeta mediaprovider.MediaItemMetadata nextTrackMeta mediaprovider.MediaItemMetadata - lastSeekSecs int - seekedAt time.Time + lastStartTime int + stopwatch util.Stopwatch proxyServer *http.Server proxyActive atomic.Bool @@ -101,8 +102,9 @@ func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadat return err } d.state = playing - d.seekedAt = time.Now() - d.lastSeekSecs = 0 + d.stopwatch.Reset() + d.stopwatch.Start() + d.lastStartTime = 0 d.InvokeOnPlaying() d.InvokeOnTrackChange() return nil @@ -123,24 +125,12 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat return d.avTransport.SetNextAVTransportMedia(context.Background(), media) } -func (d *DLNAPlayer) addURLToProxy(url string) string { - hash := md5.Sum([]byte(url)) - key := base64.StdEncoding.EncodeToString(hash[:]) - d.proxyURLLock.Lock() - d.proxyURLs[key] = url - d.proxyURLLock.Unlock() - return key -} - -func (d *DLNAPlayer) urlForItem(key string) string { - return fmt.Sprintf("http://%s:%d/%s", d.localIP, d.proxyPort, key) -} - func (d *DLNAPlayer) Continue() error { if err := d.avTransport.Play(context.Background()); err != nil { return err } d.state = playing + d.stopwatch.Start() d.InvokeOnPlaying() return nil } @@ -149,6 +139,7 @@ func (d *DLNAPlayer) Pause() error { if err := d.avTransport.Pause(context.Background()); err != nil { return err } + d.stopwatch.Stop() d.state = paused d.InvokeOnPaused() return nil @@ -158,6 +149,8 @@ func (d *DLNAPlayer) Stop() error { if err := d.avTransport.Pause(context.Background()); err != nil { return err } + d.stopwatch.Reset() + d.lastStartTime = 0 d.state = stopped d.InvokeOnStopped() return nil @@ -170,8 +163,13 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error { return err } d.seeking = false - d.seekedAt = time.Now() - d.lastSeekSecs = int(secs) + + d.lastStartTime = int(secs) + d.stopwatch.Reset() + if d.state == playing { + d.stopwatch.Start() + } + d.InvokeOnSeek() return nil } @@ -188,7 +186,7 @@ func (d *DLNAPlayer) GetStatus() player.Status { state = player.Paused } - time := time.Now().Sub(d.seekedAt) + time.Duration(d.lastSeekSecs)*time.Second + time := time.Duration(d.lastStartTime)*time.Second + d.stopwatch.Elapsed() return player.Status{ State: state, @@ -203,40 +201,13 @@ func (d *DLNAPlayer) Destroy() { } } -func getLocalIP() (string, error) { - interfaces, err := net.Interfaces() - if err != nil { - return "", err - } - - for _, iface := range interfaces { - if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { - continue - } - - addrs, err := iface.Addrs() - if err != nil { - return "", err - } - - for _, addr := range addrs { - ipnet, ok := addr.(*net.IPNet) - if ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil { - return ipnet.IP.String(), nil - } - } - } - - return "", fmt.Errorf("no suitable interface found") -} - func (d *DLNAPlayer) ensureSetupProxy() error { if d.proxyActive.Swap(true) { return nil // already active } var err error - d.localIP, err = getLocalIP() + d.localIP, err = util.GetLocalIP() if err != nil { return err } @@ -255,8 +226,17 @@ func (d *DLNAPlayer) ensureSetupProxy() error { return nil } -type proxy struct { - url string +func (d *DLNAPlayer) addURLToProxy(url string) string { + hash := md5.Sum([]byte(url)) + key := base64.StdEncoding.EncodeToString(hash[:]) + d.proxyURLLock.Lock() + d.proxyURLs[key] = url + d.proxyURLLock.Unlock() + return key +} + +func (d *DLNAPlayer) urlForItem(key string) string { + return fmt.Sprintf("http://%s:%d/%s", d.localIP, d.proxyPort, key) } func (d *DLNAPlayer) handleRequest(w http.ResponseWriter, r *http.Request) { diff --git a/backend/util/util.go b/backend/util/util.go index 9111db8..bf573c8 100644 --- a/backend/util/util.go +++ b/backend/util/util.go @@ -1,7 +1,9 @@ package util import ( + "fmt" "io" + "net" "os" ) @@ -21,3 +23,30 @@ func CopyFile(srcPath, dstPath string) error { _, err = io.Copy(fout, fin) return err } + +func GetLocalIP() (string, error) { + interfaces, err := net.Interfaces() + if err != nil { + return "", err + } + + for _, iface := range interfaces { + if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { + continue + } + + addrs, err := iface.Addrs() + if err != nil { + return "", err + } + + for _, addr := range addrs { + ipnet, ok := addr.(*net.IPNet) + if ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil { + return ipnet.IP.String(), nil + } + } + } + + return "", fmt.Errorf("no suitable interface found") +}