Compare commits

...

3 Commits

Author SHA1 Message Date
nemunaire 85ecd89104 Update synthetic-fm url
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2024-03-02 12:42:16 +01:00
nemunaire 19fa419d89 Can skip to a random track on double click
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2024-01-14 10:22:13 +01:00
nemunaire 677f93723b migration to SvelteKit 2
continuous-integration/drone/push Build is passing Details
2024-01-09 09:59:06 +01:00
11 changed files with 491 additions and 238 deletions

View File

@ -13,7 +13,7 @@ workspace:
steps:
- name: build front
image: node:20-alpine
image: node:21
commands:
- mkdir deploy
- cd ui

View File

@ -1,4 +1,4 @@
FROM node:20-alpine as nodebuild
FROM node:21 as nodebuild
WORKDIR /ui

View File

@ -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 {

View File

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

View File

@ -25,6 +25,7 @@ type ControlableInput interface {
type PlaylistInput interface {
HasPlaylist() bool
NextTrack() error
NextRandomTrack() error
PreviousTrack() error
}

View File

@ -24,11 +24,11 @@ type MPVSource struct {
func init() {
sources.SoundSources["mpv-nig"] = &MPVSource{
Name: "Radio NIG",
File: "https://mediaserv38.live-streams.nl:18030/stream",
File: "http://stream.syntheticfm.com:8030/stream",
}
sources.SoundSources["mpv-synthfm"] = &MPVSource{
Name: "Radio Synthetic FM",
File: "https://mediaserv38.live-streams.nl:18040/live",
File: "http://stream.syntheticfm.com:8040/stream",
}
sources.SoundSources["mpv-nrw"] = &MPVSource{
Name: "NewRetroWave",
@ -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")

627
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,21 +10,22 @@
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.20.4",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5",
"vite": "^4.4.2"
"vite": "^5.0.0"
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/adapter-static": "^3.0.0",
"bootstrap": "^5.3.2",
"bootstrap-icons": "^1.11.1",
"sass": "^1.69.5"
}
}
}

View File

@ -24,6 +24,7 @@
<button
class="btn btn-sm btn-primary"
on:click={() => source.nexttrack()}
on:dblclick={() => source.nextrandomtrack()}
>
<i class="bi bi-skip-forward-fill"></i>
</button>

View File

@ -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) {

View File

@ -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) {