summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhysick <96335032+DegustatorPonos@users.noreply.github.com>2025-12-06 12:05:43 +0500
committerPhysick <96335032+DegustatorPonos@users.noreply.github.com>2025-12-06 12:05:43 +0500
commit5fd81183f54662fdf08549c0d635e80166c62fe9 (patch)
tree0b730dd4f19c7446fabcc2ee7cc7313e94e67fe3
initial endpoints
-rw-r--r--Audio/Meta.go35
-rw-r--r--Endpoints/Audio.go46
-rw-r--r--Endpoints/Common.go8
-rw-r--r--Endpoints/SongList.go56
-rw-r--r--Settings.json3
-rw-r--r--Settings/Settings.go66
-rw-r--r--go.mod5
-rw-r--r--go.sum2
-rw-r--r--main.go19
-rw-r--r--static/index.html9
-rw-r--r--static/index.js5
11 files changed, 254 insertions, 0 deletions
diff --git a/Audio/Meta.go b/Audio/Meta.go
new file mode 100644
index 0000000..9e288f3
--- /dev/null
+++ b/Audio/Meta.go
@@ -0,0 +1,35 @@
+package audio
+
+import (
+ "io"
+ "os"
+ "time"
+
+ "github.com/tcolgate/mp3"
+)
+
+func GetSongDuration(path string) (time.Duration, error) {
+ var file, fopenErr = os.Open(path)
+ if fopenErr != nil {
+ return time.Duration(0), fopenErr
+ }
+ defer file.Close()
+
+ var track = mp3.NewDecoder(file)
+ var duration time.Duration
+ var frame mp3.Frame
+ var skipped = 0
+
+ for {
+ var err = track.Decode(&frame, &skipped)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return time.Duration(0), err
+ }
+
+ duration += frame.Duration()
+ }
+ return duration, nil
+}
diff --git a/Endpoints/Audio.go b/Endpoints/Audio.go
new file mode 100644
index 0000000..f9dca20
--- /dev/null
+++ b/Endpoints/Audio.go
@@ -0,0 +1,46 @@
+package endpoints
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+
+ settings "physick.ru/culture_exam/Settings"
+)
+
+type getErr struct {
+ Error string
+}
+
+func (base getErr) String() string {
+ var outp, jsonErr = json.Marshal(base)
+ if jsonErr != nil {
+ return fmt.Sprintf("Failed to parse settings: %s", jsonErr.Error())
+ }
+ return string(outp)
+}
+
+func getSong(w http.ResponseWriter, r *http.Request) {
+ var songName = r.URL.Query().Get("name")
+ if len(songName) == 0 {
+ fmt.Fprint(w, getErr { Error: "No song name provided" })
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+ var fullPath = fmt.Sprintf("%s/%s", settings.Current.SongsLocation, songName)
+ var file, fopenErr = os.ReadFile(fullPath)
+ if fopenErr != nil {
+ fmt.Fprint(w, getErr { Error: fopenErr.Error()})
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+ w.Header().Add("Content-Type", "audio/mpeg")
+ var audioChunks = bytes.NewBuffer(file)
+
+ if _, err := audioChunks.WriteTo(w); err != nil {
+ log.Println(err)
+ }
+}
diff --git a/Endpoints/Common.go b/Endpoints/Common.go
new file mode 100644
index 0000000..b30afd0
--- /dev/null
+++ b/Endpoints/Common.go
@@ -0,0 +1,8 @@
+package endpoints
+
+import "net/http"
+
+func RegisterEndpoints() {
+ http.HandleFunc("/api/getSongs", getSongs)
+ http.HandleFunc("/api/getSong", getSong)
+}
diff --git a/Endpoints/SongList.go b/Endpoints/SongList.go
new file mode 100644
index 0000000..9830c74
--- /dev/null
+++ b/Endpoints/SongList.go
@@ -0,0 +1,56 @@
+package endpoints
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+
+ audio "physick.ru/culture_exam/Audio"
+ settings "physick.ru/culture_exam/Settings"
+)
+
+type Songs = struct {
+ Name string
+ Duration int
+}
+
+type SongsList struct {
+ Songs []Songs
+}
+
+func (base SongsList) String() string {
+ var outp, jsonErr = json.Marshal(base)
+ if jsonErr != nil {
+ return fmt.Sprintf("Failed to parse settings: %s", jsonErr.Error())
+ }
+ return string(outp)
+}
+
+func getSongs(w http.ResponseWriter, r *http.Request) {
+ var files, dirErr = os.ReadDir(settings.Current.SongsLocation)
+ if dirErr != nil {
+ log.Printf("Failed to look up songs list: %s", dirErr.Error())
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ var outp = SongsList {
+ Songs: make([]Songs, 0, len(files)),
+ }
+ for _, v := range files {
+ var fullPath = fmt.Sprintf("%s/%s", settings.Current.SongsLocation, v.Name())
+ var duration, durErr = audio.GetSongDuration(fullPath)
+ if durErr != nil {
+ log.Printf("Failed to parse file metadata: File: %s, error: %s\n", fullPath, durErr.Error())
+ continue
+ }
+ var song = Songs {
+ Name: v.Name(),
+ Duration: int(duration.Seconds()),
+ }
+ outp.Songs = append(outp.Songs, song)
+ }
+ w.Header().Add("Content-Type", "application/json")
+ fmt.Fprint(w, outp)
+}
diff --git a/Settings.json b/Settings.json
new file mode 100644
index 0000000..9f14747
--- /dev/null
+++ b/Settings.json
@@ -0,0 +1,3 @@
+{
+ "SongsLocation": "/home/physick/Music/Architects"
+}
diff --git a/Settings/Settings.go b/Settings/Settings.go
new file mode 100644
index 0000000..e859677
--- /dev/null
+++ b/Settings/Settings.go
@@ -0,0 +1,66 @@
+package settings
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+)
+
+const settingsLocation = "Settings.json"
+
+type Settings struct {
+ SongsLocation string
+}
+
+var defaultSettings = Settings {
+ SongsLocation: "./Songs",
+}
+
+func (base Settings) String() string {
+ var outp, jsonErr = json.Marshal(base)
+ if jsonErr != nil {
+ return fmt.Sprintf("Failed to parse settings: %s", jsonErr.Error())
+ }
+ return string(outp)
+}
+
+var Current Settings = Settings{}
+
+func ReadSettings() {
+ var file, fopenerr = os.ReadFile(settingsLocation)
+ if fopenerr != nil {
+ var success, inintBody = createSettingsFile()
+ if success {
+ file = inintBody
+ } else {
+ panic(fmt.Sprintf("Unable to load or create settings file. \nOriginal error: %v\n", fopenerr.Error()))
+ }
+ }
+ var data = Settings{}
+ json.Unmarshal(file, &data)
+ var validationErr = data.Validate()
+ if validationErr != nil {
+ panic(fmt.Sprintf("Failed to load settings file: %s", validationErr.Error()))
+ }
+ Current = data
+}
+
+func createSettingsFile() (bool, []byte ) {
+ var file, err = os.Create(settingsLocation)
+ if err != nil {
+ return false, nil
+ }
+ defer file.Close()
+ var inititalData = defaultSettings
+ var body, jsonerr = json.MarshalIndent(inititalData, "", " ")
+ if jsonerr != nil {
+ return false, nil
+ }
+ fmt.Fprint(file, string(body))
+ return true, body
+}
+
+// Returns an error with the issue description if the settings are invalid
+func (base *Settings) Validate() error {
+ return nil
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ce7d948
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module physick.ru/culture_exam
+
+go 1.25.4
+
+require github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..f2eda8d
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 h1:XQdibLKagjdevRB6vAjVY4qbSr8rQ610YzTkWcxzxSI=
+github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300/go.mod h1:FNa/dfN95vAYCNFrIKRrlRo+MBLbwmR9Asa5f2ljmBI=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..72e796f
--- /dev/null
+++ b/main.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+
+ endpoints "physick.ru/culture_exam/Endpoints"
+ settings "physick.ru/culture_exam/Settings"
+)
+
+func main() {
+ settings.ReadSettings()
+ fmt.Println(settings.Current)
+ endpoints.RegisterEndpoints()
+ http.Handle("/", http.FileServer(http.FS(os.DirFS("./static"))))
+
+ http.ListenAndServe(":6969", nil)
+}
diff --git a/static/index.html b/static/index.html
new file mode 100644
index 0000000..8f1caf6
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <script src="./index.js"></script>
+ </head>
+ <body>
+ <h1> xdx </h1>
+ <button onclick="GetSongs()"> Get songs </button>
+ </body>
+</html>
diff --git a/static/index.js b/static/index.js
new file mode 100644
index 0000000..9f5070b
--- /dev/null
+++ b/static/index.js
@@ -0,0 +1,5 @@
+function GetSongs() {
+ fetch("/api/getSongs").then(r => r.json().then(body => {
+ console.log(body);
+ }));
+}