aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Garrelou <simon.garrelou@gmail.com>2022-12-05 21:40:13 +0100
committerSimon Garrelou <simon.garrelou@gmail.com>2022-12-05 21:40:13 +0100
commitbadc7b254edb837e4338152b4acd34ab5b9b5ddd (patch)
tree0b51d0f9dcaecb417749819946b4a36ca45bdddb
parent5d67e5c43c9123b2508c0b4840def4738744a4d6 (diff)
downloadtermsonic-badc7b254edb837e4338152b4acd34ab5b9b5ddd.tar.gz
termsonic-badc7b254edb837e4338152b4acd34ab5b9b5ddd.zip
Load artists & albums from the server
-rw-r--r--src/app.go24
-rw-r--r--src/page_artists.go97
-rw-r--r--src/page_config.go57
3 files changed, 153 insertions, 25 deletions
diff --git a/src/app.go b/src/app.go
index c18c1f5..58fc0db 100644
--- a/src/app.go
+++ b/src/app.go
@@ -9,12 +9,18 @@ import (
9) 9)
10 10
11type app struct { 11type app struct {
12 // General GUI
12 tv *tview.Application 13 tv *tview.Application
13 pages *tview.Pages 14 pages *tview.Pages
14 header *tview.TextView 15 header *tview.TextView
15 footer *tview.TextView 16 footer *tview.TextView
16 cfg *Config 17 cfg *Config
17 18
19 // Artists panel
20 artistsTree *tview.TreeView
21 songsList *tview.List
22
23 // Subsonic variables
18 sub *subsonic.Client 24 sub *subsonic.Client
19} 25}
20 26
@@ -42,16 +48,28 @@ func Run(cfg *Config) {
42 }) 48 })
43 fmt.Fprintf(a.header, `["artists"]F1: Artists[""] | ["playlists"]F2: Playlists[""] | ["config"]F3: Configuration[""]`) 49 fmt.Fprintf(a.header, `["artists"]F1: Artists[""] | ["playlists"]F2: Playlists[""] | ["config"]F3: Configuration[""]`)
44 50
51 a.pages.SetBorder(true)
45 a.pages.AddPage("config", configPage(a), true, false) 52 a.pages.AddPage("config", configPage(a), true, false)
46 a.pages.AddPage("artists", artistsPage(a), true, false) 53 a.pages.AddPage("artists", artistsPage(a), true, false)
47 54
48 mainLayout := tview.NewFlex(). 55 mainLayout := tview.NewFlex().
49 SetDirection(tview.FlexRow). 56 SetDirection(tview.FlexRow).
50 AddItem(a.header, 0, 1, false). 57 AddItem(a.header, 1, 1, false).
51 AddItem(a.pages, 0, 3, true). 58 AddItem(a.pages, 0, 3, true).
52 AddItem(a.footer, 0, 1, false) 59 AddItem(a.footer, 1, 1, false)
60
61 if testConfig(a.cfg) != nil {
62 switchToPage(a, "config")
63 } else {
64 a.sub, _ = buildSubsonicClient(a.cfg)
65 err := refreshArtists(a)
66 if err != nil {
67 alert(a, "Could not refresh artists: %v", err)
68 } else {
69 switchToPage(a, "artists")
70 }
71 }
53 72
54 switchToPage(a, "config")
55 if err := a.tv.SetRoot(mainLayout, true).EnableMouse(true).SetFocus(mainLayout).Run(); err != nil { 73 if err := a.tv.SetRoot(mainLayout, true).EnableMouse(true).SetFocus(mainLayout).Run(); err != nil {
56 fmt.Printf("Error running termsonic: %v", err) 74 fmt.Printf("Error running termsonic: %v", err)
57 os.Exit(1) 75 os.Exit(1)
diff --git a/src/page_artists.go b/src/page_artists.go
index 6b6def6..c064785 100644
--- a/src/page_artists.go
+++ b/src/page_artists.go
@@ -1,15 +1,102 @@
1package src 1package src
2 2
3import "github.com/rivo/tview" 3import (
4 "fmt"
5 "time"
6
7 "github.com/gdamore/tcell/v2"
8 "github.com/rivo/tview"
9)
10
11type selection struct {
12 entryType string
13 id string
14}
4 15
5func artistsPage(a *app) tview.Primitive { 16func artistsPage(a *app) tview.Primitive {
6 grid := tview.NewGrid(). 17 grid := tview.NewGrid().
7 SetRows(1). 18 SetColumns(40, 0).
8 SetColumns(30, 0).
9 SetBorders(true) 19 SetBorders(true)
10 20
11 grid.AddItem(tview.NewTextView().SetText("Artist & Album list"), 0, 0, 1, 1, 0, 0, true) 21 root := tview.NewTreeNode("Subsonic server").SetColor(tcell.ColorYellow)
12 grid.AddItem(tview.NewTextView().SetText("Song list!"), 0, 1, 1, 2, 0, 0, false) 22 a.artistsTree = tview.NewTreeView().
23 SetRoot(root).
24 SetCurrentNode(root).
25 SetPrefixes([]string{"", " ", " "}).
26 SetSelectedFunc(func(node *tview.TreeNode) {
27 if node.GetReference() == nil {
28 return
29 }
30
31 sel := node.GetReference().(selection)
32 if sel.entryType == "artist" {
33 node.SetExpanded(!node.IsExpanded())
34 return
35 }
36
37 loadAlbumInPanel(a, sel.id)
38 a.tv.SetFocus(a.songsList)
39 })
40
41 a.songsList = tview.NewList()
42 a.songsList.ShowSecondaryText(false)
43
44 grid.AddItem(a.artistsTree, 0, 0, 1, 1, 0, 0, true)
45 grid.AddItem(a.songsList, 0, 1, 1, 2, 0, 0, false)
13 46
14 return grid 47 return grid
15} 48}
49
50func refreshArtists(a *app) error {
51 artistsID3, err := a.sub.GetArtists(nil)
52 if err != nil {
53 return err
54 }
55
56 a.artistsTree.GetRoot().ClearChildren()
57 for _, index := range artistsID3.Index {
58 for _, artist := range index.Artist {
59 node := tview.NewTreeNode(artist.Name)
60 node.SetReference(selection{"artist", artist.ID})
61 node.SetColor(tcell.ColorRed)
62 node.SetSelectable(true)
63 node.SetExpanded(false)
64
65 albums, err := a.sub.GetMusicDirectory(artist.ID)
66 if err != nil {
67 return err
68 }
69
70 for _, album := range albums.Child {
71 subnode := tview.NewTreeNode(album.Title)
72 subnode.SetReference(selection{"album", album.ID})
73 subnode.SetColor(tcell.ColorBlue)
74 subnode.SetSelectable(true)
75
76 node.AddChild(subnode)
77 }
78
79 a.artistsTree.GetRoot().AddChild(node)
80 }
81 }
82
83 a.artistsTree.GetRoot().SetExpanded(true)
84
85 return nil
86}
87
88func loadAlbumInPanel(a *app, id string) error {
89 album, err := a.sub.GetMusicDirectory(id)
90 if err != nil {
91 return err
92 }
93
94 a.songsList.SetTitle(album.Name)
95 a.songsList.Clear()
96 for _, song := range album.Child {
97 dur := time.Duration(song.Duration) * time.Second
98 a.songsList.AddItem(fmt.Sprintf("%-10s %d - %s", fmt.Sprintf("[%s]", dur.String()), song.Track, song.Title), "", 0, nil)
99 }
100
101 return nil
102}
diff --git a/src/page_config.go b/src/page_config.go
index bb8afca..090a70b 100644
--- a/src/page_config.go
+++ b/src/page_config.go
@@ -1,6 +1,7 @@
1package src 1package src
2 2
3import ( 3import (
4 "fmt"
4 "net/http" 5 "net/http"
5 6
6 "github.com/delucks/go-subsonic" 7 "github.com/delucks/go-subsonic"
@@ -8,20 +9,14 @@ import (
8) 9)
9 10
10func configPage(a *app) *tview.Form { 11func configPage(a *app) *tview.Form {
12 var err error
13
11 form := tview.NewForm(). 14 form := tview.NewForm().
12 AddInputField("Server URL", a.cfg.BaseURL, 40, nil, func(txt string) { a.cfg.BaseURL = txt }). 15 AddInputField("Server URL", a.cfg.BaseURL, 40, nil, func(txt string) { a.cfg.BaseURL = txt }).
13 AddInputField("Username", a.cfg.Username, 20, nil, func(txt string) { a.cfg.Username = txt }). 16 AddInputField("Username", a.cfg.Username, 20, nil, func(txt string) { a.cfg.Username = txt }).
14 AddPasswordField("Password", a.cfg.Password, 20, '*', func(txt string) { a.cfg.Password = txt }). 17 AddPasswordField("Password", a.cfg.Password, 20, '*', func(txt string) { a.cfg.Password = txt }).
15 AddButton("Test", func() { 18 AddButton("Test", func() {
16 tmpSub := &subsonic.Client{ 19 if err = testConfig(a.cfg); err != nil {
17 Client: http.DefaultClient,
18 BaseUrl: a.cfg.BaseURL,
19 User: a.cfg.Username,
20 ClientName: "termsonic",
21 PasswordAuth: true,
22 }
23
24 if err := tmpSub.Authenticate(a.cfg.Password); err != nil {
25 alert(a, "Could not auth: %v", err) 20 alert(a, "Could not auth: %v", err)
26 } else { 21 } else {
27 alert(a, "Success.") 22 alert(a, "Success.")
@@ -34,14 +29,8 @@ func configPage(a *app) *tview.Form {
34 return 29 return
35 } 30 }
36 31
37 a.sub = &subsonic.Client{ 32 a.sub, err = buildSubsonicClient(a.cfg)
38 Client: http.DefaultClient, 33 if err != nil {
39 BaseUrl: a.cfg.BaseURL,
40 User: a.cfg.Username,
41 ClientName: "termsonic",
42 PasswordAuth: true,
43 }
44 if err := a.sub.Authenticate(a.cfg.Password); err != nil {
45 alert(a, "Could not auth: %v", err) 34 alert(a, "Could not auth: %v", err)
46 } else { 35 } else {
47 alert(a, "All good!") 36 alert(a, "All good!")
@@ -49,3 +38,37 @@ func configPage(a *app) *tview.Form {
49 }) 38 })
50 return form 39 return form
51} 40}
41
42func testConfig(cfg *Config) error {
43 if cfg.BaseURL == "" {
44 return fmt.Errorf("empty base URL")
45 }
46
47 if cfg.Username == "" {
48 return fmt.Errorf("empty username")
49 }
50
51 if cfg.Password == "" {
52 return fmt.Errorf("empty password")
53 }
54
55 _, err := buildSubsonicClient(cfg)
56 return err
57}
58
59func buildSubsonicClient(cfg *Config) (*subsonic.Client, error) {
60 tmpSub := &subsonic.Client{
61 Client: http.DefaultClient,
62 BaseUrl: cfg.BaseURL,
63 User: cfg.Username,
64 ClientName: "termsonic",
65 PasswordAuth: true,
66 }
67
68 err := tmpSub.Authenticate(cfg.Password)
69 if err != nil {
70 return nil, err
71 }
72
73 return tmpSub, nil
74}