more progress on radios
This commit is contained in:
@@ -424,6 +424,7 @@ func (s *subsonicMediaProvider) GetRadioStations() ([]*mediaprovider.RadioStatio
|
|||||||
}
|
}
|
||||||
return sharedutil.MapSlice(rs, func(rs *subsonic.InternetRadioStation) *mediaprovider.RadioStation {
|
return sharedutil.MapSlice(rs, func(rs *subsonic.InternetRadioStation) *mediaprovider.RadioStation {
|
||||||
return &mediaprovider.RadioStation{
|
return &mediaprovider.RadioStation{
|
||||||
|
// TODO - subsonic library is missing ID in its radiostation object. add it
|
||||||
ID: "radio-" + strings.ReplaceAll(rs.Name, " ", ""),
|
ID: "radio-" + strings.ReplaceAll(rs.Name, " ", ""),
|
||||||
Name: rs.Name,
|
Name: rs.Name,
|
||||||
HomePageURL: rs.HomePageUrl,
|
HomePageURL: rs.HomePageUrl,
|
||||||
|
|||||||
@@ -235,6 +235,31 @@ func (p *playbackEngine) LoadTracks(tracks []*mediaprovider.Track, insertQueueMo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) {
|
||||||
|
if insertMode == Replace {
|
||||||
|
p.player.Stop()
|
||||||
|
p.nowPlayingIdx = -1
|
||||||
|
p.playQueue = nil
|
||||||
|
}
|
||||||
|
needToSetNext := insertMode == InsertNext || (insertMode == Append && p.nowPlayingIdx == len(p.playQueue)-1)
|
||||||
|
insertIdx := len(p.playQueue)
|
||||||
|
if insertMode == InsertNext {
|
||||||
|
insertIdx = p.nowPlayingIdx + 1
|
||||||
|
}
|
||||||
|
new := make([]mediaprovider.MediaItem, len(p.playQueue)+1)
|
||||||
|
firstHalf := p.playQueue[:insertIdx]
|
||||||
|
copy(new, firstHalf)
|
||||||
|
new[len(firstHalf)] = radio
|
||||||
|
copy(new[len(firstHalf)+1:], p.playQueue[insertIdx:])
|
||||||
|
p.playQueue = new
|
||||||
|
|
||||||
|
if needToSetNext {
|
||||||
|
p.setNextTrack(p.nowPlayingIdx + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.invokeNoArgCallbacks(p.onQueueChange)
|
||||||
|
}
|
||||||
|
|
||||||
// Stop playback and clear the play queue.
|
// Stop playback and clear the play queue.
|
||||||
func (p *playbackEngine) StopAndClearPlayQueue() {
|
func (p *playbackEngine) StopAndClearPlayQueue() {
|
||||||
changed := len(p.playQueue) > 0
|
changed := len(p.playQueue) > 0
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ func (p *PlaybackManager) PlaySimilarSongs(id string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PlaybackManager) LoadRadioStation(station *mediaprovider.RadioStation, queueMode InsertQueueMode) {
|
||||||
|
p.engine.LoadRadioStation(station, queueMode)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) fetchAndPlayTracks(fetchFn func() ([]*mediaprovider.Track, error)) {
|
func (p *PlaybackManager) fetchAndPlayTracks(fetchFn func() ([]*mediaprovider.Track, error)) {
|
||||||
if songs, err := fetchFn(); err != nil {
|
if songs, err := fetchFn(); err != nil {
|
||||||
log.Printf("error fetching tracks: %s", err.Error())
|
log.Printf("error fetching tracks: %s", err.Error())
|
||||||
|
|||||||
@@ -140,11 +140,13 @@ func (b *BrowsingPane) AddSettingsMenuSeparator() {
|
|||||||
fyne.NewMenuItemSeparator())
|
fyne.NewMenuItemSeparator())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrowsingPane) AddNavigationButton(icon fyne.Resource, pageName controller.PageName, action func()) {
|
func (b *BrowsingPane) AddNavigationButton(icon fyne.Resource, pageName controller.PageName, action func()) *widget.Button {
|
||||||
// make a copy of the icon, because it can change the color
|
// make a copy of the icon, because it can change the color
|
||||||
browsingPaneIcon := theme.NewThemedResource(icon)
|
browsingPaneIcon := theme.NewThemedResource(icon)
|
||||||
b.navBtnsContainer.Add(widget.NewButtonWithIcon("", browsingPaneIcon, action))
|
btn := widget.NewButtonWithIcon("", browsingPaneIcon, action)
|
||||||
|
b.navBtnsContainer.Add(btn)
|
||||||
b.navBtnsPageMap[pageName] = browsingPaneIcon
|
b.navBtnsPageMap[pageName] = browsingPaneIcon
|
||||||
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BrowsingPane) DisableNavigationButtons() {
|
func (b *BrowsingPane) DisableNavigationButtons() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dweymouth/supersonic/backend"
|
||||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
"github.com/dweymouth/supersonic/sharedutil"
|
"github.com/dweymouth/supersonic/sharedutil"
|
||||||
"github.com/dweymouth/supersonic/ui/controller"
|
"github.com/dweymouth/supersonic/ui/controller"
|
||||||
@@ -25,6 +26,7 @@ type RadiosPage struct {
|
|||||||
|
|
||||||
contr *controller.Controller
|
contr *controller.Controller
|
||||||
rp mediaprovider.RadioProvider
|
rp mediaprovider.RadioProvider
|
||||||
|
pm *backend.PlaybackManager
|
||||||
radios []*mediaprovider.RadioStation
|
radios []*mediaprovider.RadioStation
|
||||||
list *RadioList
|
list *RadioList
|
||||||
|
|
||||||
@@ -33,19 +35,24 @@ type RadiosPage struct {
|
|||||||
searcher *widgets.SearchEntry
|
searcher *widgets.SearchEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRadiosPage(contr *controller.Controller, rp mediaprovider.RadioProvider) *RadiosPage {
|
func NewRadiosPage(contr *controller.Controller, rp mediaprovider.RadioProvider, pm *backend.PlaybackManager) *RadiosPage {
|
||||||
return newRadiosPage(contr, rp, "", 0)
|
return newRadiosPage(contr, rp, pm, "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRadiosPage(contr *controller.Controller, rp mediaprovider.RadioProvider, searchText string, scrollPos float32) *RadiosPage {
|
func newRadiosPage(contr *controller.Controller, rp mediaprovider.RadioProvider, pm *backend.PlaybackManager, searchText string, scrollPos float32) *RadiosPage {
|
||||||
a := &RadiosPage{
|
a := &RadiosPage{
|
||||||
contr: contr,
|
contr: contr,
|
||||||
rp: rp,
|
rp: rp,
|
||||||
|
pm: pm,
|
||||||
titleDisp: widget.NewRichTextWithText("Internet Radio Stations"),
|
titleDisp: widget.NewRichTextWithText("Internet Radio Stations"),
|
||||||
}
|
}
|
||||||
a.ExtendBaseWidget(a)
|
a.ExtendBaseWidget(a)
|
||||||
a.titleDisp.Segments[0].(*widget.TextSegment).Style.SizeName = theme.SizeNameHeadingText
|
a.titleDisp.Segments[0].(*widget.TextSegment).Style.SizeName = theme.SizeNameHeadingText
|
||||||
a.list = NewRadioList()
|
a.list = NewRadioList()
|
||||||
|
a.list.OnPlay = func(station *mediaprovider.RadioStation) {
|
||||||
|
pm.LoadRadioStation(station, backend.Replace)
|
||||||
|
pm.PlayFromBeginning()
|
||||||
|
}
|
||||||
a.searcher = widgets.NewSearchEntry()
|
a.searcher = widgets.NewSearchEntry()
|
||||||
a.searcher.PlaceHolder = "Search page"
|
a.searcher.PlaceHolder = "Search page"
|
||||||
a.searcher.OnSearched = a.onSearched
|
a.searcher.OnSearched = a.onSearched
|
||||||
@@ -105,9 +112,7 @@ func (a *RadiosPage) Scroll(amount float32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *RadiosPage) Route() controller.Route {
|
func (a *RadiosPage) Route() controller.Route {
|
||||||
// TODO
|
return controller.RadiosRoute()
|
||||||
//return controller.RadiosRoute()
|
|
||||||
return controller.Route{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *RadiosPage) Reload() {
|
func (a *RadiosPage) Reload() {
|
||||||
@@ -118,6 +123,7 @@ func (a *RadiosPage) Save() SavedPage {
|
|||||||
return &savedrRadiosPage{
|
return &savedrRadiosPage{
|
||||||
contr: a.contr,
|
contr: a.contr,
|
||||||
rp: a.rp,
|
rp: a.rp,
|
||||||
|
pm: a.pm,
|
||||||
searchText: a.searcher.Entry.Text,
|
searchText: a.searcher.Entry.Text,
|
||||||
scrollPos: a.list.list.GetScrollOffset(),
|
scrollPos: a.list.list.GetScrollOffset(),
|
||||||
}
|
}
|
||||||
@@ -126,12 +132,13 @@ func (a *RadiosPage) Save() SavedPage {
|
|||||||
type savedrRadiosPage struct {
|
type savedrRadiosPage struct {
|
||||||
contr *controller.Controller
|
contr *controller.Controller
|
||||||
rp mediaprovider.RadioProvider
|
rp mediaprovider.RadioProvider
|
||||||
|
pm *backend.PlaybackManager
|
||||||
searchText string
|
searchText string
|
||||||
scrollPos float32
|
scrollPos float32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *savedrRadiosPage) Restore() Page {
|
func (s *savedrRadiosPage) Restore() Page {
|
||||||
return newRadiosPage(s.contr, s.rp, s.searchText, s.scrollPos)
|
return newRadiosPage(s.contr, s.rp, s.pm, s.searchText, s.scrollPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *RadiosPage) buildContainer() {
|
func (a *RadiosPage) buildContainer() {
|
||||||
@@ -150,7 +157,7 @@ func (a *RadiosPage) CreateRenderer() fyne.WidgetRenderer {
|
|||||||
type RadioList struct {
|
type RadioList struct {
|
||||||
widget.BaseWidget
|
widget.BaseWidget
|
||||||
|
|
||||||
OnPlay func(string)
|
OnPlay func(*mediaprovider.RadioStation)
|
||||||
|
|
||||||
radios []*mediaprovider.RadioStation
|
radios []*mediaprovider.RadioStation
|
||||||
|
|
||||||
@@ -228,7 +235,7 @@ func (g *RadioList) SetRadios(radios []*mediaprovider.RadioStation) {
|
|||||||
|
|
||||||
func (a *RadioList) onPlayRadio(item *mediaprovider.RadioStation) {
|
func (a *RadioList) onPlayRadio(item *mediaprovider.RadioStation) {
|
||||||
if a.OnPlay != nil {
|
if a.OnPlay != nil {
|
||||||
a.OnPlay(item.ID)
|
a.OnPlay(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ func (r Router) CreatePage(rte controller.Route) Page {
|
|||||||
return NewPlaylistsPage(r.Controller, r.widgetPool, &r.App.Config.PlaylistsPage, r.App.ServerManager.Server)
|
return NewPlaylistsPage(r.Controller, r.widgetPool, &r.App.Config.PlaylistsPage, r.App.ServerManager.Server)
|
||||||
case controller.Tracks:
|
case controller.Tracks:
|
||||||
return NewTracksPage(r.Controller, &r.App.Config.TracksPage, r.widgetPool, r.App.ServerManager.Server, r.App.ImageManager)
|
return NewTracksPage(r.Controller, &r.App.Config.TracksPage, r.widgetPool, r.App.ServerManager.Server, r.App.ImageManager)
|
||||||
|
case controller.Radios:
|
||||||
|
var rp mediaprovider.RadioProvider
|
||||||
|
rp, _ = r.App.ServerManager.Server.(mediaprovider.RadioProvider)
|
||||||
|
return NewRadiosPage(r.Controller, rp, r.App.PlaybackManager)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const (
|
|||||||
Playlist
|
Playlist
|
||||||
Playlists
|
Playlists
|
||||||
Tracks
|
Tracks
|
||||||
|
Radios
|
||||||
)
|
)
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
@@ -61,6 +62,10 @@ func ArtistsRoute() Route {
|
|||||||
return Route{Page: Artists}
|
return Route{Page: Artists}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RadiosRoute() Route {
|
||||||
|
return Route{Page: Radios}
|
||||||
|
}
|
||||||
|
|
||||||
func NowPlayingRoute(highlightedTrackID string) Route {
|
func NowPlayingRoute(highlightedTrackID string) Route {
|
||||||
return Route{Page: NowPlaying, Arg: highlightedTrackID}
|
return Route{Page: NowPlaying, Arg: highlightedTrackID}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ type MainWindow struct {
|
|||||||
theme *theme.MyTheme
|
theme *theme.MyTheme
|
||||||
haveSystemTray bool
|
haveSystemTray bool
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
|
|
||||||
|
// needs to bes shown/hidden when switching between servers based on whether they support radio
|
||||||
|
radioBtn *widget.Button
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, app *backend.App, size fyne.Size) MainWindow {
|
func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, app *backend.App, size fyne.Size) MainWindow {
|
||||||
@@ -183,6 +186,11 @@ func (m *MainWindow) RunOnServerConnectedTasks(app *backend.App, displayAppName
|
|||||||
}
|
}
|
||||||
m.App.Config.Application.LastCheckedVersion = t
|
m.App.Config.Application.LastCheckedVersion = t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, supportsRadio := m.App.ServerManager.Server.(mediaprovider.RadioProvider)
|
||||||
|
m.radioBtn.Hidden = !supportsRadio
|
||||||
|
m.radioBtn.Refresh()
|
||||||
|
|
||||||
m.App.SaveConfigFile()
|
m.App.SaveConfigFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +273,9 @@ func (m *MainWindow) addNavigationButtons() {
|
|||||||
m.BrowsingPane.AddNavigationButton(theme.TracksIcon, controller.Tracks, func() {
|
m.BrowsingPane.AddNavigationButton(theme.TracksIcon, controller.Tracks, func() {
|
||||||
m.Router.NavigateTo(controller.TracksRoute())
|
m.Router.NavigateTo(controller.TracksRoute())
|
||||||
})
|
})
|
||||||
|
m.radioBtn = m.BrowsingPane.AddNavigationButton(theme.TracksIcon /*todo*/, controller.Radios, func() {
|
||||||
|
m.Router.NavigateTo(controller.RadiosRoute())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MainWindow) addShortcuts() {
|
func (m *MainWindow) addShortcuts() {
|
||||||
|
|||||||
Reference in New Issue
Block a user