diff --git a/.idea/MusicServer.iml b/.idea/MusicServer.iml
deleted file mode 100644
index 47bf3f7..0000000
--- a/.idea/MusicServer.iml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
deleted file mode 100644
index 9a5fffc..0000000
--- a/.idea/dataSources.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- postgresql
- true
- org.postgresql.Driver
- jdbc:postgresql://localhost:5432/music_dev_local
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 9ce91d9..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index c37058b..0000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,192 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index d51ec60..7890a43 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,9 +19,9 @@ ENV DB_PASSWORD ""
ENV DB_NAME ""
COPY --from=build_go /music-server/MusicServer .
-COPY ./doc/swagger.yaml .
+COPY docss/swagger.yaml .
COPY ./songs/ ./songs/
COPY ./init.sh .
-RUN chmod 777 ./init.sh
+RUN chmod 777 ./conf.sh
CMD ./init.sh
\ No newline at end of file
diff --git a/app.yaml b/app.yaml
index 0a77807..083a3d4 100644
--- a/app.yaml
+++ b/app.yaml
@@ -1,5 +1,5 @@
application: musicserver
-version: 2.1.4
+version: 2.2.0
runtime: go115
api_version: go1
diff --git a/cmd/backend/main.go b/cmd/backend/main.go
new file mode 100644
index 0000000..dfc5ffd
--- /dev/null
+++ b/cmd/backend/main.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+ "music-server/pkg/conf"
+)
+
+func main() {
+ conf.SetupDb()
+
+ conf.SetupRestServer()
+
+ conf.CloseDb()
+}
diff --git a/database.go b/database.go
deleted file mode 100644
index 6312015..0000000
--- a/database.go
+++ /dev/null
@@ -1,246 +0,0 @@
-package main
-
-import (
- "context"
- "fmt"
- "github.com/jackc/pgtype"
- "github.com/jackc/pgx/v4/pgxpool"
- "os"
- "time"
-)
-
-var dbPool *pgxpool.Pool
-
-func initDB(host string, port int, user string, password string, dbname string) {
-
- psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
- "password=%s dbname=%s sslmode=disable",
- host, port, user, password, dbname)
-
- fmt.Println(psqlInfo)
-
- var err error
- dbPool, err = pgxpool.Connect(context.Background(), psqlInfo)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
- os.Exit(1)
- }
-
- var success string
- err = dbPool.QueryRow(context.Background(), "select 'Successfully connected!'").Scan(&success)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- os.Exit(1)
- }
- fmt.Println(success)
-}
-
-func closeDb() {
- dbPool.Close()
-}
-
-func getGameName(gameId int) string {
- var gameName = ""
- err := dbPool.QueryRow(context.Background(),
- "SELECT game_name FROM game WHERE id = $1", gameId).Scan(&gameName)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- return ""
- }
- return gameName
-}
-
-func setGameDeletionDate() {
- _, err := dbPool.Exec(context.Background(),
- "UPDATE game SET deleted=$1", time.Now())
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func clearSongs(gameId int) {
- if gameId == -1 {
- _, err := dbPool.Exec(context.Background(), "DELETE FROM song")
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
- } else {
- _, err := dbPool.Exec(context.Background(), "DELETE FROM song where game_id=$1", gameId)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
- }
-}
-
-func clearGames() {
- _, err := dbPool.Exec(context.Background(), "DELETE FROM game")
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func updateGameName(id int, name string, path string) {
- _, err := dbPool.Exec(context.Background(),
- "UPDATE game SET game_name=$1, path=$2, last_changed=$3 WHERE id=$4",
- name, path, time.Now(), id)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func removeDeletionDate(id int) {
- _, err := dbPool.Exec(context.Background(),
- "UPDATE game SET deleted=null WHERE id=$1", id)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func addSong(song SongData) {
- _, err := dbPool.Exec(context.Background(),
- "INSERT INTO song(game_id, song_name, path) VALUES ($1, $2, $3)",
- song.gameId, song.songName, song.path)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func findSongsFromGame(id int) []SongData {
- rows, err := dbPool.Query(context.Background(),
- "SELECT song_name, path, times_played FROM song WHERE game_id = $1", id)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- return nil
- }
-
- var songDataList []SongData
- for rows.Next() {
- var songName string
- var path string
- var timesPlayed int
-
- err := rows.Scan(&songName, &path, ×Played)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- }
-
- songDataList = append(songDataList, SongData{
- gameId: id,
- songName: songName,
- path: path,
- timesPlayed: timesPlayed,
- })
- }
- return songDataList
-}
-
-func getIdByGameName(name string) int {
- var gameId = -1
- err := dbPool.QueryRow(context.Background(),
- "SELECT id FROM game WHERE game_name = $1", name).Scan(&gameId)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- return -1
- }
- return gameId
-}
-
-func insertGame(name string, path string) int {
- gameId := -1
- err := dbPool.QueryRow(context.Background(),
- "INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
- name, path, time.Now()).Scan(&gameId)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- resetGameIdSeq()
- err2 := dbPool.QueryRow(context.Background(),
- "INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
- name, path, time.Now()).Scan(&gameId)
- if err2 != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- return -1
- }
- }
- return gameId
-}
-
-func insertGameWithExistingId(id int, name string, path string) {
- _, err := dbPool.Exec(context.Background(),
- "INSERT INTO game(id, game_name, path, added) VALUES ($1, $2, $3, $4)",
- id, name, path, time.Now())
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func resetGameIdSeq() {
- _, err := dbPool.Query(context.Background(), "SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);")
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func findAllGames() []GameData {
- rows, err := dbPool.Query(context.Background(),
- "SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs "+
- "FROM game "+
- "ORDER BY game_name")
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- }
- var gameList []GameData
- for rows.Next() {
- var id, timesPlayed int
- var numberOfSongs pgtype.Int4
- var gameName, path string
- var added, deleted, lastChanged, lastPlayed pgtype.Timestamp
- err := rows.Scan(&id, &gameName, &added, &deleted, &lastChanged, &path, ×Played, &lastPlayed, &numberOfSongs)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
- }
- gameList = append(gameList, GameData{
- id: id,
- gameName: gameName,
- added: added.Time,
- deleted: deleted.Time,
- lastChanged: lastChanged.Time,
- path: path,
- timesPlayed: timesPlayed,
- lastPlayed: lastPlayed.Time,
- numberOfSongs: numberOfSongs.Int,
- })
- }
- return gameList
-}
-
-func addGamePlayed(id int) {
- _, err := dbPool.Exec(context.Background(),
- "UPDATE game SET times_played=times_played+1, last_played=now() WHERE id=$1", id)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func addSongPlayed(id int, name string) {
- _, err := dbPool.Exec(context.Background(),
- "UPDATE song SET times_played=times_played+1 WHERE game_id=$1 AND song_name=$2", id, name)
- if err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
- }
-}
-
-func testf() {
- rows, dbErr := dbPool.Query(context.Background(), "select game_name from game")
- if dbErr != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
- os.Exit(1)
- }
- for rows.Next() {
- var gameName string
- dbErr = rows.Scan(&gameName)
- if dbErr != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
- }
- _, _ = fmt.Fprintf(os.Stderr, "%v\n", gameName)
- }
-}
diff --git a/doc/swagger.yaml b/docs/swagger.yaml
similarity index 98%
rename from doc/swagger.yaml
rename to docs/swagger.yaml
index 542c60e..57aa742 100644
--- a/doc/swagger.yaml
+++ b/docs/swagger.yaml
@@ -1,8 +1,8 @@
openapi: 3.0.3
info:
- version: "2.1.4"
+ version: "2.2.0"
title: "Music Server"
- description: "Added a check to see if song exists before returning it, if not a new song will be picked up."
+ description: "Changed the structure of the whole application, should be no changes to functionality."
contact:
email: "zarnor91@gmail.com"
servers:
diff --git a/go.mod b/go.mod
index f62ba4a..ca33012 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module MusicServer
+module music-server
go 1.15
diff --git a/indexFacade.go b/indexFacade.go
deleted file mode 100644
index 486a871..0000000
--- a/indexFacade.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package main
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "os"
-)
-
-func indexHandler(w http.ResponseWriter, r *http.Request) {
- setCorsAndNoCacheHeaders(&w, r)
-
- if r.URL.Path == "/version" {
- w.Header().Add("Content-Type", "application/json")
-
- testf()
-
- data := VersionData{Version: "2.1.4",
- Changelog: "Game list should now be sorted, a new endpoint with the game list in random order have been added.",
- History: []VersionData{
- {
- Version: "2.1.3",
- Changelog: "Added a check to see if song exists before returning it, if not a new song will be picked up.",
- },
- {
- Version: "2.1.2",
- Changelog: "Added test server to swagger file.",
- },
- {
- Version: "2.1.1",
- Changelog: "Fixed bug where wrong song was showed as currently played.",
- },
- {
- Version: "2.1.0",
- Changelog: "Added /addQue to add the last received song to the songQue. " +
- "Changed /rand and /rand/low to not add song to the que. " +
- "Changed /next to not call /rand when the end of the que is reached, instead the last song in the que will be resent.",
- },
- {
- Version: "2.0.3",
- Changelog: "Another small change that should fix the caching problem.",
- },
- {
- Version: "2.0.2",
- Changelog: "Hopefully fixed the caching problem with random.",
- },
- {
- Version: "2.0.1",
- Changelog: "Fixed CORS",
- },
- {
- Version: "2.0.0",
- Changelog: "Rebuilt the application in Go.",
- },
- {
- Version: "1.2.0",
- Changelog: "Made the /sync endpoint async. " +
- "Fixed bug where the game list wasn't reloaded when using /reset. " +
- "Fixed bug where the songNo showed in the list didn't match what should be sent.",
- },
- {
- Version: "1.1.0",
- Changelog: "Added sync endpoint, don't really trust it to 100%, would say beta. " +
- "Fixed bug with /next after /previous. Added /reset endpoint. " +
- "Added some info more to /info and /list.",
- },
- {
- Version: "1.0.0",
- Changelog: "Added swagger documentation. Created version 1.0.",
- },
- {
- Version: "0.5.5",
- Changelog: "Added increase played endpoint.",
- },
- },
- }
- _ = json.NewEncoder(w).Encode(data)
-
- } else if r.URL.Path == "/doc" {
- http.ServeFile(w, r, "./doc/swagger.yaml")
-
- } else if r.URL.Path == "/" {
- rows, dbErr := dbPool.Query(context.Background(), "select game_name from game")
- if dbErr != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
- os.Exit(1)
- }
- for rows.Next() {
- var gameName string
- dbErr = rows.Scan(&gameName)
- if dbErr != nil {
- _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
- }
- _, _ = fmt.Fprintf(w, "%v\n", gameName)
- }
-
- _, err := fmt.Fprint(w, "Hello, World!!")
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- }
- } else {
- http.NotFound(w, r)
- return
- }
-
-}
diff --git a/musicFacade.go b/musicFacade.go
deleted file mode 100644
index eb04651..0000000
--- a/musicFacade.go
+++ /dev/null
@@ -1,348 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math/rand"
- "net/http"
- "os"
- "strconv"
- "time"
-)
-
-var currentSong = -1
-var games []GameData
-var songQue []SongData
-var lastFetched SongData
-
-func getSoundCheckSong() string {
- reset()
-
- files, err := ioutil.ReadDir("songs")
- if err != nil {
- log.Fatal(err)
- }
- fileInfo := files[rand.Intn(len(files))]
- return "songs/" + fileInfo.Name()
-}
-
-func reset() {
- songQue = nil
- currentSong = -1
- games = findAllGames()
-}
-
-func addLatestToQue() {
- if lastFetched.path != "" {
- currentSong = len(songQue)
- songQue = append(songQue, lastFetched)
- lastFetched = SongData{}
- }
-}
-
-func getRandomSong() string {
- if games == nil || len(games) == 0 {
- games = findAllGames()
- }
-
- song := getSongFromList(games)
-
- lastFetched = song
- return song.path
-}
-
-func getSongFromList(games []GameData) SongData {
- songFound := false
- var song SongData
- for !songFound {
- game := getRandomGame(games)
- songs := findSongsFromGame(game.id)
- song = songs[rand.Intn(len(songs))]
-
- //Check if file exists and open
- openFile, err := os.Open(song.path)
- if err != nil {
- //File not found
- log.Fatal("Song not found, maybe delete song and/or game" + song.songName + " songPath: " + song.path)
- } else {
- songFound = true
- }
-
- err = openFile.Close()
- if err != nil {
- log.Fatal(err)
- }
- }
- return song
-}
-
-func getRandomSongLowChance() string {
- gameList := findAllGames()
-
- var listOfGames []GameData
-
- var averagePlayed = getAveragePlayed(gameList)
-
- for _, data := range gameList {
- var timesToAdd = averagePlayed - data.timesPlayed
- if timesToAdd <= 0 {
- listOfGames = append(listOfGames, data)
- } else {
- for i := 0; i < timesToAdd; i++ {
- listOfGames = append(listOfGames, data)
- }
- }
- }
-
- song := getSongFromList(listOfGames)
-
- lastFetched = song
- return song.path
-
-}
-
-func getSongInfo() SongInfo {
- if songQue == nil {
- return SongInfo{}
- }
- var currentSongData = songQue[currentSong]
-
- currentGameData := getCurrentGame(currentSongData)
-
- return SongInfo{
- Game: currentGameData.gameName,
- GamePlayed: currentGameData.timesPlayed,
- Song: currentSongData.songName,
- SongPlayed: currentSongData.timesPlayed,
- CurrentlyPlaying: true,
- SongNo: currentSong,
- }
-}
-
-func getPlayedSongs() []SongInfo {
- var songList []SongInfo
-
- for i, song := range songQue {
- gameData := getCurrentGame(song)
- songList = append(songList, SongInfo{
- Game: gameData.gameName,
- GamePlayed: gameData.timesPlayed,
- Song: song.songName,
- SongPlayed: song.timesPlayed,
- CurrentlyPlaying: i == currentSong,
- SongNo: i,
- })
- }
- return songList
-}
-
-func getSong(song string) string {
- currentSong, _ = strconv.Atoi(song)
- if currentSong >= len(songQue) {
- currentSong = len(songQue) - 1
- } else if currentSong < 0 {
- currentSong = 0
- }
- var songData = songQue[currentSong]
- return songData.path
-}
-
-func getAllGames() []string {
- if games == nil || len(games) == 0 {
- games = findAllGames()
- }
-
- var jsonArray []string
- for _, game := range games {
- jsonArray = append(jsonArray, game.gameName)
- }
- return jsonArray
-}
-
-func getAllGamesRandom() []string {
- if games == nil || len(games) == 0 {
- games = findAllGames()
- }
-
- var jsonArray []string
- for _, game := range games {
- jsonArray = append(jsonArray, game.gameName)
- }
- rand.Seed(time.Now().UnixNano())
- rand.Shuffle(len(jsonArray), func(i, j int) { jsonArray[i], jsonArray[j] = jsonArray[j], jsonArray[i] })
- return jsonArray
-}
-
-func setPlayed(songNumber int) {
- if songQue == nil || len(songQue) == 0 || songNumber >= len(songQue) {
- return
- }
- var songData = songQue[songNumber]
- addGamePlayed(songData.gameId)
- addSongPlayed(songData.gameId, songData.songName)
-}
-
-func getNextSong() string {
- if songQue == nil {
- return ""
- }
- if currentSong == len(songQue)-1 || currentSong == -1 {
- var songData = songQue[currentSong]
- return songData.path
- } else {
- currentSong = currentSong + 1
- var songData = songQue[currentSong]
- return songData.path
- }
-}
-
-func getPreviousSong() string {
- if songQue == nil {
- return ""
- }
- if currentSong == -1 || currentSong == 0 {
- var songData = songQue[0]
- return songData.path
- } else {
- currentSong = currentSong - 1
- var songData = songQue[currentSong]
- return songData.path
- }
-}
-
-func musicHandler(w http.ResponseWriter, r *http.Request) {
- setCorsAndNoCacheHeaders(&w, r)
- if r.URL.Path == "/music" && r.Method == http.MethodGet {
- song := r.URL.Query().Get("song")
- if song == "" {
- w.WriteHeader(http.StatusBadRequest)
- _, err := fmt.Fprint(w, "song can't be empty")
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- }
- } else {
- s := getSong(song)
- sendSong(w, s)
- }
- } else if r.URL.Path == "/music/first" && r.Method == http.MethodGet {
- song := getSoundCheckSong()
- sendSong(w, song)
-
- } else if r.URL.Path == "/music/reset" && r.Method == http.MethodGet {
- reset()
- w.WriteHeader(http.StatusOK)
-
- } else if r.URL.Path == "/music/rand" && r.Method == http.MethodGet {
- song := getRandomSong()
- sendSong(w, song)
-
- } else if r.URL.Path == "/music/rand/low" && r.Method == http.MethodGet {
- chance := getRandomSongLowChance()
- sendSong(w, chance)
-
- } else if r.URL.Path == "/music/info" && r.Method == http.MethodGet {
- w.Header().Add("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(getSongInfo())
-
- } else if r.URL.Path == "/music/list" && r.Method == http.MethodGet {
- w.Header().Add("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(getPlayedSongs())
-
- } else if r.URL.Path == "/music/next" {
- song := getNextSong()
- sendSong(w, song)
-
- } else if r.URL.Path == "/music/previous" {
- song := getPreviousSong()
- sendSong(w, song)
-
- } else if r.URL.Path == "/music/all" && r.Method == http.MethodGet {
- w.Header().Add("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(getAllGames())
-
- } else if r.URL.Path == "/music/all/random" && r.Method == http.MethodGet {
- w.Header().Add("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(getAllGamesRandom())
-
- } else if r.URL.Path == "/music/played" && r.Method == http.MethodPut {
- var p Played
- err := json.NewDecoder(r.Body).Decode(&p)
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- setPlayed(p.song)
- w.WriteHeader(http.StatusOK)
- } else if r.URL.Path == "/music/addQue" && r.Method == http.MethodGet {
- addLatestToQue()
- w.WriteHeader(http.StatusOK)
- }
-}
-
-func sendSong(writer http.ResponseWriter, Filename string) {
- fmt.Println("Client requests: " + Filename)
-
- //Check if file exists and open
- openFile, err := os.Open(Filename)
- if err != nil {
- //File not found, send 404
- http.Error(writer, "Song not found.", 404)
- return
- }
- defer func(openFile *os.File) {
- _ = openFile.Close()
- }(openFile) //Close after function return
-
- //File is found, create and send the correct headers
-
- //Get the Content-Type of the file
- //Create a buffer to store the header of the file in
- FileHeader := make([]byte, 512)
- //Copy the headers into the FileHeader buffer
- _, _ = openFile.Read(FileHeader)
- //Get content type of file
- //FileContentType := http.DetectContentType(FileHeader)
-
- //Get the file size
- FileStat, _ := openFile.Stat() //Get info from file
- FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string
-
- //Send the headers
- //writer.Header().Set("Content-Disposition", "attachment; filename="+Filename)
- writer.Header().Set("Content-Type", "audio/mpeg")
- writer.Header().Set("Content-Length", FileSize)
-
- //Send the file
- //We read 512 bytes from the file already, so we reset the offset back to 0
- _, _ = openFile.Seek(0, 0)
- _, _ = io.Copy(writer, openFile) //'Copy' the file to the client
- return
-}
-
-type Played struct {
- song int
-}
-
-func getCurrentGame(currentSongData SongData) GameData {
- for _, game := range games {
- if game.id == currentSongData.gameId {
- return game
- }
- }
- return GameData{}
-}
-
-func getAveragePlayed(gameList []GameData) int {
- var sum int
- for _, data := range gameList {
- sum += data.timesPlayed
- }
- return sum / len(gameList)
-}
-
-func getRandomGame(listOfGames []GameData) GameData {
- return listOfGames[rand.Intn(len(listOfGames))]
-}
diff --git a/musicserver.go b/musicserver.go
deleted file mode 100644
index b406e39..0000000
--- a/musicserver.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package main
-
-import (
- "fmt"
- "github.com/gorilla/mux"
- "log"
- "net/http"
- "os"
- "strconv"
- "time"
-)
-
-func main() {
- // Get the value of an Environment Variable
- host := os.Getenv("DB_HOST")
- dbPort, dbPortErr := strconv.Atoi(os.Getenv("DB_PORT"))
- if dbPortErr != nil {
- dbPort = 0
- }
- username := os.Getenv("DB_USERNAME")
- password := os.Getenv("DB_PASSWORD")
- dbName := os.Getenv("DB_NAME")
-
- fmt.Printf("host: %s, dbPort: %v, username: %s, password: %s, dbName: %s\n",
- host, dbPort, username, password, dbName)
-
- if host == "" {
- host = "localhost"
- }
- if dbPort == 0 {
- dbPort = 5432
- }
- if username == "" {
- username = "sebastian"
- }
- if password == "" {
- password = "950100"
- }
- if dbName == "" {
- dbName = "music_dev_local"
- }
-
- initDB(host, dbPort, username, password, dbName)
- defer closeDb()
-
- r := mux.NewRouter()
- r.HandleFunc("/sync", syncHandler)
- r.HandleFunc("/sync/{func}", syncHandler)
- r.HandleFunc("/music", musicHandler)
- r.HandleFunc("/music/{func}", musicHandler)
- r.HandleFunc("/music/{func}/{func2}", musicHandler)
- r.HandleFunc("/{func}", indexHandler)
- http.Handle("/", r)
-
- port := os.Getenv("PORT")
- if port == "" {
- port = "8080"
- log.Printf("Defaulting to port %s", port)
- }
-
- log.Printf("Listening on port %s", port)
- log.Printf("Open http://localhost:%s in the browser", port)
- log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
-}
-
-type VersionData struct {
- Version string `json:"version"`
- Changelog string `json:"changelog"`
- History []VersionData `json:"history"`
-}
-
-type SongInfo struct {
- Game string `json:"Game"`
- GamePlayed int `json:"GamePlayed"`
- Song string `json:"Song"`
- SongPlayed int `json:"SongPlayed"`
- CurrentlyPlaying bool `json:"CurrentlyPlaying"`
- SongNo int `json:"SongNo"`
-}
-
-type GameData struct {
- id int
- gameName string
- added time.Time
- deleted time.Time
- lastChanged time.Time
- path string
- timesPlayed int
- lastPlayed time.Time
- numberOfSongs int32
-}
-
-type SongData struct {
- gameId int
- songName string
- path string
- timesPlayed int
-}
-
-func setCorsAndNoCacheHeaders(w *http.ResponseWriter, r *http.Request) {
- var etagHeaders = []string{
- "ETag",
- "If-Modified-Since",
- "If-Match",
- "If-None-Match",
- "If-Range",
- "If-Unmodified-Since",
- }
-
- (*w).Header().Set("Expires", "Tue, 03 Jul 2001 06:00:00 GMT")
- (*w).Header().Set("Last-Modified", time.Now().String()+" GMT")
- (*w).Header().Set("Cache-Control", "no-cache, no-store, private, max-age=0")
- (*w).Header().Set("Pragma", "no-cache")
- (*w).Header().Set("X-Accel-Expires", "0")
- (*w).Header().Set("Access-Control-Allow-Origin", "*")
-
- for _, v := range etagHeaders {
- if r.Header.Get(v) != "" {
- r.Header.Del(v)
- }
- }
-}
diff --git a/pkg/api/index.go b/pkg/api/index.go
new file mode 100644
index 0000000..505ca7a
--- /dev/null
+++ b/pkg/api/index.go
@@ -0,0 +1,32 @@
+package api
+
+import (
+ "encoding/json"
+ "fmt"
+ "music-server/pkg/helpers"
+ "music-server/pkg/server"
+ "net/http"
+)
+
+func IndexHandler(w http.ResponseWriter, r *http.Request) {
+ helpers.SetCorsAndNoCacheHeaders(&w, r)
+
+ if r.URL.Path == "/version" {
+ w.Header().Add("Content-Type", "application/json")
+
+ history := server.GetVersionHistory()
+ _ = json.NewEncoder(w).Encode(history)
+
+ } else if r.URL.Path == "/docs" {
+ http.ServeFile(w, r, "./docs/swagger.yaml")
+
+ } else if r.URL.Path == "/" {
+ _, err := fmt.Fprint(w, "Hello, World!!")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ } else {
+ http.NotFound(w, r)
+ return
+ }
+}
diff --git a/pkg/api/music.go b/pkg/api/music.go
new file mode 100644
index 0000000..77813cf
--- /dev/null
+++ b/pkg/api/music.go
@@ -0,0 +1,79 @@
+package api
+
+import (
+ "encoding/json"
+ "fmt"
+ "music-server/pkg/helpers"
+ "music-server/pkg/models"
+ "music-server/pkg/server"
+ "net/http"
+)
+
+func MusicHandler(w http.ResponseWriter, r *http.Request) {
+ helpers.SetCorsAndNoCacheHeaders(&w, r)
+ if r.URL.Path == "/music" && r.Method == http.MethodGet {
+ song := r.URL.Query().Get("song")
+ if song == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ _, err := fmt.Fprint(w, "song can't be empty")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ } else {
+ s := server.GetSong(song)
+ helpers.SendSong(w, s)
+ }
+ } else if r.URL.Path == "/music/first" && r.Method == http.MethodGet {
+ song := server.GetSoundCheckSong()
+ helpers.SendSong(w, song)
+
+ } else if r.URL.Path == "/music/reset" && r.Method == http.MethodGet {
+ server.Reset()
+ w.WriteHeader(http.StatusOK)
+
+ } else if r.URL.Path == "/music/rand" && r.Method == http.MethodGet {
+ song := server.GetRandomSong()
+ helpers.SendSong(w, song)
+
+ } else if r.URL.Path == "/music/rand/low" && r.Method == http.MethodGet {
+ chance := server.GetRandomSongLowChance()
+ helpers.SendSong(w, chance)
+
+ } else if r.URL.Path == "/music/info" && r.Method == http.MethodGet {
+ w.Header().Add("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(server.GetSongInfo())
+
+ } else if r.URL.Path == "/music/list" && r.Method == http.MethodGet {
+ w.Header().Add("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(server.GetPlayedSongs())
+
+ } else if r.URL.Path == "/music/next" {
+ song := server.GetNextSong()
+ helpers.SendSong(w, song)
+
+ } else if r.URL.Path == "/music/previous" {
+ song := server.GetPreviousSong()
+ helpers.SendSong(w, song)
+
+ } else if r.URL.Path == "/music/all" && r.Method == http.MethodGet {
+ w.Header().Add("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(server.GetAllGames())
+
+ } else if r.URL.Path == "/music/all/random" && r.Method == http.MethodGet {
+ w.Header().Add("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(server.GetAllGamesRandom())
+
+ } else if r.URL.Path == "/music/played" && r.Method == http.MethodPut {
+ var p models.Played
+ err := json.NewDecoder(r.Body).Decode(&p)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ server.SetPlayed(p.Song)
+ w.WriteHeader(http.StatusOK)
+ } else if r.URL.Path == "/music/addQue" && r.Method == http.MethodGet {
+ server.AddLatestToQue()
+ w.WriteHeader(http.StatusOK)
+ }
+}
diff --git a/pkg/api/sync.go b/pkg/api/sync.go
new file mode 100644
index 0000000..56b738e
--- /dev/null
+++ b/pkg/api/sync.go
@@ -0,0 +1,28 @@
+package api
+
+import (
+ "fmt"
+ "music-server/pkg/helpers"
+ "music-server/pkg/server"
+ "net/http"
+)
+
+func SyncHandler(w http.ResponseWriter, r *http.Request) {
+ helpers.SetCorsAndNoCacheHeaders(&w, r)
+ if r.URL.Path == "/sync" {
+ w.Header().Add("Content-Type", "application/json")
+ server.SyncGames()
+ _, err := fmt.Fprint(w, "Games are synced")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+
+ } else if r.URL.Path == "/sync/reset" {
+ w.Header().Add("Content-Type", "application/json")
+ server.ResetDB()
+ _, err := fmt.Fprint(w, "Games and songs are deleted from the database")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ }
+}
diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go
new file mode 100644
index 0000000..ebaf095
--- /dev/null
+++ b/pkg/conf/conf.go
@@ -0,0 +1,70 @@
+package conf
+
+import (
+ "fmt"
+ "github.com/gorilla/mux"
+ "log"
+ "music-server/pkg/api"
+ "music-server/pkg/db"
+ "net/http"
+ "os"
+ "strconv"
+)
+
+func SetupDb() {
+ // Get the value of an Environment Variable
+ host := os.Getenv("DB_HOST")
+ dbPort, dbPortErr := strconv.Atoi(os.Getenv("DB_PORT"))
+ if dbPortErr != nil {
+ dbPort = 0
+ }
+ username := os.Getenv("DB_USERNAME")
+ password := os.Getenv("DB_PASSWORD")
+ dbName := os.Getenv("DB_NAME")
+
+ fmt.Printf("host: %s, dbPort: %v, username: %s, password: %s, dbName: %s\n",
+ host, dbPort, username, password, dbName)
+
+ if host == "" {
+ host = "localhost"
+ }
+ if dbPort == 0 {
+ dbPort = 5432
+ }
+ if username == "" {
+ username = "sebastian"
+ }
+ if password == "" {
+ password = "950100"
+ }
+ if dbName == "" {
+ dbName = "music_dev_local"
+ }
+
+ db.InitDB(host, dbPort, username, password, dbName)
+}
+
+func CloseDb() {
+ defer db.CloseDb()
+}
+
+func SetupRestServer() {
+ r := mux.NewRouter()
+ r.HandleFunc("/sync", api.SyncHandler)
+ r.HandleFunc("/sync/{func}", api.SyncHandler)
+ r.HandleFunc("/music", api.MusicHandler)
+ r.HandleFunc("/music/{func}", api.MusicHandler)
+ r.HandleFunc("/music/{func}/{func2}", api.MusicHandler)
+ r.HandleFunc("/{func}", api.IndexHandler)
+ http.Handle("/", r)
+
+ port := os.Getenv("PORT")
+ if port == "" {
+ port = "8080"
+ log.Printf("Defaulting to port %s", port)
+ }
+
+ log.Printf("Listening on port %s", port)
+ log.Printf("Open http://localhost:%s in the browser", port)
+ log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
+}
diff --git a/pkg/db/dbHelper.go b/pkg/db/dbHelper.go
new file mode 100644
index 0000000..fcc4ff0
--- /dev/null
+++ b/pkg/db/dbHelper.go
@@ -0,0 +1,61 @@
+package db
+
+import (
+ "context"
+ "fmt"
+ "github.com/jackc/pgx/v4/pgxpool"
+ "os"
+)
+
+var pool *pgxpool.Pool
+
+func InitDB(host string, port int, user string, password string, dbname string) {
+
+ psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
+ "password=%s dbname=%s sslmode=disable",
+ host, port, user, password, dbname)
+
+ fmt.Println(psqlInfo)
+
+ var err error
+ pool, err = pgxpool.Connect(context.Background(), psqlInfo)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
+ os.Exit(1)
+ }
+
+ var success string
+ err = pool.QueryRow(context.Background(), "select 'Successfully connected!'").Scan(&success)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Println(success)
+}
+
+func CloseDb() {
+ pool.Close()
+}
+
+func Testf() {
+ rows, dbErr := pool.Query(context.Background(), "select game_name from game")
+ if dbErr != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
+ os.Exit(1)
+ }
+ for rows.Next() {
+ var gameName string
+ dbErr = rows.Scan(&gameName)
+ if dbErr != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
+ }
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", gameName)
+ }
+}
+
+func resetGameIdSeq() {
+ _, err := pool.Query(context.Background(), "SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);")
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
diff --git a/pkg/db/game.go b/pkg/db/game.go
new file mode 100644
index 0000000..0dc0a36
--- /dev/null
+++ b/pkg/db/game.go
@@ -0,0 +1,133 @@
+package db
+
+import (
+ "context"
+ "fmt"
+ "github.com/jackc/pgtype"
+ "music-server/pkg/models"
+ "os"
+ "time"
+)
+
+func GetGameName(gameId int) string {
+ var gameName = ""
+ err := pool.QueryRow(context.Background(),
+ "SELECT game_name FROM game WHERE id = $1", gameId).Scan(&gameName)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ return ""
+ }
+ return gameName
+}
+
+func SetGameDeletionDate() {
+ _, err := pool.Exec(context.Background(),
+ "UPDATE game SET deleted=$1", time.Now())
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
+
+func ClearGames() {
+ _, err := pool.Exec(context.Background(), "DELETE FROM game")
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
+
+func UpdateGameName(id int, name string, path string) {
+ _, err := pool.Exec(context.Background(),
+ "UPDATE game SET game_name=$1, path=$2, last_changed=$3 WHERE id=$4",
+ name, path, time.Now(), id)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
+
+func RemoveDeletionDate(id int) {
+ _, err := pool.Exec(context.Background(),
+ "UPDATE game SET deleted=null WHERE id=$1", id)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
+
+func GetIdByGameName(name string) int {
+ var gameId = -1
+ err := pool.QueryRow(context.Background(),
+ "SELECT id FROM game WHERE game_name = $1", name).Scan(&gameId)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ return -1
+ }
+ return gameId
+}
+
+func InsertGame(name string, path string) int {
+ gameId := -1
+ err := pool.QueryRow(context.Background(),
+ "INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
+ name, path, time.Now()).Scan(&gameId)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ resetGameIdSeq()
+ err2 := pool.QueryRow(context.Background(),
+ "INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
+ name, path, time.Now()).Scan(&gameId)
+ if err2 != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ return -1
+ }
+ }
+ return gameId
+}
+
+func InsertGameWithExistingId(id int, name string, path string) {
+ _, err := pool.Exec(context.Background(),
+ "INSERT INTO game(id, game_name, path, added) VALUES ($1, $2, $3, $4)",
+ id, name, path, time.Now())
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
+
+func FindAllGames() []models.GameData {
+ rows, err := pool.Query(context.Background(),
+ "SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs "+
+ "FROM game "+
+ "ORDER BY game_name")
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ }
+ var gameList []models.GameData
+ for rows.Next() {
+ var id, timesPlayed int
+ var numberOfSongs pgtype.Int4
+ var gameName, path string
+ var added, deleted, lastChanged, lastPlayed pgtype.Timestamp
+ err := rows.Scan(&id, &gameName, &added, &deleted, &lastChanged, &path, ×Played, &lastPlayed, &numberOfSongs)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ }
+ gameList = append(gameList, models.GameData{
+ Id: id,
+ GameName: gameName,
+ Added: added.Time,
+ Deleted: deleted.Time,
+ LastChanged: lastChanged.Time,
+ Path: path,
+ TimesPlayed: timesPlayed,
+ LastPlayed: lastPlayed.Time,
+ NumberOfSongs: numberOfSongs.Int,
+ })
+ }
+ return gameList
+}
+
+func AddGamePlayed(id int) {
+ _, err := pool.Exec(context.Background(),
+ "UPDATE game SET times_played=times_played+1, last_played=now() WHERE id=$1", id)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
diff --git a/pkg/db/song.go b/pkg/db/song.go
new file mode 100644
index 0000000..4e847e1
--- /dev/null
+++ b/pkg/db/song.go
@@ -0,0 +1,68 @@
+package db
+
+import (
+ "context"
+ "fmt"
+ "music-server/pkg/models"
+ "os"
+)
+
+func ClearSongs(gameId int) {
+ if gameId == -1 {
+ _, err := pool.Exec(context.Background(), "DELETE FROM song")
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+ } else {
+ _, err := pool.Exec(context.Background(), "DELETE FROM song where game_id=$1", gameId)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+ }
+}
+
+func AddSong(song models.SongData) {
+ _, err := pool.Exec(context.Background(),
+ "INSERT INTO song(game_id, song_name, path) VALUES ($1, $2, $3)",
+ song.GameId, song.SongName, song.Path)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
+
+func FindSongsFromGame(id int) []models.SongData {
+ rows, err := pool.Query(context.Background(),
+ "SELECT song_name, path, times_played FROM song WHERE game_id = $1", id)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ return nil
+ }
+
+ var songDataList []models.SongData
+ for rows.Next() {
+ var songName string
+ var path string
+ var timesPlayed int
+
+ err := rows.Scan(&songName, &path, ×Played)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
+ }
+
+ songDataList = append(songDataList, models.SongData{
+ GameId: id,
+ SongName: songName,
+ Path: path,
+ TimesPlayed: timesPlayed,
+ })
+ }
+ return songDataList
+}
+
+func AddSongPlayed(id int, name string) {
+ _, err := pool.Exec(context.Background(),
+ "UPDATE song SET times_played=times_played+1 WHERE game_id=$1 AND song_name=$2", id, name)
+ if err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
+ }
+}
diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go
new file mode 100644
index 0000000..13e6629
--- /dev/null
+++ b/pkg/helpers/helpers.go
@@ -0,0 +1,74 @@
+package helpers
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "strconv"
+ "time"
+)
+
+func SetCorsAndNoCacheHeaders(w *http.ResponseWriter, r *http.Request) {
+ var etagHeaders = []string{
+ "ETag",
+ "If-Modified-Since",
+ "If-Match",
+ "If-None-Match",
+ "If-Range",
+ "If-Unmodified-Since",
+ }
+
+ (*w).Header().Set("Expires", "Tue, 03 Jul 2001 06:00:00 GMT")
+ (*w).Header().Set("Last-Modified", time.Now().String()+" GMT")
+ (*w).Header().Set("Cache-Control", "no-cache, no-store, private, max-age=0")
+ (*w).Header().Set("Pragma", "no-cache")
+ (*w).Header().Set("X-Accel-Expires", "0")
+ (*w).Header().Set("Access-Control-Allow-Origin", "*")
+
+ for _, v := range etagHeaders {
+ if r.Header.Get(v) != "" {
+ r.Header.Del(v)
+ }
+ }
+}
+
+func SendSong(writer http.ResponseWriter, Filename string) {
+ fmt.Println("Client requests: " + Filename)
+
+ //Check if file exists and open
+ openFile, err := os.Open(Filename)
+ if err != nil {
+ //File not found, send 404
+ http.Error(writer, "Song not found.", 404)
+ return
+ }
+ defer func(openFile *os.File) {
+ _ = openFile.Close()
+ }(openFile) //Close after function return
+
+ //File is found, create and send the correct headers
+
+ //Get the Content-Type of the file
+ //Create a buffer to store the header of the file in
+ FileHeader := make([]byte, 512)
+ //Copy the headers into the FileHeader buffer
+ _, _ = openFile.Read(FileHeader)
+ //Get content type of file
+ //FileContentType := http.DetectContentType(FileHeader)
+
+ //Get the file size
+ FileStat, _ := openFile.Stat() //Get info from file
+ FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string
+
+ //Send the headers
+ //writer.Header().Set("Content-Disposition", "attachment; filename="+Filename)
+ writer.Header().Set("Content-Type", "audio/mpeg")
+ writer.Header().Set("Content-Length", FileSize)
+
+ //Send the file
+ //We read 512 bytes from the file already, so we reset the offset back to 0
+ _, _ = openFile.Seek(0, 0)
+ _, _ = io.Copy(writer, openFile) //'Copy' the file to the client
+ return
+}
diff --git a/pkg/models/models.go b/pkg/models/models.go
new file mode 100644
index 0000000..bd9167d
--- /dev/null
+++ b/pkg/models/models.go
@@ -0,0 +1,41 @@
+package models
+
+import "time"
+
+type Played struct {
+ Song int
+}
+
+type VersionData struct {
+ Version string `json:"version"`
+ Changelog string `json:"changelog"`
+ History []VersionData `json:"history"`
+}
+
+type SongInfo struct {
+ Game string `json:"Game"`
+ GamePlayed int `json:"GamePlayed"`
+ Song string `json:"Song"`
+ SongPlayed int `json:"SongPlayed"`
+ CurrentlyPlaying bool `json:"CurrentlyPlaying"`
+ SongNo int `json:"SongNo"`
+}
+
+type GameData struct {
+ Id int
+ GameName string
+ Added time.Time
+ Deleted time.Time
+ LastChanged time.Time
+ Path string
+ TimesPlayed int
+ LastPlayed time.Time
+ NumberOfSongs int32
+}
+
+type SongData struct {
+ GameId int
+ SongName string
+ Path string
+ TimesPlayed int
+}
diff --git a/pkg/server/index.go b/pkg/server/index.go
new file mode 100644
index 0000000..05ddba2
--- /dev/null
+++ b/pkg/server/index.go
@@ -0,0 +1,76 @@
+package server
+
+import (
+ "music-server/pkg/db"
+ "music-server/pkg/models"
+)
+
+func GetVersionHistory() models.VersionData {
+ db.Testf()
+
+ data := models.VersionData{Version: "2.2.0",
+ Changelog: "Changed the structure of the whole application, should be no changes to functionality.",
+ History: []models.VersionData{
+ {
+ Version: "2.1.4",
+ Changelog: "Game list should now be sorted, a new endpoint with the game list in random order have been added.",
+ },
+ {
+ Version: "2.1.3",
+ Changelog: "Added a check to see if song exists before returning it, if not a new song will be picked up.",
+ },
+ {
+ Version: "2.1.2",
+ Changelog: "Added test server to swagger file.",
+ },
+ {
+ Version: "2.1.1",
+ Changelog: "Fixed bug where wrong song was showed as currently played.",
+ },
+ {
+ Version: "2.1.0",
+ Changelog: "Added /addQue to add the last received song to the songQue. " +
+ "Changed /rand and /rand/low to not add song to the que. " +
+ "Changed /next to not call /rand when the end of the que is reached, instead the last song in the que will be resent.",
+ },
+ {
+ Version: "2.0.3",
+ Changelog: "Another small change that should fix the caching problem.",
+ },
+ {
+ Version: "2.0.2",
+ Changelog: "Hopefully fixed the caching problem with random.",
+ },
+ {
+ Version: "2.0.1",
+ Changelog: "Fixed CORS",
+ },
+ {
+ Version: "2.0.0",
+ Changelog: "Rebuilt the application in Go.",
+ },
+ {
+ Version: "1.2.0",
+ Changelog: "Made the /sync endpoint async. " +
+ "Fixed bug where the game list wasn't reloaded when using /reset. " +
+ "Fixed bug where the songNo showed in the list didn't match what should be sent.",
+ },
+ {
+ Version: "1.1.0",
+ Changelog: "Added sync endpoint, don't really trust it to 100%, would say beta. " +
+ "Fixed bug with /next after /previous. Added /reset endpoint. " +
+ "Added some info more to /info and /list.",
+ },
+ {
+ Version: "1.0.0",
+ Changelog: "Added swagger documentation. Created version 1.0.",
+ },
+ {
+ Version: "0.5.5",
+ Changelog: "Added increase played endpoint.",
+ },
+ },
+ }
+
+ return data
+}
diff --git a/pkg/server/music.go b/pkg/server/music.go
new file mode 100644
index 0000000..ee4c04d
--- /dev/null
+++ b/pkg/server/music.go
@@ -0,0 +1,233 @@
+package server
+
+import (
+ "io/ioutil"
+ "log"
+ "math/rand"
+ "music-server/pkg/db"
+ "music-server/pkg/models"
+ "os"
+ "strconv"
+ "time"
+)
+
+var currentSong = -1
+var games []models.GameData
+var songQue []models.SongData
+var lastFetched models.SongData
+
+func GetSoundCheckSong() string {
+ Reset()
+
+ files, err := ioutil.ReadDir("songs")
+ if err != nil {
+ log.Fatal(err)
+ }
+ fileInfo := files[rand.Intn(len(files))]
+ return "songs/" + fileInfo.Name()
+}
+
+func Reset() {
+ songQue = nil
+ currentSong = -1
+ games = db.FindAllGames()
+}
+
+func AddLatestToQue() {
+ if lastFetched.Path != "" {
+ currentSong = len(songQue)
+ songQue = append(songQue, lastFetched)
+ lastFetched = models.SongData{}
+ }
+}
+
+func GetRandomSong() string {
+ if games == nil || len(games) == 0 {
+ games = db.FindAllGames()
+ }
+
+ song := getSongFromList(games)
+
+ lastFetched = song
+ return song.Path
+}
+
+func GetRandomSongLowChance() string {
+ gameList := db.FindAllGames()
+
+ var listOfGames []models.GameData
+
+ var averagePlayed = getAveragePlayed(gameList)
+
+ for _, data := range gameList {
+ var timesToAdd = averagePlayed - data.TimesPlayed
+ if timesToAdd <= 0 {
+ listOfGames = append(listOfGames, data)
+ } else {
+ for i := 0; i < timesToAdd; i++ {
+ listOfGames = append(listOfGames, data)
+ }
+ }
+ }
+
+ song := getSongFromList(listOfGames)
+
+ lastFetched = song
+ return song.Path
+
+}
+
+func GetSongInfo() models.SongInfo {
+ if songQue == nil {
+ return models.SongInfo{}
+ }
+ var currentSongData = songQue[currentSong]
+
+ currentGameData := getCurrentGame(currentSongData)
+
+ return models.SongInfo{
+ Game: currentGameData.GameName,
+ GamePlayed: currentGameData.TimesPlayed,
+ Song: currentSongData.SongName,
+ SongPlayed: currentSongData.TimesPlayed,
+ CurrentlyPlaying: true,
+ SongNo: currentSong,
+ }
+}
+
+func GetPlayedSongs() []models.SongInfo {
+ var songList []models.SongInfo
+
+ for i, song := range songQue {
+ gameData := getCurrentGame(song)
+ songList = append(songList, models.SongInfo{
+ Game: gameData.GameName,
+ GamePlayed: gameData.TimesPlayed,
+ Song: song.SongName,
+ SongPlayed: song.TimesPlayed,
+ CurrentlyPlaying: i == currentSong,
+ SongNo: i,
+ })
+ }
+ return songList
+}
+
+func GetSong(song string) string {
+ currentSong, _ = strconv.Atoi(song)
+ if currentSong >= len(songQue) {
+ currentSong = len(songQue) - 1
+ } else if currentSong < 0 {
+ currentSong = 0
+ }
+ var songData = songQue[currentSong]
+ return songData.Path
+}
+
+func GetAllGames() []string {
+ if games == nil || len(games) == 0 {
+ games = db.FindAllGames()
+ }
+
+ var jsonArray []string
+ for _, game := range games {
+ jsonArray = append(jsonArray, game.GameName)
+ }
+ return jsonArray
+}
+
+func GetAllGamesRandom() []string {
+ if games == nil || len(games) == 0 {
+ games = db.FindAllGames()
+ }
+
+ var jsonArray []string
+ for _, game := range games {
+ jsonArray = append(jsonArray, game.GameName)
+ }
+ rand.Seed(time.Now().UnixNano())
+ rand.Shuffle(len(jsonArray), func(i, j int) { jsonArray[i], jsonArray[j] = jsonArray[j], jsonArray[i] })
+ return jsonArray
+}
+
+func SetPlayed(songNumber int) {
+ if songQue == nil || len(songQue) == 0 || songNumber >= len(songQue) {
+ return
+ }
+ var songData = songQue[songNumber]
+ db.AddGamePlayed(songData.GameId)
+ db.AddSongPlayed(songData.GameId, songData.SongName)
+}
+
+func GetNextSong() string {
+ if songQue == nil {
+ return ""
+ }
+ if currentSong == len(songQue)-1 || currentSong == -1 {
+ var songData = songQue[currentSong]
+ return songData.Path
+ } else {
+ currentSong = currentSong + 1
+ var songData = songQue[currentSong]
+ return songData.Path
+ }
+}
+
+func GetPreviousSong() string {
+ if songQue == nil {
+ return ""
+ }
+ if currentSong == -1 || currentSong == 0 {
+ var songData = songQue[0]
+ return songData.Path
+ } else {
+ currentSong = currentSong - 1
+ var songData = songQue[currentSong]
+ return songData.Path
+ }
+}
+
+func getSongFromList(games []models.GameData) models.SongData {
+ songFound := false
+ var song models.SongData
+ for !songFound {
+ game := getRandomGame(games)
+ songs := db.FindSongsFromGame(game.Id)
+ song = songs[rand.Intn(len(songs))]
+
+ //Check if file exists and open
+ openFile, err := os.Open(song.Path)
+ if err != nil {
+ //File not found
+ log.Fatal("Song not found, maybe delete song and/or game" + song.SongName + " songPath: " + song.Path)
+ } else {
+ songFound = true
+ }
+
+ err = openFile.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ return song
+}
+
+func getCurrentGame(currentSongData models.SongData) models.GameData {
+ for _, game := range games {
+ if game.Id == currentSongData.GameId {
+ return game
+ }
+ }
+ return models.GameData{}
+}
+
+func getAveragePlayed(gameList []models.GameData) int {
+ var sum int
+ for _, data := range gameList {
+ sum += data.TimesPlayed
+ }
+ return sum / len(gameList)
+}
+
+func getRandomGame(listOfGames []models.GameData) models.GameData {
+ return listOfGames[rand.Intn(len(listOfGames))]
+}
diff --git a/syncFacade.go b/pkg/server/sync.go
similarity index 68%
rename from syncFacade.go
rename to pkg/server/sync.go
index 58f949b..8f111d9 100644
--- a/syncFacade.go
+++ b/pkg/server/sync.go
@@ -1,37 +1,18 @@
-package main
+package server
import (
"fmt"
"io/ioutil"
"log"
- "net/http"
+ "music-server/pkg/db"
+ "music-server/pkg/models"
"os"
"sort"
"strconv"
"strings"
)
-func syncHandler(w http.ResponseWriter, r *http.Request) {
- setCorsAndNoCacheHeaders(&w, r)
- if r.URL.Path == "/sync" {
- w.Header().Add("Content-Type", "application/json")
- syncGames()
- _, err := fmt.Fprint(w, "Games are synced")
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- }
-
- } else if r.URL.Path == "/sync/reset" {
- w.Header().Add("Content-Type", "application/json")
- resetDB()
- _, err := fmt.Fprint(w, "Games and songs are deleted from the database")
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- }
- }
-}
-
-func syncGames() {
+func SyncGames() {
host := os.Getenv("DB_HOST")
var dir string
if host != "" {
@@ -42,7 +23,7 @@ func syncGames() {
fmt.Printf("dir: %s\n", dir)
foldersToSkip := []string{".sync"}
fmt.Println(foldersToSkip)
- setGameDeletionDate()
+ db.SetGameDeletionDate()
files, err := ioutil.ReadDir(dir)
if err != nil {
@@ -76,6 +57,11 @@ func syncGames() {
}
}
+func ResetDB() {
+ db.ClearSongs(-1)
+ db.ClearGames()
+}
+
func getIdFromFile(file os.FileInfo) int {
name := file.Name()
if !file.IsDir() && strings.HasSuffix(name, ".id") {
@@ -89,24 +75,24 @@ func getIdFromFile(file os.FileInfo) int {
func checkIfChanged(id int, name string, path string) {
fmt.Printf("Id from file: %v\n", id)
- nameFromDb := getGameName(id)
+ nameFromDb := db.GetGameName(id)
fmt.Printf("Name from file: %v\n", name)
fmt.Printf("Name from DB: %v\n", nameFromDb)
if nameFromDb == "" {
fmt.Println("Not in db")
- insertGameWithExistingId(id, name, path)
+ db.InsertGameWithExistingId(id, name, path)
fmt.Println("Added to db")
} else if name != nameFromDb {
fmt.Println("Diff name")
- updateGameName(id, name, path)
+ db.UpdateGameName(id, name, path)
}
- removeDeletionDate(id)
+ db.RemoveDeletionDate(id)
}
func addNewGame(name string, path string) {
- newId := getIdByGameName(name)
+ newId := db.GetIdByGameName(name)
if newId == -1 {
- newId = insertGame(name, path)
+ newId = db.InsertGame(name, path)
}
fmt.Printf("newId = %v", newId)
@@ -121,8 +107,8 @@ func addNewGame(name string, path string) {
}
func checkSongs(gameDir string, gameId int) {
- songs := make([]SongData, 0)
- findSongsFromGame := findSongsFromGame(gameId)
+ songs := make([]models.SongData, 0)
+ findSongsFromGame := db.FindSongsFromGame(gameId)
files, err := ioutil.ReadDir(gameDir)
if err != nil {
@@ -132,22 +118,17 @@ func checkSongs(gameDir string, gameId int) {
path := gameDir + entry.Name()
songName := entry.Name()
if !entry.IsDir() && !strings.HasSuffix(songName, ".id") {
- songs = append(songs, SongData{gameId: gameId, songName: songName, path: path})
+ songs = append(songs, models.SongData{GameId: gameId, SongName: songName, Path: path})
}
}
if len(songs) != len(findSongsFromGame) {
- clearSongs(gameId)
+ db.ClearSongs(gameId)
for _, song := range songs {
- addSong(song)
+ db.AddSong(song)
}
}
}
-func resetDB() {
- clearSongs(-1)
- clearGames()
-}
-
func contains(s []string, searchTerm string) bool {
i := sort.SearchStrings(s, searchTerm)
return i < len(s) && s[i] == searchTerm
diff --git a/songs/01. Opening.mp3 b/songs/01. Opening.mp3
deleted file mode 100755
index 373c2d1..0000000
Binary files a/songs/01. Opening.mp3 and /dev/null differ
diff --git a/songs/01. Title.mp3 b/songs/01. Title.mp3
deleted file mode 100755
index 15bc24d..0000000
Binary files a/songs/01. Title.mp3 and /dev/null differ