playlist browser
This commit is contained in:
parent
8a248999a0
commit
de4c668710
@ -48,6 +48,8 @@ func Run(ctx *gctx.Context, client *spotify.Client, args []string) error {
|
|||||||
return commands.RefillRadio(ctx, client)
|
return commands.RefillRadio(ctx, client)
|
||||||
case "tracks":
|
case "tracks":
|
||||||
return tui.DisplayList(ctx, client)
|
return tui.DisplayList(ctx, client)
|
||||||
|
case "playlists":
|
||||||
|
return tui.DisplayPlaylists(ctx, client)
|
||||||
case "status":
|
case "status":
|
||||||
return commands.Status(ctx, client)
|
return commands.Status(ctx, client)
|
||||||
case "devices":
|
case "devices":
|
||||||
|
@ -32,7 +32,6 @@ func Play(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.Println("Playing!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +59,6 @@ func PlayUrl(ctx *gctx.Context, client *spotify.Client, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Playing!")
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
@ -70,7 +68,6 @@ func PlayUrl(ctx *gctx.Context, client *spotify.Client, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Playing!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,13 +83,66 @@ func QueueSong(ctx *gctx.Context, client *spotify.Client, id spotify.ID) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Queued!")
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.Println("Queued!")
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RadioGivenSong(ctx *gctx.Context, client *spotify.Client, song_id spotify.ID) error {
|
||||||
|
seed := spotify.Seeds{
|
||||||
|
Tracks: []spotify.ID{song_id},
|
||||||
|
}
|
||||||
|
recomendations, err := client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
recomendationIds := []spotify.ID{}
|
||||||
|
for _, song := range recomendations.Tracks {
|
||||||
|
recomendationIds = append(recomendationIds, song.ID)
|
||||||
|
}
|
||||||
|
err = ClearRadio(ctx, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
radioPlaylist, err := GetRadioPlaylist(ctx, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, recomendationIds...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client.PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
|
PlaybackOffset: &spotify.PlaybackOffset{
|
||||||
|
Position: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
err = client.Repeat(ctx, "context")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
id := rand.Intn(len(recomendationIds)-2) + 1
|
||||||
|
seed := spotify.Seeds{
|
||||||
|
Tracks: []spotify.ID{recomendationIds[id]},
|
||||||
|
}
|
||||||
|
additional_recs, err := client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
additionalRecsIds := []spotify.ID{}
|
||||||
|
for _, song := range additional_recs.Tracks {
|
||||||
|
additionalRecsIds = append(additionalRecsIds, song.ID)
|
||||||
|
}
|
||||||
|
_, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, additionalRecsIds...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,62 +176,7 @@ func Radio(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
seed_song = tracks.Tracks[rand.Intn(len(tracks.Tracks))].SimpleTrack
|
seed_song = tracks.Tracks[rand.Intn(len(tracks.Tracks))].SimpleTrack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return RadioGivenSong(ctx, client, seed_song.ID)
|
||||||
seed := spotify.Seeds{
|
|
||||||
Tracks: []spotify.ID{seed_song.ID},
|
|
||||||
}
|
|
||||||
fmt.Println("GETTING RECOMENDATIONS FOR", seed_song.Name)
|
|
||||||
recomendations, err := client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
recomendationIds := []spotify.ID{}
|
|
||||||
for _, song := range recomendations.Tracks {
|
|
||||||
recomendationIds = append(recomendationIds, song.ID)
|
|
||||||
}
|
|
||||||
err = ClearRadio(ctx, client)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
radioPlaylist, err := GetRadioPlaylist(ctx, client)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, recomendationIds...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
client.PlayOpt(ctx, &spotify.PlayOptions{
|
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
err = client.Repeat(ctx, "context")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("RADIO STARTED")
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
id := rand.Intn(len(recomendationIds)-2) + 1
|
|
||||||
seed := spotify.Seeds{
|
|
||||||
Tracks: []spotify.ID{recomendationIds[id]},
|
|
||||||
}
|
|
||||||
additional_recs, err := client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
additionalRecsIds := []spotify.ID{}
|
|
||||||
for _, song := range additional_recs.Tracks {
|
|
||||||
additionalRecsIds = append(additionalRecsIds, song.ID)
|
|
||||||
}
|
|
||||||
_, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, additionalRecsIds...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("500 TRACKS QUEUED")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RefillRadio(ctx *gctx.Context, client *spotify.Client) error {
|
func RefillRadio(ctx *gctx.Context, client *spotify.Client) error {
|
||||||
@ -209,7 +204,6 @@ func RefillRadio(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
}
|
}
|
||||||
recomendationIds := []spotify.ID{}
|
recomendationIds := []spotify.ID{}
|
||||||
if len(to_remove) > 0 {
|
if len(to_remove) > 0 {
|
||||||
fmt.Println("REPLENISHING", len(to_remove), "SONGS")
|
|
||||||
_, err = client.RemoveTracksFromPlaylist(ctx, radioPlaylist.ID, to_remove...)
|
_, err = client.RemoveTracksFromPlaylist(ctx, radioPlaylist.ID, to_remove...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -236,7 +230,6 @@ func RefillRadio(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("RADIO REPLENISHED")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +245,6 @@ func ClearRadio(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
configDir, _ := os.UserConfigDir()
|
configDir, _ := os.UserConfigDir()
|
||||||
os.Remove(filepath.Join(configDir, "gospt/radio.json"))
|
os.Remove(filepath.Join(configDir, "gospt/radio.json"))
|
||||||
client.Pause(ctx)
|
client.Pause(ctx)
|
||||||
fmt.Println("Radio emptied")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +261,6 @@ func Pause(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Pausing!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +284,6 @@ func Like(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Pausing!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +296,6 @@ func Unlike(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Pausing!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +304,6 @@ func Skip(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Skipping!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +312,6 @@ func Previous(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx.Println("Previous!")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,6 +359,14 @@ func TrackList(ctx *gctx.Context, client *spotify.Client, page int) (*spotify.Sa
|
|||||||
return client.CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
return client.CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Playlists(ctx *gctx.Context, client *spotify.Client, page int) (*spotify.SimplePlaylistPage, error) {
|
||||||
|
return client.CurrentUsersPlaylists(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlaylistTracks(ctx *gctx.Context, client *spotify.Client, playlist spotify.ID, page int) (*spotify.PlaylistTrackPage, error) {
|
||||||
|
return client.GetPlaylistTracks(ctx, playlist, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
|
}
|
||||||
|
|
||||||
func PrintState(state *spotify.PlayerState) error {
|
func PrintState(state *spotify.PlayerState) error {
|
||||||
state.Item.AvailableMarkets = []string{}
|
state.Item.AvailableMarkets = []string{}
|
||||||
state.Item.Album.AvailableMarkets = []string{}
|
state.Item.Album.AvailableMarkets = []string{}
|
||||||
|
@ -53,6 +53,14 @@ func (m deviceModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
fmt.Println("DEVICE SET")
|
fmt.Println("DEVICE SET")
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
|
case tea.MouseMsg:
|
||||||
|
if msg.Type == 5 {
|
||||||
|
m.list.CursorUp()
|
||||||
|
}
|
||||||
|
if msg.Type == 6 {
|
||||||
|
m.list.CursorDown()
|
||||||
|
}
|
||||||
|
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
h, v := docStyle.GetFrameSize()
|
h, v := docStyle.GetFrameSize()
|
||||||
m.list.SetSize(msg.Width-h, msg.Height-v)
|
m.list.SetSize(msg.Width-h, msg.Height-v)
|
||||||
@ -84,7 +92,7 @@ func DisplayDevices(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
m := deviceModel{list: list.New(items, list.NewDefaultDelegate(), 0, 0), page: 1, ctx: ctx, client: client}
|
m := deviceModel{list: list.New(items, list.NewDefaultDelegate(), 0, 0), page: 1, ctx: ctx, client: client}
|
||||||
m.list.Title = "Saved Tracks"
|
m.list.Title = "Saved Tracks"
|
||||||
|
|
||||||
p := tea.NewProgram(m, tea.WithAltScreen())
|
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||||
|
|
||||||
if _, err := p.Run(); err != nil {
|
if _, err := p.Run(); err != nil {
|
||||||
fmt.Println("Error running program:", err)
|
fmt.Println("Error running program:", err)
|
||||||
|
@ -43,22 +43,25 @@ func (m model) Init() tea.Cmd {
|
|||||||
|
|
||||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
if m.list.Paginator.OnLastPage() {
|
if m.list.Paginator.OnLastPage() {
|
||||||
tracks, err := commands.TrackList(m.ctx, m.client, (m.page + 1))
|
// if last request was still full request more
|
||||||
if err != nil {
|
if len(m.list.Items())%50 == 0 {
|
||||||
return m, tea.Quit
|
tracks, err := commands.TrackList(m.ctx, m.client, (m.page + 1))
|
||||||
}
|
if err != nil {
|
||||||
m.page++
|
return m, tea.Quit
|
||||||
items := []list.Item{}
|
}
|
||||||
for _, track := range tracks.Tracks {
|
m.page++
|
||||||
items = append(items, item{
|
items := []list.Item{}
|
||||||
Name: track.Name,
|
for _, track := range tracks.Tracks {
|
||||||
Artist: track.Artists[0],
|
items = append(items, item{
|
||||||
Duration: track.TimeDuration().Round(time.Second).String(),
|
Name: track.Name,
|
||||||
ID: track.ID,
|
Artist: track.Artists[0],
|
||||||
})
|
Duration: track.TimeDuration().Round(time.Second).String(),
|
||||||
}
|
ID: track.ID,
|
||||||
for _, item := range items {
|
})
|
||||||
m.list.InsertItem(len(m.list.Items())+1, item)
|
}
|
||||||
|
for _, item := range items {
|
||||||
|
m.list.InsertItem(len(m.list.Items())+1, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
@ -66,6 +69,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if msg.String() == "ctrl+c" {
|
if msg.String() == "ctrl+c" {
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
|
if msg.String() == "ctrl+r" {
|
||||||
|
track := m.list.SelectedItem()
|
||||||
|
err := commands.RadioGivenSong(m.ctx, m.client, track.(item).ID)
|
||||||
|
if err != nil {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
if msg.String() == "enter" {
|
if msg.String() == "enter" {
|
||||||
track := m.list.SelectedItem()
|
track := m.list.SelectedItem()
|
||||||
var err error
|
var err error
|
||||||
@ -77,7 +87,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
m.ctx.Printf(err.Error())
|
m.ctx.Printf(err.Error())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
case tea.MouseMsg:
|
||||||
|
if msg.Type == 5 {
|
||||||
|
m.list.CursorUp()
|
||||||
|
}
|
||||||
|
if msg.Type == 6 {
|
||||||
|
m.list.CursorDown()
|
||||||
}
|
}
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
h, v := docStyle.GetFrameSize()
|
h, v := docStyle.GetFrameSize()
|
||||||
@ -108,10 +124,15 @@ func DisplayList(ctx *gctx.Context, client *spotify.Client) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
m := model{list: list.New(items, list.NewDefaultDelegate(), 0, 0), page: 1, ctx: ctx, client: client}
|
m := model{
|
||||||
|
list: list.New(items, list.NewDefaultDelegate(), 0, 0),
|
||||||
|
page: 1,
|
||||||
|
ctx: ctx,
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
m.list.Title = "Saved Tracks"
|
m.list.Title = "Saved Tracks"
|
||||||
|
|
||||||
p := tea.NewProgram(m, tea.WithAltScreen())
|
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||||
|
|
||||||
if _, err := p.Run(); err != nil {
|
if _, err := p.Run(); err != nil {
|
||||||
fmt.Println("Error running program:", err)
|
fmt.Println("Error running program:", err)
|
||||||
|
123
internal/tui/playlists.go
Normal file
123
internal/tui/playlists.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gospt/internal/commands"
|
||||||
|
"gospt/internal/gctx"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/list"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/zmb3/spotify/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var playlistsDocStyle = lipgloss.NewStyle().Margin(1, 2)
|
||||||
|
|
||||||
|
type playlistItem struct {
|
||||||
|
Name string
|
||||||
|
Desc string
|
||||||
|
ID spotify.ID
|
||||||
|
spotify.SimplePlaylist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i playlistItem) Title() string { return i.Name }
|
||||||
|
func (i playlistItem) Description() string { return i.Desc }
|
||||||
|
func (i playlistItem) FilterValue() string { return i.Title() + i.Desc }
|
||||||
|
|
||||||
|
type playlistModel struct {
|
||||||
|
list list.Model
|
||||||
|
page int
|
||||||
|
ctx *gctx.Context
|
||||||
|
client *spotify.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m playlistModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m playlistModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
if m.list.Paginator.OnLastPage() {
|
||||||
|
// if the last request was not full
|
||||||
|
if len(m.list.Items())%50 == 0 {
|
||||||
|
playlists, err := commands.Playlists(m.ctx, m.client, (m.page + 1))
|
||||||
|
if err != nil {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
m.page++
|
||||||
|
items := []list.Item{}
|
||||||
|
for _, playlist := range playlists.Playlists {
|
||||||
|
items = append(items, playlistItem{
|
||||||
|
Name: playlist.Name,
|
||||||
|
Desc: playlist.Description,
|
||||||
|
ID: playlist.ID,
|
||||||
|
SimplePlaylist: playlist,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, item := range items {
|
||||||
|
m.list.InsertItem(len(m.list.Items())+1, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
if msg.String() == "ctrl+c" {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
if msg.String() == "enter" {
|
||||||
|
playlist := m.list.SelectedItem().(playlistItem).SimplePlaylist
|
||||||
|
PlaylistTracks(m.ctx, m.client, playlist)
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
case tea.MouseMsg:
|
||||||
|
if msg.Type == 5 {
|
||||||
|
m.list.CursorUp()
|
||||||
|
}
|
||||||
|
if msg.Type == 6 {
|
||||||
|
m.list.CursorDown()
|
||||||
|
}
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
h, v := docStyle.GetFrameSize()
|
||||||
|
m.list.SetSize(msg.Width-h, msg.Height-v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd tea.Cmd
|
||||||
|
m.list, cmd = m.list.Update(msg)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m playlistModel) View() string {
|
||||||
|
return docStyle.Render(m.list.View())
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisplayPlaylists(ctx *gctx.Context, client *spotify.Client) error {
|
||||||
|
items := []list.Item{}
|
||||||
|
playlists, err := commands.Playlists(ctx, client, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, playlist := range playlists.Playlists {
|
||||||
|
items = append(items, playlistItem{
|
||||||
|
Name: playlist.Name,
|
||||||
|
Desc: playlist.Description,
|
||||||
|
ID: playlist.ID,
|
||||||
|
SimplePlaylist: playlist,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
m := playlistModel{
|
||||||
|
list: list.New(items, list.NewDefaultDelegate(), 0, 0),
|
||||||
|
page: 1,
|
||||||
|
ctx: ctx,
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
m.list.Title = "Saved Tracks"
|
||||||
|
|
||||||
|
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||||
|
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Println("Error running program:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
145
internal/tui/playlisttracks.go
Normal file
145
internal/tui/playlisttracks.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gospt/internal/commands"
|
||||||
|
"gospt/internal/gctx"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/list"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/zmb3/spotify/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type track struct {
|
||||||
|
Name string
|
||||||
|
Duration string
|
||||||
|
Artist spotify.SimpleArtist
|
||||||
|
ID spotify.ID
|
||||||
|
spotify.SavedTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i track) Title() string { return i.Name }
|
||||||
|
func (i track) Description() string {
|
||||||
|
return fmt.Sprint(i.Duration, " by ", i.Artist.Name)
|
||||||
|
}
|
||||||
|
func (i track) FilterValue() string { return i.Title() + i.Artist.Name }
|
||||||
|
|
||||||
|
type playlistTracksModel struct {
|
||||||
|
list list.Model
|
||||||
|
page int
|
||||||
|
ctx *gctx.Context
|
||||||
|
client *spotify.Client
|
||||||
|
playlist spotify.SimplePlaylist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m playlistTracksModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m playlistTracksModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
if m.list.Paginator.OnLastPage() {
|
||||||
|
// if last request was still full request more
|
||||||
|
if len(m.list.Items())%50 == 0 {
|
||||||
|
tracks, err := commands.PlaylistTracks(m.ctx, m.client, m.playlist.ID, (m.page + 1))
|
||||||
|
if err != nil {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
m.page++
|
||||||
|
items := []list.Item{}
|
||||||
|
for _, track := range tracks.Tracks {
|
||||||
|
items = append(items, item{
|
||||||
|
Name: track.Track.Name,
|
||||||
|
Artist: track.Track.Artists[0],
|
||||||
|
Duration: track.Track.TimeDuration().Round(time.Second).String(),
|
||||||
|
ID: track.Track.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, item := range items {
|
||||||
|
m.list.InsertItem(len(m.list.Items())+1, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
if msg.String() == "backspace" || msg.String() == "q" {
|
||||||
|
DisplayPlaylists(m.ctx, m.client)
|
||||||
|
}
|
||||||
|
if msg.String() == "ctrl+c" {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
if msg.String() == "ctrl+r" {
|
||||||
|
track := m.list.SelectedItem()
|
||||||
|
err := commands.RadioGivenSong(m.ctx, m.client, track.(item).ID)
|
||||||
|
if err != nil {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msg.String() == "enter" {
|
||||||
|
track := m.list.SelectedItem()
|
||||||
|
var err error
|
||||||
|
err = commands.QueueSong(m.ctx, m.client, track.(item).ID)
|
||||||
|
if err != nil {
|
||||||
|
m.ctx.Printf(err.Error())
|
||||||
|
}
|
||||||
|
err = commands.Skip(m.ctx, m.client)
|
||||||
|
if err != nil {
|
||||||
|
m.ctx.Printf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case tea.MouseMsg:
|
||||||
|
if msg.Type == 5 {
|
||||||
|
m.list.CursorUp()
|
||||||
|
}
|
||||||
|
if msg.Type == 6 {
|
||||||
|
m.list.CursorDown()
|
||||||
|
}
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
h, v := docStyle.GetFrameSize()
|
||||||
|
m.list.SetSize(msg.Width-h, msg.Height-v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd tea.Cmd
|
||||||
|
m.list, cmd = m.list.Update(msg)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m playlistTracksModel) View() string {
|
||||||
|
return docStyle.Render(m.list.View())
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlaylistTracks(ctx *gctx.Context, client *spotify.Client, playlist spotify.SimplePlaylist) error {
|
||||||
|
items := []list.Item{}
|
||||||
|
tracks, err := commands.PlaylistTracks(ctx, client, playlist.ID, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, track := range tracks.Tracks {
|
||||||
|
items = append(items, item{
|
||||||
|
Name: track.Track.Name,
|
||||||
|
Artist: track.Track.Artists[0],
|
||||||
|
Duration: track.Track.TimeDuration().Round(time.Second).String(),
|
||||||
|
ID: track.Track.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
m := playlistTracksModel{
|
||||||
|
list: list.New(items, list.NewDefaultDelegate(), 0, 0),
|
||||||
|
page: 1,
|
||||||
|
ctx: ctx,
|
||||||
|
client: client,
|
||||||
|
playlist: playlist,
|
||||||
|
}
|
||||||
|
m.list.Title = "Saved Tracks"
|
||||||
|
|
||||||
|
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||||
|
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Println("Error running program:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user