From 19fa419d893fbf5c255cf28f5554fe54d054a542 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 14 Jan 2024 10:17:03 +0100 Subject: [PATCH] Can skip to a random track on double click --- api/inputs.go | 15 ++++++++++++ api/sources.go | 22 +++++++++++++++++ inputs/interfaces.go | 1 + sources/mpv/source.go | 30 +++++++++++++++++++++++ ui/src/lib/components/Applications.svelte | 1 + ui/src/lib/input.js | 7 ++++++ ui/src/lib/source.js | 7 ++++++ 7 files changed, 83 insertions(+) diff --git a/api/inputs.go b/api/inputs.go index 99a1ad2..49a4565 100644 --- a/api/inputs.go +++ b/api/inputs.go @@ -162,6 +162,21 @@ func declareInputsRoutes(cfg *config.Config, router *gin.RouterGroup) { c.JSON(http.StatusOK, true) }) + streamRoutes.POST("/next_random_track", func(c *gin.Context) { + input, ok := c.MustGet("input").(inputs.PlaylistInput) + if !ok { + c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "The source doesn't support that"}) + return + } + + err := input.NextRandomTrack() + if err != nil { + c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, true) + }) streamRoutes.POST("/prev_track", func(c *gin.Context) { input, ok := c.MustGet("input").(inputs.PlaylistInput) if !ok { diff --git a/api/sources.go b/api/sources.go index bad066e..5be7167 100644 --- a/api/sources.go +++ b/api/sources.go @@ -194,6 +194,28 @@ func declareSourcesRoutes(cfg *config.Config, router *gin.RouterGroup) { 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) diff --git a/inputs/interfaces.go b/inputs/interfaces.go index ded94b2..b06e82f 100644 --- a/inputs/interfaces.go +++ b/inputs/interfaces.go @@ -25,6 +25,7 @@ type ControlableInput interface { type PlaylistInput interface { HasPlaylist() bool NextTrack() error + NextRandomTrack() error PreviousTrack() error } diff --git a/sources/mpv/source.go b/sources/mpv/source.go index defa15f..f987cf8 100644 --- a/sources/mpv/source.go +++ b/sources/mpv/source.go @@ -269,6 +269,36 @@ func (s *MPVSource) NextTrack() error { return nil } +func (s *MPVSource) NextRandomTrack() error { + if s.ipcSocketDir == "" { + return fmt.Errorf("Not supported") + } + + conn := mpvipc.NewConnection(s.ipcSocket()) + err := conn.Open() + if err != nil { + return err + } + defer conn.Close() + + _, err = conn.Call("playlist-shuffle") + if err != nil { + return err + } + + _, err = conn.Call("playlist-next", "weak") + if err != nil { + return err + } + + _, err = conn.Call("playlist-unshuffle") + if err != nil { + return err + } + + return nil +} + func (s *MPVSource) PreviousTrack() error { if s.ipcSocketDir == "" { return fmt.Errorf("Not supported") diff --git a/ui/src/lib/components/Applications.svelte b/ui/src/lib/components/Applications.svelte index d782e24..ea7bd3c 100644 --- a/ui/src/lib/components/Applications.svelte +++ b/ui/src/lib/components/Applications.svelte @@ -24,6 +24,7 @@ diff --git a/ui/src/lib/input.js b/ui/src/lib/input.js index 7add4de..71fe367 100644 --- a/ui/src/lib/input.js +++ b/ui/src/lib/input.js @@ -39,6 +39,13 @@ export class Input { } } + async nextrandomtrack(idstream) { + const data = await fetch(`api/inputs/${this.id}/streams/${idstream}/next_random_track`, {headers: {'Accept': 'application/json'}, method: 'POST'}); + if (data.status != 200) { + throw new Error((await res.json()).errmsg); + } + } + async prevtrack(idstream) { const data = await fetch(`api/inputs/${this.id}/streams/${idstream}/prev_track`, {headers: {'Accept': 'application/json'}, method: 'POST'}); if (data.status != 200) { diff --git a/ui/src/lib/source.js b/ui/src/lib/source.js index 181173d..ba2b470 100644 --- a/ui/src/lib/source.js +++ b/ui/src/lib/source.js @@ -46,6 +46,13 @@ export class Source { } } + async nextrandomtrack() { + const data = await fetch(`api/sources/${this.id}/next_random_track`, {headers: {'Accept': 'application/json'}, method: 'POST'}); + if (data.status != 200) { + throw new Error((await res.json()).errmsg); + } + } + async prevtrack() { const data = await fetch(`api/sources/${this.id}/prev_track`, {headers: {'Accept': 'application/json'}, method: 'POST'}); if (data.status != 200) {