254 lines
6.5 KiB
Go
254 lines
6.5 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"git.nemunai.re/nemunaire/hathoris/config"
|
|
"git.nemunai.re/nemunaire/hathoris/inputs"
|
|
"git.nemunai.re/nemunaire/hathoris/sources"
|
|
)
|
|
|
|
type SourceState struct {
|
|
Name string `json:"name"`
|
|
Enabled bool `json:"enabled"`
|
|
Active *bool `json:"active,omitempty"`
|
|
Controlable bool `json:"controlable,omitempty"`
|
|
HasPlaylist bool `json:"hasplaylist,omitempty"`
|
|
CurrentTitle string `json:"currentTitle,omitempty"`
|
|
}
|
|
|
|
func declareSourcesRoutes(cfg *config.Config, router *gin.RouterGroup) {
|
|
router.GET("/sources", func(c *gin.Context) {
|
|
ret := map[string]*SourceState{}
|
|
|
|
for k, src := range sources.SoundSources {
|
|
active := src.IsActive()
|
|
_, controlable := src.(inputs.ControlableInput)
|
|
|
|
var hasPlaylist bool
|
|
if p, withPlaylist := src.(inputs.PlaylistInput); withPlaylist {
|
|
hasPlaylist = p.HasPlaylist()
|
|
}
|
|
|
|
var title string
|
|
if s, ok := src.(sources.PlayingSource); ok && active {
|
|
title = s.CurrentlyPlaying()
|
|
}
|
|
|
|
ret[k] = &SourceState{
|
|
Name: src.GetName(),
|
|
Enabled: src.IsEnabled(),
|
|
Active: &active,
|
|
Controlable: controlable,
|
|
HasPlaylist: hasPlaylist,
|
|
CurrentTitle: title,
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, ret)
|
|
})
|
|
|
|
sourcesRoutes := router.Group("/sources/:source")
|
|
sourcesRoutes.Use(SourceHandler)
|
|
|
|
sourcesRoutes.GET("", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
active := src.IsActive()
|
|
_, controlable := src.(inputs.ControlableInput)
|
|
|
|
var hasPlaylist bool
|
|
if p, withPlaylist := src.(inputs.PlaylistInput); withPlaylist {
|
|
hasPlaylist = p.HasPlaylist()
|
|
}
|
|
|
|
var title string
|
|
if s, ok := src.(sources.PlayingSource); ok && active {
|
|
title = s.CurrentlyPlaying()
|
|
}
|
|
|
|
c.JSON(http.StatusOK, &SourceState{
|
|
Name: src.GetName(),
|
|
Enabled: src.IsEnabled(),
|
|
Active: &active,
|
|
Controlable: controlable,
|
|
HasPlaylist: hasPlaylist,
|
|
CurrentTitle: title,
|
|
})
|
|
})
|
|
sourcesRoutes.GET("/settings", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, c.MustGet("source"))
|
|
})
|
|
sourcesRoutes.GET("/currently", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if !src.IsActive() {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, gin.H{"errmsg": "Source not active"})
|
|
return
|
|
}
|
|
|
|
s, ok := src.(sources.PlayingSource)
|
|
if !ok {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, s.CurrentlyPlaying())
|
|
})
|
|
sourcesRoutes.POST("/enable", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if src.IsEnabled() {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "The source is already enabled"})
|
|
return
|
|
}
|
|
|
|
// Disable all sources
|
|
for k, src := range sources.SoundSources {
|
|
if src.IsEnabled() {
|
|
err := src.Disable()
|
|
if err != nil {
|
|
log.Printf("Unable to disable %s: %s", k, err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
err := src.Enable()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to enable the source: %s", err.Error())})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, true)
|
|
})
|
|
sourcesRoutes.POST("/disable", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
err := src.Disable()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to disable the source: %s", err.Error())})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, true)
|
|
})
|
|
|
|
// ControlableInput
|
|
sourcesRoutes.POST("/pause", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if !src.IsActive() {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, gin.H{"errmsg": "Source not active"})
|
|
return
|
|
}
|
|
|
|
s, ok := src.(inputs.ControlableInput)
|
|
if !ok {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, s.TogglePause("default"))
|
|
})
|
|
|
|
// PlaylistInput
|
|
sourcesRoutes.POST("/has_playlist", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if !src.IsActive() {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, gin.H{"errmsg": "Source not active"})
|
|
return
|
|
}
|
|
|
|
s, ok := src.(inputs.PlaylistInput)
|
|
if !ok {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, s.HasPlaylist())
|
|
})
|
|
sourcesRoutes.POST("/next_track", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if !src.IsActive() {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, gin.H{"errmsg": "Source not active"})
|
|
return
|
|
}
|
|
|
|
s, ok := src.(inputs.PlaylistInput)
|
|
if !ok || !s.HasPlaylist() {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support"})
|
|
return
|
|
}
|
|
|
|
err := s.NextTrack()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, true)
|
|
})
|
|
sourcesRoutes.POST("/next_random_track", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if !src.IsActive() {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, gin.H{"errmsg": "Source not active"})
|
|
return
|
|
}
|
|
|
|
s, ok := src.(inputs.PlaylistInput)
|
|
if !ok || !s.HasPlaylist() {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support"})
|
|
return
|
|
}
|
|
|
|
err := s.NextRandomTrack()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, true)
|
|
})
|
|
sourcesRoutes.POST("/prev_track", func(c *gin.Context) {
|
|
src := c.MustGet("source").(sources.SoundSource)
|
|
|
|
if !src.IsActive() {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, gin.H{"errmsg": "Source not active"})
|
|
return
|
|
}
|
|
|
|
s, ok := src.(inputs.PlaylistInput)
|
|
if !ok || !s.HasPlaylist() {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support"})
|
|
return
|
|
}
|
|
|
|
err := s.PreviousTrack()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, true)
|
|
})
|
|
}
|
|
|
|
func SourceHandler(c *gin.Context) {
|
|
src, ok := sources.SoundSources[c.Param("source")]
|
|
if !ok {
|
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("Source not found: %s", c.Param("source"))})
|
|
return
|
|
}
|
|
|
|
c.Set("source", src)
|
|
|
|
c.Next()
|
|
}
|