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.Soundtrack 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.Soundtrack { if len(gamesNew) == 0 { initRepo() gamesNew, _ = BackendRepo().FindAllSoundtracks(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().FindAllSoundtracks(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().AddSoundtrackPlayed(BackendCtx(), currentSongData.SoundtrackID) BackendRepo().AddSongPlayed(BackendCtx(), repository.AddSongPlayedParams{SoundtrackID: currentSongData.SoundtrackID, SongName: currentSongData.SongName}) } func SetPlayed(songNumber int) { if len(songQueNew) == 0 || songNumber >= len(songQueNew) { return } songData := songQueNew[songNumber] initRepo() BackendRepo().AddSoundtrackPlayed(BackendCtx(), songData.SoundtrackID) BackendRepo().AddSongPlayed(BackendCtx(), repository.AddSongPlayedParams{SoundtrackID: songData.SoundtrackID, 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.Soundtrack 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().FindSongsFromSoundtrack(BackendCtx(), game.ID) listOfAllSongs = append(listOfAllSongs, songList...) } songFound := false var song repository.Song for !songFound { song = listOfAllSongs[rand.Intn(len(listOfAllSongs))] gameData, err := BackendRepo().GetSoundtrackById(BackendCtx(), song.SoundtrackID) 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.SoundtrackName), 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.SoundtrackName), 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.SoundtrackName, 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.SoundtrackName, 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 GetAllSoundtracks() []string { getAllGames() var jsonArray []string for _, game := range gamesNew { jsonArray = append(jsonArray, game.SoundtrackName) } return jsonArray } func GetAllSoundtracksRandom() []string { getAllGames() var jsonArray []string for _, game := range gamesNew { jsonArray = append(jsonArray, game.SoundtrackName) } 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.Soundtrack) repository.Song { songFound := false var song repository.Song for !songFound { game := getRandomGame(games) songs, _ := BackendRepo().FindSongsFromSoundtrack(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.SoundtrackName), 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.Soundtrack { for _, game := range gamesNew { if game.ID == currentSongData.SoundtrackID { return game } } return repository.Soundtrack{} } func getAveragePlayed() int32 { getAllGames() var sum int32 for _, data := range gamesNew { sum += data.TimesPlayed } return sum / int32(len(gamesNew)) } func getRandomGame(listOfGames []repository.Soundtrack) repository.Soundtrack { return listOfGames[rand.Intn(len(listOfGames))] }