gapless DLNA working
This commit is contained in:
+2
-2
@@ -404,7 +404,7 @@ func (a *App) SetupWindowsSMTC(hwnd uintptr) {
|
|||||||
})
|
})
|
||||||
a.PlaybackManager.OnSeek(func() {
|
a.PlaybackManager.OnSeek(func() {
|
||||||
dur := a.PlaybackManager.NowPlaying().Metadata().Duration
|
dur := a.PlaybackManager.NowPlaying().Metadata().Duration
|
||||||
smtc.UpdatePosition(int(a.PlaybackManager.CurrentPlayer().GetStatus().TimePos*1000), dur*1000)
|
smtc.UpdatePosition(int(a.PlaybackManager.PlayerStatus().TimePos*1000), dur*1000)
|
||||||
})
|
})
|
||||||
a.PlaybackManager.OnPlaying(func() {
|
a.PlaybackManager.OnPlaying(func() {
|
||||||
smtc.SetEnabled(true)
|
smtc.SetEnabled(true)
|
||||||
@@ -465,7 +465,7 @@ func (a *App) Shutdown() {
|
|||||||
a.PlaybackManager.DisableCallbacks()
|
a.PlaybackManager.DisableCallbacks()
|
||||||
a.PlaybackManager.Stop() // will trigger scrobble check
|
a.PlaybackManager.Stop() // will trigger scrobble check
|
||||||
a.cancel()
|
a.cancel()
|
||||||
//a.LocalPlayer.Destroy()
|
a.LocalPlayer.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SavePlayQueueIfEnabled() {
|
func (a *App) SavePlayQueueIfEnabled() {
|
||||||
|
|||||||
+27
-15
@@ -55,9 +55,13 @@ type playbackEngine struct {
|
|||||||
|
|
||||||
// flags for handleOnTrackChange / handleOnStopped callbacks - reset to false in the callbacks
|
// flags for handleOnTrackChange / handleOnStopped callbacks - reset to false in the callbacks
|
||||||
wasStopped bool // true iff player was stopped before handleOnTrackChange invocation
|
wasStopped bool // true iff player was stopped before handleOnTrackChange invocation
|
||||||
noIncrementNextTrackChange bool // true iff the nowPlayingIndex should not be incremented on the next onTrackChange
|
|
||||||
alreadyScrobbled bool // true iff the previously-playing track was already scrobbled
|
alreadyScrobbled bool // true iff the previously-playing track was already scrobbled
|
||||||
|
|
||||||
|
// if >= 0, track number that was requested by PlayTrackAt
|
||||||
|
// onTrackChange callback should set nowPlayingIdx to this,
|
||||||
|
// and reset this to -1
|
||||||
|
pendingTrackChangeNum int
|
||||||
|
|
||||||
pendingPlayerChange bool
|
pendingPlayerChange bool
|
||||||
pendingPlayerChangeTimePos float64
|
pendingPlayerChangeTimePos float64
|
||||||
|
|
||||||
@@ -136,7 +140,7 @@ func (p *playbackEngine) registerPlayerCallbacks(pl player.BasePlayer) {
|
|||||||
func (p *playbackEngine) SetPlayer(pl player.BasePlayer) {
|
func (p *playbackEngine) SetPlayer(pl player.BasePlayer) {
|
||||||
needToUnpause := false
|
needToUnpause := false
|
||||||
|
|
||||||
stat := p.player.GetStatus()
|
stat := p.PlayerStatus()
|
||||||
switch stat.State {
|
switch stat.State {
|
||||||
case player.Stopped:
|
case player.Stopped:
|
||||||
// nothing
|
// nothing
|
||||||
@@ -149,6 +153,7 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) {
|
|||||||
needToUnpause = true
|
needToUnpause = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldVol := p.player.GetVolume()
|
||||||
p.player = pl
|
p.player = pl
|
||||||
p.registerPlayerCallbacks(pl)
|
p.registerPlayerCallbacks(pl)
|
||||||
|
|
||||||
@@ -158,6 +163,12 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) {
|
|||||||
p.pendingPlayerChange = false
|
p.pendingPlayerChange = false
|
||||||
}
|
}
|
||||||
p.invokeNoArgCallbacks(p.onPlayerChange)
|
p.invokeNoArgCallbacks(p.onPlayerChange)
|
||||||
|
vol := pl.GetVolume()
|
||||||
|
if oldVol != vol {
|
||||||
|
for _, cb := range p.onVolumeChange {
|
||||||
|
cb(vol)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *playbackEngine) PlayTrackAt(idx int) error {
|
func (p *playbackEngine) PlayTrackAt(idx int) error {
|
||||||
@@ -167,11 +178,8 @@ func (p *playbackEngine) PlayTrackAt(idx int) error {
|
|||||||
// scrobble current track if needed
|
// scrobble current track if needed
|
||||||
p.checkScrobble()
|
p.checkScrobble()
|
||||||
p.alreadyScrobbled = true
|
p.alreadyScrobbled = true
|
||||||
p.noIncrementNextTrackChange = true
|
p.pendingTrackChangeNum = idx
|
||||||
err := p.setTrack(idx, false)
|
err := p.setTrack(idx, false)
|
||||||
if err == nil {
|
|
||||||
p.nowPlayingIdx = idx
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,14 +230,14 @@ func (p *playbackEngine) CurrentPlayer() player.BasePlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *playbackEngine) SeekNext() error {
|
func (p *playbackEngine) SeekNext() error {
|
||||||
if p.CurrentPlayer().GetStatus().State == player.Stopped {
|
if p.PlayerStatus().State == player.Stopped {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return p.PlayTrackAt(p.nowPlayingIdx + 1)
|
return p.PlayTrackAt(p.nowPlayingIdx + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *playbackEngine) SeekBackOrPrevious() error {
|
func (p *playbackEngine) SeekBackOrPrevious() error {
|
||||||
if p.nowPlayingIdx == 0 || p.player.GetStatus().TimePos > 3 {
|
if p.nowPlayingIdx == 0 || p.PlayerStatus().TimePos > 3 {
|
||||||
return p.player.SeekSeconds(0)
|
return p.player.SeekSeconds(0)
|
||||||
}
|
}
|
||||||
return p.PlayTrackAt(p.nowPlayingIdx - 1)
|
return p.PlayTrackAt(p.nowPlayingIdx - 1)
|
||||||
@@ -237,7 +245,7 @@ func (p *playbackEngine) SeekBackOrPrevious() error {
|
|||||||
|
|
||||||
func (p *playbackEngine) SeekFwdBackN(n int) error {
|
func (p *playbackEngine) SeekFwdBackN(n int) error {
|
||||||
idx := p.nowPlayingIdx
|
idx := p.nowPlayingIdx
|
||||||
if n < 0 && p.player.GetStatus().TimePos > 3 {
|
if n < 0 && p.PlayerStatus().TimePos > 3 {
|
||||||
n += 1 // first seek back is just seek to beginning of current
|
n += 1 // first seek back is just seek to beginning of current
|
||||||
}
|
}
|
||||||
if n == 0 || (idx == 0 && n < 0) {
|
if n == 0 || (idx == 0 && n < 0) {
|
||||||
@@ -511,21 +519,23 @@ func (p *playbackEngine) handleOnTrackChange() {
|
|||||||
p.checkScrobble()
|
p.checkScrobble()
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.player.GetStatus().State == player.Playing {
|
if p.PlayerStatus().State == player.Playing {
|
||||||
p.playTimeStopwatch.Start()
|
p.playTimeStopwatch.Start()
|
||||||
}
|
}
|
||||||
if !p.noIncrementNextTrackChange && (p.wasStopped || p.loopMode != LoopOne) {
|
if p.pendingTrackChangeNum < 0 && (p.wasStopped || p.loopMode != LoopOne) {
|
||||||
p.nowPlayingIdx++
|
p.nowPlayingIdx++
|
||||||
if p.loopMode == LoopAll && p.nowPlayingIdx == len(p.playQueue) {
|
if p.loopMode == LoopAll && p.nowPlayingIdx == len(p.playQueue) {
|
||||||
p.nowPlayingIdx = 0 // wrapped around
|
p.nowPlayingIdx = 0 // wrapped around
|
||||||
}
|
}
|
||||||
|
} else if p.pendingTrackChangeNum >= 0 {
|
||||||
|
p.nowPlayingIdx = p.pendingTrackChangeNum
|
||||||
|
p.pendingTrackChangeNum = -1
|
||||||
}
|
}
|
||||||
nowPlaying := p.playQueue[p.nowPlayingIdx]
|
nowPlaying := p.playQueue[p.nowPlayingIdx]
|
||||||
_, isRadio := nowPlaying.(*mediaprovider.RadioStation)
|
_, isRadio := nowPlaying.(*mediaprovider.RadioStation)
|
||||||
p.isRadio = isRadio
|
p.isRadio = isRadio
|
||||||
|
|
||||||
// reset flags
|
// reset flags
|
||||||
p.noIncrementNextTrackChange = false
|
|
||||||
p.wasStopped = false
|
p.wasStopped = false
|
||||||
p.alreadyScrobbled = false
|
p.alreadyScrobbled = false
|
||||||
|
|
||||||
@@ -594,9 +604,11 @@ func (p *playbackEngine) setNextTrackAfterQueueUpdate() {
|
|||||||
func (p *playbackEngine) setTrack(idx int, next bool) error {
|
func (p *playbackEngine) setTrack(idx int, next bool) error {
|
||||||
if urlP, ok := p.player.(player.URLPlayer); ok {
|
if urlP, ok := p.player.(player.URLPlayer); ok {
|
||||||
url := ""
|
url := ""
|
||||||
|
var meta mediaprovider.MediaItemMetadata
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
var err error
|
var err error
|
||||||
item := p.playQueue[idx]
|
item := p.playQueue[idx]
|
||||||
|
meta = item.Metadata()
|
||||||
if tr, ok := item.(*mediaprovider.Track); ok {
|
if tr, ok := item.(*mediaprovider.Track); ok {
|
||||||
url, err = p.sm.Server.GetStreamURL(tr.ID, p.transcodeCfg.ForceRawFile)
|
url, err = p.sm.Server.GetStreamURL(tr.ID, p.transcodeCfg.ForceRawFile)
|
||||||
} else {
|
} else {
|
||||||
@@ -607,9 +619,9 @@ func (p *playbackEngine) setTrack(idx int, next bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if next {
|
if next {
|
||||||
return urlP.SetNextFile(url)
|
return urlP.SetNextFile(url, meta)
|
||||||
}
|
}
|
||||||
return urlP.PlayFile(url)
|
return urlP.PlayFile(url, meta)
|
||||||
} else if trP, ok := p.player.(player.TrackPlayer); ok {
|
} else if trP, ok := p.player.(player.TrackPlayer); ok {
|
||||||
var track *mediaprovider.Track
|
var track *mediaprovider.Track
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
@@ -744,7 +756,7 @@ func (p *playbackEngine) doUpdateTimePos(seeked bool) {
|
|||||||
if p.callbacksDisabled {
|
if p.callbacksDisabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s := p.player.GetStatus()
|
s := p.PlayerStatus()
|
||||||
if s.TimePos > p.latestTrackPosition {
|
if s.TimePos > p.latestTrackPosition {
|
||||||
p.latestTrackPosition = s.TimePos
|
p.latestTrackPosition = s.TimePos
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package dlna
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -9,12 +11,16 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
"github.com/dweymouth/supersonic/backend/player"
|
"github.com/dweymouth/supersonic/backend/player"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -29,9 +35,24 @@ type DLNAPlayer struct {
|
|||||||
player.BasePlayerCallbackImpl
|
player.BasePlayerCallbackImpl
|
||||||
|
|
||||||
avTransport *avtransport.Client
|
avTransport *avtransport.Client
|
||||||
|
renderControl *renderingcontrol.Client
|
||||||
|
|
||||||
state int // stopped, playing, paused
|
state int // stopped, playing, paused
|
||||||
seeking bool
|
seeking bool
|
||||||
|
|
||||||
|
curTrackMeta mediaprovider.MediaItemMetadata
|
||||||
|
nextTrackMeta mediaprovider.MediaItemMetadata
|
||||||
|
|
||||||
|
lastSeekSecs int
|
||||||
|
seekedAt time.Time
|
||||||
|
|
||||||
|
proxyServer *http.Server
|
||||||
|
proxyActive atomic.Bool
|
||||||
|
localIP string
|
||||||
|
proxyPort int
|
||||||
|
|
||||||
|
proxyURLLock sync.Mutex
|
||||||
|
proxyURLs map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDLNAPlayer(device *device.MediaRenderer) (*DLNAPlayer, error) {
|
func NewDLNAPlayer(device *device.MediaRenderer) (*DLNAPlayer, error) {
|
||||||
@@ -39,29 +60,38 @@ func NewDLNAPlayer(device *device.MediaRenderer) (*DLNAPlayer, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &DLNAPlayer{avTransport: avt}, nil
|
rc, err := device.RenderingControlClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &DLNAPlayer{
|
||||||
|
avTransport: avt,
|
||||||
|
renderControl: rc,
|
||||||
|
proxyURLs: make(map[string]string),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DLNAPlayer) SetVolume(vol int) error {
|
func (d *DLNAPlayer) SetVolume(vol int) error {
|
||||||
return unimplemented
|
return d.renderControl.SetVolume(context.Background(), vol)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DLNAPlayer) GetVolume() int {
|
func (d *DLNAPlayer) GetVolume() int {
|
||||||
return 0
|
vol, _ := d.renderControl.GetVolume(context.Background())
|
||||||
|
return vol
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DLNAPlayer) PlayFile(urlstr string) error {
|
func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata) error {
|
||||||
ensureSetupProxies()
|
d.ensureSetupProxy()
|
||||||
|
|
||||||
proxyURLLock.Lock()
|
log.Println("playing track " + meta.Name)
|
||||||
dlnaProxyCurrent.url = urlstr
|
|
||||||
proxyURLLock.Unlock()
|
d.curTrackMeta = meta
|
||||||
|
key := d.addURLToProxy(urlstr)
|
||||||
|
|
||||||
media := avtransport.MediaItem{
|
media := avtransport.MediaItem{
|
||||||
URL: "http://" + localIP + ":8080/current",
|
URL: d.urlForItem(key),
|
||||||
Title: "Supersonic media item",
|
Title: meta.Name,
|
||||||
}
|
}
|
||||||
log.Printf("URL %s", media.URL)
|
|
||||||
|
|
||||||
err := d.avTransport.SetAVTransportMedia(context.Background(), &media)
|
err := d.avTransport.SetAVTransportMedia(context.Background(), &media)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,26 +101,41 @@ func (d *DLNAPlayer) PlayFile(urlstr string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d.state = playing
|
d.state = playing
|
||||||
|
d.seekedAt = time.Now()
|
||||||
|
d.lastSeekSecs = 0
|
||||||
d.InvokeOnPlaying()
|
d.InvokeOnPlaying()
|
||||||
|
d.InvokeOnTrackChange()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DLNAPlayer) SetNextFile(url string) error {
|
func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadata) error {
|
||||||
var media *avtransport.MediaItem
|
var media *avtransport.MediaItem
|
||||||
|
d.nextTrackMeta = meta
|
||||||
if url != "" {
|
if url != "" {
|
||||||
ensureSetupProxies()
|
d.ensureSetupProxy()
|
||||||
|
|
||||||
proxyURLLock.Lock()
|
|
||||||
dlnaProxyCurrent.url = url
|
|
||||||
proxyURLLock.Unlock()
|
|
||||||
|
|
||||||
|
key := d.addURLToProxy(url)
|
||||||
media = &avtransport.MediaItem{
|
media = &avtransport.MediaItem{
|
||||||
URL: "http://" + localIP + ":8080/next",
|
URL: d.urlForItem(key),
|
||||||
|
Title: meta.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
||||||
@@ -125,6 +170,8 @@ 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.InvokeOnSeek()
|
d.InvokeOnSeek()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -141,10 +188,12 @@ func (d *DLNAPlayer) GetStatus() player.Status {
|
|||||||
state = player.Paused
|
state = player.Paused
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - the rest
|
time := time.Now().Sub(d.seekedAt) + time.Duration(d.lastSeekSecs)*time.Second
|
||||||
|
|
||||||
return player.Status{
|
return player.Status{
|
||||||
State: state,
|
State: state,
|
||||||
|
TimePos: time.Seconds(),
|
||||||
|
Duration: float64(d.curTrackMeta.Duration),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,38 +224,51 @@ func getLocalIP() (string, error) {
|
|||||||
return "", fmt.Errorf("no suitable interface found")
|
return "", fmt.Errorf("no suitable interface found")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func (d *DLNAPlayer) ensureSetupProxy() error {
|
||||||
localIP string
|
if d.proxyActive.Swap(true) {
|
||||||
proxyURLLock sync.Mutex
|
return nil // already active
|
||||||
dlnaProxyCurrent proxy
|
|
||||||
dlnaProxyNext proxy
|
|
||||||
proxyActive atomic.Bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func ensureSetupProxies() {
|
|
||||||
if proxyActive.Swap(true) {
|
|
||||||
return // already active
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localIP, _ = getLocalIP()
|
var err error
|
||||||
log.Println(localIP)
|
d.localIP, err = getLocalIP()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
listener, err := net.Listen("tcp", ":0")
|
||||||
mux.HandleFunc("/current", dlnaProxyCurrent.handleRequest)
|
if err != nil {
|
||||||
mux.HandleFunc("/next", dlnaProxyNext.handleRequest)
|
return err
|
||||||
go http.ListenAndServe(":8080", mux)
|
}
|
||||||
|
d.proxyPort = listener.Addr().(*net.TCPAddr).Port
|
||||||
|
|
||||||
|
d.proxyServer = &http.Server{
|
||||||
|
Handler: http.HandlerFunc(d.handleRequest),
|
||||||
|
}
|
||||||
|
|
||||||
|
go d.proxyServer.Serve(listener)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type proxy struct {
|
type proxy struct {
|
||||||
url string
|
url string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proxy) handleRequest(w http.ResponseWriter, r *http.Request) {
|
func (d *DLNAPlayer) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var url string
|
||||||
|
key := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
|
d.proxyURLLock.Lock()
|
||||||
|
if u, ok := d.proxyURLs[key]; ok {
|
||||||
|
url = u
|
||||||
|
}
|
||||||
|
d.proxyURLLock.Unlock()
|
||||||
|
|
||||||
|
if url == "" {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte("404"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new request to the target server
|
// Create a new request to the target server
|
||||||
proxyURLLock.Lock()
|
|
||||||
url := p.url
|
|
||||||
proxyURLLock.Unlock()
|
|
||||||
log.Println("Got request for " + url)
|
|
||||||
proxyReq, err := http.NewRequest(r.Method, url, r.Body)
|
proxyReq, err := http.NewRequest(r.Method, url, r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
"github.com/dweymouth/supersonic/backend/player"
|
"github.com/dweymouth/supersonic/backend/player"
|
||||||
"github.com/supersonic-app/go-mpv"
|
"github.com/supersonic-app/go-mpv"
|
||||||
)
|
)
|
||||||
@@ -133,7 +134,7 @@ func (p *Player) Init(maxCacheMB int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Plays the specified file, clearing the previous play queue, if any.
|
// Plays the specified file, clearing the previous play queue, if any.
|
||||||
func (p *Player) PlayFile(url string) error {
|
func (p *Player) PlayFile(url string, _ mediaprovider.MediaItemMetadata) error {
|
||||||
if !p.initialized {
|
if !p.initialized {
|
||||||
return ErrUnitialized
|
return ErrUnitialized
|
||||||
}
|
}
|
||||||
@@ -169,7 +170,7 @@ func (p *Player) Stop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) SetNextFile(url string) error {
|
func (p *Player) SetNextFile(url string, _ mediaprovider.MediaItemMetadata) error {
|
||||||
if p.lenPlaylist > p.curPlaylistPos+1 {
|
if p.lenPlaylist > p.curPlaylistPos+1 {
|
||||||
if err := p.mpv.Command([]string{"playlist-remove", strconv.Itoa(int(p.curPlaylistPos) + 1)}); err != nil {
|
if err := p.mpv.Command([]string{"playlist-remove", strconv.Itoa(int(p.curPlaylistPos) + 1)}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import "github.com/dweymouth/supersonic/backend/mediaprovider"
|
|||||||
|
|
||||||
type URLPlayer interface {
|
type URLPlayer interface {
|
||||||
BasePlayer
|
BasePlayer
|
||||||
PlayFile(url string) error
|
PlayFile(url string, metadata mediaprovider.MediaItemMetadata) error
|
||||||
SetNextFile(url string) error
|
SetNextFile(url string, metadata mediaprovider.MediaItemMetadata) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrackPlayer interface {
|
type TrackPlayer interface {
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ func (a *NowPlayingPage) Reload() {
|
|||||||
}
|
}
|
||||||
switch a.tabs.SelectedIndex() {
|
switch a.tabs.SelectedIndex() {
|
||||||
case 1: /*lyrics*/
|
case 1: /*lyrics*/
|
||||||
a.lastPlayPos = a.pm.CurrentPlayer().GetStatus().TimePos
|
a.lastPlayPos = a.pm.PlayerStatus().TimePos
|
||||||
a.updateLyrics()
|
a.updateLyrics()
|
||||||
case 2: /*related*/
|
case 2: /*related*/
|
||||||
a.updateRelatedList()
|
a.updateRelatedList()
|
||||||
@@ -527,7 +527,7 @@ func (a *NowPlayingPage) saveSelectedTab(tabNum int) {
|
|||||||
|
|
||||||
func (a *NowPlayingPage) formatStatusLine() {
|
func (a *NowPlayingPage) formatStatusLine() {
|
||||||
curPlayer := a.pm.CurrentPlayer()
|
curPlayer := a.pm.CurrentPlayer()
|
||||||
playerStats := curPlayer.GetStatus()
|
playerStats := a.pm.PlayerStatus()
|
||||||
lastStatus := a.statusLabel.Text
|
lastStatus := a.statusLabel.Text
|
||||||
stopped := lang.L("Stopped")
|
stopped := lang.L("Stopped")
|
||||||
state := stopped
|
state := stopped
|
||||||
|
|||||||
Reference in New Issue
Block a user