web api version

This commit is contained in:
Erik Winter 2023-08-19 12:07:31 +02:00
parent 8ebac4b864
commit 69eab07e9a
5 changed files with 152 additions and 90 deletions

71
main.go
View File

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

68
player/handler.go Normal file
View File

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

View File

@ -1,6 +1,7 @@
package player
import (
"fmt"
"net/http"
"time"
@ -14,15 +15,25 @@ 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{
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,
}
}

View File

@ -1,14 +0,0 @@
package player
//
//type App struct {
// *Player
//}
//
//func NewApp() *App {
// return &App{
// Player: NewPlayer(),
// }
//}
//
//func (a *App) Run() error {

34
service.go Normal file
View File

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