Files
MusicServer/internal/server/music_handler.go
T
Sansan 0ec2425f78 Return every error as {"error": "..."}
Echo errors, middleware and handlers used three different formats (message object, error object, bare string). One error handler now writes all of them the same way; status codes are unchanged.
2026-10-06 16:21:14 +02:00

241 lines
7.4 KiB
Go

package server
import (
"net/http"
"strconv"
"music-server/internal/backend"
"music-server/internal/logging"
"github.com/labstack/echo/v5"
"go.uber.org/zap"
)
type MusicHandler struct {
music *backend.MusicService
}
func NewMusicHandler(music *backend.MusicService) *MusicHandler {
return &MusicHandler{music: music}
}
// GetSong godoc
// @Summary Get a specific song
// @Description Returns the song at the given position in the queue
// @Tags music
// @Produce audio/mpeg
// @Param song query int true "Song number in the queue"
// @Success 200 {file} file
// @Success 206 {file} file
// @Failure 400 {string} string "song must be a number"
// @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 {
song, err := strconv.Atoi(ctx.QueryParam("song"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "song must be a number")
}
path, err := m.music.Song(song)
return serveSong(ctx, path, err)
}
// 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 {
path, err := m.music.SoundCheckSong()
return serveSong(ctx, path, err)
}
// ResetMusic godoc
// @Summary Reset music state
// @Description Resets the music state
// @Tags music
// @Success 200
// @Failure 423 {string} string "Syncing is in progress"
// @Router /music/reset [get]
func (m *MusicHandler) ResetMusic(ctx *echo.Context) error {
if err := m.music.Reset(ctx.Request().Context()); err != nil {
return songError(err)
}
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 {
path, err := m.music.RandomSong(ctx.Request().Context())
return serveSong(ctx, path, err)
}
// GetRandomSongLowChance godoc
// @Summary Get random song with low chance
// @Description Returns a random song, favouring soundtracks that have been played less
// @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 {
path, err := m.music.RandomSongLowChance(ctx.Request().Context())
return serveSong(ctx, path, err)
}
// GetRandomSongClassic godoc
// @Summary Get random classic song
// @Description Returns a random song where every song has the same chance
// @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 {
path, err := m.music.RandomSongClassic(ctx.Request().Context())
return serveSong(ctx, path, err)
}
// GetSongInfo godoc
// @Summary Get current song info
// @Description Returns information about the current song
// @Tags music
// @Produce json
// @Success 200 {object} backend.SongInfo
// @Router /music/info [get]
func (m *MusicHandler) GetSongInfo(ctx *echo.Context) error {
return ctx.JSON(http.StatusOK, m.music.SongInfo(ctx.Request().Context()))
}
// GetPlayedSongs godoc
// @Summary Get played songs list
// @Description Returns a list of played songs
// @Tags music
// @Produce json
// @Success 200 {array} backend.SongInfo
// @Router /music/list [get]
func (m *MusicHandler) GetPlayedSongs(ctx *echo.Context) error {
return ctx.JSON(http.StatusOK, m.music.PlayedSongs(ctx.Request().Context()))
}
// 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 {
path, err := m.music.NextSong()
return serveSong(ctx, path, err)
}
// 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 {
path, err := m.music.PreviousSong()
return serveSong(ctx, path, err)
}
// GetAllSoundtracks godoc
// @Summary Get all soundtracks
// @Description Returns a list of all soundtracks in order
// @Tags music
// @Produce json
// @Success 200 {array} string
// @Failure 423 {string} string "Syncing is in progress"
// @Router /music/all/order [get]
func (m *MusicHandler) GetAllSoundtracks(ctx *echo.Context) error {
names, err := m.music.SoundtrackNames(ctx.Request().Context())
if err != nil {
return songError(err)
}
return ctx.JSON(http.StatusOK, names)
}
// GetAllSoundtracksRandom godoc
// @Summary Get all soundtracks random
// @Description Returns a list of all soundtracks in random order
// @Tags music
// @Produce json
// @Success 200 {array} string
// @Failure 423 {string} string "Syncing is in progress"
// @Router /music/all/random [get]
func (m *MusicHandler) GetAllSoundtracksRandom(ctx *echo.Context) error {
names, err := m.music.SoundtrackNamesRandom(ctx.Request().Context())
if err != nil {
return songError(err)
}
return ctx.JSON(http.StatusOK, names)
}
// PutPlayed godoc
// @Summary Mark song as played
// @Description Marks the song at the given queue position as played
// @Tags music
// @Param song query int true "Song number in the queue"
// @Success 200
// @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 {
song, err := strconv.Atoi(ctx.QueryParam("song"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "song must be a number")
}
logging.GetLogger().Info("Marking song as played", zap.Int("song_id", song))
if err := m.music.SetPlayed(ctx.Request().Context(), song); err != nil {
return songError(err)
}
return ctx.NoContent(http.StatusOK)
}
// AddLatestToQue godoc
// @Summary Add latest to queue
// @Description Adds the latest song to the queue
// @Tags music
// @Success 200
// @Failure 423 {string} string "Syncing is in progress"
// @Router /music/addQue [get]
func (m *MusicHandler) AddLatestToQue(ctx *echo.Context) error {
m.music.AddLatestToQueue()
return ctx.NoContent(http.StatusOK)
}
// AddLatestPlayed godoc
// @Summary Add latest to played
// @Description Marks the current song as played
// @Tags music
// @Success 200
// @Failure 423 {string} string "Syncing is in progress"
// @Router /music/addPlayed [get]
func (m *MusicHandler) AddLatestPlayed(ctx *echo.Context) error {
if err := m.music.AddLatestPlayed(ctx.Request().Context()); err != nil {
return songError(err)
}
return ctx.NoContent(http.StatusOK)
}