🎉 live server seems to be working now
This commit is contained in:
200
cmd/live-server/main.go
Normal file
200
cmd/live-server/main.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Sendspin/sendspin-go/pkg/sendspin"
|
||||
|
||||
"rpi-sendspin/internal/live"
|
||||
)
|
||||
|
||||
// flagSpec describes one user-facing option. The flag is registered
|
||||
// under all names; help output (custom Usage below) groups them onto a
|
||||
// single line so `-s, --source` reads as one option rather than two.
|
||||
type flagSpec struct {
|
||||
long string
|
||||
short string // optional
|
||||
usage string
|
||||
}
|
||||
|
||||
var specs []flagSpec
|
||||
|
||||
func addStr(target *string, def, long, short, usage string) {
|
||||
flag.StringVar(target, long, def, usage)
|
||||
if short != "" {
|
||||
flag.StringVar(target, short, def, usage)
|
||||
}
|
||||
specs = append(specs, flagSpec{long, short, usage})
|
||||
}
|
||||
|
||||
func addInt(target *int, def int, long, short, usage string) {
|
||||
flag.IntVar(target, long, def, usage)
|
||||
if short != "" {
|
||||
flag.IntVar(target, short, def, usage)
|
||||
}
|
||||
specs = append(specs, flagSpec{long, short, usage})
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
port int
|
||||
name string
|
||||
httpPort int
|
||||
configPath string
|
||||
source string
|
||||
mqttURL string
|
||||
mqttUser string
|
||||
mqttPass string
|
||||
mqttBase string
|
||||
mqttDiscovery string
|
||||
rate int
|
||||
channels int
|
||||
)
|
||||
|
||||
addInt(&port, 8927, "port", "p", "Sendspin WebSocket port")
|
||||
addStr(&name, "Live Audio", "name", "n", "advertised server name")
|
||||
addInt(&httpPort, 8080, "http-port", "", "HTTP/API port")
|
||||
addStr(&configPath, defaultConfigPath(), "config", "c", "presets file path")
|
||||
addStr(&source, "", "source", "s", "PipeWire source node (e.g. alsa_input.usb-...pro-input-0.monitor); when set, overrides preset.source")
|
||||
addStr(&mqttURL, "", "mqtt-url", "", "MQTT broker URL (e.g. tcp://broker.local:1883); empty disables MQTT")
|
||||
addStr(&mqttUser, "", "mqtt-username", "", "MQTT username")
|
||||
addStr(&mqttPass, "", "mqtt-password", "", "MQTT password")
|
||||
addStr(&mqttBase, "sendspin/live", "mqtt-base", "", "MQTT base topic")
|
||||
addStr(&mqttDiscovery, "homeassistant", "mqtt-discovery", "", "Home Assistant discovery prefix")
|
||||
addInt(&rate, 48000, "rate", "r", "capture sample rate")
|
||||
addInt(&channels, 2, "channels", "", "capture channels")
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||
|
||||
store, err := live.NewStore(configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("load presets: %v", err)
|
||||
}
|
||||
|
||||
src := live.NewSource(rate, channels)
|
||||
roster := live.NewRoster()
|
||||
|
||||
var mgr *live.Manager
|
||||
server, err := sendspin.NewServer(sendspin.ServerConfig{
|
||||
Port: port,
|
||||
Name: name,
|
||||
Source: src,
|
||||
EnableMDNS: true,
|
||||
DiscoverClients: true,
|
||||
ClientFilter: func(id string) bool {
|
||||
if mgr == nil {
|
||||
return false
|
||||
}
|
||||
return mgr.ClientAllowed(id)
|
||||
},
|
||||
OnClientHello: roster.Record,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("create sendspin server: %v", err)
|
||||
}
|
||||
mgr = live.NewManager(store, server, src)
|
||||
mgr.SetSourceOverride(source)
|
||||
if source != "" {
|
||||
log.Printf("source override: %s (preset.source ignored)", source)
|
||||
}
|
||||
|
||||
disc := live.NewDiscovery()
|
||||
disc.Start()
|
||||
defer disc.Stop()
|
||||
|
||||
httpSrv := &http.Server{
|
||||
Addr: ":" + strconv.Itoa(httpPort),
|
||||
Handler: live.NewHTTPServer(mgr, store, disc, roster, server).Handler(),
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
go func() {
|
||||
log.Printf("HTTP API listening on %s", httpSrv.Addr)
|
||||
if err := httpSrv.ListenAndServe(); err != http.ErrServerClosed {
|
||||
log.Fatalf("http: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var bridge *live.MQTTBridge
|
||||
if mqttURL != "" {
|
||||
bridge = live.NewMQTTBridge(live.MQTTConfig{
|
||||
URL: mqttURL,
|
||||
Username: mqttUser,
|
||||
Password: mqttPass,
|
||||
BaseTopic: mqttBase,
|
||||
DiscoveryPrefix: mqttDiscovery,
|
||||
}, mgr)
|
||||
if err := bridge.Start(); err != nil {
|
||||
log.Fatalf("mqtt: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
serverDone := make(chan error, 1)
|
||||
go func() { serverDone <- server.Start() }()
|
||||
log.Printf("Sendspin server %q on :%d", name, port)
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sig:
|
||||
log.Printf("signal received, shutting down")
|
||||
case err := <-serverDone:
|
||||
log.Printf("sendspin server exited: %v", err)
|
||||
}
|
||||
|
||||
if bridge != nil {
|
||||
bridge.Stop()
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_ = httpSrv.Shutdown(ctx)
|
||||
server.Stop()
|
||||
_ = src.Close()
|
||||
}
|
||||
|
||||
func defaultConfigPath() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "presets.json"
|
||||
}
|
||||
return filepath.Join(home, ".config", "sendspin-live", "presets.json")
|
||||
}
|
||||
|
||||
// usage renders one line per logical option, pairing the short alias
|
||||
// with the long form (e.g. "-s, --source"). The default flag.Usage
|
||||
// would list each registration separately and double-print everything.
|
||||
func usage() {
|
||||
out := flag.CommandLine.Output()
|
||||
fmt.Fprintf(out, "Usage: %s [flags]\n\nFlags:\n", os.Args[0])
|
||||
|
||||
sort.Slice(specs, func(i, j int) bool { return specs[i].long < specs[j].long })
|
||||
|
||||
for _, s := range specs {
|
||||
f := flag.Lookup(s.long)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
head := " --" + s.long
|
||||
if s.short != "" {
|
||||
head = " -" + s.short + ", --" + s.long
|
||||
}
|
||||
if f.DefValue != "" {
|
||||
head += "=" + f.DefValue
|
||||
}
|
||||
fmt.Fprintf(out, "%-44s %s\n", head, s.usage)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user