reveil/api/federation.go
Pierre-Olivier Mercier c8c6282216
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Refactor federation + can sync track between instances
2024-07-30 11:00:05 +02:00

177 lines
4.4 KiB
Go

package api
import (
"bytes"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"git.nemunai.re/nemunaire/reveil/config"
"git.nemunai.re/nemunaire/reveil/model"
"git.nemunai.re/nemunaire/reveil/player"
)
func declareFederationRoutes(cfg *config.Config, router *gin.RouterGroup) {
router.POST("/federation/wakeup", func(c *gin.Context) {
var s map[string]interface{}
c.ShouldBind(s)
if player.CommonPlayer == nil {
var seed int64
if tmp, ok := s["seed"].(int64); ok {
seed = tmp
} else {
seed := time.Now().Unix()
seed -= seed % 172800
}
err := player.WakeUpFromFederation(cfg, seed, nil)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
} else {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Player already running"})
return
}
c.JSON(http.StatusOK, true)
})
router.POST("/federation/wakeok", func(c *gin.Context) {
if player.CommonPlayer != nil {
err := player.CommonPlayer.Stop()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
}
c.JSON(http.StatusOK, true)
})
router.GET("/federation", func(c *gin.Context) {
settings, err := reveil.ReadSettings(cfg.SettingsFile)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
c.JSON(http.StatusOK, settings.Federation)
})
federationsRoutes := router.Group("/federation/:fid")
federationsRoutes.Use(func(c *gin.Context) {
settings, err := reveil.ReadSettings(cfg.SettingsFile)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
f, ok := settings.Federation[string(c.Param("fid"))]
if !ok {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Action not found"})
return
}
c.Set("federation", &f)
c.Next()
})
federationsRoutes.GET("", func(c *gin.Context) {
c.JSON(http.StatusOK, c.MustGet("federation"))
})
federationsRoutes.POST("sync", func(c *gin.Context) {
srv := c.MustGet("federation").(*reveil.FederationServer)
// Retrieve music list on remote
remoteMusics, err := srv.GetMusics()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to retrieve remote tracks lists: %s", err.Error())})
return
}
// Retrieve local music list
localMusics, err := reveil.LoadTracks(cfg)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to retrieve local tracks: %s", err.Error())})
return
}
// Compute diff
var newMusics []reveil.Track
var oldMusics []reveil.Track
var musicsToEnable []reveil.Track
for _, rTrack := range remoteMusics {
found := false
for _, lTrack := range localMusics {
if bytes.Compare(lTrack.Id, rTrack.Id) == 0 || lTrack.Name == rTrack.Name {
if lTrack.Enabled != rTrack.Enabled {
if lTrack.Enabled {
musicsToEnable = append(musicsToEnable, rTrack)
} else {
oldMusics = append(oldMusics, *lTrack)
}
}
found = true
break
}
}
if !found && rTrack.Enabled {
oldMusics = append(oldMusics, rTrack)
}
}
for _, lTrack := range localMusics {
found := false
for _, rTrack := range remoteMusics {
if bytes.Compare(lTrack.Id, rTrack.Id) == 0 || lTrack.Name == rTrack.Name {
found = true
break
}
}
if !found && lTrack.Enabled {
newMusics = append(newMusics, *lTrack)
}
}
// Disable unexistant musics on local
for _, t := range oldMusics {
t.Enabled = false
err = srv.UpdateTrack(&t)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs when disabling remote tracks (unexistant on local): %s: %s", t.Id.ToString(), err.Error())})
return
}
}
// Enable existant musics on remote
for _, t := range musicsToEnable {
t.Enabled = true
err = srv.UpdateTrack(&t)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
}
// Send new musics
for _, t := range newMusics {
err = srv.SendTrack(&t)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
}
c.JSON(http.StatusOK, true)
})
}