2020-02-29 20:13:02 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
2020-03-01 19:59:28 +00:00
|
|
|
"time"
|
2020-02-29 20:13:02 +00:00
|
|
|
|
2020-03-01 19:59:28 +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
|
|
|
|
2023-07-08 14:39:12 +00:00
|
|
|
"github.com/gdamore/tcell/v2"
|
2020-11-15 17:05:46 +00:00
|
|
|
"github.com/spf13/pflag"
|
|
|
|
|
"github.com/spf13/viper"
|
2020-02-29 20:13:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var ctx *context.AppContext
|
|
|
|
|
|
|
|
|
|
func main() {
|
2020-11-15 17:05:46 +00:00
|
|
|
pflag.String("username", "", "your di.fm username")
|
|
|
|
|
pflag.String("password", "", "your di.fm password")
|
2020-02-29 20:13:02 +00:00
|
|
|
|
2020-11-15 17:05:46 +00:00
|
|
|
pflag.Parse()
|
|
|
|
|
viper.BindPFlags(pflag.CommandLine)
|
|
|
|
|
|
|
|
|
|
username := viper.GetString("username")
|
|
|
|
|
password := viper.GetString("password")
|
2023-06-02 00:04:01 +00:00
|
|
|
|
2020-11-15 17:05:46 +00:00
|
|
|
var token string
|
|
|
|
|
if len(username) > 0 && len(password) > 0 {
|
2023-05-23 22:41:13 +00:00
|
|
|
difm.Authenticate(ctx, username, password)
|
2020-11-15 17:05:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = config.GetToken()
|
2020-02-29 20:13:02 +00:00
|
|
|
if token == "" {
|
2020-12-15 00:50:47 +00:00
|
|
|
fmt.Println("Authenticate by running: di-tui --username USER --password PASSWORD\n\n" +
|
|
|
|
|
"Or, visit https://github.com/acaloiaro/di-tui#authenticate for other options.")
|
2020-11-15 17:05:46 +00:00
|
|
|
os.Exit(1)
|
2020-02-29 20:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-04 23:03:16 +00:00
|
|
|
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()
|
2020-03-04 23:03:16 +00:00
|
|
|
updateScreenLayout()
|
2023-05-23 22:41:13 +00:00
|
|
|
FetchFavoritesAndChannels()
|
2020-03-01 17:40:54 +00:00
|
|
|
|
2023-07-08 14:39:12 +00:00
|
|
|
ctx.View.Keybindings.Bindings = views.GetKeybindings()
|
|
|
|
|
|
2020-03-04 23:03:16 +00:00
|
|
|
err := ctx.View.App.Run()
|
2020-03-01 17:40:54 +00:00
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-04 23:03:16 +00:00
|
|
|
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)
|
|
|
|
|
|
2020-03-04 23:03:16 +00:00
|
|
|
primaryView := tview.NewFlex()
|
|
|
|
|
primaryView.
|
2020-03-01 19:59:28 +00:00
|
|
|
AddItem(favsAndChannels, 30, 0, false).
|
|
|
|
|
AddItem(ctx.View.NowPlaying, 0, 4, false)
|
2020-03-01 17:40:54 +00:00
|
|
|
|
2020-03-04 23:03:16 +00:00
|
|
|
if ctx.ShowStatus {
|
|
|
|
|
main.AddItem(ctx.View.Status, 4, 0, false)
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 17:40:54 +00:00
|
|
|
main.
|
2020-03-04 23:03:16 +00:00
|
|
|
AddItem(primaryView, 0, 3, false).
|
2020-03-01 19:59:28 +00:00
|
|
|
AddItem(ctx.View.Keybindings, 4, 0, false)
|
2020-03-01 17:40:54 +00:00
|
|
|
|
2020-03-04 23:03:16 +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() {
|
2020-03-01 19:59:28 +00:00
|
|
|
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)
|
|
|
|
|
}
|
2023-05-23 22:41:13 +00:00
|
|
|
case 'F':
|
|
|
|
|
current := focus.GetCurrentItem()
|
|
|
|
|
if focus == ctx.View.ChannelList {
|
|
|
|
|
ctx.HighlightedChannel = &ctx.ChannelList[current]
|
|
|
|
|
} else {
|
|
|
|
|
highlightedFavorite := ctx.FavoriteList[current]
|
|
|
|
|
ctx.HighlightedChannel = difm.FavoriteItemChannel(ctx, highlightedFavorite)
|
|
|
|
|
}
|
|
|
|
|
difm.ToggleFavorite(ctx)
|
|
|
|
|
FetchFavoritesAndChannels()
|
2020-02-29 20:13:02 +00:00
|
|
|
case 'q':
|
|
|
|
|
ctx.View.App.Stop()
|
|
|
|
|
case 'j': //scroll down
|
2023-05-23 22:41:13 +00:00
|
|
|
current := focus.GetCurrentItem() + 1
|
|
|
|
|
focus.SetCurrentItem(current)
|
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
|
|
|
|
|
})
|
2020-03-01 19:59:28 +00:00
|
|
|
|
|
|
|
|
// keep 'now playing' up to date second-by-second
|
|
|
|
|
go func() {
|
|
|
|
|
c := time.Tick(1 * time.Second)
|
|
|
|
|
for range c {
|
2020-11-15 17:05:46 +00:00
|
|
|
elapsed := time.Since(ctx.View.NowPlaying.Track.StartTime)
|
2020-03-01 19:59:28 +00:00
|
|
|
|
|
|
|
|
// 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() {})
|
|
|
|
|
}
|
|
|
|
|
}()
|
2020-03-04 23:03:16 +00:00
|
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
2023-05-23 22:41:13 +00:00
|
|
|
func FetchFavoritesAndChannels() {
|
|
|
|
|
ctx.View.ChannelList.Clear()
|
|
|
|
|
ctx.View.FavoriteList.Clear()
|
2020-02-29 20:13:02 +00:00
|
|
|
|
2020-03-04 23:03:16 +00:00
|
|
|
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()]
|
2023-05-23 22:41:13 +00:00
|
|
|
chn := difm.FavoriteItemChannel(ctx, f)
|
|
|
|
|
app.PlayChannel(chn, ctx)
|
2020-03-01 18:38:51 +00:00
|
|
|
})
|
|
|
|
|
}
|
2023-05-23 22:41:13 +00:00
|
|
|
ctx.ChannelList = channels
|
|
|
|
|
ctx.FavoriteList = favorites
|
2020-02-29 20:13:02 +00:00
|
|
|
}
|