Introduce settings.json to store custom sources

This commit is contained in:
nemunaire 2024-10-01 19:10:45 +02:00
parent 85ecd89104
commit ded6ef6095
6 changed files with 143 additions and 23 deletions

52
app.go
View File

@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"fmt"
"log" "log"
"net/http" "net/http"
"time" "time"
@ -10,14 +11,16 @@ import (
"git.nemunai.re/nemunaire/hathoris/api" "git.nemunai.re/nemunaire/hathoris/api"
"git.nemunai.re/nemunaire/hathoris/config" "git.nemunai.re/nemunaire/hathoris/config"
"git.nemunai.re/nemunaire/hathoris/settings"
"git.nemunai.re/nemunaire/hathoris/sources" "git.nemunai.re/nemunaire/hathoris/sources"
"git.nemunai.re/nemunaire/hathoris/ui" "git.nemunai.re/nemunaire/hathoris/ui"
) )
type App struct { type App struct {
cfg *config.Config cfg *config.Config
router *gin.Engine router *gin.Engine
srv *http.Server settings *settings.Settings
srv *http.Server
} }
func NewApp(cfg *config.Config) *App { func NewApp(cfg *config.Config) *App {
@ -33,8 +36,15 @@ func NewApp(cfg *config.Config) *App {
// Prepare struct // Prepare struct
app := &App{ app := &App{
cfg: cfg, cfg: cfg,
router: router, router: router,
settings: loadUserSettings(cfg),
}
// Load user settings
err := app.loadCustomSources()
if err != nil {
log.Fatal(err.Error())
} }
// Register routes // Register routes
@ -48,6 +58,33 @@ func NewApp(cfg *config.Config) *App {
return app return app
} }
func loadUserSettings(cfg *config.Config) (s *settings.Settings) {
var err error
s, err = settings.Load(cfg)
if err != nil {
log.Fatal("Unable to load settings: ", err.Error())
}
return
}
func (app *App) loadCustomSources() error {
for id, csrc := range app.settings.CustomSources {
if newss, ok := sources.LoadableSources[csrc.Source]; !ok {
return fmt.Errorf("Unable to load source #%d: %q: no such source registered", id, csrc.Source)
} else {
src, err := newss(csrc.KV)
if err != nil {
return fmt.Errorf("Unable to load source #%d (%s): %w", id, csrc.Source, err)
}
sources.SoundSources[fmt.Sprintf("%s-%d", csrc.Source, id)] = src
}
}
return nil
}
func (app *App) Start() { func (app *App) Start() {
app.srv = &http.Server{ app.srv = &http.Server{
Addr: app.cfg.Bind, Addr: app.cfg.Bind,
@ -82,6 +119,11 @@ func (app *App) Stop() {
time.Sleep(2000 * time.Millisecond) time.Sleep(2000 * time.Millisecond)
} }
err := app.settings.Save(app.cfg.SettingsPath)
if err != nil {
log.Println("Unable to save settings: ", err.Error())
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
if err := app.srv.Shutdown(ctx); err != nil { if err := app.srv.Shutdown(ctx); err != nil {

View File

@ -9,6 +9,7 @@ func (c *Config) declareFlags() {
flag.StringVar(&c.BaseURL, "baseurl", c.BaseURL, "URL prepended to each URL") flag.StringVar(&c.BaseURL, "baseurl", c.BaseURL, "URL prepended to each URL")
flag.StringVar(&c.Bind, "bind", c.Bind, "Bind port/socket") flag.StringVar(&c.Bind, "bind", c.Bind, "Bind port/socket")
flag.StringVar(&c.DevProxy, "dev", c.DevProxy, "Use ui directory instead of embedded assets") flag.StringVar(&c.DevProxy, "dev", c.DevProxy, "Use ui directory instead of embedded assets")
flag.StringVar(&c.SettingsPath, "settings-file", c.SettingsPath, "Path to settings.json file")
// Others flags are declared in some other files when they need specials configurations // Others flags are declared in some other files when they need specials configurations
} }
@ -16,7 +17,8 @@ func (c *Config) declareFlags() {
func Consolidated() (cfg *Config, err error) { func Consolidated() (cfg *Config, err error) {
// Define defaults options // Define defaults options
cfg = &Config{ cfg = &Config{
Bind: "127.0.0.1:8080", Bind: "127.0.0.1:8080",
SettingsPath: "settings.json",
} }
cfg.declareFlags() cfg.declareFlags()

View File

@ -6,9 +6,10 @@ import (
) )
type Config struct { type Config struct {
DevProxy string DevProxy string
Bind string Bind string
BaseURL string BaseURL string
SettingsPath string
} }
// parseLine treats a config line and place the read value in the variable // parseLine treats a config line and place the read value in the variable

66
settings/settings.go Normal file
View File

@ -0,0 +1,66 @@
package settings
import (
"encoding/json"
"fmt"
"os"
"git.nemunai.re/nemunaire/hathoris/config"
)
type CustomSource struct {
Source string `json:"src"`
KV map[string]string `json:"kv"`
}
type Settings struct {
CustomSources []CustomSource `json:"custom_sources"`
}
func Load(cfg *config.Config) (*Settings, error) {
if cfg.SettingsPath == "" {
return &Settings{}, nil
}
if st, err := os.Stat(cfg.SettingsPath); os.IsNotExist(err) || (err == nil && st.Size() == 0) {
fd, err := os.Create(cfg.SettingsPath)
if err != nil {
return nil, fmt.Errorf("unable to create settings file: %w", err)
}
_, err = fd.Write([]byte("{}"))
fd.Close()
if err != nil {
return nil, fmt.Errorf("unable to write to settings file: %w", err)
}
}
fd, err := os.Open(cfg.SettingsPath)
if err != nil {
return nil, fmt.Errorf("unable to open settings: %w", err)
}
defer fd.Close()
var settings Settings
err = json.NewDecoder(fd).Decode(&settings)
if err != nil {
return nil, fmt.Errorf("unable to read settings: %w", err)
}
return &settings, nil
}
func (settings *Settings) Save(path string) error {
fd, err := os.Create(path)
if err != nil {
return fmt.Errorf("unable to create settings file: %w", err)
}
defer fd.Close()
err = json.NewEncoder(fd).Encode(settings)
if err != nil {
return fmt.Errorf("unable to read settings: %w", err)
}
return nil
}

View File

@ -2,7 +2,10 @@ package sources
import () import ()
var SoundSources = map[string]SoundSource{} var (
LoadableSources = map[string]LoadaleSource{}
SoundSources = map[string]SoundSource{}
)
type SoundSource interface { type SoundSource interface {
GetName() string GetName() string
@ -15,3 +18,5 @@ type SoundSource interface {
type PlayingSource interface { type PlayingSource interface {
CurrentlyPlaying() string CurrentlyPlaying() string
} }
type LoadaleSource func(map[string]string) (SoundSource, error)

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"strings"
"time" "time"
"github.com/DexterLB/mpvipc" "github.com/DexterLB/mpvipc"
@ -22,22 +23,25 @@ type MPVSource struct {
} }
func init() { func init() {
sources.SoundSources["mpv-nig"] = &MPVSource{ sources.LoadableSources["mpv"] = NewMPVSource
Name: "Radio NIG", }
File: "http://stream.syntheticfm.com:8030/stream",
func NewMPVSource(kv map[string]string) (sources.SoundSource, error) {
var s MPVSource
if name, ok := kv["name"]; ok {
s.Name = name
} }
sources.SoundSources["mpv-synthfm"] = &MPVSource{
Name: "Radio Synthetic FM", if opts, ok := kv["opts"]; ok {
File: "http://stream.syntheticfm.com:8040/stream", s.Options = strings.Split(opts, " ")
} }
sources.SoundSources["mpv-nrw"] = &MPVSource{
Name: "NewRetroWave", if file, ok := kv["file"]; ok {
File: "https://youtube.com/channel/UCD-4g5w1h8xQpLaNS_ghU4g/videos", s.File = file
}
sources.SoundSources["mpv-abgt"] = &MPVSource{
Name: "ABGT",
File: "https://youtube.com/playlist?list=PL6RLee9oArCArCAjnOtZ17dlVZQxaHG8G",
} }
return &s, nil
} }
func (s *MPVSource) GetName() string { func (s *MPVSource) GetName() string {