diff --git a/main.go b/main.go deleted file mode 100644 index 37da64a..0000000 --- a/main.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "os" - "strconv" - "strings" - - "player/player" -) - -func main() { - - stations := []player.Station{ - {Name: "KEXP", URL: "https://kexp-mp3-128.streamguys1.com/kexp128.mp3"}, - {Name: "StuBru", URL: "http://icecast.vrtcdn.be/stubru-high.mp3"}, - {Name: "StuBru Bruut", URL: "http://icecast.vrtcdn.be/stubru_bruut-high.mp3"}, - {Name: "StuBru Untz", URL: "http://icecast.vrtcdn.be/stubru_untz-high.mp3"}, - {Name: "StuBru Hooray", URL: "http://icecast.vrtcdn.be/stubru_hiphophooray-high.mp3"}, - } - - radio := player.NewPlayer() - if err := radio.Init(); err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println("Stations:") - for i, stream := range stations { - fmt.Printf("%d: %s\n", i, stream.Name) - } - - for { - fmt.Println("Press a number to change stations, 9 to quit") - - number, err := getNext() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if number == 9 { - os.Exit(0) - } - - station := stations[number] - fmt.Printf("Playing %s\n", station.Name) - - if err := radio.Select(station); err != nil { - fmt.Println(err) - os.Exit(1) - } - - } -} - -func getNext() (int, error) { - reader := bufio.NewReader(os.Stdin) - input, err := reader.ReadString('\n') - if err != nil { - return 9, err - } - - number, err := strconv.Atoi(strings.TrimSpace(input)) - if err != nil { - return 9, err - } - - return number, nil -} diff --git a/player/handler.go b/player/handler.go new file mode 100644 index 0000000..bd2bb21 --- /dev/null +++ b/player/handler.go @@ -0,0 +1,68 @@ +package player + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +type API struct { + radio *Player +} + +func NewAPI(radio *Player) *API { + return &API{ + radio: radio, + } +} + +func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + a.Status(w, r) + case http.MethodPost: + a.Select(w, r) + default: + http.NotFound(w, r) + } + +} + +func (a *API) Status(w http.ResponseWriter, r *http.Request) { + body, err := json.Marshal(a.radio.Status()) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, string(body)) +} + +func (a *API) Select(w http.ResponseWriter, r *http.Request) { + reqBody, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + command := struct { + Action string `json:"action"` + Channel int `json:"channel"` + }{} + if err := json.Unmarshal(reqBody, &command); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + if command.Action == "play" { + if err := a.radio.Select(command.Channel); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + } else { + a.radio.Stop() + } + + a.Status(w, r) +} diff --git a/player/player.go b/player/player.go index 6396383..d177169 100644 --- a/player/player.go +++ b/player/player.go @@ -1,6 +1,7 @@ package player import ( + "fmt" "net/http" "time" @@ -14,17 +15,27 @@ type Station struct { URL string } +type Status struct { + Playing bool + Station string + Channels []string +} + type Player struct { + stations []Station + current int oldStreamer beep.StreamCloser oldCtrl *beep.Ctrl mixer *beep.Mixer sr beep.SampleRate } -func NewPlayer() *Player { +func NewPlayer(stations []Station) *Player { return &Player{ - mixer: &beep.Mixer{}, - sr: beep.SampleRate(48000), + stations: stations, + current: -1, + mixer: &beep.Mixer{}, + sr: beep.SampleRate(48000), } } @@ -39,8 +50,12 @@ func (p *Player) Init() error { return nil } -func (p *Player) Select(station Station) error { - res, err := http.Get(station.URL) +func (p *Player) Select(channel int) error { + if channel < 0 || channel >= len(p.stations) { + return fmt.Errorf("unknown channel: %d", channel) + } + + res, err := http.Get(p.stations[channel].URL) if err != nil { return err } @@ -53,6 +68,7 @@ func (p *Player) Select(station Station) error { resampled := Resample(4, format.SampleRate, p.sr, streamer) p.PlayStream(resampled) + p.current = channel return nil } @@ -80,3 +96,32 @@ func (p *Player) PlayStream(streamer beep.StreamCloser) { p.oldCtrl = ctrl p.oldStreamer = streamer } + +func (p *Player) Stop() { + speaker.Lock() + if p.oldCtrl != nil { + p.oldCtrl.Paused = true + p.oldCtrl.Streamer = nil + p.oldStreamer.Close() + } + speaker.Unlock() + + p.current = -1 +} + +func (p *Player) Status() Status { + channels := make([]string, len(p.stations)) + for i, stat := range p.stations { + channels[i] = stat.Name + } + + if p.current < 0 || p.current >= len(p.stations) { + return Status{Channels: channels} + } + + return Status{ + Playing: true, + Station: p.stations[p.current].Name, + Channels: channels, + } +} diff --git a/player/service.go b/player/service.go deleted file mode 100644 index ed7faeb..0000000 --- a/player/service.go +++ /dev/null @@ -1,14 +0,0 @@ -package player - -// -//type App struct { -// *Player -//} -// -//func NewApp() *App { -// return &App{ -// Player: NewPlayer(), -// } -//} -// -//func (a *App) Run() error { diff --git a/service.go b/service.go new file mode 100644 index 0000000..6bafecd --- /dev/null +++ b/service.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + + "player/player" +) + +func main() { + + stations := []player.Station{ + {Name: "KEXP", URL: "https://kexp-mp3-128.streamguys1.com/kexp128.mp3"}, + {Name: "StuBru", URL: "http://icecast.vrtcdn.be/stubru-high.mp3"}, + {Name: "StuBru Bruut", URL: "http://icecast.vrtcdn.be/stubru_bruut-high.mp3"}, + {Name: "StuBru Untz", URL: "http://icecast.vrtcdn.be/stubru_untz-high.mp3"}, + {Name: "StuBru Hooray", URL: "http://icecast.vrtcdn.be/stubru_hiphophooray-high.mp3"}, + } + + radio := player.NewPlayer(stations) + if err := radio.Init(); err != nil { + fmt.Println(err) + os.Exit(1) + } + + go http.ListenAndServe(":8080", player.NewAPI(radio)) + + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + <-c +}