cec408187d
- 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>
326 lines
9.8 KiB
Go
326 lines
9.8 KiB
Go
package server
|
|
|
|
import (
|
|
"music-server/internal/backend"
|
|
"music-server/internal/logging"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/labstack/echo/v5"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type MusicHandler struct {
|
|
}
|
|
|
|
func NewMusicHandler() *MusicHandler {
|
|
return &MusicHandler{}
|
|
}
|
|
|
|
// GetSong godoc
|
|
// @Summary Get a specific song
|
|
// @Description Returns a specific song by name
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Produce audio/mpeg
|
|
// @Param song query string true "Song name"
|
|
// @Success 200 {file} file
|
|
// @Failure 400 {string} string "song can't be empty"
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music [get]
|
|
func (m *MusicHandler) GetSong(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
song := ctx.QueryParam("song")
|
|
if song == "" {
|
|
return ctx.String(http.StatusBadRequest, "song can't be empty")
|
|
}
|
|
songPath := backend.GetSong(song)
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// GetSoundCheckSong godoc
|
|
// @Summary Get sound check song
|
|
// @Description Returns the sound check song
|
|
// @Tags music
|
|
// @Produce audio/mpeg
|
|
// @Success 200 {file} file
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/soundTest [get]
|
|
func (m *MusicHandler) GetSoundCheckSong(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
songPath := backend.GetSoundCheckSong()
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// ResetMusic godoc
|
|
// @Summary Reset music state
|
|
// @Description Resets the music state
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Success 204
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/reset [get]
|
|
func (m *MusicHandler) ResetMusic(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
backend.Reset()
|
|
return ctx.NoContent(http.StatusOK)
|
|
}
|
|
|
|
// GetRandomSong godoc
|
|
// @Summary Get random song
|
|
// @Description Returns a random song
|
|
// @Tags music
|
|
// @Produce audio/mpeg
|
|
// @Success 200 {file} file
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/rand [get]
|
|
func (m *MusicHandler) GetRandomSong(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
songPath := backend.GetRandomSong()
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// GetRandomSongLowChance godoc
|
|
// @Summary Get random song with low chance
|
|
// @Description Returns a random song with low chance selection
|
|
// @Tags music
|
|
// @Produce audio/mpeg
|
|
// @Success 200 {file} file
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/rand/low [get]
|
|
func (m *MusicHandler) GetRandomSongLowChance(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
songPath := backend.GetRandomSongLowChance()
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// GetRandomSongClassic godoc
|
|
// @Summary Get random classic song
|
|
// @Description Returns a random song from the classic selection
|
|
// @Tags music
|
|
// @Produce audio/mpeg
|
|
// @Success 200 {file} file
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/rand/classic [get]
|
|
func (m *MusicHandler) GetRandomSongClassic(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
songPath := backend.GetRandomSongClassic()
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// GetSongInfo godoc
|
|
// @Summary Get current song info
|
|
// @Description Returns information about the current song
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /music/info [get]
|
|
func (m *MusicHandler) GetSongInfo(ctx *echo.Context) error {
|
|
song := backend.GetSongInfo()
|
|
return ctx.JSON(http.StatusOK, song)
|
|
}
|
|
|
|
// GetPlayedSongs godoc
|
|
// @Summary Get played songs list
|
|
// @Description Returns a list of played songs
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {array} map[string]interface{}
|
|
// @Router /music/list [get]
|
|
func (m *MusicHandler) GetPlayedSongs(ctx *echo.Context) error {
|
|
songList := backend.GetPlayedSongs()
|
|
return ctx.JSON(http.StatusOK, songList)
|
|
}
|
|
|
|
// GetNextSong godoc
|
|
// @Summary Get next song
|
|
// @Description Returns the next song in the queue
|
|
// @Tags music
|
|
// @Produce audio/mpeg
|
|
// @Success 200 {file} file
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/next [get]
|
|
func (m *MusicHandler) GetNextSong(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
songPath := backend.GetNextSong()
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// GetPreviousSong godoc
|
|
// @Summary Get previous song
|
|
// @Description Returns the previous song in the queue
|
|
// @Tags music
|
|
// @Produce audio/mpeg
|
|
// @Success 200 {file} file
|
|
// @Failure 404 {string} string "Not Found"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/previous [get]
|
|
func (m *MusicHandler) GetPreviousSong(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
songPath := backend.GetPreviousSong()
|
|
file, err := os.Open(songPath)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
|
}
|
|
defer file.Close()
|
|
return ctx.Stream(http.StatusOK, "audio/mpeg", file)
|
|
}
|
|
|
|
// GetAllSoundtracks godoc
|
|
// @Summary Get all soundtracks
|
|
// @Description Returns a list of all games in order
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {array} map[string]interface{}
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/all/order [get]
|
|
func (m *MusicHandler) GetAllSoundtracks(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
soundtrackList := backend.GetAllSoundtracks()
|
|
return ctx.JSON(http.StatusOK, soundtrackList)
|
|
}
|
|
|
|
// GetAllSoundtracksRandom godoc
|
|
// @Summary Get all soundtracks random
|
|
// @Description Returns a list of all games in random order
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {array} map[string]interface{}
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/all/random [get]
|
|
func (m *MusicHandler) GetAllSoundtracksRandom(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
soundtrackList := backend.GetAllSoundtracksRandom()
|
|
return ctx.JSON(http.StatusOK, soundtrackList)
|
|
}
|
|
|
|
// PutPlayed godoc
|
|
// @Summary Mark song as played
|
|
// @Description Marks a song as played by its ID
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param song query int true "Song ID"
|
|
// @Success 204
|
|
// @Failure 400 {string} string "Bad Request"
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/played [put]
|
|
func (m *MusicHandler) PutPlayed(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
song, err := strconv.Atoi(ctx.QueryParam("song"))
|
|
if err != nil {
|
|
return ctx.JSON(http.StatusBadRequest, err.Error())
|
|
}
|
|
logging.GetLogger().Info("Marking song as played", zap.Int("song_id", song))
|
|
backend.SetPlayed(song)
|
|
return ctx.NoContent(http.StatusOK)
|
|
}
|
|
|
|
// AddLatestToQue godoc
|
|
// @Summary Add latest to queue
|
|
// @Description Adds the latest song to the queue
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Success 204
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/addQue [get]
|
|
func (m *MusicHandler) AddLatestToQue(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
backend.AddLatestToQue()
|
|
return ctx.NoContent(http.StatusOK)
|
|
}
|
|
|
|
// AddLatestPlayed godoc
|
|
// @Summary Add latest to played
|
|
// @Description Adds the latest song to the played list
|
|
// @Tags music
|
|
// @Accept json
|
|
// @Success 204
|
|
// @Failure 423 {string} string "Syncing is in progress"
|
|
// @Router /music/addPlayed [get]
|
|
func (m *MusicHandler) AddLatestPlayed(ctx *echo.Context) error {
|
|
if backend.Syncing {
|
|
logging.GetLogger().Info("Syncing is in progress")
|
|
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
|
}
|
|
backend.AddLatestPlayed()
|
|
return ctx.NoContent(http.StatusOK)
|
|
}
|