commit
766cfd3d9a
55
README.md
55
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
|
||||
|
34
config.go
34
config.go
@ -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.")
|
||||
}
|
||||
}
|
74
config/config.go
Normal file
74
config/config.go
Normal file
@ -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 ""
|
||||
}
|
68
config/kMap.go
Normal file
68
config/kMap.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
36
config/kMap_test.go
Normal file
36
config/kMap_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
238
main.go
238
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
|
||||
}
|
||||
})
|
||||
|
||||
|
26
utils.go
26
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 ""
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user