From ae11ed555cb350ea4c11fc1625f4a56229969ddd Mon Sep 17 00:00:00 2001 From: Physick <96335032+DegustatorPonos@users.noreply.github.com> Date: Sat, 13 Dec 2025 12:32:15 +0500 Subject: Tracks meta --- .gitignore | 1 + Audio/List.go | 31 +++++++++++++++++++++++++++++++ Audio/Meta.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ Endpoints/SongList.go | 26 +++++++++++++++++++++----- Settings.json | 7 ++++++- Settings/Distribution.go | 28 ++++++++++++++++++++++++++++ Settings/Settings.go | 6 ++++++ go.mod | 5 ++++- go.sum | 2 ++ main.go | 3 ++- static/index.css | 46 ++++++++++++++++++++++++++++++++++++++++++++++ static/index.html | 24 ++++++++++++++++++++---- static/index.js | 18 ++++++++++++++++++ 13 files changed, 230 insertions(+), 12 deletions(-) create mode 100644 .gitignore create mode 100644 Audio/List.go create mode 100644 Settings/Distribution.go create mode 100644 static/index.css 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 @@
+ -