diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.go | 52 | ||||
-rw-r--r-- | src/footer.go | 8 | ||||
-rw-r--r-- | src/keybinds.go | 44 | ||||
-rw-r--r-- | src/page_artists.go | 5 | ||||
-rw-r--r-- | src/page_playlists.go | 98 |
5 files changed, 193 insertions, 14 deletions
@@ -20,13 +20,21 @@ type app struct { | |||
20 | cfg *Config | 20 | cfg *Config |
21 | 21 | ||
22 | // Artists page | 22 | // Artists page |
23 | artistsTree *tview.TreeView | 23 | artistsLoaded bool |
24 | songsList *tview.List | 24 | artistsTree *tview.TreeView |
25 | currentSongs []*subsonic.Child | 25 | songsList *tview.List |
26 | currentSongs []*subsonic.Child | ||
26 | 27 | ||
27 | // Play queue page | 28 | // Play queue page |
28 | playQueueList *tview.List | 29 | playQueueList *tview.List |
29 | 30 | ||
31 | // Playlist page | ||
32 | playlistsLoaded bool | ||
33 | playlistsList *tview.List | ||
34 | playlistSongs *tview.List | ||
35 | allPlaylists []*subsonic.Playlist | ||
36 | currentPlaylist *subsonic.Playlist | ||
37 | |||
30 | // Subsonic variables | 38 | // Subsonic variables |
31 | sub *subsonic.Client | 39 | sub *subsonic.Client |
32 | playQueue *music.Queue | 40 | playQueue *music.Queue |
@@ -47,6 +55,7 @@ func Run(cfg *Config) { | |||
47 | a.pages.AddPage("config", a.configPage(), true, false) | 55 | a.pages.AddPage("config", a.configPage(), true, false) |
48 | a.pages.AddPage("artists", a.artistsPage(), true, false) | 56 | a.pages.AddPage("artists", a.artistsPage(), true, false) |
49 | a.pages.AddPage("playqueue", a.queuePage(), true, false) | 57 | a.pages.AddPage("playqueue", a.queuePage(), true, false) |
58 | a.pages.AddPage("playlists", a.playlistsPage(), true, false) | ||
50 | 59 | ||
51 | mainLayout := tview.NewFlex(). | 60 | mainLayout := tview.NewFlex(). |
52 | SetDirection(tview.FlexRow). | 61 | SetDirection(tview.FlexRow). |
@@ -59,12 +68,26 @@ func Run(cfg *Config) { | |||
59 | } else { | 68 | } else { |
60 | a.sub, _ = buildSubsonicClient(a.cfg) | 69 | a.sub, _ = buildSubsonicClient(a.cfg) |
61 | a.playQueue.SetClient(a.sub) | 70 | a.playQueue.SetClient(a.sub) |
62 | err := a.refreshArtists() | 71 | |
63 | if err != nil { | 72 | fmt.Printf("Loading artists...") |
64 | a.alert("Could not refresh artists: %v", err) | 73 | if err := a.refreshArtists(); err != nil { |
74 | fmt.Println("ERR") | ||
75 | a.alert("Loading artists: %v", err) | ||
65 | } else { | 76 | } else { |
66 | a.switchToPage("artists") | 77 | fmt.Println("OK") |
78 | a.artistsLoaded = true | ||
79 | } | ||
80 | |||
81 | fmt.Printf("Loading playlists...") | ||
82 | if err := a.refreshPlaylists(); err != nil { | ||
83 | fmt.Println("ERR") | ||
84 | a.alert("Loading playlists: %v", err) | ||
85 | } else { | ||
86 | fmt.Println("OK") | ||
87 | a.playlistsLoaded = true | ||
67 | } | 88 | } |
89 | |||
90 | a.switchToPage("artists") | ||
68 | } | 91 | } |
69 | 92 | ||
70 | // Keyboard shortcuts | 93 | // Keyboard shortcuts |
@@ -101,6 +124,12 @@ func Run(cfg *Config) { | |||
101 | func (a *app) switchToPage(name string) { | 124 | func (a *app) switchToPage(name string) { |
102 | switch name { | 125 | switch name { |
103 | case "artists": | 126 | case "artists": |
127 | if !a.artistsLoaded { | ||
128 | if err := a.refreshArtists(); err != nil { | ||
129 | a.alert("Error: %v", err) | ||
130 | } | ||
131 | a.artistsLoaded = true | ||
132 | } | ||
104 | a.pages.SwitchToPage("artists") | 133 | a.pages.SwitchToPage("artists") |
105 | a.headerSections.Highlight("artists") | 134 | a.headerSections.Highlight("artists") |
106 | a.tv.SetFocus(a.artistsTree) | 135 | a.tv.SetFocus(a.artistsTree) |
@@ -111,9 +140,16 @@ func (a *app) switchToPage(name string) { | |||
111 | a.tv.SetFocus(a.playQueueList) | 140 | a.tv.SetFocus(a.playQueueList) |
112 | a.pages.SetBorder(true) | 141 | a.pages.SetBorder(true) |
113 | case "playlists": | 142 | case "playlists": |
143 | if !a.playlistsLoaded { | ||
144 | if err := a.refreshPlaylists(); err != nil { | ||
145 | a.alert("Error: %v", err) | ||
146 | } | ||
147 | a.playlistsLoaded = true | ||
148 | } | ||
114 | a.pages.SwitchToPage("playlists") | 149 | a.pages.SwitchToPage("playlists") |
115 | a.headerSections.Highlight("playlists") | 150 | a.headerSections.Highlight("playlists") |
116 | a.pages.SetBorder(true) | 151 | a.tv.SetFocus(a.playlistsList) |
152 | a.pages.SetBorder(false) | ||
117 | case "config": | 153 | case "config": |
118 | a.pages.SwitchToPage("config") | 154 | a.pages.SwitchToPage("config") |
119 | a.headerSections.Highlight("config") | 155 | a.headerSections.Highlight("config") |
diff --git a/src/footer.go b/src/footer.go index be275c3..ab6181d 100644 --- a/src/footer.go +++ b/src/footer.go | |||
@@ -11,8 +11,12 @@ func (a *app) updateFooter() { | |||
11 | case "playqueue": | 11 | case "playqueue": |
12 | a.footer.SetText("[blue]l:[yellow] Next song [blue]p:[yellow] Toggle pause [blue]d:[yellow] Remove [blue]j:[yellow] Move up [blue]k:[yellow] Move down") | 12 | a.footer.SetText("[blue]l:[yellow] Next song [blue]p:[yellow] Toggle pause [blue]d:[yellow] Remove [blue]j:[yellow] Move up [blue]k:[yellow] Move down") |
13 | case "playlists": | 13 | case "playlists": |
14 | a.footer.SetText("") | 14 | if a.tv.GetFocus() == a.playlistsList { |
15 | a.footer.SetText("[blue]l:[yellow] Next song [blue]p:[yellow] Toggle pause [blue]e:[yellow] Play playlist last [blue]n:[yellow] Play playlist next") | ||
16 | } else if a.tv.GetFocus() == a.playlistSongs { | ||
17 | a.footer.SetText("[blue]l:[yellow] Next song [blue]p:[yellow] Toggle pause [blue]e:[yellow] Play song last [blue]n:[yellow] Play song next") | ||
18 | } | ||
15 | case "config": | 19 | case "config": |
16 | a.footer.SetText("") | 20 | a.footer.SetText("[yellow]No shortcuts here") |
17 | } | 21 | } |
18 | } | 22 | } |
diff --git a/src/keybinds.go b/src/keybinds.go index ffdf64a..74f7953 100644 --- a/src/keybinds.go +++ b/src/keybinds.go | |||
@@ -120,6 +120,50 @@ func (a *app) setupMusicControlKeys(p *tview.Box) { | |||
120 | } | 120 | } |
121 | } | 121 | } |
122 | 122 | ||
123 | if a.tv.GetFocus() == a.playlistsList { | ||
124 | if event.Rune() == 'e' { | ||
125 | sel := a.playlistsList.GetCurrentItem() | ||
126 | pl, err := a.sub.GetPlaylist(a.allPlaylists[sel].ID) | ||
127 | if err != nil { | ||
128 | a.alert("Error: %v", err) | ||
129 | return nil | ||
130 | } | ||
131 | |||
132 | for _, s := range pl.Entry { | ||
133 | a.playQueue.Append(s) | ||
134 | } | ||
135 | |||
136 | a.updatePageQueue() | ||
137 | } else if event.Rune() == 'n' { | ||
138 | sel := a.playlistsList.GetCurrentItem() | ||
139 | pl, err := a.sub.GetPlaylist(a.allPlaylists[sel].ID) | ||
140 | if err != nil { | ||
141 | a.alert("Error: %v", err) | ||
142 | return nil | ||
143 | } | ||
144 | |||
145 | for i := len(pl.Entry) - 1; i >= 0; i-- { | ||
146 | a.playQueue.Insert(1, pl.Entry[i]) | ||
147 | } | ||
148 | |||
149 | a.updatePageQueue() | ||
150 | } | ||
151 | } | ||
152 | |||
153 | if a.tv.GetFocus() == a.playlistSongs { | ||
154 | if event.Rune() == 'e' { | ||
155 | sel := a.playlistSongs.GetCurrentItem() | ||
156 | a.playQueue.Append(a.currentPlaylist.Entry[sel]) | ||
157 | |||
158 | a.updatePageQueue() | ||
159 | } else if event.Rune() == 'n' { | ||
160 | sel := a.playlistSongs.GetCurrentItem() | ||
161 | a.playQueue.Insert(1, a.currentPlaylist.Entry[sel]) | ||
162 | |||
163 | a.updatePageQueue() | ||
164 | } | ||
165 | } | ||
166 | |||
123 | return event | 167 | return event |
124 | }) | 168 | }) |
125 | } | 169 | } |
diff --git a/src/page_artists.go b/src/page_artists.go index 874a675..b90d785 100644 --- a/src/page_artists.go +++ b/src/page_artists.go | |||
@@ -3,7 +3,6 @@ package src | |||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | 5 | ||
6 | "github.com/delucks/go-subsonic" | ||
7 | "github.com/gdamore/tcell/v2" | 6 | "github.com/gdamore/tcell/v2" |
8 | "github.com/rivo/tview" | 7 | "github.com/rivo/tview" |
9 | ) | 8 | ) |
@@ -115,10 +114,8 @@ func (a *app) loadAlbumInPanel(id string) error { | |||
115 | } | 114 | } |
116 | 115 | ||
117 | a.songsList.Clear() | 116 | a.songsList.Clear() |
118 | a.currentSongs = make([]*subsonic.Child, 0) | 117 | a.currentSongs = album.Child |
119 | for _, song := range album.Child { | 118 | for _, song := range album.Child { |
120 | a.currentSongs = append(a.currentSongs, song) | ||
121 | |||
122 | txt := fmt.Sprintf("%-2d - %s", song.Track, song.Title) | 119 | txt := fmt.Sprintf("%-2d - %s", song.Track, song.Title) |
123 | 120 | ||
124 | a.songsList.AddItem(txt, "", 0, func() { | 121 | a.songsList.AddItem(txt, "", 0, func() { |
diff --git a/src/page_playlists.go b/src/page_playlists.go new file mode 100644 index 0000000..303a9a7 --- /dev/null +++ b/src/page_playlists.go | |||
@@ -0,0 +1,98 @@ | |||
1 | package src | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/gdamore/tcell/v2" | ||
7 | "github.com/rivo/tview" | ||
8 | ) | ||
9 | |||
10 | func (a *app) playlistsPage() tview.Primitive { | ||
11 | flex := tview.NewFlex().SetDirection(tview.FlexColumn) | ||
12 | |||
13 | a.playlistsList = tview.NewList(). | ||
14 | SetMainTextColor(tcell.ColorRed). | ||
15 | SetHighlightFullLine(true). | ||
16 | ShowSecondaryText(false) | ||
17 | a.playlistsList.SetBorder(true).SetBorderAttributes(tcell.AttrDim) | ||
18 | |||
19 | a.playlistSongs = tview.NewList(). | ||
20 | SetHighlightFullLine(true). | ||
21 | ShowSecondaryText(false) | ||
22 | a.playlistSongs.SetBorder(true).SetBorderAttributes(tcell.AttrDim) | ||
23 | |||
24 | // Change the left-right keys to switch between the panels | ||
25 | a.playlistsList.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { | ||
26 | if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyRight { | ||
27 | a.tv.SetFocus(a.playlistSongs) | ||
28 | a.updateFooter() | ||
29 | return nil | ||
30 | } | ||
31 | return event | ||
32 | }) | ||
33 | a.playlistSongs.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { | ||
34 | if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyRight { | ||
35 | a.tv.SetFocus(a.playlistsList) | ||
36 | a.updateFooter() | ||
37 | return nil | ||
38 | } | ||
39 | return event | ||
40 | }) | ||
41 | |||
42 | // Setup e & n keybinds | ||
43 | a.setupMusicControlKeys(flex.Box) | ||
44 | |||
45 | flex.AddItem(a.playlistsList, 0, 1, false) | ||
46 | flex.AddItem(a.playlistSongs, 0, 1, false) | ||
47 | |||
48 | return flex | ||
49 | } | ||
50 | |||
51 | func (a *app) refreshPlaylists() error { | ||
52 | playlists, err := a.sub.GetPlaylists(nil) | ||
53 | if err != nil { | ||
54 | return err | ||
55 | } | ||
56 | |||
57 | a.allPlaylists = playlists | ||
58 | |||
59 | a.playlistsList.Clear() | ||
60 | for _, pl := range playlists { | ||
61 | id := pl.ID | ||
62 | a.playlistsList.AddItem(pl.Name, "", 0, func() { | ||
63 | a.loadPlaylist(id) | ||
64 | a.tv.SetFocus(a.playlistSongs) | ||
65 | a.updateFooter() | ||
66 | }) | ||
67 | } | ||
68 | |||
69 | a.playlistsList.SetCurrentItem(0) | ||
70 | |||
71 | return nil | ||
72 | } | ||
73 | |||
74 | func (a *app) loadPlaylist(id string) error { | ||
75 | a.playlistSongs.Clear() | ||
76 | pl, err := a.sub.GetPlaylist(id) | ||
77 | if err != nil { | ||
78 | return err | ||
79 | } | ||
80 | |||
81 | a.currentPlaylist = pl | ||
82 | |||
83 | for _, s := range a.currentPlaylist.Entry { | ||
84 | a.playlistSongs.AddItem(fmt.Sprintf("%s - %s", s.Title, s.Artist), "", 0, func() { | ||
85 | sel := a.playlistSongs.GetCurrentItem() | ||
86 | a.playQueue.Clear() | ||
87 | for _, s := range a.currentPlaylist.Entry[sel:] { | ||
88 | a.playQueue.Append(s) | ||
89 | } | ||
90 | |||
91 | if err := a.playQueue.Play(); err != nil { | ||
92 | a.alert("Error: %v", err) | ||
93 | } | ||
94 | }) | ||
95 | } | ||
96 | |||
97 | return nil | ||
98 | } | ||