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) }