06cbad708d
- Add Database struct in internal/db/database.go with Pool, Ctx, and RunMigrations() - Update server.go to use Database struct with NewServerInstance() - Add backend.go with InitBackend(), BackendRepo(), BackendCtx(), BackendPool() - Update music.go and sync.go to use BackendRepo() and BackendCtx() instead of db.Dbpool/db.Ctx - Update token_handler.go to accept pool parameter - Update routes.go to use s.db.Pool for middleware - Update cmd/main.go to use NewServerInstance() and HTTPServer() - Update test_helpers.go to initialize backend with test database - Update test files to use backend.BackendPool() and backend.BackendCtx() Benefits: - Easier to mock database for unit tests - Follows Go best practices (dependency injection) - Better architecture with explicit dependencies - RunMigrations() replaces old Migrate_db() function Note: Global db.Dbpool and db.Ctx still exist in dbHelper.go for backward compatibility with test_helpers.go, but production code no longer uses them. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
323 lines
7.9 KiB
Go
323 lines
7.9 KiB
Go
package backend
|
|
|
|
import (
|
|
"math/rand"
|
|
"music-server/internal/db/repository"
|
|
"music-server/internal/logging"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type SongInfo struct {
|
|
Game string `json:"Game"`
|
|
GamePlayed int32 `json:"GamePlayed"`
|
|
Song string `json:"Song"`
|
|
SongPlayed int32 `json:"SongPlayed"`
|
|
CurrentlyPlaying bool `json:"CurrentlyPlaying"`
|
|
SongNo int `json:"SongNo"`
|
|
}
|
|
|
|
var currentSong = -1
|
|
|
|
var gamesNew []repository.Game
|
|
|
|
var songQueNew []repository.Song
|
|
|
|
var lastFetchedNew repository.Song
|
|
|
|
func initRepo() {
|
|
// This function is kept for backward compatibility
|
|
// but now uses the backend package's initialized repo
|
|
// If not initialized, this will panic intentionally
|
|
if BackendRepo() == nil {
|
|
panic("backend not initialized - call backend.InitBackend() first")
|
|
}
|
|
}
|
|
|
|
func getAllGames() []repository.Game {
|
|
if len(gamesNew) == 0 {
|
|
initRepo()
|
|
gamesNew, _ = BackendRepo().FindAllGames(BackendCtx())
|
|
}
|
|
return gamesNew
|
|
|
|
}
|
|
|
|
func GetSoundCheckSong() string {
|
|
files, err := os.ReadDir("songs")
|
|
if err != nil {
|
|
logging.GetLogger().Fatal("Failed to read songs directory", zap.String("error", err.Error()))
|
|
}
|
|
fileInfo := files[rand.Intn(len(files))]
|
|
return "songs/" + fileInfo.Name()
|
|
}
|
|
|
|
func Reset() {
|
|
songQueNew = nil
|
|
currentSong = -1
|
|
initRepo()
|
|
gamesNew, _ = BackendRepo().FindAllGames(BackendCtx())
|
|
}
|
|
|
|
func AddLatestToQue() {
|
|
if lastFetchedNew.Path != "" {
|
|
currentSong = len(songQueNew)
|
|
songQueNew = append(songQueNew, lastFetchedNew)
|
|
lastFetchedNew = repository.Song{}
|
|
}
|
|
}
|
|
|
|
func AddLatestPlayed() {
|
|
if len(songQueNew) == 0 {
|
|
return
|
|
}
|
|
currentSongData := songQueNew[currentSong]
|
|
|
|
initRepo()
|
|
BackendRepo().AddGamePlayed(BackendCtx(), currentSongData.GameID)
|
|
BackendRepo().AddSongPlayed(BackendCtx(), repository.AddSongPlayedParams{GameID: currentSongData.GameID, SongName: currentSongData.SongName})
|
|
}
|
|
|
|
func SetPlayed(songNumber int) {
|
|
if len(songQueNew) == 0 || songNumber >= len(songQueNew) {
|
|
return
|
|
}
|
|
songData := songQueNew[songNumber]
|
|
initRepo()
|
|
BackendRepo().AddGamePlayed(BackendCtx(), songData.GameID)
|
|
BackendRepo().AddSongPlayed(BackendCtx(), repository.AddSongPlayedParams{GameID: songData.GameID, SongName: songData.SongName})
|
|
}
|
|
|
|
func GetRandomSong() string {
|
|
getAllGames()
|
|
if len(gamesNew) == 0 {
|
|
return ""
|
|
}
|
|
|
|
song := getSongFromList(gamesNew)
|
|
lastFetchedNew = song
|
|
return song.Path
|
|
}
|
|
|
|
func GetRandomSongLowChance() string {
|
|
getAllGames()
|
|
|
|
var listOfGames []repository.Game
|
|
|
|
var averagePlayed = getAveragePlayed()
|
|
|
|
for _, data := range gamesNew {
|
|
timesToAdd := averagePlayed - data.TimesPlayed
|
|
if timesToAdd <= 0 {
|
|
listOfGames = append(listOfGames, data)
|
|
} else {
|
|
for i := int32(0); i < timesToAdd; i++ {
|
|
listOfGames = append(listOfGames, data)
|
|
}
|
|
}
|
|
}
|
|
song := getSongFromList(listOfGames)
|
|
|
|
lastFetchedNew = song
|
|
return song.Path
|
|
|
|
}
|
|
|
|
func GetRandomSongClassic() string {
|
|
getAllGames()
|
|
|
|
var listOfAllSongs []repository.Song
|
|
for _, game := range gamesNew {
|
|
songList, _ := BackendRepo().FindSongsFromGame(BackendCtx(), game.ID)
|
|
listOfAllSongs = append(listOfAllSongs, songList...)
|
|
}
|
|
|
|
songFound := false
|
|
var song repository.Song
|
|
for !songFound {
|
|
song = listOfAllSongs[rand.Intn(len(listOfAllSongs))]
|
|
gameData, err := BackendRepo().GetGameById(BackendCtx(), song.GameID)
|
|
|
|
if err != nil {
|
|
BackendRepo().RemoveBrokenSong(BackendCtx(), song.Path)
|
|
logging.GetLogger().Warn("Song not found, removed from database",
|
|
zap.String("song", song.SongName),
|
|
zap.String("game", gameData.GameName),
|
|
zap.String("filename", *song.FileName))
|
|
continue
|
|
}
|
|
|
|
//Check if file exists and open
|
|
openFile, err := os.Open(song.Path)
|
|
if err != nil || (song.FileName != nil && gameData.Path+*song.FileName != song.Path) {
|
|
//File not found
|
|
BackendRepo().RemoveBrokenSong(BackendCtx(), song.Path)
|
|
logging.GetLogger().Warn("Song not found, removed from database",
|
|
zap.String("song", song.SongName),
|
|
zap.String("game", gameData.GameName),
|
|
zap.String("filename", *song.FileName))
|
|
} else {
|
|
songFound = true
|
|
}
|
|
err = openFile.Close()
|
|
if err != nil {
|
|
logging.GetLogger().Error("Failed to close file", zap.String("error", err.Error()))
|
|
}
|
|
}
|
|
lastFetchedNew = song
|
|
return song.Path
|
|
}
|
|
|
|
func GetSongInfo() SongInfo {
|
|
if songQueNew == nil {
|
|
return SongInfo{}
|
|
}
|
|
var currentSongData = songQueNew[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 songQueNew {
|
|
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(songQueNew) {
|
|
currentSong = len(songQueNew) - 1
|
|
} else if currentSong < 0 {
|
|
currentSong = 0
|
|
}
|
|
songData := songQueNew[currentSong]
|
|
return songData.Path
|
|
}
|
|
|
|
func GetAllGames() []string {
|
|
getAllGames()
|
|
|
|
var jsonArray []string
|
|
for _, game := range gamesNew {
|
|
jsonArray = append(jsonArray, game.GameName)
|
|
}
|
|
return jsonArray
|
|
}
|
|
|
|
func GetAllGamesRandom() []string {
|
|
getAllGames()
|
|
|
|
var jsonArray []string
|
|
for _, game := range gamesNew {
|
|
jsonArray = append(jsonArray, game.GameName)
|
|
}
|
|
rand.Shuffle(len(jsonArray), func(i, j int) { jsonArray[i], jsonArray[j] = jsonArray[j], jsonArray[i] })
|
|
return jsonArray
|
|
}
|
|
|
|
func GetNextSong() string {
|
|
if songQueNew == nil {
|
|
return ""
|
|
}
|
|
if currentSong == len(songQueNew)-1 || currentSong == -1 {
|
|
songData := songQueNew[currentSong]
|
|
return songData.Path
|
|
} else {
|
|
currentSong = currentSong + 1
|
|
songData := songQueNew[currentSong]
|
|
return songData.Path
|
|
}
|
|
}
|
|
|
|
func GetPreviousSong() string {
|
|
if songQueNew == nil {
|
|
return ""
|
|
}
|
|
if currentSong == -1 || currentSong == 0 {
|
|
songData := songQueNew[0]
|
|
return songData.Path
|
|
} else {
|
|
currentSong = currentSong - 1
|
|
songData := songQueNew[currentSong]
|
|
return songData.Path
|
|
}
|
|
}
|
|
|
|
func getSongFromList(games []repository.Game) repository.Song {
|
|
songFound := false
|
|
var song repository.Song
|
|
for !songFound {
|
|
game := getRandomGame(games)
|
|
songs, _ := BackendRepo().FindSongsFromGame(BackendCtx(), game.ID)
|
|
if len(songs) == 0 {
|
|
continue
|
|
}
|
|
song = songs[rand.Intn(len(songs))]
|
|
logging.GetLogger().Debug("Selected song", zap.String("song", song.SongName), zap.String("path", song.Path))
|
|
|
|
//Check if file exists and open
|
|
openFile, err := os.Open(song.Path)
|
|
if err != nil || (song.FileName != nil && game.Path+*song.FileName != song.Path) || (song.FileName != nil && strings.HasSuffix(*song.FileName, ".wav")) {
|
|
//File not found
|
|
BackendRepo().RemoveBrokenSong(BackendCtx(), song.Path)
|
|
logging.GetLogger().Warn("Song not found, removed from database",
|
|
zap.String("song", song.SongName),
|
|
zap.String("game", game.GameName),
|
|
zap.Any("filename", song.FileName))
|
|
} else {
|
|
songFound = true
|
|
}
|
|
|
|
err = openFile.Close()
|
|
if err != nil {
|
|
logging.GetLogger().Error("Failed to close file", zap.String("path", song.Path), zap.String("error", err.Error()))
|
|
}
|
|
}
|
|
return song
|
|
}
|
|
|
|
func getCurrentGame(currentSongData repository.Song) repository.Game {
|
|
for _, game := range gamesNew {
|
|
if game.ID == currentSongData.GameID {
|
|
return game
|
|
}
|
|
}
|
|
return repository.Game{}
|
|
}
|
|
|
|
func getAveragePlayed() int32 {
|
|
getAllGames()
|
|
var sum int32
|
|
for _, data := range gamesNew {
|
|
sum += data.TimesPlayed
|
|
}
|
|
return sum / int32(len(gamesNew))
|
|
}
|
|
|
|
func getRandomGame(listOfGames []repository.Game) repository.Game {
|
|
return listOfGames[rand.Intn(len(listOfGames))]
|
|
}
|