summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhysick <96335032+DegustatorPonos@users.noreply.github.com>2025-12-13 12:32:15 +0500
committerPhysick <96335032+DegustatorPonos@users.noreply.github.com>2025-12-13 12:32:15 +0500
commitae11ed555cb350ea4c11fc1625f4a56229969ddd (patch)
tree1d02b84131b713a2eed9a332ffac51997b0ec285
parent4b8ef84059e772ef7636c451356680ec5b0983c4 (diff)
Tracks meta
-rw-r--r--.gitignore1
-rw-r--r--Audio/List.go31
-rw-r--r--Audio/Meta.go45
-rw-r--r--Endpoints/SongList.go26
-rw-r--r--Settings.json7
-rw-r--r--Settings/Distribution.go28
-rw-r--r--Settings/Settings.go6
-rw-r--r--go.mod5
-rw-r--r--go.sum2
-rw-r--r--main.go3
-rw-r--r--static/index.css46
-rw-r--r--static/index.html24
-rw-r--r--static/index.js18
13 files changed, 230 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0ab0a99
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+Tracks/*
diff --git a/Audio/List.go b/Audio/List.go
new file mode 100644
index 0000000..46bdfff
--- /dev/null
+++ b/Audio/List.go
@@ -0,0 +1,31 @@
+package audio
+
+import (
+ "log"
+ "os"
+
+ settings "physick.ru/culture_exam/Settings"
+)
+
+type TracksList struct {
+ Tracks []TrackMeta
+}
+
+func GenerateTrackList(verbouse bool) (TracksList, error) {
+ var files, dirErr = os.ReadDir(settings.Current.SongsLocation)
+ if dirErr != nil {
+ return TracksList{}, dirErr
+ }
+ var outp = TracksList {
+ Tracks: make([]TrackMeta, 0, len(files)),
+ }
+ for _, v := range files {
+ var meta, err = GenerateMetadata(v.Name())
+ if err != nil {
+ log.Printf("Failed to parse the file info: %s", err.Error())
+ continue
+ }
+ outp.Tracks = append(outp.Tracks, meta)
+ }
+ return outp, nil
+}
diff --git a/Audio/Meta.go b/Audio/Meta.go
index 9e288f3..6b8343c 100644
--- a/Audio/Meta.go
+++ b/Audio/Meta.go
@@ -1,13 +1,44 @@
package audio
import (
+ "fmt"
"io"
"os"
"time"
+ "github.com/dhowden/tag"
"github.com/tcolgate/mp3"
+ settings "physick.ru/culture_exam/Settings"
)
+type TrackMeta struct {
+ Name string
+ Author string
+ Album string
+ Location string
+ Duration time.Duration
+}
+
+func GenerateMetadata(fileName string) (TrackMeta, error) {
+ var fullPath = fmt.Sprintf("%s/%s", settings.Current.SongsLocation, fileName)
+ var duration, error = GetSongDuration(fullPath)
+ // If file doesn't exist that will be caught there
+ if error != nil {
+ return TrackMeta{}, error
+ }
+ var Artist, Title, Album, metaErr = GetInfo(fullPath)
+ if metaErr != nil {
+ return TrackMeta{}, metaErr
+ }
+ return TrackMeta {
+ Name: Title,
+ Author: Artist,
+ Album: Album,
+ Location: fullPath,
+ Duration: duration,
+ }, nil
+}
+
func GetSongDuration(path string) (time.Duration, error) {
var file, fopenErr = os.Open(path)
if fopenErr != nil {
@@ -33,3 +64,17 @@ func GetSongDuration(path string) (time.Duration, error) {
}
return duration, nil
}
+
+// Author - name - album
+func GetInfo (filePath string) (string, string, string, error) {
+ f, err := os.Open(filePath)
+ if err != nil {
+ return "", "", "", err
+ }
+ defer f.Close()
+ m, err := tag.ReadFrom(f)
+ if err != nil {
+ return "", "", "", err
+ }
+ return m.Artist(), m.Title(), m.Album(), nil
+}
diff --git a/Endpoints/SongList.go b/Endpoints/SongList.go
index fe9ad60..0d0bf58 100644
--- a/Endpoints/SongList.go
+++ b/Endpoints/SongList.go
@@ -16,12 +16,15 @@ type Song struct {
Name string
Duration int
Start int
+ Meta audio.TrackMeta
}
type SongsList struct {
Songs []Song
}
+var cache *SongsList = nil
+
func (base Song) String() string {
var outp, jsonErr = json.Marshal(base)
if jsonErr != nil {
@@ -39,7 +42,7 @@ func (base SongsList) String() string {
}
func getSongs(w http.ResponseWriter, r *http.Request) {
- var songsList, err = getAvaliableSongs()
+ var songsList, err = GetAvaliableSongs()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Failed to get files: %v\n", err.Error())
@@ -50,7 +53,7 @@ func getSongs(w http.ResponseWriter, r *http.Request) {
}
func getRandomSong(w http.ResponseWriter, r *http.Request) {
- var songsList, err = getAvaliableSongs()
+ var songsList, err = GetAvaliableSongs()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Failed to get files: %v\n", err.Error())
@@ -58,10 +61,15 @@ func getRandomSong(w http.ResponseWriter, r *http.Request) {
}
var index = rand.Intn(len(songsList.Songs))
w.Header().Add("Content-Type", "application/json")
- fmt.Fprint(w, songsList.Songs[index])
+ var outp = songsList.Songs[index]
+ outp.Start = settings.Current.Distribution.GetDistributedInt(outp.Duration)
+ fmt.Fprint(w, outp)
}
-func getAvaliableSongs() (SongsList, error) {
+func GetAvaliableSongs() (SongsList, error) {
+ if cache != nil {
+ return *cache, nil
+ }
var files, dirErr = os.ReadDir(settings.Current.SongsLocation)
if dirErr != nil {
return SongsList{}, dirErr
@@ -69,18 +77,26 @@ func getAvaliableSongs() (SongsList, error) {
var outp = SongsList {
Songs: make([]Song, 0, len(files)),
}
- for _, v := range files {
+ for i, v := range files {
+ fmt.Printf("Indexing %d/%d\n", i+1, len(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 meta, metaErr = audio.GenerateMetadata(v.Name())
+ if metaErr != nil {
+ log.Printf("Failed to get metadata from file: %s", metaErr)
+ continue
+ }
var song = Song {
Name: v.Name(),
Duration: int(duration.Seconds()),
+ Meta: meta,
}
outp.Songs = append(outp.Songs, song)
}
+ cache = &outp
return outp, nil
}
diff --git a/Settings.json b/Settings.json
index 9f14747..fbf587c 100644
--- a/Settings.json
+++ b/Settings.json
@@ -1,3 +1,8 @@
{
- "SongsLocation": "/home/physick/Music/Architects"
+ "SongsLocation": "/home/physick/Git/culture_exam/Tracks",
+ "Distribution": {
+ "RandomisationChance": 0.5,
+ "MaxPointPercent": 0.75,
+ "StartSnapPercent": 0.1
+ }
}
diff --git a/Settings/Distribution.go b/Settings/Distribution.go
new file mode 100644
index 0000000..fc8ae13
--- /dev/null
+++ b/Settings/Distribution.go
@@ -0,0 +1,28 @@
+package settings
+
+import "math/rand"
+
+type distributionOptions struct {
+ // The chance of a value to be defaulted by zero
+ RandomisationChance float64
+ // The song start point will be between [0; len(song) * MaxPointPercent]
+ MaxPointPercent float64
+ // All the values less than this param will be defaulted to zero
+
+ StartSnapPercent float64
+}
+
+func (base distributionOptions) GetDistributedInt(dur int) int {
+ if rand.Float64() < base.RandomisationChance {
+ return 0
+ }
+ var random = rand.Float64() * base.MaxPointPercent
+ if random >= 1 {
+ panic("Random is greater than 1")
+ }
+ var randomDur = int(random * float64(dur))
+ if randomDur <= int(base.StartSnapPercent * float64(dur)) {
+ return 0
+ }
+ return randomDur
+}
diff --git a/Settings/Settings.go b/Settings/Settings.go
index e859677..bd22056 100644
--- a/Settings/Settings.go
+++ b/Settings/Settings.go
@@ -10,10 +10,16 @@ const settingsLocation = "Settings.json"
type Settings struct {
SongsLocation string
+ Distribution distributionOptions
}
var defaultSettings = Settings {
SongsLocation: "./Songs",
+ Distribution: distributionOptions{
+ RandomisationChance: 0.5,
+ MaxPointPercent: 0.75,
+ StartSnapPercent: 0.1,
+ },
}
func (base Settings) String() string {
diff --git a/go.mod b/go.mod
index ce7d948..2d5944b 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,7 @@ module physick.ru/culture_exam
go 1.25.4
-require github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 // indirect
+require (
+ github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 // indirect
+ github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 // indirect
+)
diff --git a/go.sum b/go.sum
index f2eda8d..3e4eb6b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,4 @@
+github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 h1:OtSeLS5y0Uy01jaKK4mA/WVIYtpzVm63vLVAPzJXigg=
+github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8/go.mod h1:apkPC/CR3s48O2D7Y++n1XWEpgPNNCjXYga3PPbJe2E=
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
index 72e796f..9ab2ae9 100644
--- a/main.go
+++ b/main.go
@@ -11,9 +11,10 @@ import (
func main() {
settings.ReadSettings()
- fmt.Println(settings.Current)
endpoints.RegisterEndpoints()
http.Handle("/", http.FileServer(http.FS(os.DirFS("./static"))))
+ var _, _ = endpoints.GetAvaliableSongs()
+ fmt.Println(settings.Current)
http.ListenAndServe(":6969", nil)
}
diff --git a/static/index.css b/static/index.css
new file mode 100644
index 0000000..7748957
--- /dev/null
+++ b/static/index.css
@@ -0,0 +1,46 @@
+body {
+ background-color: wheat;
+ height: 100%;
+ overflow: hidden;
+ margin: 0px;
+ padding: 0px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
+
+.controls > * {
+ padding: 0px;
+ margin: 0px;
+}
+
+.controls {
+ background-color: rgb(0%, 0%, 0%, 0.5);
+ height: 5%;
+ padding: 0px;
+ padding-top: 3px;
+ padding-left: 10px;
+ padding-right: 10px;
+ border-radius: 10px 10px 0px 0px;
+ margin: 0px;
+ display: flex;
+ flex-direction: row;
+ gap: 5px;
+
+ border-radius: 10px 10px 0px 0px;
+}
+
+.controlBtn {
+ background-color: rgb(0%, 0%, 0%, 0);
+ border: 1px solid black;
+ border-radius: 5px;
+ background-color: lightgray;
+ padding-left: 5px;
+ padding-right: 5px;
+ margin: 0px;
+}
+
+.controlElem {
+ margin: 0px;
+ padding: 0px;
+}
diff --git a/static/index.html b/static/index.html
index f6127d7..4ebe902 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1,13 +1,29 @@
<html>
<head>
+ <link href="index.css" rel="stylesheet" />
<script src="./index.js"></script>
</head>
<body>
- <h1> xdx </h1>
- <button onclick="GetSongs()"> Get songs </button>
- <button onclick="SetRandomSong()"> Set random song </button>
+ <div class="Info">
+ <h1> Rock culture exam </h1>
+ <h1 id="Artist" > Artist </h1>
+ <h1 id="Title" > Title </h1>
+ <h1 id="Album" > Album </h1>
+ <h1 id="Start" > Start position </h1>
+ </div>
+ <div class="controls">
+ <button onclick="Pause()" class="controlBtn">
+ <h1 onclick="Pause" class="controlElem"> Pause </h1>
+ </button>
+ <button onclick="Resume()" class="controlBtn">
+ <h1 onclick="Pause" class="controlElem"> Resume </h1>
+ </button>
+ <button onclick="SetRandomSong()" class="controlBtn">
+ <h1 onclick="Pause" class="controlElem"> Next song </h1>
+ </button>
+ </div>
<audio id="mainAudio">
- <source type="audio/mpeg" src=""/>
+ <source type="audio/mpeg" />
</audio>
</body>
</html>
diff --git a/static/index.js b/static/index.js
index b7056b1..6cda2f7 100644
--- a/static/index.js
+++ b/static/index.js
@@ -12,5 +12,23 @@ function SetRandomSong() {
console.log(body);
player.src = "/api/getSong?name=" + encodeURI(body.Name);
player.play();
+
+ document.getElementById("Artist").innerHTML = "Artist: " + body.Meta.Name;
+ document.getElementById("Title").innerHTML = "Title: " + body.Meta.Author;
+ document.getElementById("Album").innerHTML = "Album: " + body.Meta.Album;
+ document.getElementById("Start").innerHTML = "Start: " + body.Start + "s";
+
}));
}
+
+function Pause() {
+ const player = document.getElementById("mainAudio");
+ player.pause();
+ console.log("Paused audio");
+}
+
+function Resume() {
+ const player = document.getElementById("mainAudio");
+ player.play();
+ console.log("Paused audio");
+}