Rough code refactor

This commit is contained in:
Adriano Caloiaro 2020-02-29 12:07:12 -05:00
parent 9d315f923c
commit 6486ea784a
No known key found for this signature in database
GPG key ID: 9FFD0E7601F166AB

168
di.go
View file

@ -31,25 +31,33 @@ Listen history: POST /_papi/v1/di/listen_history
Currently playing (all stations): https://www.di.fm/_papi/v1/di/currently_playing
Skip track: https://www.di.fm/_papi/v1/di/skip_events
*/
var ctrls = make(chan int)
var done = make(chan bool)
var chanList *tview.List
var nowPlaying *nowPlayingView
var app *tview.Application
var paused bool
var token string
var speakerInitialized bool
var audioStream beep.StreamSeekCloser
var currentChannel = &channelItem{Name: "N/A"}
var ctx appContext
const (
// CTRLPAUSE pauses playback
CTRLPAUSE = iota
// CTRLRESUME resumes playback
CTRLRESUME
// CTRLPLAY begins playback
CTRLPLAY
)
func createAppContext() appContext {
return appContext{
view: appView{
app: tview.NewApplication(),
channelList: createChannelList(),
nowPlaying: newNowPlaying(&channelItem{Name: "N/A"}),
},
}
}
type appContext struct {
audioStream beep.StreamSeekCloser
controls chan (int)
currentChannel *channelItem
difmToken string
isPlaying bool
speakerInitialized bool
view appView
}
type appView struct {
app *tview.Application
channelList *tview.List
nowPlaying *nowPlayingView
}
func init() {
viper.SetConfigName("config")
@ -64,26 +72,24 @@ func init() {
}
func init() {
app = tview.NewApplication()
chanList = createChannelList()
nowPlaying = newNowPlaying(currentChannel)
nowPlaying.
ctx = createAppContext()
ctx.view.nowPlaying.
SetBorder(true).
SetTitle(" Now Playing ")
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
ctx.view.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Rune() {
case 'q':
app.Stop()
ctx.view.app.Stop()
case 'j': //scroll down
chanList.SetCurrentItem(chanList.GetCurrentItem() + 1)
ctx.view.channelList.SetCurrentItem(ctx.view.channelList.GetCurrentItem() + 1)
case 'k': //scroll up
current := chanList.GetCurrentItem()
current := ctx.view.channelList.GetCurrentItem()
if current > 0 {
chanList.SetCurrentItem(chanList.GetCurrentItem() - 1)
ctx.view.channelList.SetCurrentItem(current - 1)
}
case 'p': // pause/resume
ctrls <- CTRLPAUSE
togglePause()
}
return event
@ -92,8 +98,7 @@ func init() {
func main() {
auth()
controlListener()
drawUI()
run()
}
func auth() {
@ -109,11 +114,30 @@ func auth() {
authenticate(username, password)
}
token = viper.GetString("token")
token := viper.GetString("token")
if token == "" {
fmt.Println("First, authenticate with by running: dicli -username USER -password PASSWORD")
os.Exit(1)
}
ctx.difmToken = token
}
func run() {
flex := tview.NewFlex()
flex.
AddItem(ctx.view.channelList, 0, 1, false).
AddItem(ctx.view.nowPlaying, 0, 2, false)
err := ctx.view.app.
SetRoot(flex, true).
SetFocus(ctx.view.channelList).
Run()
if err != nil {
panic(err)
}
}
func authenticate(username, password string) {
@ -154,21 +178,6 @@ func writeConfig() {
viper.WriteConfig()
}
func drawUI() {
flex := tview.NewFlex()
flex.
AddItem(chanList, 0, 1, false).
AddItem(nowPlaying, 0, 2, false)
if err := app.
SetRoot(flex, true).
SetFocus(chanList).
Run(); err != nil {
panic(err)
}
}
func createChannelList() *tview.List {
channels := list()
list := tview.NewList()
@ -181,12 +190,7 @@ func createChannelList() *tview.List {
list.AddItem(chn.Name, "", 0, func() {
go func() {
chn := channels[list.GetCurrentItem()]
app.QueueUpdateDraw(func() {
nowPlaying.setChannel(&chn)
})
currentChannel = &chn
ctrls <- CTRLPLAY
playChannel(&chn)
}()
})
}
@ -194,38 +198,26 @@ func createChannelList() *tview.List {
return list
}
func controlListener() {
go func() {
for {
switch <-ctrls {
case CTRLPAUSE:
// togglePause pauses/unpauses audio when a channel is playing
func togglePause() {
// nothing to do if nothing has been streamed
if audioStream == nil {
continue
if ctx.audioStream == nil {
return
}
audioStream.Close()
if paused {
playChannel(currentChannel)
paused = false
} else {
paused = true
}
case CTRLPLAY, CTRLRESUME:
if audioStream != nil {
audioStream.Close()
ctx.audioStream.Close()
if !ctx.isPlaying {
playChannel(ctx.currentChannel)
}
playChannel(currentChannel)
}
}
}()
ctx.isPlaying = !ctx.isPlaying
}
// stream streams the provided URL using the given di.fm premium token
func stream(url string) {
client := &http.Client{}
u := fmt.Sprintf("%s?%s", url, token)
u := fmt.Sprintf("%s?%s", url, ctx.difmToken)
req, _ := http.NewRequest("GET", u, nil)
resp, err := client.Do(req)
if err != nil {
@ -235,21 +227,32 @@ func stream(url string) {
}
var format beep.Format
audioStream, format, err = mp3.Decode(resp.Body)
ctx.audioStream, format, err = mp3.Decode(resp.Body)
if err != nil {
// TODO: Don't exit here. Once there's a status message area in the app, populate it with the error
log.Println("unable to stream channel:", resp.StatusCode)
os.Exit(1)
}
if !speakerInitialized {
if !ctx.speakerInitialized {
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
}
speaker.Play(audioStream)
speaker.Play(ctx.audioStream)
ctx.isPlaying = true
}
// playChannel begins streaming the provided channel after fetching its playlist
// If a channel is already playing, the old stream is stopped first, clearing up resources.
// This function is asynchronous and creates a single streaming resource: he audio stream held by the application
// context. To clean up resources created by this function, Close() the application's audio stream.
func playChannel(chn *channelItem) {
// when other channels are already playing, close their stream before playing a new one
if ctx.audioStream != nil {
ctx.audioStream.Close()
}
go func() {
client := &http.Client{}
req, _ := http.NewRequest("GET", chn.Playlist, nil)
@ -261,12 +264,19 @@ func playChannel(chn *channelItem) {
body, err := ioutil.ReadAll(resp.Body)
if streamURL, ok := getStreamURL(body); ok {
currentChannel = chn
stream(streamURL)
setCurrentChannel(chn)
}
}()
}
func setCurrentChannel(chn *channelItem) {
ctx.currentChannel = chn
ctx.view.app.QueueUpdateDraw(func() {
ctx.view.nowPlaying.setChannel(chn)
})
}
// listChannels lists all premium MP3 channels
func list() (channels []channelItem) {
client := &http.Client{}