feat: buffering indicator implemented, solved bug reading when paused

This commit is contained in:
José Carlos Cuevas 2023-12-08 01:52:40 +01:00
parent 43f40b7e6d
commit 8c48b48371
3 changed files with 70 additions and 52 deletions

1
go.mod
View file

@ -6,5 +6,6 @@ require (
fyne.io/fyne/v2 v2.4.2
github.com/ebitengine/oto/v3 v3.1.0 // indirect
github.com/ebitengine/purego v0.5.1 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
golang.org/x/sys v0.15.0 // indirect
)

3
go.sum
View file

@ -243,6 +243,8 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
@ -502,6 +504,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

118
main.go
View file

@ -36,7 +36,6 @@ package main
*/
import (
"bufio"
"encoding/json"
"flag"
"image"
@ -51,6 +50,7 @@ import (
"time"
"github.com/ebitengine/oto/v3"
"github.com/muesli/cancelreader"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
@ -61,6 +61,21 @@ import (
"fyne.io/fyne/v2/widget"
)
// Enums and constants
const RADIOSPIRAL_STREAM = "https://radiospiral.radio/stream.mp3"
const RADIOSPIRAL_JSON_ENDPOINT = "https://radiospiral.net/wp-json/radio/broadcast"
const (
Loading int = iota
Playing
Paused
Stopped
)
// Cancel Reader
var reader cancelreader.CancelReader
// helper
func check(err error) {
if err != nil {
@ -220,7 +235,6 @@ func (player *StreamPlayer) Close() {
player.in.Close()
player.out.Close()
player.audio.Close()
// player.command.Cancel()
player.stream_url = ""
}
@ -240,13 +254,8 @@ func (player *StreamPlayer) Mute() {
func (player *StreamPlayer) Pause() {
if player.IsPlaying() {
if !player.paused {
log.Println("[oto] Pausing")
player.paused = true
player.otoPlayer.Pause()
} else {
log.Println("[oto] Playing")
player.paused = false
player.otoPlayer.Play()
}
}
}
@ -284,8 +293,6 @@ func loadImageURL(url string) image.Image {
}
func main() {
const RADIOSPIRAL_STREAM = "https://radiospiral.radio/stream.mp3"
const RADIOSPIRAL_JSON_ENDPOINT = "https://radiospiral.net/wp-json/radio/broadcast"
PLAYER_CMD := "ffmpeg"
if runtime.GOOS == "windows" {
@ -313,9 +320,6 @@ func main() {
// Create our StreamPlayer instance
streamPlayer := StreamPlayer{player_name: PLAYER_CMD, pipe_chan: pipe_chan}
// Make sure that StreamPlayer closes when the program ends
defer streamPlayer.Close()
// Create our app and window
app := app.New()
window := app.NewWindow("RadioSpiral Player")
@ -324,7 +328,7 @@ func main() {
window.SetIcon(resourceIconPng)
// Keep the status of the player
playStatus := false
playStatus := Stopped
// Placeholder avatar
radioSpiralAvatar := loadImageURL("https://radiospiral.net/wp-content/uploads/2018/03/Radio-Spiral-Logo-1.png")
@ -343,7 +347,6 @@ func main() {
// Player section
nowPlayingLabelHeader := widget.NewLabel("Now playing:")
nowPlayingLabel := widget.NewLabel("")
var playButton *widget.Button
volumeDown := widget.NewButtonWithIcon("", theme.VolumeDownIcon(), func() {
streamPlayer.DecVolume()
})
@ -364,34 +367,65 @@ func main() {
nowPlayingLabel.Alignment = fyne.TextAlignCenter
nowPlayingLabel.Wrapping = fyne.TextWrapWord
// Process the output of Mplayer here in a separate goroutine
var playButton *widget.Button
playButton = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() {
// Here we control each time the button is pressed and update its
// appearance anytime it is clicked. We make the player start playing
// or pause.
if !streamPlayer.IsPlaying() && !streamPlayer.paused {
playButton.SetIcon(theme.MediaPauseIcon())
playButton.SetText("(Buffering)")
streamPlayer.Load(RADIOSPIRAL_STREAM)
streamPlayer.Play()
playStatus = Loading
} else {
if playStatus == Playing {
playStatus = Paused
playButton.SetIcon(theme.MediaPlayIcon())
streamPlayer.Pause()
} else {
reader.Cancel()
playStatus = Loading
playButton.SetText("(Buffering)")
playButton.SetIcon(theme.MediaPauseIcon())
streamPlayer.Load(RADIOSPIRAL_STREAM)
streamPlayer.Play()
}
}
})
// Process the output of ffmpeg here in a separate goroutine
go func() {
for {
out_pipe := <-pipe_chan
reader := bufio.NewReader(out_pipe)
var err error
reader, err = cancelreader.NewReader(out_pipe)
if err != nil {
log.Println("Error opening reader")
}
for {
data, err := reader.ReadString('\n')
var data [255]byte
_, err := reader.Read(data[:])
if err != nil {
log.Fatal(err)
log.Println("Reloading player")
streamPlayer.Close()
pipe_chan = make(chan io.ReadCloser)
streamPlayer = StreamPlayer{player_name: PLAYER_CMD, pipe_chan: pipe_chan}
streamPlayer.Load(RADIOSPIRAL_STREAM)
streamPlayer.Play()
playStatus = true
playButton.SetIcon(theme.MediaPlayIcon())
defer streamPlayer.Close()
} else {
log.Println(err)
break
}
lines := strings.Split(string(data[:]), "\n")
for _, line := range lines {
// Log, if enabled, the output of StreamPlayer
if *loggingToFilePtr {
log.Print("[" + streamPlayer.player_name + "] " + data)
log.Print("[" + streamPlayer.player_name + "] " + line)
}
if strings.Contains(line, "Output #0") {
playStatus = Playing
playButton.SetText("")
}
// Check if there's an updated title and reflect it on the
// GUI
if strings.Contains(data, "StreamTitle: ") {
if strings.Contains(line, "StreamTitle: ") {
log.Println("Found new stream title, updating GUI")
newTitleParts := strings.Split(data, "StreamTitle: ")
newTitleParts := strings.Split(line, "StreamTitle: ")
nowPlayingLabel.SetText(newTitleParts[1])
}
}
@ -399,27 +433,6 @@ func main() {
}
}()
playButton = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() {
// Here we control each time the button is pressed and update its
// appearance anytime it is clicked. We make the player start playing
// or pause.
if !streamPlayer.IsPlaying() {
playButton.SetIcon(theme.MediaPauseIcon())
streamPlayer.Load(RADIOSPIRAL_STREAM)
streamPlayer.Play()
playStatus = true
} else {
if playStatus {
playStatus = false
playButton.SetIcon(theme.MediaPlayIcon())
} else {
playStatus = true
playButton.SetIcon(theme.MediaPauseIcon())
}
streamPlayer.Pause()
}
})
rsUrl, err := url.Parse("https://radiospiral.net")
if err != nil {
@ -477,4 +490,5 @@ func main() {
// Showtime!
window.ShowAndRun()
streamPlayer.Close()
}