Files
MusicServer/internal/backend/music.go
T
Sansan 90d621c195 feat: Rename game to soundtrack throughout codebase
- Database migration: rename game table to soundtrack
- Rename game_name to soundtrack_name, game_id to soundtrack_id
- Update all SQL queries in soundtrack.sql, song.sql, song_list.sql, statistics.sql
- Regenerate sqlc code (soundtrack.sql.go, song.sql.go, etc.)
- Update backend: music.go, sync.go, statistics.go
- Update server: musicHandler.go, syncHandler.go, routes.go
- Update frontend: hello.go
- Keep URL paths as /games for backward compatibility

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-01 20:23:05 +02:00

323 lines
8.1 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.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))]
}