chore: refactor the radio player to its own file
This commit is contained in:
parent
f99baee6e0
commit
d5a7bb9a0b
3 changed files with 174 additions and 167 deletions
4
Makefile
4
Makefile
|
@ -7,8 +7,8 @@ GO_OPTIONS=-buildmode=default
|
|||
|
||||
all: radiospiral
|
||||
|
||||
radiospiral: main.go bundle.go
|
||||
$(GO) -o radiospiral $(GO_OPTIONS) main.go bundle.go
|
||||
radiospiral: main.go radioplayer.go bundle.go
|
||||
$(GO) -o radiospiral $(GO_OPTIONS) main.go radioplayer.go bundle.go
|
||||
|
||||
# It's a phony so we can always call it and regenerate the file
|
||||
.PHONY: generate
|
||||
|
|
168
main.go
168
main.go
|
@ -44,13 +44,11 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ebitengine/oto/v3"
|
||||
"github.com/muesli/cancelreader"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
|
@ -69,7 +67,6 @@ const RADIOSPIRAL_JSON_ENDPOINT = "https://radiospiral.net/wp-json/radio/broadca
|
|||
const (
|
||||
Loading int = iota
|
||||
Playing
|
||||
Paused
|
||||
Stopped
|
||||
)
|
||||
|
||||
|
@ -122,165 +119,6 @@ type HostsData struct {
|
|||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Radio player interface
|
||||
type RadioPlayer interface {
|
||||
Load(stream_url string)
|
||||
IsPlaying() bool
|
||||
Play()
|
||||
Mute()
|
||||
Pause()
|
||||
IncVolume()
|
||||
DecVolume()
|
||||
Close()
|
||||
}
|
||||
|
||||
// StreamPlayer
|
||||
type StreamPlayer struct {
|
||||
player_name string
|
||||
stream_url string
|
||||
command *exec.Cmd
|
||||
in io.WriteCloser
|
||||
out io.ReadCloser
|
||||
audio io.ReadCloser
|
||||
pipe_chan chan io.ReadCloser
|
||||
otoContext *oto.Context
|
||||
otoPlayer *oto.Player
|
||||
currentVolume float64
|
||||
paused bool
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) IsPlaying() bool {
|
||||
if player.otoPlayer == nil {
|
||||
log.Println("Player not loaded!")
|
||||
return false
|
||||
}
|
||||
|
||||
return player.otoPlayer.IsPlaying()
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Load(stream_url string) {
|
||||
if (player.otoPlayer == nil) || (!player.otoPlayer.IsPlaying()) {
|
||||
var err error
|
||||
is_playlist := strings.HasSuffix(stream_url, ".m3u") || strings.HasSuffix(stream_url, ".pls")
|
||||
if is_playlist {
|
||||
// TODO: Check ffmpeg's ability to deal with playlists
|
||||
// player.command = exec.Command(player.player_name, "-quiet", "-playlist", stream_url)
|
||||
player.command = exec.Command(player.player_name, "-nodisp", "-loglevel", "verbose", "-playlist", stream_url)
|
||||
} else {
|
||||
player.command = exec.Command(player.player_name, "-loglevel", "verbose", "-i", stream_url, "-f", "wav", "-")
|
||||
}
|
||||
|
||||
// In to send things over stdin to ffmpeg
|
||||
player.in, err = player.command.StdinPipe()
|
||||
check(err)
|
||||
// Out will be the wave data we will read and play
|
||||
player.audio, err = player.command.StdoutPipe()
|
||||
check(err)
|
||||
// Err is the output of ffmpeg, used to get stream title
|
||||
player.out, err = player.command.StderrPipe()
|
||||
check(err)
|
||||
|
||||
log.Println("Starting ffmpeg")
|
||||
err = player.command.Start()
|
||||
check(err)
|
||||
|
||||
player.stream_url = stream_url
|
||||
|
||||
op := &oto.NewContextOptions{
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
Format: oto.FormatSignedInt16LE,
|
||||
}
|
||||
|
||||
if player.otoContext == nil {
|
||||
otoContext, readyChan, err := oto.NewContext(op)
|
||||
player.otoContext = otoContext
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-readyChan
|
||||
}
|
||||
|
||||
player.otoPlayer = player.otoContext.NewPlayer(player.audio)
|
||||
// Save current volume for the mute function
|
||||
player.currentVolume = player.otoPlayer.Volume()
|
||||
|
||||
player.paused = false
|
||||
|
||||
go func() {
|
||||
player.pipe_chan <- player.out
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Play() {
|
||||
if player.otoPlayer == nil {
|
||||
log.Println("Stream not loaded")
|
||||
return
|
||||
}
|
||||
|
||||
if !player.otoPlayer.IsPlaying() {
|
||||
if player.command == nil {
|
||||
player.Load(player.stream_url)
|
||||
}
|
||||
player.otoPlayer.Play()
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Close() {
|
||||
if player.IsPlaying() {
|
||||
err := player.otoPlayer.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
player.in.Close()
|
||||
player.out.Close()
|
||||
player.audio.Close()
|
||||
|
||||
player.stream_url = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Mute() {
|
||||
if player.IsPlaying() {
|
||||
if player.otoPlayer.Volume() > 0 {
|
||||
player.currentVolume = player.otoPlayer.Volume()
|
||||
player.otoPlayer.SetVolume(0.0)
|
||||
} else {
|
||||
player.otoPlayer.SetVolume(player.currentVolume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Pause() {
|
||||
if player.IsPlaying() {
|
||||
if !player.paused {
|
||||
player.paused = true
|
||||
player.otoPlayer.Pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) IncVolume() {
|
||||
if player.IsPlaying() {
|
||||
player.currentVolume += 0.05
|
||||
if player.currentVolume >= 1.0 {
|
||||
player.currentVolume = 1.0
|
||||
}
|
||||
player.otoPlayer.SetVolume(player.currentVolume)
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) DecVolume() {
|
||||
if player.IsPlaying() {
|
||||
player.currentVolume -= 0.05
|
||||
if player.currentVolume <= 0.0 {
|
||||
player.currentVolume = 0.0
|
||||
}
|
||||
player.otoPlayer.SetVolume(player.currentVolume)
|
||||
}
|
||||
}
|
||||
|
||||
// Load images from URLs
|
||||
func loadImageURL(url string) image.Image {
|
||||
parts := strings.Split(url, "?")
|
||||
|
@ -380,21 +218,21 @@ func main() {
|
|||
// appearance anytime it is clicked. We make the player start playing
|
||||
// or pause.
|
||||
if !streamPlayer.IsPlaying() && !streamPlayer.paused {
|
||||
playButton.SetIcon(theme.MediaPauseIcon())
|
||||
playButton.SetIcon(theme.MediaStopIcon())
|
||||
playButton.SetText("(Buffering)")
|
||||
streamPlayer.Load(RADIOSPIRAL_STREAM)
|
||||
streamPlayer.Play()
|
||||
playStatus = Loading
|
||||
} else {
|
||||
if playStatus == Playing {
|
||||
playStatus = Paused
|
||||
playStatus = Stopped
|
||||
playButton.SetIcon(theme.MediaPlayIcon())
|
||||
streamPlayer.Pause()
|
||||
} else {
|
||||
reader.Cancel()
|
||||
playStatus = Loading
|
||||
playButton.SetText("(Buffering)")
|
||||
playButton.SetIcon(theme.MediaPauseIcon())
|
||||
playButton.SetIcon(theme.MediaStopIcon())
|
||||
streamPlayer.Load(RADIOSPIRAL_STREAM)
|
||||
streamPlayer.Play()
|
||||
}
|
||||
|
|
169
radioplayer.go
Normal file
169
radioplayer.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/ebitengine/oto/v3"
|
||||
)
|
||||
|
||||
// Radio player interface
|
||||
type RadioPlayer interface {
|
||||
Load(stream_url string)
|
||||
IsPlaying() bool
|
||||
Play()
|
||||
Mute()
|
||||
Pause()
|
||||
IncVolume()
|
||||
DecVolume()
|
||||
Close()
|
||||
}
|
||||
|
||||
// StreamPlayer
|
||||
type StreamPlayer struct {
|
||||
player_name string
|
||||
stream_url string
|
||||
command *exec.Cmd
|
||||
in io.WriteCloser
|
||||
out io.ReadCloser
|
||||
audio io.ReadCloser
|
||||
pipe_chan chan io.ReadCloser
|
||||
otoContext *oto.Context
|
||||
otoPlayer *oto.Player
|
||||
currentVolume float64
|
||||
paused bool
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) IsPlaying() bool {
|
||||
if player.otoPlayer == nil {
|
||||
log.Println("Player not loaded!")
|
||||
return false
|
||||
}
|
||||
|
||||
return player.otoPlayer.IsPlaying()
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Load(stream_url string) {
|
||||
if (player.otoPlayer == nil) || (!player.otoPlayer.IsPlaying()) {
|
||||
var err error
|
||||
is_playlist := strings.HasSuffix(stream_url, ".m3u") || strings.HasSuffix(stream_url, ".pls")
|
||||
if is_playlist {
|
||||
// TODO: Check ffmpeg's ability to deal with playlists
|
||||
// player.command = exec.Command(player.player_name, "-quiet", "-playlist", stream_url)
|
||||
player.command = exec.Command(player.player_name, "-nodisp", "-loglevel", "verbose", "-playlist", stream_url)
|
||||
} else {
|
||||
player.command = exec.Command(player.player_name, "-loglevel", "verbose", "-i", stream_url, "-f", "wav", "-")
|
||||
}
|
||||
|
||||
// In to send things over stdin to ffmpeg
|
||||
player.in, err = player.command.StdinPipe()
|
||||
check(err)
|
||||
// Out will be the wave data we will read and play
|
||||
player.audio, err = player.command.StdoutPipe()
|
||||
check(err)
|
||||
// Err is the output of ffmpeg, used to get stream title
|
||||
player.out, err = player.command.StderrPipe()
|
||||
check(err)
|
||||
|
||||
log.Println("Starting ffmpeg")
|
||||
err = player.command.Start()
|
||||
check(err)
|
||||
|
||||
player.stream_url = stream_url
|
||||
|
||||
op := &oto.NewContextOptions{
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
Format: oto.FormatSignedInt16LE,
|
||||
}
|
||||
|
||||
if player.otoContext == nil {
|
||||
otoContext, readyChan, err := oto.NewContext(op)
|
||||
player.otoContext = otoContext
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-readyChan
|
||||
}
|
||||
|
||||
player.otoPlayer = player.otoContext.NewPlayer(player.audio)
|
||||
// Save current volume for the mute function
|
||||
player.currentVolume = player.otoPlayer.Volume()
|
||||
|
||||
player.paused = false
|
||||
|
||||
go func() {
|
||||
player.pipe_chan <- player.out
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Play() {
|
||||
if player.otoPlayer == nil {
|
||||
log.Println("Stream not loaded")
|
||||
return
|
||||
}
|
||||
|
||||
if !player.otoPlayer.IsPlaying() {
|
||||
if player.command == nil {
|
||||
player.Load(player.stream_url)
|
||||
}
|
||||
player.otoPlayer.Play()
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Close() {
|
||||
if player.IsPlaying() {
|
||||
err := player.otoPlayer.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
player.in.Close()
|
||||
player.out.Close()
|
||||
player.audio.Close()
|
||||
|
||||
player.stream_url = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Mute() {
|
||||
if player.IsPlaying() {
|
||||
if player.otoPlayer.Volume() > 0 {
|
||||
player.currentVolume = player.otoPlayer.Volume()
|
||||
player.otoPlayer.SetVolume(0.0)
|
||||
} else {
|
||||
player.otoPlayer.SetVolume(player.currentVolume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) Pause() {
|
||||
if player.IsPlaying() {
|
||||
if !player.paused {
|
||||
player.paused = true
|
||||
player.otoPlayer.Pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) IncVolume() {
|
||||
if player.IsPlaying() {
|
||||
player.currentVolume += 0.05
|
||||
if player.currentVolume >= 1.0 {
|
||||
player.currentVolume = 1.0
|
||||
}
|
||||
player.otoPlayer.SetVolume(player.currentVolume)
|
||||
}
|
||||
}
|
||||
|
||||
func (player *StreamPlayer) DecVolume() {
|
||||
if player.IsPlaying() {
|
||||
player.currentVolume -= 0.05
|
||||
if player.currentVolume <= 0.0 {
|
||||
player.currentVolume = 0.0
|
||||
}
|
||||
player.otoPlayer.SetVolume(player.currentVolume)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue