diff --git a/README.md b/README.md index 0a18b5b..7c8538f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ https://user-images.githubusercontent.com/51816057/140478368-5b724b2f-2499-4150- - [ ] Visual Mode (like vim) for updating playlists - [ ] Music Visualizer -# Prerequisites +# Setting Up - Music Player Daemon must be setup - Go Should Be Installed ( for building ) @@ -30,6 +30,59 @@ cd goMP && go build ``` +# Configuration + +## Key Mappings + +Following Keys can be used for Mappings + +| Keys | Using them in Config | +|-----------------|-----------------------| +| a-z | a-z | +| A-Z | A-z | +| {,},(,),[,],<,> | {,},(,),[,],<,> | +| Enter(Return) | ENTER/RETURN | +| Tab | TAB | +| Space | SPACE | + +See config/kMap.go for more information + +For mapping a key to some function use the following format: + + +```yml +Function: [ firstMapping, secondMapping, thirdMapping] +``` +for.eg + + +```yml +togglePlayBack : [ "p", "T" ] # using the quotes is neccessary. +``` + +Following functions are provided : + +| Functions | +|------------------------------------| +| "showChildrenContent", | +| "togglePlayBack", | +| "showParentContent", | +| "nextSong", | +| "clearPlaylist", | +| "previousSong", | +| "addToPlaylist", | +| "toggleRandom", | +| "toggleRepeat", | +| "decreaseVolume", | +| "increaseVolume", | +| "navigateToFiles", | +| "navigateToPlaylist", | +| "navigateToMostPlayed", | +| "quit", | +| "stop", | +| "updateDB", | +| "deleteSongFromPlaylist", | + ### Tested on following terminals: - Alacritty diff --git a/config.go b/config.go deleted file mode 100644 index 4040fec..0000000 --- a/config.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/spf13/viper" -) - -var ( - HOME_DIR, _ = os.UserHomeDir() - defaults = map[string]interface{}{ - "ADDITIONAL_PADDING_X": 12, - "ADDITIONAL_PADDING_Y": 16, - "IMAGE_WIDTH_EXTRA_X": -1.5, - "IMAGE_WIDTH_EXTRA_Y": -3.75, - "MUSIC_DIRECTORY": getMusicDirectory() + "/", - "PORT": "6600", - "DEFAULT_IMAGE_PATH": "default.jpg", - "COVER_IMAGE_PATH": "cover.jpg", - } -) - -func readConfig() { - for k, v := range defaults { - viper.SetDefault(k, v) - } - viper.SetConfigName("config") - viper.AddConfigPath(HOME_DIR + "/.config/goMP") - err := viper.ReadInConfig() - if err != nil { - fmt.Println("Could Not Read Config file.") - } -} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..c98ee95 --- /dev/null +++ b/config/config.go @@ -0,0 +1,74 @@ +package config + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/spf13/viper" +) + +var ( + HOME_DIR, _ = os.UserHomeDir() + defaults = map[string]interface{}{ + "ADDITIONAL_PADDING_X": 12, + "ADDITIONAL_PADDING_Y": 16, + "IMAGE_WIDTH_EXTRA_X": -1.5, + "IMAGE_WIDTH_EXTRA_Y": -3.75, + "MUSIC_DIRECTORY": getMusicDirectory() + "/", + "PORT": "6600", + "DEFAULT_IMAGE_PATH": "default.jpg", + "COVER_IMAGE_PATH": "cover.jpg", + } +) + +func ReadConfig() { + for k, v := range defaults { + viper.SetDefault(k, v) + } + viper.SetConfigName("config") + viper.AddConfigPath(HOME_DIR + "/.config/goMP") + err := viper.ReadInConfig() + if err != nil { + fmt.Println("Could Not Read Config file.") + } +} + +func GenerateKeyMap(funcMap map[string]func()) { + for k := range funcMap { + mappingsForFunctionK := viper.GetStringSlice(k) + if len(mappingsForFunctionK) != 0 { + for _, i := range mappingsForFunctionK { + aV, err := GetAsciiValue(i) + if err == nil { + KEY_MAP[aV] = k + } + } + } + } +} + +func getMusicDirectory() string { + content, err := ioutil.ReadFile(HOME_DIR + "/.config/mpd/mpd.conf") + if err != nil { + fmt.Println("No Config File for mpd Found") + panic(err) + } + ab := string(content) + maps := strings.Split(ab, "\n") + for _, j := range maps { + if strings.Contains(j, "music_directory") { + s := strings.SplitAfter(strings.ReplaceAll(j, " ", ""), "y")[1] + s = strings.ReplaceAll(s, "\t", "") + d := "" + for z, m := range s { + if (z != 0) && (z != (len(s) - 1)) { + d += string(m) + } + } + return d + } + } + return "" +} diff --git a/config/kMap.go b/config/kMap.go new file mode 100644 index 0000000..21b46cd --- /dev/null +++ b/config/kMap.go @@ -0,0 +1,68 @@ +package config + +import ( + "errors" +) + +var ( + SPECIAL_KEYS = map[string]int{ + "TAB": 9, + "RETURN": 13, + "ENTER": 13, + "SPACE": 32, + "[": 91, + "]": 93, + "(": 40, + ")": 41, + "{": 123, + "}": 125, + "<": 60, + ">": 62, + "?": 63, + "/": 47, + ";": 59, + ":": 58, + "'": 39, + "\"": 34, + } + + // Generating Default KEY_MAP which will then later be changed by GenerateKeyMap + + KEY_MAP = map[int]string{ + 108: "showChildrenContent", + 112: "togglePlayBack", + 104: "showParentContent", + 110: "nextSong", + 99: "clearPlaylist", + 78: "previousSong", + 97: "addToPlaylist", + 122: "toggleRandom", + 114: "toggleRepeat", + 45: "decreaseVolume", + 61: "increaseVolume", + 50: "navigateToFiles", + 49: "navigateToPlaylist", + 51: "navigateToMostPlayed", + 113: "quit", + 115: "stop", + 117: "updateDB", + 100: "deleteSongFromPlaylist", + } +) + +func GetAsciiValue(s string) (int, error) { + if len([]rune(s)) == 1 { + char := []rune(s)[0] + if (int(char) >= 65 && int(char) <= 90) || (int(char) >= 97 && int(char) <= 122) { + return int(char), nil + } else if val, ok := SPECIAL_KEYS[s]; ok { + return val, nil + } else { + return -1, errors.New("Not Found in the range") + } + } else if val, ok := SPECIAL_KEYS[s]; ok { + return val, nil + } else { + return -1, errors.New("Not Found") + } +} diff --git a/config/kMap_test.go b/config/kMap_test.go new file mode 100644 index 0000000..6a7c2d4 --- /dev/null +++ b/config/kMap_test.go @@ -0,0 +1,36 @@ +package config + +import ( + "testing" +) + +func TestGetAsciiValue(t *testing.T) { + for k, v := range SPECIAL_KEYS { + result, err := GetAsciiValue(k) + if result != v { + t.Errorf("Values From KMAP Failed") + } else if err != nil { + t.Errorf("Error Received: %v", err) + } + } + for i := 65; i < (65 + 26); i++ { + result, err := GetAsciiValue(string(rune(i))) + if err != nil { + t.Errorf("Error Received! %v", err) + } else if result != i { + t.Errorf("Invalid Value Received for small alphabets Result Received: %d expecting %d", result, i) + } + } + for i := 97; i < (97 + 26); i++ { + result, err := GetAsciiValue(string(rune(i))) + if err != nil { + t.Errorf("Error Received: %v", err) + } else if result != i { + t.Errorf("Invalid Value Received for Big alphabets Result Received: %d expecting %d", result, i) + } + } + result, err := GetAsciiValue("") + if err == nil || result != -1 { + t.Errorf("Wrong Result Received for Control Characters %v %v", result, err) + } +} diff --git a/main.go b/main.go index 3a8f98d..3d06d48 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "strconv" "time" + "github.com/aditya-K2/goMP/config" "github.com/fhs/gompd/mpd" "github.com/gdamore/tcell/v2" "github.com/spf13/viper" @@ -16,7 +17,7 @@ var Repeat bool var InsidePlaylist bool = true func main() { - readConfig() + config.ReadConfig() // Connect to MPD server conn, err := mpd.Dial("tcp", "localhost:"+viper.GetString("MPD_PORT")) if err != nil { @@ -53,153 +54,120 @@ func main() { return UI.expandedView.GetInnerRect() }) - UI.expandedView.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey { - switch e.Rune() { - case 108: // L : Key - { - r, _ := UI.expandedView.GetSelection() - if !InsidePlaylist { - if len(dirTree.children[r].children) == 0 { - id, _ := conn.AddId(dirTree.children[r].absolutePath, -1) - conn.PlayId(id) - } else { - Update(*conn, dirTree.children[r].children, UI.expandedView) - dirTree = &dirTree.children[r] - } + var FUNC_MAP = map[string]func(){ + "showChildrenContent": func() { + r, _ := UI.expandedView.GetSelection() + if !InsidePlaylist { + if len(dirTree.children[r].children) == 0 { + id, _ := conn.AddId(dirTree.children[r].absolutePath, -1) + conn.PlayId(id) } else { - conn.Play(r) + Update(*conn, dirTree.children[r].children, UI.expandedView) + dirTree = &dirTree.children[r] } - return nil + } else { + conn.Play(r) } - case 112: // P : Key - { - togglePlayBack(*conn) - return nil - } - case 104: // H : Key - { - if !InsidePlaylist { - if dirTree.parent != nil { - Update(*conn, dirTree.parent.children, UI.expandedView) - dirTree = dirTree.parent - } + }, + "togglePlayBack": func() { + togglePlayBack(*conn) + }, + "showParentContent": func() { + if !InsidePlaylist { + if dirTree.parent != nil { + Update(*conn, dirTree.parent.children, UI.expandedView) + dirTree = dirTree.parent } - return nil } - case 110: // N : Key - { - conn.Next() - return nil - } - case 99: // C : Key - { - conn.Clear() - if InsidePlaylist { - UpdatePlaylist(*conn, UI.expandedView) - } - return nil - } - case 78: // Shift - N : Key - { - conn.Previous() - return nil - } - case 97: // A : Key - { - if !InsidePlaylist { - r, _ := UI.expandedView.GetSelection() - conn.Add(dirTree.children[r].absolutePath) - } - return nil - } - case 122: // Z : Key - { - err := conn.Random(!Random) - if err == nil { - Random = !Random - } - return nil - } - case 114: // R : Key - { - err := conn.Repeat(!Repeat) - if err == nil { - Repeat = !Repeat - } - return nil - } - case 45: // Minus : Key - { - if Volume <= 0 { - Volume = 0 - } else { - Volume -= 10 - } - conn.SetVolume(int(Volume)) - return nil - } - case 61: // Plus : Key - { - if Volume >= 100 { - Volume = 100 - } else { - Volume += 10 - } - conn.SetVolume(int(Volume)) - return nil - } - case 50: // 2 : Key - { - InsidePlaylist = false - UI.Navbar.Select(1, 0) - Update(*conn, dirTree.children, UI.expandedView) - return nil - } - case 49: // 1 : Key - { - InsidePlaylist = true - UI.Navbar.Select(0, 0) + }, + "nextSong": func() { + conn.Next() + }, + "clearPlaylist": func() { + conn.Clear() + if InsidePlaylist { UpdatePlaylist(*conn, UI.expandedView) - return nil } - case 51: // 3 : Key - { - InsidePlaylist = false - UI.Navbar.Select(2, 0) - return nil + }, + "previousSong": func() { + conn.Previous() + }, + "addToPlaylist": func() { + if !InsidePlaylist { + r, _ := UI.expandedView.GetSelection() + conn.Add(dirTree.children[r].absolutePath) } - case 113: // q : Key - { - UI.App.Stop() - return nil + }, + "toggleRandom": func() { + err := conn.Random(!Random) + if err == nil { + Random = !Random } - case 115: // s: key - { - conn.Stop() - return nil + }, + "toggleRepeat": func() { + err := conn.Repeat(!Repeat) + if err == nil { + Repeat = !Repeat } - case 117: // u : key - { - _, err = conn.Update("") - if err != nil { - panic(err) - } - return nil + }, + "decreaseVolume": func() { + if Volume <= 0 { + Volume = 0 + } else { + Volume -= 10 } - case 100: // d : key - { - if InsidePlaylist { - r, _ := UI.expandedView.GetSelection() - conn.Delete(r, -1) - return nil - } else { - return e - } + conn.SetVolume(int(Volume)) + }, + "increaseVolume": func() { + if Volume >= 100 { + Volume = 100 + } else { + Volume += 10 } - default: - { - return e + conn.SetVolume(int(Volume)) + }, + "navigateToFiles": func() { + InsidePlaylist = false + UI.Navbar.Select(1, 0) + Update(*conn, dirTree.children, UI.expandedView) + }, + "navigateToPlaylist": func() { + InsidePlaylist = true + UI.Navbar.Select(0, 0) + UpdatePlaylist(*conn, UI.expandedView) + }, + "navigateToMostPlayed": func() { + InsidePlaylist = false + UI.Navbar.Select(2, 0) + }, + "quit": func() { + UI.App.Stop() + }, + "stop": func() { + conn.Stop() + }, + "updateDB": func() { + _, err = conn.Update("") + if err != nil { + panic(err) } + }, + "deleteSongFromPlaylist": func() { + if InsidePlaylist { + r, _ := UI.expandedView.GetSelection() + conn.Delete(r, -1) + } + }, + } + + config.GenerateKeyMap(FUNC_MAP) + + UI.expandedView.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey { + if val, ok := config.KEY_MAP[int(e.Rune())]; ok { + FUNC_MAP[val]() + return nil + } else { + return e } }) diff --git a/utils.go b/utils.go index 1d558dc..e89f3a1 100644 --- a/utils.go +++ b/utils.go @@ -1,8 +1,6 @@ package main import ( - "fmt" - "io/ioutil" "strconv" "strings" "syscall" @@ -82,27 +80,3 @@ func formatString(a interface{}) string { return "Paused" } } - -func getMusicDirectory() string { - content, err := ioutil.ReadFile(HOME_DIR + "/.config/mpd/mpd.conf") - if err != nil { - fmt.Println("No Config File for mpd Found") - panic(err) - } - ab := string(content) - maps := strings.Split(ab, "\n") - for _, j := range maps { - if strings.Contains(j, "music_directory") { - s := strings.SplitAfter(strings.ReplaceAll(j, " ", ""), "y")[1] - s = strings.ReplaceAll(s, "\t", "") - d := "" - for z, m := range s { - if (z != 0) && (z != (len(s) - 1)) { - d += string(m) - } - } - return d - } - } - return "" -}