reorganize; add stopwatch to dlna player for play time tracking
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user