di-tui/main.go

201 lines
5 KiB
Go
Raw Normal View History

2020-02-29 20:13:02 +00:00
package main
import (
"fmt"
"os"
"time"
2020-02-29 20:13:02 +00:00
"github.com/acaloiaro/di-tui/app"
"github.com/acaloiaro/di-tui/config"
"github.com/acaloiaro/di-tui/context"
"github.com/acaloiaro/di-tui/difm"
"github.com/acaloiaro/di-tui/views"
2020-03-01 17:40:54 +00:00
"github.com/rivo/tview"
2020-02-29 20:13:02 +00:00
"github.com/gdamore/tcell"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var ctx *context.AppContext
2020-04-06 14:52:26 +00:00
func init() {
// when true color is on, tcell does not respect your terminal colors
os.Setenv("TCELL_TRUECOLOR", "disable")
}
2020-02-29 20:13:02 +00:00
func main() {
pflag.String("username", "", "your di.fm username")
pflag.String("password", "", "your di.fm password")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
username := viper.GetString("username")
password := viper.GetString("password")
var token string
if len(username) > 0 && len(password) > 0 {
2020-11-18 16:45:27 +00:00
difm.Authenticate(username, password)
2020-02-29 20:13:02 +00:00
}
token = config.GetToken()
if token == "" {
fmt.Println("First, authenticate with by running: di-tui --username USER --password PASSWORD")
2020-02-29 20:13:02 +00:00
os.Exit(1)
}
ctx = context.CreateAppContext(views.CreateViewContext())
2020-02-29 20:13:02 +00:00
ctx.DifmToken = token
run()
}
func run() {
2020-03-01 17:40:54 +00:00
configureEventHandling()
updateScreenLayout()
2020-03-01 17:40:54 +00:00
configureUIComponents()
err := ctx.View.App.Run()
2020-03-01 17:40:54 +00:00
if err != nil {
panic(err)
}
}
func updateScreenLayout() {
focusView := ctx.View.App.GetFocus()
2020-03-01 17:40:54 +00:00
main := tview.NewFlex()
main.SetDirection(tview.FlexRow)
2020-03-01 18:38:51 +00:00
favsAndChannels := tview.NewFlex().
AddItem(ctx.View.FavoriteList, 0, 1, false).
AddItem(ctx.View.ChannelList, 0, 2, false).
SetDirection(tview.FlexRow)
primaryView := tview.NewFlex()
primaryView.
AddItem(favsAndChannels, 30, 0, false).
AddItem(ctx.View.NowPlaying, 0, 4, false)
2020-03-01 17:40:54 +00:00
if ctx.ShowStatus {
main.AddItem(ctx.View.Status, 4, 0, false)
}
2020-03-01 17:40:54 +00:00
main.
AddItem(primaryView, 0, 3, false).
AddItem(ctx.View.Keybindings, 4, 0, false)
2020-03-01 17:40:54 +00:00
if focusView == nil {
focusView = ctx.View.FavoriteList
}
ctx.View.App.
SetRoot(main, true).
SetFocus(focusView)
2020-03-01 17:40:54 +00:00
}
func configureEventHandling() {
2020-02-29 20:13:02 +00:00
ctx.View.App.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
2020-03-01 18:38:51 +00:00
focus := ctx.View.App.GetFocus().(*tview.List)
2020-02-29 20:13:02 +00:00
switch event.Rune() {
case 'c':
if focus != ctx.View.ChannelList {
ctx.View.App.SetFocus(ctx.View.ChannelList)
}
case 'f':
if focus != ctx.View.FavoriteList {
ctx.View.App.SetFocus(ctx.View.FavoriteList)
}
2020-02-29 20:13:02 +00:00
case 'q':
ctx.View.App.Stop()
case 'j': //scroll down
2020-03-01 18:38:51 +00:00
focus.SetCurrentItem(focus.GetCurrentItem() + 1)
2020-02-29 20:13:02 +00:00
case 'k': //scroll up
2020-03-01 18:38:51 +00:00
current := focus.GetCurrentItem()
2020-02-29 20:13:02 +00:00
if current > 0 {
2020-03-01 18:38:51 +00:00
focus.SetCurrentItem(current - 1)
2020-02-29 20:13:02 +00:00
}
case 'p': // pause/resume
app.TogglePause(ctx)
}
return event
})
// keep 'now playing' up to date second-by-second
go func() {
c := time.Tick(1 * time.Second)
for range c {
2020-11-18 16:45:27 +00:00
elapsed := time.Since(ctx.View.NowPlaying.Track.StartTime)
// If the current time is past the end of the track, then a new track is playing and the now playing track needs
// to be refreshed.
if ctx.View.NowPlaying.Track.Duration > 0 && ctx.View.NowPlaying.Track.Duration < elapsed.Seconds() {
app.UpdateNowPlaying(ctx.CurrentChannel, ctx)
}
if ctx.CurrentChannel != nil && elapsed.Seconds() > 0 {
ctx.View.NowPlaying.Elapsed = elapsed.Seconds()
}
ctx.View.App.QueueUpdateDraw(func() {})
}
}()
// display the status pane when new status messages arrive
go func() {
for {
status := <-ctx.StatusChannel
ctx.View.Status.Message = status.Message
updateScreenLayout() // add the status pane to the screen
<-time.Tick(status.Duration)
ctx.ShowStatus = false
updateScreenLayout() // remove the status pane from the screen
}
}()
2020-03-01 17:40:54 +00:00
}
func configureUIComponents() {
2020-02-29 20:13:02 +00:00
2020-03-01 17:40:54 +00:00
// configure the channel list
channels := difm.ListChannels(ctx)
2020-02-29 20:13:02 +00:00
for _, chn := range channels {
ctx.View.ChannelList.AddItem(chn.Name, "", 0, func() {
2020-03-01 17:40:54 +00:00
chn := channels[ctx.View.ChannelList.GetCurrentItem()]
app.PlayChannel(&chn, ctx)
2020-02-29 20:13:02 +00:00
})
}
2020-03-01 18:38:51 +00:00
favorites := difm.ListFavorites(ctx)
for _, fav := range favorites {
ctx.View.FavoriteList.AddItem(fav.Name, "", 0, func() {
f := favorites[ctx.View.FavoriteList.GetCurrentItem()]
for _, chn := range channels {
// favorites are prefixed with "DI.fm - <CHANNEL NAME>", shave it off before comparing
// TODO: this feels a bit hacky -- consider doing something else.
2020-03-01 18:38:51 +00:00
if chn.Name == f.Name[8:len(f.Name)] {
app.PlayChannel(&chn, ctx)
return
}
}
})
}
2020-03-01 17:40:54 +00:00
// configure the keybinding view
bindings := []views.UIKeybinding{
views.UIKeybinding{Shortcut: "c", Description: "Channels", Func: func() {}},
views.UIKeybinding{Shortcut: "f", Description: "Favorites", Func: func() {}},
2020-03-02 01:51:52 +00:00
views.UIKeybinding{Shortcut: "j", Description: "Scroll Down", Func: func() {}},
views.UIKeybinding{Shortcut: "k", Description: "Scroll Up", Func: func() {}},
views.UIKeybinding{Shortcut: "q", Description: "Quit", Func: func() {}},
views.UIKeybinding{Shortcut: "p", Description: "Pause", Func: func() {}},
views.UIKeybinding{Shortcut: "Enter", Description: "Play", Func: func() {}},
2020-02-29 20:13:02 +00:00
}
2020-03-01 17:40:54 +00:00
ctx.View.Keybindings.Bindings = bindings
2020-02-29 20:13:02 +00:00
}