297 lines
9.5 KiB
Go
297 lines
9.5 KiB
Go
package client
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/fhs/gompd/mpd"
|
|
|
|
"strings"
|
|
|
|
"github.com/aditya-K2/gomp/utils"
|
|
"github.com/aditya-K2/tview"
|
|
)
|
|
|
|
var (
|
|
CONN *mpd.Client
|
|
ArtistTree map[string]map[string]map[string]string
|
|
NotificationServer interface {
|
|
Send(string)
|
|
}
|
|
WHITE_AND_BOLD string = "[#ffffff::b]"
|
|
)
|
|
|
|
func SetConnection(c *mpd.Client) {
|
|
CONN = c
|
|
}
|
|
|
|
func SetNotificationServer(n interface{ Send(string) }) {
|
|
NotificationServer = n
|
|
}
|
|
|
|
func TogglePlayBack() error {
|
|
status, err := CONN.Status()
|
|
if status["state"] == "play" && err == nil {
|
|
CONN.Pause(true)
|
|
} else if status["state"] == "pause" && err == nil {
|
|
CONN.Play(-1)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func UpdatePlaylist(inputTable *tview.Table) {
|
|
_playlistAttr, _ := CONN.PlaylistInfo(-1, -1)
|
|
|
|
inputTable.Clear()
|
|
for i, j := range _playlistAttr {
|
|
_, _, w, _ := inputTable.GetInnerRect()
|
|
if j["Title"] == "" || j["Artist"] == "" || j["Album"] == "" {
|
|
inputTable.SetCell(i, 0, tview.NewTableCell(utils.GetFormattedString(j["file"], w/3)))
|
|
} else {
|
|
inputTable.SetCell(i, 0, tview.NewTableCell(utils.GetFormattedString("[green]"+j["Title"], w/3)))
|
|
inputTable.SetCell(i, 1, tview.NewTableCell(utils.GetFormattedString("[magenta]"+j["Artist"], w/3)))
|
|
inputTable.SetCell(i, 2, tview.NewTableCell("[yellow]"+j["Album"]))
|
|
}
|
|
}
|
|
}
|
|
|
|
// The GenerateContentSlice returns a slice of the content to be displayed on the Search View. The Slice is generated
|
|
// because the random nature of maps as they return values randomly hence the draw function keeps changing the order
|
|
// in which the results appear.
|
|
func GenerateContentSlice(selectedSuggestion string) ([]interface{}, error) {
|
|
var ContentSlice []interface{}
|
|
if strings.TrimRight(selectedSuggestion, " ") == "" {
|
|
NotificationServer.Send("Empty Search!")
|
|
return nil, errors.New("empty Search String Provided")
|
|
}
|
|
if _, ok := ArtistTree[selectedSuggestion]; ok {
|
|
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Artists :")
|
|
ContentSlice = append(ContentSlice, selectedSuggestion)
|
|
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Artist Albums :")
|
|
for albumName := range ArtistTree[selectedSuggestion] {
|
|
ContentSlice = append(ContentSlice, [2]string{albumName, selectedSuggestion})
|
|
}
|
|
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Artist Tracks :")
|
|
for albumName, trackList := range ArtistTree[selectedSuggestion] {
|
|
for track := range trackList {
|
|
ContentSlice = append(ContentSlice, [3]string{track, selectedSuggestion, albumName})
|
|
}
|
|
}
|
|
}
|
|
if aMap := QueryArtistTreeForAlbums(ArtistTree, selectedSuggestion); len(aMap) != 0 {
|
|
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Albums :")
|
|
for mSlice := range aMap {
|
|
ContentSlice = append(ContentSlice, mSlice)
|
|
}
|
|
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Album Tracks :")
|
|
for a, pathSlice := range aMap {
|
|
for _, path := range pathSlice {
|
|
ContentSlice = append(ContentSlice, [3]string{path[0], a[1], a[0]})
|
|
}
|
|
}
|
|
}
|
|
if tMap := QueryArtistTreeForTracks(ArtistTree, selectedSuggestion); len(tMap) != 0 {
|
|
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Tracks :")
|
|
for mSlice := range tMap {
|
|
ContentSlice = append(ContentSlice, mSlice)
|
|
}
|
|
}
|
|
return ContentSlice, nil
|
|
}
|
|
|
|
// UpdateSearchView as the name suggests Updates the Search View the idea is to basically keep a fourth option called
|
|
// Search in the Navigation bar which will render things from a global ContentSlice at least in the context of the main
|
|
// function this will also help in persisting the Search Results.
|
|
func UpdateSearchView(inputTable *tview.Table, c []interface{}) {
|
|
inputTable.Clear()
|
|
_, _, width, _ := inputTable.GetInnerRect()
|
|
for i, content := range c {
|
|
switch content.(type) {
|
|
case [3]string:
|
|
{
|
|
inputTable.SetCell(i, 0, tview.NewTableCell(utils.GetFormattedString("[green]"+content.([3]string)[0], width/3)))
|
|
inputTable.SetCell(i, 1, tview.NewTableCell(utils.GetFormattedString("[magenta]"+content.([3]string)[1], width/3)))
|
|
inputTable.SetCell(i, 2, tview.NewTableCell(utils.GetFormattedString("[yellow]"+content.([3]string)[2], width/3)))
|
|
}
|
|
case [2]string:
|
|
{
|
|
inputTable.SetCell(i, 0, tview.NewTableCell(utils.GetFormattedString("[green]"+content.([2]string)[0], width/3)))
|
|
inputTable.SetCell(i, 1, tview.NewTableCell(utils.GetFormattedString("[magenta]"+content.([2]string)[1], width/3)))
|
|
}
|
|
case string:
|
|
{
|
|
b := content.(string)
|
|
if !strings.HasPrefix(b, WHITE_AND_BOLD) {
|
|
inputTable.SetCell(i, 0, tview.NewTableCell("[green]"+content.(string)))
|
|
} else {
|
|
inputTable.SetCell(i, 0, tview.NewTableCell(content.(string)).SetSelectable(false))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func Update(f []FileNode, inputTable *tview.Table) {
|
|
inputTable.Clear()
|
|
for i, j := range f {
|
|
if len(j.Children) == 0 {
|
|
_songAttributes, err := CONN.ListAllInfo(j.AbsolutePath)
|
|
if err == nil && _songAttributes[0]["Title"] != "" {
|
|
_, _, w, _ := inputTable.GetInnerRect()
|
|
inputTable.SetCell(i, 0,
|
|
tview.NewTableCell("[green]"+utils.GetFormattedString(_songAttributes[0]["Title"], w/3)).
|
|
SetAlign(tview.AlignLeft))
|
|
|
|
inputTable.SetCell(i, 1,
|
|
tview.NewTableCell("[magenta]"+utils.GetFormattedString(_songAttributes[0]["Artist"], w/3)).
|
|
SetAlign(tview.AlignLeft))
|
|
|
|
inputTable.SetCell(i, 2,
|
|
tview.NewTableCell("[yellow]"+_songAttributes[0]["Album"]).
|
|
SetAlign(tview.AlignLeft))
|
|
|
|
} else if _songAttributes[0]["Title"] == "" {
|
|
inputTable.SetCell(i, 0,
|
|
tview.NewTableCell("[blue]"+j.Path).
|
|
SetAlign(tview.AlignLeft))
|
|
}
|
|
} else {
|
|
inputTable.SetCell(i, 0,
|
|
tview.NewTableCell("[yellow::b]"+j.Path).
|
|
SetAlign(tview.AlignLeft))
|
|
}
|
|
}
|
|
}
|
|
|
|
// GenerateArtistTree Artist Tree is a map of Artist to their Album Map
|
|
// Album Tree is a map of the tracks in that particular album.
|
|
func GenerateArtistTree() (map[string]map[string]map[string]string, error) {
|
|
ArtistTree = make(map[string]map[string]map[string]string)
|
|
AllInfo, err := CONN.ListAllInfo("/")
|
|
if err == nil {
|
|
for _, i := range AllInfo {
|
|
if _, ArtistExists := ArtistTree[i["Artist"]]; !ArtistExists {
|
|
ArtistTree[i["Artist"]] = make(map[string]map[string]string)
|
|
}
|
|
if _, AlbumExists := ArtistTree[i["Artist"]][i["Album"]]; !AlbumExists {
|
|
ArtistTree[i["Artist"]][i["Album"]] = make(map[string]string)
|
|
}
|
|
if _, TitleExists := ArtistTree[i["Artist"]][i["Album"]][i["Title"]]; !TitleExists {
|
|
ArtistTree[i["Artist"]][i["Album"]][i["Title"]] = i["file"]
|
|
}
|
|
}
|
|
return ArtistTree, nil
|
|
} else {
|
|
return nil, errors.New("Could Not Generate Artist Tree")
|
|
}
|
|
}
|
|
|
|
func PrintArtistTree(a map[string]map[string]map[string]string) {
|
|
for k, v := range a {
|
|
fmt.Println(k, " : ")
|
|
for k1, v1 := range v {
|
|
fmt.Println("\t|---", k1, " : ")
|
|
for k2 := range v1 {
|
|
fmt.Println("\t\t|---", k2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adds All tracks from a specified album to a playlist
|
|
func AddAlbum(a map[string]map[string]map[string]string, alb string, artist string) {
|
|
for _, v := range a[artist][alb] {
|
|
err := CONN.Add(v)
|
|
if err != nil {
|
|
NotificationServer.Send("Could Not Add Song : " + v)
|
|
}
|
|
}
|
|
NotificationServer.Send("Album Added : " + alb)
|
|
}
|
|
|
|
// Adds All tracks from a specified artist to a playlist
|
|
func AddArtist(a map[string]map[string]map[string]string, artist string) {
|
|
if val, ok := a[artist]; ok {
|
|
for _, v := range val {
|
|
for _, path := range v {
|
|
err := CONN.Add(path)
|
|
if err != nil {
|
|
NotificationServer.Send("Could Not Add Song : " + path)
|
|
}
|
|
}
|
|
}
|
|
NotificationServer.Send("Artist Added : " + artist)
|
|
}
|
|
}
|
|
|
|
// Adds Specified Track to the Playlist
|
|
func AddTitle(a map[string]map[string]map[string]string, artist, alb, track string, addAndPlay bool) {
|
|
if addAndPlay {
|
|
id, err := CONN.AddId(a[artist][alb][track], -1)
|
|
CONN.PlayId(id)
|
|
if err != nil {
|
|
NotificationServer.Send("Could Not Add Track : " + track)
|
|
}
|
|
} else {
|
|
err := CONN.Add(a[artist][alb][track])
|
|
if err != nil {
|
|
NotificationServer.Send("Could Not Add Track : " + track)
|
|
}
|
|
}
|
|
NotificationServer.Send("Track Added : " + track)
|
|
}
|
|
|
|
/* Querys the Artist Tree for a track and returns a TrackMap (i.e [3]string{artist, album, track} -> Path) which will help us
|
|
to add tracks to the playlist */
|
|
func QueryArtistTreeForTracks(a map[string]map[string]map[string]string, track string) map[[3]string]string {
|
|
TrackMap := make(map[[3]string]string)
|
|
for artistName, albumMap := range a {
|
|
for albumName, trackList := range albumMap {
|
|
for trackName, path := range trackList {
|
|
if trackName == track {
|
|
TrackMap[[3]string{trackName, artistName, albumName}] = path
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TrackMap
|
|
}
|
|
|
|
/* Querys the Artist Tree for an album and returns a AlbumMap (i.e [3]string{artist, album } ->[]Path of songs in the album)
|
|
which will help us to add all album tracks to the playlist */
|
|
func QueryArtistTreeForAlbums(a map[string]map[string]map[string]string, album string) map[[2]string][][2]string {
|
|
AlbumMap := make(map[[2]string][][2]string)
|
|
for artistName, albumMap := range a {
|
|
for albumName, trackList := range albumMap {
|
|
if albumName == album {
|
|
var pathSlice [][2]string
|
|
for trackName, path := range trackList {
|
|
pathSlice = append(pathSlice, [2]string{trackName, path})
|
|
}
|
|
AlbumMap[[2]string{albumName, artistName}] = pathSlice
|
|
}
|
|
}
|
|
}
|
|
return AlbumMap
|
|
}
|
|
|
|
func AddToPlaylist(a interface{}, addAndPlay bool) {
|
|
switch a.(type) {
|
|
case [3]string:
|
|
{
|
|
b := a.([3]string)
|
|
AddTitle(ArtistTree, b[1], b[2], b[0], addAndPlay)
|
|
}
|
|
case [2]string:
|
|
{
|
|
b := a.([2]string)
|
|
AddAlbum(ArtistTree, b[0], b[1])
|
|
}
|
|
case string:
|
|
{
|
|
b := a.(string)
|
|
AddArtist(ArtistTree, b)
|
|
}
|
|
}
|
|
}
|