web api version
This commit is contained in:
parent
8ebac4b864
commit
69eab07e9a
71
main.go
71
main.go
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package player
|
||||
|
||||
//
|
||||
//type App struct {
|
||||
// *Player
|
||||
//}
|
||||
//
|
||||
//func NewApp() *App {
|
||||
// return &App{
|
||||
// Player: NewPlayer(),
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (a *App) Run() error {
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue