aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Garrelou <simon.garrelou@gmail.com>2022-12-08 22:35:17 +0100
committerSimon Garrelou <simon.garrelou@gmail.com>2022-12-08 22:35:17 +0100
commit7e4333dac70cdb003e71b6805ed4d81f18aa233a (patch)
tree26190c147141f765d69880d98d891bf50071e891 /src
parent96cc5db2b4062ced82faf01ddac24abef04df343 (diff)
downloadtermsonic-7e4333dac70cdb003e71b6805ed4d81f18aa233a.tar.gz
termsonic-7e4333dac70cdb003e71b6805ed4d81f18aa233a.zip
Music playback working
Diffstat (limited to 'src')
-rw-r--r--src/app.go45
-rw-r--r--src/footer.go9
-rw-r--r--src/header.go47
-rw-r--r--src/page_artists.go53
4 files changed, 110 insertions, 44 deletions
diff --git a/src/app.go b/src/app.go
index 8eed517..029644c 100644
--- a/src/app.go
+++ b/src/app.go
@@ -4,6 +4,7 @@ import (
4 "fmt" 4 "fmt"
5 "os" 5 "os"
6 6
7 "git.sixfoisneuf.fr/termsonic/music"
7 "github.com/delucks/go-subsonic" 8 "github.com/delucks/go-subsonic"
8 "github.com/gdamore/tcell/v2" 9 "github.com/gdamore/tcell/v2"
9 "github.com/rivo/tview" 10 "github.com/rivo/tview"
@@ -11,23 +12,26 @@ import (
11 12
12type app struct { 13type app struct {
13 // General GUI 14 // General GUI
14 tv *tview.Application 15 tv *tview.Application
15 pages *tview.Pages 16 pages *tview.Pages
16 header *tview.TextView 17 headerSections *tview.TextView
17 footer *tview.TextView 18 headerNowPlaying *tview.TextView
18 cfg *Config 19 footer *tview.TextView
20 cfg *Config
19 21
20 // Artists panel 22 // Artists panel
21 artistsTree *tview.TreeView 23 artistsTree *tview.TreeView
22 songsList *tview.List 24 songsList *tview.List
23 25
24 // Subsonic variables 26 // Subsonic variables
25 sub *subsonic.Client 27 sub *subsonic.Client
28 playQueue *music.Queue
26} 29}
27 30
28func Run(cfg *Config) { 31func Run(cfg *Config) {
29 a := &app{ 32 a := &app{
30 cfg: cfg, 33 cfg: cfg,
34 playQueue: music.NewQueue(nil),
31 } 35 }
32 36
33 a.tv = tview.NewApplication() 37 a.tv = tview.NewApplication()
@@ -35,28 +39,13 @@ func Run(cfg *Config) {
35 a.footer = tview.NewTextView(). 39 a.footer = tview.NewTextView().
36 SetDynamicColors(true) 40 SetDynamicColors(true)
37 41
38 a.header = tview.NewTextView().
39 SetRegions(true).
40 SetChangedFunc(func() {
41 a.tv.Draw()
42 }).
43 SetHighlightedFunc(func(added, _, _ []string) {
44 hl := added[0]
45 cur, _ := a.pages.GetFrontPage()
46
47 if hl != cur {
48 a.switchToPage(hl)
49 }
50 })
51 fmt.Fprintf(a.header, `["artists"]F1: Artists[""] | ["playlists"]F2: Playlists[""] | ["config"]F3: Configuration[""]`)
52
53 a.pages.SetBorder(true) 42 a.pages.SetBorder(true)
54 a.pages.AddPage("config", a.configPage(), true, false) 43 a.pages.AddPage("config", a.configPage(), true, false)
55 a.pages.AddPage("artists", a.artistsPage(), true, false) 44 a.pages.AddPage("artists", a.artistsPage(), true, false)
56 45
57 mainLayout := tview.NewFlex(). 46 mainLayout := tview.NewFlex().
58 SetDirection(tview.FlexRow). 47 SetDirection(tview.FlexRow).
59 AddItem(a.header, 1, 1, false). 48 AddItem(a.buildHeader(), 1, 1, false).
60 AddItem(a.pages, 0, 3, true). 49 AddItem(a.pages, 0, 3, true).
61 AddItem(a.footer, 1, 1, false) 50 AddItem(a.footer, 1, 1, false)
62 51
@@ -64,6 +53,7 @@ func Run(cfg *Config) {
64 a.switchToPage("config") 53 a.switchToPage("config")
65 } else { 54 } else {
66 a.sub, _ = buildSubsonicClient(a.cfg) 55 a.sub, _ = buildSubsonicClient(a.cfg)
56 a.playQueue.SetClient(a.sub)
67 err := a.refreshArtists() 57 err := a.refreshArtists()
68 if err != nil { 58 if err != nil {
69 a.alert("Could not refresh artists: %v", err) 59 a.alert("Could not refresh artists: %v", err)
@@ -104,14 +94,17 @@ func (a *app) switchToPage(name string) {
104 switch name { 94 switch name {
105 case "artists": 95 case "artists":
106 a.pages.SwitchToPage("artists") 96 a.pages.SwitchToPage("artists")
107 a.header.Highlight("artists") 97 a.headerSections.Highlight("artists")
108 a.tv.SetFocus(a.artistsTree) 98 a.tv.SetFocus(a.artistsTree)
99 a.pages.SetBorder(false)
109 case "playlists": 100 case "playlists":
110 a.pages.SwitchToPage("playlists") 101 a.pages.SwitchToPage("playlists")
111 a.header.Highlight("playlists") 102 a.headerSections.Highlight("playlists")
103 a.pages.SetBorder(true)
112 case "config": 104 case "config":
113 a.pages.SwitchToPage("config") 105 a.pages.SwitchToPage("config")
114 a.header.Highlight("config") 106 a.headerSections.Highlight("config")
107 a.pages.SetBorder(true)
115 } 108 }
116 109
117 a.updateFooter() 110 a.updateFooter()
diff --git a/src/footer.go b/src/footer.go
index b0494d0..2695a61 100644
--- a/src/footer.go
+++ b/src/footer.go
@@ -1,14 +1,9 @@
1package src 1package src
2 2
3func (a *app) updateFooter() { 3func (a *app) updateFooter() {
4 switch a.header.GetHighlights()[0] { 4 switch a.headerSections.GetHighlights()[0] {
5 case "artists": 5 case "artists":
6 switch a.tv.GetFocus() { 6 a.footer.SetText("[blue]l:[yellow] Next song [blue]k:[yellow] Toggle pause")
7 case a.artistsTree:
8 a.footer.SetText("Artists: [blue]Up/Down:[yellow] Move selection [blue]Space:[yellow] Select entry")
9 case a.songsList:
10 a.footer.SetText("Songs: [blue]Up/Down:[yellow] Move selection [blue]Space:[yellow] Play")
11 }
12 case "playlists": 7 case "playlists":
13 a.footer.SetText("Come back later!") 8 a.footer.SetText("Come back later!")
14 case "config": 9 case "config":
diff --git a/src/header.go b/src/header.go
new file mode 100644
index 0000000..5c85808
--- /dev/null
+++ b/src/header.go
@@ -0,0 +1,47 @@
1package src
2
3import (
4 "fmt"
5
6 "github.com/delucks/go-subsonic"
7 "github.com/rivo/tview"
8)
9
10func (a *app) buildHeader() tview.Primitive {
11 flex := tview.NewFlex()
12 flex.SetDirection(tview.FlexColumn)
13
14 a.headerSections = tview.NewTextView().
15 SetRegions(true).
16 SetChangedFunc(func() {
17 a.tv.Draw()
18 }).
19 SetHighlightedFunc(func(added, _, _ []string) {
20 hl := added[0]
21 cur, _ := a.pages.GetFrontPage()
22
23 if hl != cur {
24 a.switchToPage(hl)
25 }
26 })
27 fmt.Fprintf(a.headerSections, `["artists"]F1: Artists[""] | ["playlists"]F2: Playlists[""] | ["config"]F3: Configuration[""]`)
28
29 a.headerNowPlaying = tview.NewTextView().SetTextAlign(tview.AlignRight)
30
31 flex.AddItem(a.headerSections, 0, 1, false)
32 flex.AddItem(a.headerNowPlaying, 0, 1, false)
33
34 a.playQueue.SetOnChangeCallback(func(song *subsonic.Child, isPaused bool) {
35 if song != nil {
36 symbol := ">"
37 if isPaused {
38 symbol = "||"
39 }
40 a.headerNowPlaying.SetText(fmt.Sprintf("%s %s - %s", symbol, song.Title, song.Artist))
41 } else {
42 a.headerNowPlaying.SetText("Not playing")
43 }
44 })
45
46 return flex
47}
diff --git a/src/page_artists.go b/src/page_artists.go
index e8ce180..4361767 100644
--- a/src/page_artists.go
+++ b/src/page_artists.go
@@ -4,6 +4,7 @@ import (
4 "fmt" 4 "fmt"
5 "time" 5 "time"
6 6
7 "github.com/delucks/go-subsonic"
7 "github.com/gdamore/tcell/v2" 8 "github.com/gdamore/tcell/v2"
8 "github.com/rivo/tview" 9 "github.com/rivo/tview"
9) 10)
@@ -14,9 +15,7 @@ type selection struct {
14} 15}
15 16
16func (a *app) artistsPage() tview.Primitive { 17func (a *app) artistsPage() tview.Primitive {
17 grid := tview.NewGrid(). 18 grid := tview.NewFlex().SetDirection(tview.FlexColumn)
18 SetColumns(40, 0).
19 SetBorders(true)
20 19
21 // Artist & album list 20 // Artist & album list
22 root := tview.NewTreeNode("Subsonic server").SetColor(tcell.ColorYellow) 21 root := tview.NewTreeNode("Subsonic server").SetColor(tcell.ColorYellow)
@@ -37,18 +36,18 @@ func (a *app) artistsPage() tview.Primitive {
37 36
38 a.loadAlbumInPanel(sel.id) 37 a.loadAlbumInPanel(sel.id)
39 a.tv.SetFocus(a.songsList) 38 a.tv.SetFocus(a.songsList)
40 a.updateFooter()
41 }) 39 })
40 a.artistsTree.SetBorderAttributes(tcell.AttrDim).SetBorder(true)
42 41
43 // Songs list for the selected album 42 // Songs list for the selected album
44 a.songsList = tview.NewList() 43 a.songsList = tview.NewList()
45 a.songsList.ShowSecondaryText(false) 44 a.songsList.ShowSecondaryText(false)
45 a.songsList.SetBorderAttributes(tcell.AttrDim).SetBorder(true)
46 46
47 // Change the left-right keys to switch between the panels 47 // Change the left-right keys to switch between the panels
48 a.artistsTree.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 48 a.artistsTree.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
49 if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyRight { 49 if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyRight {
50 a.tv.SetFocus(a.songsList) 50 a.tv.SetFocus(a.songsList)
51 a.updateFooter()
52 return nil 51 return nil
53 } 52 }
54 return event 53 return event
@@ -57,14 +56,26 @@ func (a *app) artistsPage() tview.Primitive {
57 a.songsList.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 56 a.songsList.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
58 if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyRight { 57 if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyRight {
59 a.tv.SetFocus(a.artistsTree) 58 a.tv.SetFocus(a.artistsTree)
60 a.updateFooter()
61 return nil 59 return nil
62 } 60 }
63 return event 61 return event
64 }) 62 })
65 63
66 grid.AddItem(a.artistsTree, 0, 0, 1, 1, 0, 0, true) 64 grid.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
67 grid.AddItem(a.songsList, 0, 1, 1, 2, 0, 0, false) 65 if event.Rune() == 'l' {
66 a.playQueue.Next()
67 return nil
68 }
69
70 if event.Rune() == 'k' {
71 a.playQueue.TogglePause()
72 return nil
73 }
74 return event
75 })
76
77 grid.AddItem(a.artistsTree, 0, 1, true)
78 grid.AddItem(a.songsList, 0, 1, false)
68 79
69 return grid 80 return grid
70} 81}
@@ -113,12 +124,32 @@ func (a *app) loadAlbumInPanel(id string) error {
113 return err 124 return err
114 } 125 }
115 126
116 a.songsList.SetTitle(album.Name) 127 var songs []*subsonic.Child
128
117 a.songsList.Clear() 129 a.songsList.Clear()
118 for _, song := range album.Child { 130 for i := len(album.Child) - 1; i >= 0; i-- {
131 song := album.Child[i]
132 songNoPtr := *song
133 songs = append([]*subsonic.Child{&songNoPtr}, songs...)
134
135 songsCopy := make([]*subsonic.Child, len(songs))
136 copy(songsCopy, songs)
137
119 dur := time.Duration(song.Duration) * time.Second 138 dur := time.Duration(song.Duration) * time.Second
120 a.songsList.AddItem(fmt.Sprintf("%-10s %d - %s", fmt.Sprintf("[%s]", dur.String()), song.Track, song.Title), "", 0, nil) 139
140 a.songsList.InsertItem(0, fmt.Sprintf("%-10s %d - %s", fmt.Sprintf("[%s]", dur.String()), song.Track, song.Title), "", 0, func() {
141 a.playQueue.Clear()
142 for _, s := range songsCopy {
143 a.playQueue.Append(s)
144 }
145 err := a.playQueue.Play()
146 if err != nil {
147 a.alert("Error: %v", err)
148 }
149 })
121 } 150 }
122 151
152 a.songsList.SetCurrentItem(0)
153
123 return nil 154 return nil
124} 155}