aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Garrelou <simon.garrelou@gmail.com>2022-12-05 19:44:07 +0100
committerSimon Garrelou <simon.garrelou@gmail.com>2022-12-05 19:44:07 +0100
commit5d67e5c43c9123b2508c0b4840def4738744a4d6 (patch)
tree4a4342cefc8066133cac49d884563a9bdd2d8b23 /src
parent8bcd996e28572f2362d186c6e2bbb3971462feee (diff)
downloadtermsonic-5d67e5c43c9123b2508c0b4840def4738744a4d6.tar.gz
termsonic-5d67e5c43c9123b2508c0b4840def4738744a4d6.zip
Rework code organization + add README
Diffstat (limited to 'src')
-rw-r--r--src/alert.go18
-rw-r--r--src/app.go72
-rw-r--r--src/config.go90
-rw-r--r--src/page_artists.go15
-rw-r--r--src/page_config.go51
5 files changed, 246 insertions, 0 deletions
diff --git a/src/alert.go b/src/alert.go
new file mode 100644
index 0000000..917d089
--- /dev/null
+++ b/src/alert.go
@@ -0,0 +1,18 @@
1package src
2
3import (
4 "fmt"
5
6 "github.com/rivo/tview"
7)
8
9func alert(a *app, format string, params ...interface{}) {
10 modal := tview.NewModal().
11 SetText(fmt.Sprintf(format, params...)).
12 AddButtons([]string{"OK"}).
13 SetDoneFunc(func(_ int, _ string) {
14 a.pages.RemovePage("alert")
15 })
16
17 a.pages.AddPage("alert", modal, true, true)
18}
diff --git a/src/app.go b/src/app.go
new file mode 100644
index 0000000..c18c1f5
--- /dev/null
+++ b/src/app.go
@@ -0,0 +1,72 @@
1package src
2
3import (
4 "fmt"
5 "os"
6
7 "github.com/delucks/go-subsonic"
8 "github.com/rivo/tview"
9)
10
11type app struct {
12 tv *tview.Application
13 pages *tview.Pages
14 header *tview.TextView
15 footer *tview.TextView
16 cfg *Config
17
18 sub *subsonic.Client
19}
20
21func Run(cfg *Config) {
22 a := &app{
23 cfg: cfg,
24 }
25
26 a.tv = tview.NewApplication()
27 a.pages = tview.NewPages()
28 a.footer = tview.NewTextView()
29
30 a.header = tview.NewTextView().
31 SetRegions(true).
32 SetChangedFunc(func() {
33 a.tv.Draw()
34 }).
35 SetHighlightedFunc(func(added, removed, remaining []string) {
36 hl := added[0]
37 cur, _ := a.pages.GetFrontPage()
38
39 if hl != cur {
40 switchToPage(a, hl)
41 }
42 })
43 fmt.Fprintf(a.header, `["artists"]F1: Artists[""] | ["playlists"]F2: Playlists[""] | ["config"]F3: Configuration[""]`)
44
45 a.pages.AddPage("config", configPage(a), true, false)
46 a.pages.AddPage("artists", artistsPage(a), true, false)
47
48 mainLayout := tview.NewFlex().
49 SetDirection(tview.FlexRow).
50 AddItem(a.header, 0, 1, false).
51 AddItem(a.pages, 0, 3, true).
52 AddItem(a.footer, 0, 1, false)
53
54 switchToPage(a, "config")
55 if err := a.tv.SetRoot(mainLayout, true).EnableMouse(true).SetFocus(mainLayout).Run(); err != nil {
56 fmt.Printf("Error running termsonic: %v", err)
57 os.Exit(1)
58 }
59}
60
61func switchToPage(a *app, name string) {
62 if name == "artists" {
63 a.pages.SwitchToPage("artists")
64 a.header.Highlight("artists")
65 } else if name == "playlists" {
66 a.pages.SwitchToPage("playlists")
67 a.header.Highlight("playlists")
68 } else if name == "config" {
69 a.pages.SwitchToPage("config")
70 a.header.Highlight("config")
71 }
72}
diff --git a/src/config.go b/src/config.go
new file mode 100644
index 0000000..1b4a998
--- /dev/null
+++ b/src/config.go
@@ -0,0 +1,90 @@
1package src
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "runtime"
8
9 "github.com/BurntSushi/toml"
10)
11
12type Config struct {
13 BaseURL string
14 Username string
15 Password string
16}
17
18func LoadConfigFromFile(filename string) (*Config, error) {
19 var cfg Config
20 _, err := toml.DecodeFile(filename, &cfg)
21
22 return &cfg, err
23}
24
25func LoadDefaultConfig() (*Config, error) {
26 path, err := getConfigFilePath()
27 if err != nil {
28 return nil, err
29 }
30
31 f, err := os.Open(path)
32 if err != nil && !os.IsNotExist(err) {
33 return nil, err
34 } else if os.IsNotExist(err) {
35 return &Config{}, nil
36 }
37 f.Close()
38
39 return LoadConfigFromFile(path)
40}
41
42func getConfigFilePath() (string, error) {
43 path := ""
44 if runtime.GOOS == "linux" {
45 configDir := os.Getenv("XDG_CONFIG_DIR")
46 if configDir == "" {
47 home := os.Getenv("HOME")
48 if home == "" {
49 return "", fmt.Errorf("could not determine where to store configuration")
50 }
51
52 path = filepath.Join(home, ".config")
53 os.MkdirAll(path, os.ModeDir.Perm())
54
55 path = filepath.Join(path, "termsonic.toml")
56 } else {
57 path = filepath.Join(configDir, "termsonic.toml")
58 }
59 } else if runtime.GOOS == "windows" {
60 appdata := os.Getenv("APPDATA")
61 if appdata == "" {
62 return "", fmt.Errorf("could not find %%APPDATA%%")
63 }
64
65 path = filepath.Join(appdata, "Termsonic")
66 os.MkdirAll(path, os.ModeDir.Perm())
67
68 path = filepath.Join(path, "termsonic.toml")
69 } else {
70 return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
71 }
72
73 return path, nil
74}
75
76func (c *Config) Save() error {
77 path, err := getConfigFilePath()
78 if err != nil {
79 return err
80 }
81
82 f, err := os.Create(path)
83 if err != nil {
84 return err
85 }
86 defer f.Close()
87
88 enc := toml.NewEncoder(f)
89 return enc.Encode(*c)
90}
diff --git a/src/page_artists.go b/src/page_artists.go
new file mode 100644
index 0000000..6b6def6
--- /dev/null
+++ b/src/page_artists.go
@@ -0,0 +1,15 @@
1package src
2
3import "github.com/rivo/tview"
4
5func artistsPage(a *app) tview.Primitive {
6 grid := tview.NewGrid().
7 SetRows(1).
8 SetColumns(30, 0).
9 SetBorders(true)
10
11 grid.AddItem(tview.NewTextView().SetText("Artist & Album list"), 0, 0, 1, 1, 0, 0, true)
12 grid.AddItem(tview.NewTextView().SetText("Song list!"), 0, 1, 1, 2, 0, 0, false)
13
14 return grid
15}
diff --git a/src/page_config.go b/src/page_config.go
new file mode 100644
index 0000000..bb8afca
--- /dev/null
+++ b/src/page_config.go
@@ -0,0 +1,51 @@
1package src
2
3import (
4 "net/http"
5
6 "github.com/delucks/go-subsonic"
7 "github.com/rivo/tview"
8)
9
10func configPage(a *app) *tview.Form {
11 form := tview.NewForm().
12 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 }).
14 AddPasswordField("Password", a.cfg.Password, 20, '*', func(txt string) { a.cfg.Password = txt }).
15 AddButton("Test", func() {
16 tmpSub := &subsonic.Client{
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)
26 } else {
27 alert(a, "Success.")
28 }
29 }).
30 AddButton("Save", func() {
31 err := a.cfg.Save()
32 if err != nil {
33 alert(a, "Error saving: %v", err)
34 return
35 }
36
37 a.sub = &subsonic.Client{
38 Client: http.DefaultClient,
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)
46 } else {
47 alert(a, "All good!")
48 }
49 })
50 return form
51}