reorganize; add stopwatch to dlna player for play time tracking

This commit is contained in:
Drew Weymouth
2025-03-29 15:15:13 -07:00
parent e5c8027a82
commit 436df0f199
3 changed files with 61 additions and 50 deletions
+2
View File
@@ -163,6 +163,8 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) {
if needToUnpause { if needToUnpause {
p.PlayTrackAt(p.nowPlayingIdx) 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.SeekSeconds(p.pendingPlayerChangeTimePos)
p.pendingPlayerChange = false p.pendingPlayerChange = false
} }
+30 -50
View File
@@ -18,6 +18,7 @@ import (
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/backend/player" "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/device"
"github.com/supersonic-app/go-upnpcast/services/avtransport" "github.com/supersonic-app/go-upnpcast/services/avtransport"
"github.com/supersonic-app/go-upnpcast/services/renderingcontrol" "github.com/supersonic-app/go-upnpcast/services/renderingcontrol"
@@ -43,8 +44,8 @@ type DLNAPlayer struct {
curTrackMeta mediaprovider.MediaItemMetadata curTrackMeta mediaprovider.MediaItemMetadata
nextTrackMeta mediaprovider.MediaItemMetadata nextTrackMeta mediaprovider.MediaItemMetadata
lastSeekSecs int lastStartTime int
seekedAt time.Time stopwatch util.Stopwatch
proxyServer *http.Server proxyServer *http.Server
proxyActive atomic.Bool proxyActive atomic.Bool
@@ -101,8 +102,9 @@ func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadat
return err return err
} }
d.state = playing d.state = playing
d.seekedAt = time.Now() d.stopwatch.Reset()
d.lastSeekSecs = 0 d.stopwatch.Start()
d.lastStartTime = 0
d.InvokeOnPlaying() d.InvokeOnPlaying()
d.InvokeOnTrackChange() d.InvokeOnTrackChange()
return nil return nil
@@ -123,24 +125,12 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
return d.avTransport.SetNextAVTransportMedia(context.Background(), media) 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 { func (d *DLNAPlayer) Continue() error {
if err := d.avTransport.Play(context.Background()); err != nil { if err := d.avTransport.Play(context.Background()); err != nil {
return err return err
} }
d.state = playing d.state = playing
d.stopwatch.Start()
d.InvokeOnPlaying() d.InvokeOnPlaying()
return nil return nil
} }
@@ -149,6 +139,7 @@ func (d *DLNAPlayer) Pause() error {
if err := d.avTransport.Pause(context.Background()); err != nil { if err := d.avTransport.Pause(context.Background()); err != nil {
return err return err
} }
d.stopwatch.Stop()
d.state = paused d.state = paused
d.InvokeOnPaused() d.InvokeOnPaused()
return nil return nil
@@ -158,6 +149,8 @@ func (d *DLNAPlayer) Stop() error {
if err := d.avTransport.Pause(context.Background()); err != nil { if err := d.avTransport.Pause(context.Background()); err != nil {
return err return err
} }
d.stopwatch.Reset()
d.lastStartTime = 0
d.state = stopped d.state = stopped
d.InvokeOnStopped() d.InvokeOnStopped()
return nil return nil
@@ -170,8 +163,13 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error {
return err return err
} }
d.seeking = false 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() d.InvokeOnSeek()
return nil return nil
} }
@@ -188,7 +186,7 @@ func (d *DLNAPlayer) GetStatus() player.Status {
state = player.Paused 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{ return player.Status{
State: state, 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 { func (d *DLNAPlayer) ensureSetupProxy() error {
if d.proxyActive.Swap(true) { if d.proxyActive.Swap(true) {
return nil // already active return nil // already active
} }
var err error var err error
d.localIP, err = getLocalIP() d.localIP, err = util.GetLocalIP()
if err != nil { if err != nil {
return err return err
} }
@@ -255,8 +226,17 @@ func (d *DLNAPlayer) ensureSetupProxy() error {
return nil return nil
} }
type proxy struct { func (d *DLNAPlayer) addURLToProxy(url string) string {
url 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) { func (d *DLNAPlayer) handleRequest(w http.ResponseWriter, r *http.Request) {
+29
View File
@@ -1,7 +1,9 @@
package util package util
import ( import (
"fmt"
"io" "io"
"net"
"os" "os"
) )
@@ -21,3 +23,30 @@ func CopyFile(srcPath, dstPath string) error {
_, err = io.Copy(fout, fin) _, err = io.Copy(fout, fin)
return err 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")
}