diff --git a/NOTES.md b/NOTES.md index 793ee3e..d3ce923 100644 --- a/NOTES.md +++ b/NOTES.md @@ -18,14 +18,3 @@ - [ ] save playlist - [ ] delete playlist -# mpd state - -- [x] state ("play", "stop", "pause") -- [x] repeat -- [ ] shuffle -- [ ] xfade -- [x] volume -- [x] track length/progress - - [x] track progress (seek) -- [ ] track name - diff --git a/config.ini b/config.ini deleted file mode 100644 index 3f54744..0000000 --- a/config.ini +++ /dev/null @@ -1,12 +0,0 @@ -[mpd] -host = localhost -port = 6600 -#username = -#pasword = - -[ui] -hostname = localhost -port = 8080 -tls = no -cert = cert.pem -key = key.pem diff --git a/favicon.xcf b/favicon.xcf deleted file mode 100644 index 65f9164..0000000 Binary files a/favicon.xcf and /dev/null differ diff --git a/flake.lock b/flake.lock index 37265ac..bc26a35 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { @@ -28,11 +28,11 @@ ] }, "locked": { - "lastModified": 1701687253, - "narHash": "sha256-qJCMxIKWXonJODPF2oV7mCd0xu7VYVenTucrY0bizto=", + "lastModified": 1699950847, + "narHash": "sha256-xN/yVtqHb7kimHA/WvQFrEG5WS38t0K+A/W+j/WhQWM=", "owner": "tweag", "repo": "gomod2nix", - "rev": "001bbfa22e2adeb87c34c6015e5694e88721cabe", + "rev": "05c993c9a5bd55a629cd45ed49951557b7e9c61a", "type": "github" }, "original": { @@ -43,11 +43,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1702206697, - "narHash": "sha256-vE9oEx3Y8TO5MnWwFlmopjHd1JoEBno+EhsfUCq5iR8=", + "lastModified": 1701237617, + "narHash": "sha256-Ryd8xpNDY9MJnBFDYhB37XSFIxCPVVVXAbInNPa95vs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "29d6c96900b9b576c2fb89491452f283aa979819", + "rev": "85306ef2470ba705c97ce72741d56e42d0264015", "type": "github" }, "original": { diff --git a/go.mod b/go.mod index a81631f..25cad23 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/labstack/echo-contrib v0.15.0 github.com/labstack/echo/v4 v4.11.2 golang.org/x/net v0.17.0 - gopkg.in/ini.v1 v1.67.0 ) require ( diff --git a/go.sum b/go.sum index 153f4b9..c6db544 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/mpd.go b/mpd.go deleted file mode 100644 index 12bac1c..0000000 --- a/mpd.go +++ /dev/null @@ -1,190 +0,0 @@ -package main - -import ( - "github.com/fhs/gompd/v2/mpd" - "github.com/labstack/echo/v4" - "log" - "net/http" - "strconv" - "time" -) - -// MPD API calls - -func previousTrack(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - err = conn.Previous() - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func nextTrack(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - err = conn.Next() - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func stopPlayback(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - err = conn.Stop() - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func resumePlayback(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - err = conn.Pause(false) - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func pausePlayback(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - err = conn.Pause(true) - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func seek(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - seconds, err := strconv.Atoi(c.Param("seconds")) - if err != nil { - log.Fatalln(err) - } - - if seconds < 0 { - return c.String(http.StatusBadRequest, "seconds must be positive integer") - } - - err = conn.SeekCur(time.Duration(seconds)*time.Second, false) - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func toggleRepeat(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - status, err := conn.Status() - if err != nil { - log.Fatalln(err) - } - if status["repeat"] == "1" { - err = conn.Repeat(false) - } else { - err = conn.Repeat(true) - } - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func toggleRandom(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - status, err := conn.Status() - if err != nil { - log.Fatalln(err) - } - if status["toggleRandom"] == "1" { - err = conn.Random(false) - } else { - err = conn.Random(true) - } - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} - -func setVolume(c echo.Context) error { - // Connect to MPD server - conn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer conn.Close() - - level, err := strconv.Atoi(c.Param("level")) - if err != nil { - log.Fatalln(err) - } - - if level > 100 || level < 0 { - return c.String(http.StatusBadRequest, "Volume must be between 0 and 100") - } - - err = conn.SetVolume(level) - if err != nil { - log.Fatalln(err) - } - - return c.String(http.StatusOK, "") -} diff --git a/server.go b/server.go index fbd02c5..80d1eec 100644 --- a/server.go +++ b/server.go @@ -1,52 +1,20 @@ package main import ( - "encoding/json" "fmt" "github.com/fhs/gompd/v2/mpd" "github.com/labstack/echo-contrib/echoprometheus" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "golang.org/x/net/websocket" - "gopkg.in/ini.v1" "log" "net/http" - "os" - "os/exec" + "strconv" "strings" + "time" ) -type Config struct { - MPD struct { - Hostname string `ini:"hostname"` - Port int `ini:"port"` - Username string `ini:"username"` - Password string `ini:"password"` - } `ini:"mpd"` - UI struct { - Hostname string `ini:"hostname"` - Port int `ini:"port"` - Tls bool `ini:"tls"` - Certificate string `ini:"cert"` - Key string `ini:"key"` - } `ini:"ui"` -} - func main() { - iniData, err := ini.Load("config.ini") - if err != nil { - fmt.Printf("Fail to read configuration file: %v", err) - os.Exit(1) - } - - var config Config - - err = iniData.MapTo(&config) - if err != nil { - fmt.Printf("Fail to parse configuration file: %v", err) - os.Exit(1) - } - e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) @@ -88,28 +56,17 @@ func main() { g.GET("/random", toggleRandom) g.GET("/volume/:level", setVolume) - g.GET("/download", downloadTrack) - e.GET("/ws", wsServe) - if config.UI.Tls { - e.Logger.Fatal(e.StartTLS(fmt.Sprintf("%s:%d", config.UI.Hostname, config.UI.Port), config.UI.Certificate, config.UI.Key)) - } else { - e.Logger.Fatal(e.Start(fmt.Sprintf("%s:%d", config.UI.Hostname, config.UI.Port))) - } + e.Logger.Fatal(e.StartTLS(":1323", "cert.pem", "key.pem")) + //e.Logger.Fatal(e.Start(":1323")) } func wsServe(c echo.Context) error { + fmt.Println("wsServe") websocket.Handler(func(ws *websocket.Conn) { defer ws.Close() - - // Connect to MPD server - mpdConn, err := mpd.Dial("tcp", "localhost:6600") - if err != nil { - log.Fatalln(err) - } - defer mpdConn.Close() - + fmt.Println("handler") for { // Read msg := "" @@ -118,66 +75,205 @@ func wsServe(c echo.Context) error { c.Logger().Error(err) break } else { - log.Println(msg) - if strings.ToLower(msg) == "#status" { - // TODO: Get current MPD status and return it - status, err := mpdConn.Status() - if err != nil { - log.Fatalln(err) - } - jsonData, err := json.Marshal(status) - if err != nil { - log.Fatalln(err) - } - err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_status\":%s}", string(jsonData))) + if strings.HasPrefix(strings.ToUpper(msg), "MPD#") { + // Forward MPD communication + // TODO: forward request to mpd and response back to client + err := websocket.Message.Send(ws, "MPD command received, processing... processing...") if err != nil { c.Logger().Error(err) } - } else if strings.HasPrefix(strings.ToLower(msg), "#download ") { + } else if strings.HasPrefix(strings.ToUpper(msg), "YT#") { // Download video link as audio file - uri := strings.SplitN(msg, " ", 2)[1] // TODO: implement yt-dlp integration - err := websocket.Message.Send(ws, fmt.Sprintf("Downloading %s", uri)) + err := websocket.Message.Send(ws, "YT-DLP command received, processing... processing...") if err != nil { c.Logger().Error(err) } } } + //fmt.Println(msg) } }).ServeHTTP(c.Response(), c.Request()) return nil } -func downloadTrack(c echo.Context) error { - // yt-dlp \ - // --no-wait-for-video \ - // --no-playlist \ - // --windows-filenames \ - // --newline \ - // --extract-audio \ - // --audio-format mp3 \ - // --audio-quality 0 \ - // -f bestaudio/best \ - // ${video_url} +// API calls - cmd := exec.Command( - "yt-dlp", - "--no-wait-for-video", - "--no-playlist", - "--windows-filenames", - "--newline", - "--extract-audio", - "--audio-format", "mp3", - "--audio-quality", "0", - "--format", "bestaudio/best", - c.Param("url"), - ) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { +func previousTrack(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + err = conn.Previous() + if err != nil { log.Fatalln(err) } - return c.String(http.StatusAccepted, "") + return c.String(http.StatusNoContent, "") +} + +func nextTrack(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + err = conn.Next() + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func stopPlayback(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + err = conn.Stop() + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func resumePlayback(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + err = conn.Pause(false) + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func pausePlayback(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + err = conn.Pause(true) + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func seek(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + seconds, err := strconv.Atoi(c.Param("seconds")) + if err != nil { + log.Fatalln(err) + } + + if seconds < 0 { + return c.String(http.StatusBadRequest, "seconds must be positive integer") + } + + err = conn.SeekCur(time.Duration(seconds)*time.Second, false) + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func toggleRepeat(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + status, err := conn.Status() + if err != nil { + log.Fatalln(err) + } + if status["repeat"] == "1" { + err = conn.Repeat(false) + } else { + err = conn.Repeat(true) + } + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func toggleRandom(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + status, err := conn.Status() + if err != nil { + log.Fatalln(err) + } + if status["toggleRandom"] == "1" { + err = conn.Random(false) + } else { + err = conn.Random(true) + } + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") +} + +func setVolume(c echo.Context) error { + // Connect to MPD server + conn, err := mpd.Dial("tcp", "localhost:6600") + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + level, err := strconv.Atoi(c.Param("level")) + if err != nil { + log.Fatalln(err) + } + + if level > 100 || level < 0 { + return c.String(http.StatusBadRequest, "Volume must be between 0 and 100") + } + + err = conn.SetVolume(level) + if err != nil { + log.Fatalln(err) + } + + return c.String(http.StatusNoContent, "") } diff --git a/static/favicon.ico b/static/favicon.ico deleted file mode 100644 index 6681b0e..0000000 Binary files a/static/favicon.ico and /dev/null differ diff --git a/static/test.html b/static/test.html deleted file mode 100644 index ed7c9e9..0000000 --- a/static/test.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - API test - - - - - - - - -