Merge pull request #352 from natilou/similar-songs
feat: Add Song radio option in tracklist.
This commit is contained in:
@@ -452,3 +452,11 @@ func (j *jellyfinMediaProvider) fillPlaylist(p *jellyfin.Playlist, pl *mediaprov
|
|||||||
pl.Owner = j.client.LoggedInUser()
|
pl.Owner = j.client.LoggedInUser()
|
||||||
pl.Public = false
|
pl.Public = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *jellyfinMediaProvider) GetSongRadio(trackID string, count int) ([]*mediaprovider.Track, error) {
|
||||||
|
tr, err := j.client.GetInstantMix(trackID, jellyfin.TypeSong, count)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sharedutil.MapSlice(tr, toTrack), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -200,6 +200,10 @@ type SupportsSharing interface {
|
|||||||
CanShareArtists() bool
|
CanShareArtists() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SupportsSongRadio interface {
|
||||||
|
GetSongRadio(trackID string, count int) ([]*Track, error)
|
||||||
|
}
|
||||||
|
|
||||||
type LyricsProvider interface {
|
type LyricsProvider interface {
|
||||||
GetLyrics(track *Track) (*Lyrics, error)
|
GetLyrics(track *Track) (*Lyrics, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -543,3 +543,11 @@ func fillPlaylist(pl *subsonic.Playlist, playlist *mediaprovider.Playlist) {
|
|||||||
playlist.TrackCount = pl.SongCount
|
playlist.TrackCount = pl.SongCount
|
||||||
playlist.Duration = pl.Duration
|
playlist.Duration = pl.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) GetSongRadio(trackID string, count int) ([]*mediaprovider.Track, error) {
|
||||||
|
tr, err := s.client.GetSimilarSongs(trackID, map[string]string{"count": strconv.Itoa(count)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sharedutil.MapSlice(tr, toTrack), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ var ResFilterSvg = &fyne.StaticResource{
|
|||||||
StaticContent: []byte(
|
StaticContent: []byte(
|
||||||
"<svg fill=\"#000000\" version=\"1.1\" id=\"Capa_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" \r\n\t width=\"800px\" height=\"800px\" viewBox=\"0 0 971.986 971.986\"\r\n\t xml:space=\"preserve\">\r\n<g>\r\n\t<path d=\"M370.216,459.3c10.2,11.1,15.8,25.6,15.8,40.6v442c0,26.601,32.1,40.101,51.1,21.4l123.3-141.3\r\n\t\tc16.5-19.8,25.6-29.601,25.6-49.2V500c0-15,5.7-29.5,15.8-40.601L955.615,75.5c26.5-28.8,6.101-75.5-33.1-75.5h-873\r\n\t\tc-39.2,0-59.7,46.6-33.1,75.5L370.216,459.3z\"/>\r\n</g>\r\n</svg>\r\n"),
|
"<svg fill=\"#000000\" version=\"1.1\" id=\"Capa_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" \r\n\t width=\"800px\" height=\"800px\" viewBox=\"0 0 971.986 971.986\"\r\n\t xml:space=\"preserve\">\r\n<g>\r\n\t<path d=\"M370.216,459.3c10.2,11.1,15.8,25.6,15.8,40.6v442c0,26.601,32.1,40.101,51.1,21.4l123.3-141.3\r\n\t\tc16.5-19.8,25.6-29.601,25.6-49.2V500c0-15,5.7-29.5,15.8-40.601L955.615,75.5c26.5-28.8,6.101-75.5-33.1-75.5h-873\r\n\t\tc-39.2,0-59.7,46.6-33.1,75.5L370.216,459.3z\"/>\r\n</g>\r\n</svg>\r\n"),
|
||||||
}
|
}
|
||||||
|
var ResBroadcastSvg = &fyne.StaticResource{
|
||||||
|
StaticName: "broadcast.svg",
|
||||||
|
StaticContent: []byte(
|
||||||
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<svg width=\"800px\" height=\"800px\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4.92893 2.92896L6.34315 4.34317C4.89543 5.79088 4 7.79088 4 10C4 12.2092 4.89543 14.2092 6.34315 15.6569L4.92893 17.0711C3.11929 15.2614 2 12.7614 2 10C2 7.2386 3.11929 4.7386 4.92893 2.92896ZM19.0711 2.92896C20.8807 4.7386 22 7.2386 22 10C22 12.7614 20.8807 15.2614 19.0711 17.0711L17.6569 15.6569C19.1046 14.2092 20 12.2092 20 10C20 7.79088 19.1046 5.79088 17.6569 4.34317L19.0711 2.92896ZM7.75736 5.75738L9.17157 7.1716C8.44771 7.89545 8 8.89545 8 10C8 11.1046 8.44771 12.1046 9.17157 12.8285L7.75736 14.2427C6.67157 13.1569 6 11.6569 6 10C6 8.34317 6.67157 6.84317 7.75736 5.75738ZM16.2426 5.75738C17.3284 6.84317 18 8.34317 18 10C18 11.6569 17.3284 13.1569 16.2426 14.2427L14.8284 12.8285C15.5523 12.1046 16 11.1046 16 10C16 8.89545 15.5523 7.89545 14.8284 7.1716L16.2426 5.75738ZM12 12C10.8954 12 10 11.1046 10 10C10 8.89545 10.8954 8.00002 12 8.00002C13.1046 8.00002 14 8.89545 14 10C14 11.1046 13.1046 12 12 12ZM12 14C12.5798 14 13.0774 14.413 13.1843 14.9829L14.5 22H9.5L10.8157 14.9829C10.9226 14.413 11.4202 14 12 14Z\"/>\n</svg>"),
|
||||||
|
}
|
||||||
var ResRepeatSvg = &fyne.StaticResource{
|
var ResRepeatSvg = &fyne.StaticResource{
|
||||||
StaticName: "repeat.svg",
|
StaticName: "repeat.svg",
|
||||||
StaticContent: []byte(
|
StaticContent: []byte(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ fyne bundle -append -prefix Res icons/publicdomain/theatermasks.svg >> bundled.g
|
|||||||
fyne bundle -append -prefix Res icons/publicdomain/grid.svg >> bundled.go
|
fyne bundle -append -prefix Res icons/publicdomain/grid.svg >> bundled.go
|
||||||
fyne bundle -append -prefix Res icons/publicdomain/list.svg >> bundled.go
|
fyne bundle -append -prefix Res icons/publicdomain/list.svg >> bundled.go
|
||||||
fyne bundle -append -prefix Res icons/publicdomain/filter.svg >> bundled.go
|
fyne bundle -append -prefix Res icons/publicdomain/filter.svg >> bundled.go
|
||||||
|
fyne bundle -append -prefix Res icons/remix_design/broadcast.svg >> bundled.go
|
||||||
fyne bundle -append -prefix Res icons/remix_design/repeat.svg >> bundled.go
|
fyne bundle -append -prefix Res icons/remix_design/repeat.svg >> bundled.go
|
||||||
fyne bundle -append -prefix Res icons/remix_design/repeatone.svg >> bundled.go
|
fyne bundle -append -prefix Res icons/remix_design/repeatone.svg >> bundled.go
|
||||||
fyne bundle -append -prefix Res icons/remix_design/shuffle.svg >> bundled.go
|
fyne bundle -append -prefix Res icons/remix_design/shuffle.svg >> bundled.go
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.92893 2.92896L6.34315 4.34317C4.89543 5.79088 4 7.79088 4 10C4 12.2092 4.89543 14.2092 6.34315 15.6569L4.92893 17.0711C3.11929 15.2614 2 12.7614 2 10C2 7.2386 3.11929 4.7386 4.92893 2.92896ZM19.0711 2.92896C20.8807 4.7386 22 7.2386 22 10C22 12.7614 20.8807 15.2614 19.0711 17.0711L17.6569 15.6569C19.1046 14.2092 20 12.2092 20 10C20 7.79088 19.1046 5.79088 17.6569 4.34317L19.0711 2.92896ZM7.75736 5.75738L9.17157 7.1716C8.44771 7.89545 8 8.89545 8 10C8 11.1046 8.44771 12.1046 9.17157 12.8285L7.75736 14.2427C6.67157 13.1569 6 11.6569 6 10C6 8.34317 6.67157 6.84317 7.75736 5.75738ZM16.2426 5.75738C17.3284 6.84317 18 8.34317 18 10C18 11.6569 17.3284 13.1569 16.2426 14.2427L14.8284 12.8285C15.5523 12.1046 16 11.1046 16 10C16 8.89545 15.5523 7.89545 14.8284 7.1716L16.2426 5.75738ZM12 12C10.8954 12 10 11.1046 10 10C10 8.89545 10.8954 8.00002 12 8.00002C13.1046 8.00002 14 8.89545 14 10C14 11.1046 13.1046 12 12 12ZM12 14C12.5798 14 13.0774 14.413 13.1843 14.9829L14.5 22H9.5L10.8157 14.9829C10.9226 14.413 11.4202 14 12 14Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -95,8 +95,10 @@ func newAlbumPage(
|
|||||||
a.tracklist.SetSorting(sort)
|
a.tracklist.SetSorting(sort)
|
||||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||||
|
_, canSongRadio := a.mp.(mediaprovider.SupportsSongRadio)
|
||||||
a.tracklist.Options.DisableRating = !canRate
|
a.tracklist.Options.DisableRating = !canRate
|
||||||
a.tracklist.Options.DisableSharing = !canShare
|
a.tracklist.Options.DisableSharing = !canShare
|
||||||
|
a.tracklist.Options.DisableSongRadio = !canSongRadio
|
||||||
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||||
a.cfg.TracklistColumns = cols
|
a.cfg.TracklistColumns = cols
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,8 +238,10 @@ func (a *ArtistPage) showTopTracks() {
|
|||||||
tl.Options = widgets.TracklistOptions{AutoNumber: true}
|
tl.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||||
|
_, canSongRadio := a.mp.(mediaprovider.SupportsSongRadio)
|
||||||
tl.Options.DisableRating = !canRate
|
tl.Options.DisableRating = !canRate
|
||||||
tl.Options.DisableSharing = !canShare
|
tl.Options.DisableSharing = !canShare
|
||||||
|
tl.Options.DisableSongRadio = !canSongRadio
|
||||||
tl.SetVisibleColumns(a.cfg.TracklistColumns)
|
tl.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||||
tl.SetSorting(a.trackSort)
|
tl.SetSorting(a.trackSort)
|
||||||
tl.OnVisibleColumnsChanged = func(cols []string) {
|
tl.OnVisibleColumnsChanged = func(cols []string) {
|
||||||
|
|||||||
@@ -403,8 +403,10 @@ func (a *FavoritesPage) onShowFavoriteSongs() {
|
|||||||
tracklist.Options = widgets.TracklistOptions{AutoNumber: true}
|
tracklist.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||||
|
_, canSongRadio := a.mp.(mediaprovider.SupportsSongRadio)
|
||||||
tracklist.Options.DisableRating = !canRate
|
tracklist.Options.DisableRating = !canRate
|
||||||
tracklist.Options.DisableSharing = !canShare
|
tracklist.Options.DisableSharing = !canShare
|
||||||
|
tracklist.Options.DisableSongRadio = !canSongRadio
|
||||||
tracklist.SetVisibleColumns(a.cfg.TracklistColumns)
|
tracklist.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||||
tracklist.SetSorting(a.trackSort)
|
tracklist.SetSorting(a.trackSort)
|
||||||
tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||||
|
|||||||
@@ -88,11 +88,13 @@ func newPlaylistPage(
|
|||||||
}
|
}
|
||||||
_, canRate := a.sm.Server.(mediaprovider.SupportsRating)
|
_, canRate := a.sm.Server.(mediaprovider.SupportsRating)
|
||||||
_, canShare := a.sm.Server.(mediaprovider.SupportsSharing)
|
_, canShare := a.sm.Server.(mediaprovider.SupportsSharing)
|
||||||
|
_, canSongRadio := a.sm.Server.(mediaprovider.SupportsSongRadio)
|
||||||
remove := fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist)
|
remove := fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist)
|
||||||
remove.Icon = theme.ContentClearIcon()
|
remove.Icon = theme.ContentClearIcon()
|
||||||
a.tracklist.Options = widgets.TracklistOptions{
|
a.tracklist.Options = widgets.TracklistOptions{
|
||||||
DisableRating: !canRate,
|
DisableRating: !canRate,
|
||||||
DisableSharing: !canShare,
|
DisableSharing: !canShare,
|
||||||
|
DisableSongRadio: !canSongRadio,
|
||||||
AuxiliaryMenuItems: []*fyne.MenuItem{
|
AuxiliaryMenuItems: []*fyne.MenuItem{
|
||||||
util.NewReorderTracksSubmenu(a.doSetNewTrackOrder),
|
util.NewReorderTracksSubmenu(a.doSetNewTrackOrder),
|
||||||
remove,
|
remove,
|
||||||
|
|||||||
+19
-15
@@ -34,13 +34,14 @@ type TracksPage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type tracksPageState struct {
|
type tracksPageState struct {
|
||||||
searchText string
|
searchText string
|
||||||
widgetPool *util.WidgetPool
|
widgetPool *util.WidgetPool
|
||||||
contr *controller.Controller
|
contr *controller.Controller
|
||||||
conf *backend.TracksPageConfig
|
conf *backend.TracksPageConfig
|
||||||
mp mediaprovider.MediaProvider
|
mp mediaprovider.MediaProvider
|
||||||
canRate bool
|
canRate bool
|
||||||
canShare bool
|
canShare bool
|
||||||
|
canSongRadio bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig, pool *util.WidgetPool, mp mediaprovider.MediaProvider) *TracksPage {
|
func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig, pool *util.WidgetPool, mp mediaprovider.MediaProvider) *TracksPage {
|
||||||
@@ -50,11 +51,13 @@ func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig,
|
|||||||
t.tracklist = t.obtainTracklist()
|
t.tracklist = t.obtainTracklist()
|
||||||
_, t.canRate = mp.(mediaprovider.SupportsRating)
|
_, t.canRate = mp.(mediaprovider.SupportsRating)
|
||||||
_, t.canShare = mp.(mediaprovider.SupportsSharing)
|
_, t.canShare = mp.(mediaprovider.SupportsSharing)
|
||||||
|
_, t.canSongRadio = mp.(mediaprovider.SupportsSongRadio)
|
||||||
t.tracklist.Options = widgets.TracklistOptions{
|
t.tracklist.Options = widgets.TracklistOptions{
|
||||||
DisableSorting: true,
|
DisableSorting: true,
|
||||||
DisableRating: !t.canRate,
|
DisableRating: !t.canRate,
|
||||||
DisableSharing: !t.canShare,
|
DisableSharing: !t.canShare,
|
||||||
AutoNumber: true,
|
DisableSongRadio: !t.canSongRadio,
|
||||||
|
AutoNumber: true,
|
||||||
}
|
}
|
||||||
t.tracklist.SetVisibleColumns(conf.TracklistColumns)
|
t.tracklist.SetVisibleColumns(conf.TracklistColumns)
|
||||||
t.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
t.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||||
@@ -137,10 +140,11 @@ func (t *TracksPage) doSearch(query string) {
|
|||||||
if t.searchTracklist == nil {
|
if t.searchTracklist == nil {
|
||||||
t.searchTracklist = t.obtainTracklist()
|
t.searchTracklist = t.obtainTracklist()
|
||||||
t.searchTracklist.Options = widgets.TracklistOptions{
|
t.searchTracklist.Options = widgets.TracklistOptions{
|
||||||
AutoNumber: true,
|
AutoNumber: true,
|
||||||
DisableSorting: true,
|
DisableSorting: true,
|
||||||
DisableRating: !t.canRate,
|
DisableRating: !t.canRate,
|
||||||
DisableSharing: !t.canShare,
|
DisableSharing: !t.canShare,
|
||||||
|
DisableSongRadio: !t.canSongRadio,
|
||||||
}
|
}
|
||||||
t.searchTracklist.SetVisibleColumns(t.conf.TracklistColumns)
|
t.searchTracklist.SetVisibleColumns(t.conf.TracklistColumns)
|
||||||
t.searchTracklist.SetNowPlaying(t.nowPlayingID)
|
t.searchTracklist.SetNowPlaying(t.nowPlayingID)
|
||||||
|
|||||||
@@ -145,6 +145,20 @@ func (m *Controller) connectTracklistActionsWithReplayGainMode(tracklist *widget
|
|||||||
tracklist.OnShare = func(trackID string) {
|
tracklist.OnShare = func(trackID string) {
|
||||||
go m.ShowShareDialog(trackID)
|
go m.ShowShareDialog(trackID)
|
||||||
}
|
}
|
||||||
|
tracklist.OnPlaySongRadio = func(track *mediaprovider.Track) {
|
||||||
|
go func() {
|
||||||
|
tracks, err := m.GetSongRadioTracks(track)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error getting song radio: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.App.PlaybackManager.LoadTracks(tracks, false, false)
|
||||||
|
if m.App.Config.ReplayGain.Mode == backend.ReplayGainAuto {
|
||||||
|
m.App.PlaybackManager.SetReplayGainMode(mode)
|
||||||
|
}
|
||||||
|
m.App.PlaybackManager.PlayFromBeginning()
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Controller) ConnectAlbumGridActions(grid *widgets.GridView) {
|
func (m *Controller) ConnectAlbumGridActions(grid *widgets.GridView) {
|
||||||
@@ -868,3 +882,23 @@ func (c *Controller) ShowAlbumInfoDialog(albumID, albumName string, albumCover i
|
|||||||
pop.Show()
|
pop.Show()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) GetSongRadioTracks(sourceTrack *mediaprovider.Track) ([]*mediaprovider.Track, error) {
|
||||||
|
r, ok := c.App.ServerManager.Server.(mediaprovider.SupportsSongRadio)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("server does not support song radio")
|
||||||
|
}
|
||||||
|
|
||||||
|
radioTracks, err := r.GetSongRadio(sourceTrack.ID, 100)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting song radio: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// The goal of this implementation is to place the source track first in the queue.
|
||||||
|
filteredTracks := sharedutil.FilterSlice(radioTracks, func(track *mediaprovider.Track) bool {
|
||||||
|
return track.ID != sourceTrack.ID
|
||||||
|
})
|
||||||
|
tracks := []*mediaprovider.Track{sourceTrack}
|
||||||
|
tracks = append(tracks, filteredTracks...)
|
||||||
|
return tracks, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
AlbumIcon fyne.Resource = theme.NewThemedResource(res.ResDiscSvg)
|
AlbumIcon fyne.Resource = theme.NewThemedResource(res.ResDiscSvg)
|
||||||
ArtistIcon fyne.Resource = theme.NewThemedResource(res.ResPeopleSvg)
|
ArtistIcon fyne.Resource = theme.NewThemedResource(res.ResPeopleSvg)
|
||||||
|
BroadcastIcon fyne.Resource = theme.NewThemedResource(res.ResBroadcastSvg)
|
||||||
FavoriteIcon fyne.Resource = theme.NewThemedResource(res.ResHeartFilledSvg)
|
FavoriteIcon fyne.Resource = theme.NewThemedResource(res.ResHeartFilledSvg)
|
||||||
NotFavoriteIcon fyne.Resource = theme.NewThemedResource(res.ResHeartOutlineSvg)
|
NotFavoriteIcon fyne.Resource = theme.NewThemedResource(res.ResHeartOutlineSvg)
|
||||||
NowPlayingIcon fyne.Resource = theme.NewThemedResource(res.ResHeadphonesSvg)
|
NowPlayingIcon fyne.Resource = theme.NewThemedResource(res.ResHeadphonesSvg)
|
||||||
|
|||||||
+27
-9
@@ -76,6 +76,9 @@ type TracklistOptions struct {
|
|||||||
|
|
||||||
// Disables the sharing option.
|
// Disables the sharing option.
|
||||||
DisableSharing bool
|
DisableSharing bool
|
||||||
|
|
||||||
|
// Disables the song radio option.
|
||||||
|
DisableSongRadio bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tracklist struct {
|
type Tracklist struct {
|
||||||
@@ -92,6 +95,7 @@ type Tracklist struct {
|
|||||||
OnSetRating func(trackIDs []string, rating int)
|
OnSetRating func(trackIDs []string, rating int)
|
||||||
OnDownload func(tracks []*mediaprovider.Track, downloadName string)
|
OnDownload func(tracks []*mediaprovider.Track, downloadName string)
|
||||||
OnShare func(trackID string)
|
OnShare func(trackID string)
|
||||||
|
OnPlaySongRadio func(track *mediaprovider.Track)
|
||||||
|
|
||||||
OnShowArtistPage func(artistID string)
|
OnShowArtistPage func(artistID string)
|
||||||
OnShowAlbumPage func(albumID string)
|
OnShowAlbumPage func(albumID string)
|
||||||
@@ -107,14 +111,15 @@ type Tracklist struct {
|
|||||||
tracks []*util.TrackListModel
|
tracks []*util.TrackListModel
|
||||||
tracksOrigOrder []*util.TrackListModel
|
tracksOrigOrder []*util.TrackListModel
|
||||||
|
|
||||||
nowPlayingID string
|
nowPlayingID string
|
||||||
colLayout *layouts.ColumnsLayout
|
colLayout *layouts.ColumnsLayout
|
||||||
hdr *ListHeader
|
hdr *ListHeader
|
||||||
list *FocusList
|
list *FocusList
|
||||||
ctxMenu *fyne.Menu
|
ctxMenu *fyne.Menu
|
||||||
ratingSubmenu *fyne.MenuItem
|
ratingSubmenu *fyne.MenuItem
|
||||||
shareMenuItem *fyne.MenuItem
|
shareMenuItem *fyne.MenuItem
|
||||||
container *fyne.Container
|
songRadioMenuItem *fyne.MenuItem
|
||||||
|
container *fyne.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTracklist(tracks []*mediaprovider.Track) *Tracklist {
|
func NewTracklist(tracks []*mediaprovider.Track) *Tracklist {
|
||||||
@@ -539,8 +544,12 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
add.Icon = theme.ContentAddIcon()
|
add.Icon = theme.ContentAddIcon()
|
||||||
|
t.songRadioMenuItem = fyne.NewMenuItem("Play song radio", func() {
|
||||||
|
t.onPlaySongRadio(t.selectedTracks())
|
||||||
|
})
|
||||||
|
t.songRadioMenuItem.Icon = myTheme.BroadcastIcon
|
||||||
t.ctxMenu.Items = append(t.ctxMenu.Items,
|
t.ctxMenu.Items = append(t.ctxMenu.Items,
|
||||||
play, shuffle, add)
|
play, shuffle, add, t.songRadioMenuItem)
|
||||||
}
|
}
|
||||||
playlist := fyne.NewMenuItem("Add to playlist...", func() {
|
playlist := fyne.NewMenuItem("Add to playlist...", func() {
|
||||||
if t.OnAddToPlaylist != nil {
|
if t.OnAddToPlaylist != nil {
|
||||||
@@ -579,6 +588,7 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
|||||||
}
|
}
|
||||||
t.ratingSubmenu.Disabled = t.Options.DisableRating
|
t.ratingSubmenu.Disabled = t.Options.DisableRating
|
||||||
t.shareMenuItem.Disabled = t.Options.DisableSharing || len(t.selectedTracks()) != 1
|
t.shareMenuItem.Disabled = t.Options.DisableSharing || len(t.selectedTracks()) != 1
|
||||||
|
t.songRadioMenuItem.Disabled = t.Options.DisableSongRadio || len(t.selectedTracks()) != 1
|
||||||
widget.ShowPopUpMenuAtPosition(t.ctxMenu, fyne.CurrentApp().Driver().CanvasForObject(t), e.AbsolutePosition)
|
widget.ShowPopUpMenuAtPosition(t.ctxMenu, fyne.CurrentApp().Driver().CanvasForObject(t), e.AbsolutePosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,6 +659,14 @@ func (t *Tracklist) onShare(tracks []*mediaprovider.Track) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tracklist) onPlaySongRadio(tracks []*mediaprovider.Track) {
|
||||||
|
if t.OnPlaySongRadio != nil {
|
||||||
|
if len(tracks) > 0 {
|
||||||
|
t.OnPlaySongRadio(tracks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tracklist) selectedTracks() []*mediaprovider.Track {
|
func (t *Tracklist) selectedTracks() []*mediaprovider.Track {
|
||||||
t.tracksMutex.RLock()
|
t.tracksMutex.RLock()
|
||||||
defer t.tracksMutex.RUnlock()
|
defer t.tracksMutex.RUnlock()
|
||||||
|
|||||||
Reference in New Issue
Block a user