Merge pull request #8 from aditya-K2/search

Implementing Search Functionality
Seems Stable. So merging.
This commit is contained in:
Aditya Kurdunkar 2021-11-20 21:39:59 +05:30 committed by GitHub
commit ab8753c741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 450 additions and 35 deletions

31
App.go
View File

@ -1,8 +1,8 @@
package main package main
import ( import (
"github.com/aditya-K2/tview"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
) )
var IMG_X, IMG_Y, IMG_W, IMG_H int var IMG_X, IMG_Y, IMG_W, IMG_H int
@ -11,7 +11,7 @@ type Application struct {
App *tview.Application App *tview.Application
ExpandedView *tview.Table ExpandedView *tview.Table
Navbar *tview.Table Navbar *tview.Table
SearchBar *tview.Table SearchBar *tview.InputField
ProgressBar *progressBar ProgressBar *progressBar
Pages *tview.Pages Pages *tview.Pages
} }
@ -21,7 +21,8 @@ func newApplication(r *Renderer) *Application {
var pBar *progressBar = newProgressBar(r) var pBar *progressBar = newProgressBar(r)
expandedView := tview.NewTable() expandedView := tview.NewTable()
Navbar := tview.NewTable() Navbar := tview.NewTable()
searchBar := tview.NewTable() searchBar := tview.NewInputField()
searchBar.SetFieldBackgroundColor(tcell.ColorDefault)
imagePreviewer := tview.NewBox() imagePreviewer := tview.NewBox()
imagePreviewer.SetBorder(true) imagePreviewer.SetBorder(true)
imagePreviewer.SetDrawFunc(func(s tcell.Screen, x, y, width, height int) (int, int, int, int) { imagePreviewer.SetDrawFunc(func(s tcell.Screen, x, y, width, height int) (int, int, int, int) {
@ -29,15 +30,19 @@ func newApplication(r *Renderer) *Application {
return imagePreviewer.GetInnerRect() return imagePreviewer.GetInnerRect()
}) })
searchBar.SetBorder(true).SetTitle("Search").SetTitleAlign(tview.AlignLeft) searchBar.SetTitle("Search").SetTitleAlign(tview.AlignLeft)
searchBar.SetAutocompleteBackgroundColor(tcell.GetColor("#15191a"))
searchBar.SetAutocompleteSelectBackgroundColor(tcell.GetColor("#e5e5e5"))
searchBar.SetAutocompleteMainTextColor(tcell.GetColor("#7f7f7f"))
searchBar.SetAutocompleteSelectedTextColor(tcell.GetColor("#111111"))
Navbar.SetBorder(true) Navbar.SetBorder(true)
Navbar.SetSelectable(true, false) Navbar.SetSelectable(true, false)
Navbar.SetCell(0, 0, tview.NewTableCell("PlayList")) Navbar.SetCell(0, 0, tview.NewTableCell("PlayList"))
Navbar.SetCell(1, 0, tview.NewTableCell("Files")) Navbar.SetCell(1, 0, tview.NewTableCell("Files"))
Navbar.SetCell(2, 0, tview.NewTableCell("Most Played")) Navbar.SetCell(2, 0, tview.NewTableCell("Most Played"))
Navbar.SetCell(3, 0, tview.NewTableCell("Search"))
searchNavFlex := tview.NewFlex().SetDirection(tview.FlexRow). searchNavFlex := tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(searchBar, 3, 1, false).
AddItem(Navbar, 0, 4, false). AddItem(Navbar, 0, 4, false).
AddItem(imagePreviewer, 9, 3, false) AddItem(imagePreviewer, 9, 3, false)
@ -45,8 +50,13 @@ func newApplication(r *Renderer) *Application {
AddItem(searchNavFlex, 17, 1, false). AddItem(searchNavFlex, 17, 1, false).
AddItem(expandedView, 0, 4, false) AddItem(expandedView, 0, 4, false)
searchBar.SetBorder(true)
searchBarFlex := tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(searchBar, 3, 1, false).
AddItem(sNavExpViewFlex, 0, 1, false)
mainFlex := tview.NewFlex().SetDirection(tview.FlexRow). mainFlex := tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(sNavExpViewFlex, 0, 8, false). AddItem(searchBarFlex, 0, 8, false).
AddItem(pBar.t, 5, 1, false) AddItem(pBar.t, 5, 1, false)
expandedView.SetBorderPadding(1, 1, 1, 1).SetBorder(true) expandedView.SetBorderPadding(1, 1, 1, 1).SetBorder(true)
@ -58,6 +68,15 @@ func newApplication(r *Renderer) *Application {
App := tview.NewApplication() App := tview.NewApplication()
App.SetRoot(rootPages, true).SetFocus(expandedView) App.SetRoot(rootPages, true).SetFocus(expandedView)
searchBar.SetDoneFunc(func(k tcell.Key) {
switch k {
case tcell.KeyEscape:
{
App.SetFocus(expandedView)
}
}
})
return &Application{ return &Application{
App: App, App: App,
ExpandedView: expandedView, ExpandedView: expandedView,

224
client.go
View File

@ -1,9 +1,15 @@
package main package main
import ( import (
"errors"
"fmt"
"strings" "strings"
"github.com/rivo/tview" "github.com/aditya-K2/tview"
)
var (
WHITE_AND_BOLD string = "[#ffffff::b]"
) )
func getFormattedString(s string, width int) string { func getFormattedString(s string, width int) string {
@ -42,6 +48,86 @@ func UpdatePlaylist(inputTable *tview.Table) {
} }
} }
/*
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.
*/
func GenerateContentSlice(selectedSuggestion string) ([]interface{}, error) {
var ContentSlice []interface{}
if strings.TrimRight(selectedSuggestion, " ") == "" {
NOTIFICATION_SERVER.Send("Empty Search!")
return nil, errors.New("empty Search String Provided")
}
if _, ok := ARTIST_TREE[selectedSuggestion]; ok {
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Artists :")
ContentSlice = append(ContentSlice, selectedSuggestion)
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Artist Albums :")
for albumName := range ARTIST_TREE[selectedSuggestion] {
ContentSlice = append(ContentSlice, [2]string{albumName, selectedSuggestion})
}
ContentSlice = append(ContentSlice, WHITE_AND_BOLD+"Artist Tracks :")
for albumName, trackList := range ARTIST_TREE[selectedSuggestion] {
for track := range trackList {
ContentSlice = append(ContentSlice, [3]string{track, selectedSuggestion, albumName})
}
}
}
if aMap := QueryArtistTreeForAlbums(ARTIST_TREE, 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(ARTIST_TREE, 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(getFormattedString("[green]"+content.([3]string)[0], width/3)))
inputTable.SetCell(i, 1, tview.NewTableCell(getFormattedString("[magenta]"+content.([3]string)[1], width/3)))
inputTable.SetCell(i, 2, tview.NewTableCell(getFormattedString("[yellow]"+content.([3]string)[2], width/3)))
}
case [2]string:
{
inputTable.SetCell(i, 0, tview.NewTableCell(getFormattedString("[green]"+content.([2]string)[0], width/3)))
inputTable.SetCell(i, 1, tview.NewTableCell(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 join(stringSlice []string) string { func join(stringSlice []string) string {
var _s string = stringSlice[0] var _s string = stringSlice[0]
for i := 1; i < len(stringSlice); i++ { for i := 1; i < len(stringSlice); i++ {
@ -83,3 +169,139 @@ func Update(f []FileNode, inputTable *tview.Table) {
} }
} }
} }
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 {
NOTIFICATION_SERVER.Send("Could Not Add Song : " + v)
}
}
NOTIFICATION_SERVER.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 {
NOTIFICATION_SERVER.Send("Could Not Add Song : " + path)
}
}
}
NOTIFICATION_SERVER.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 {
NOTIFICATION_SERVER.Send("Could Not Add Track : " + track)
}
} else {
err := CONN.Add(a[artist][alb][track])
if err != nil {
NOTIFICATION_SERVER.Send("Could Not Add Track : " + track)
}
}
NOTIFICATION_SERVER.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(ARTIST_TREE, b[1], b[2], b[0], addAndPlay)
}
case [2]string:
{
b := a.([2]string)
AddAlbum(ARTIST_TREE, b[0], b[1])
}
case string:
{
b := a.(string)
AddArtist(ARTIST_TREE, b)
}
}
}

View File

@ -43,10 +43,12 @@ var (
50: "navigateToFiles", 50: "navigateToFiles",
49: "navigateToPlaylist", 49: "navigateToPlaylist",
51: "navigateToMostPlayed", 51: "navigateToMostPlayed",
52: "navigateToSearch",
113: "quit", 113: "quit",
115: "stop", 115: "stop",
117: "updateDB", 117: "updateDB",
100: "deleteSongFromPlaylist", 100: "deleteSongFromPlaylist",
47: "FocusSearch",
} }
) )

10
go.mod
View File

@ -1,16 +1,18 @@
module github.com/aditya-K2/goMP module github.com/aditya-K2/goMP
go 1.17 go 1.16
require ( require (
github.com/aditya-K2/tview v0.0.0-20211115161300-6b99c2c2658c
github.com/bogem/id3v2 v1.2.0 github.com/bogem/id3v2 v1.2.0
github.com/fhs/gompd v1.0.1 github.com/fhs/gompd v1.0.1
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1
github.com/mewkiz/flac v1.0.7 github.com/mewkiz/flac v1.0.7
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/rivo/tview v0.0.0-20211001102648-5508f4b00266 github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0
github.com/spf13/viper v1.9.0 github.com/spf13/viper v1.9.0
gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06 gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
) )
require github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0

15
go.sum
View File

@ -52,6 +52,10 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA= github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA=
github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/aditya-K2/tview v0.0.0-20211115155623-217bcbdc952c h1:HE/P4zSd5GE7wdCicacdo5m2vZk6E7BSS5NOY00gaPM=
github.com/aditya-K2/tview v0.0.0-20211115155623-217bcbdc952c/go.mod h1:nPfBlFYx4SBcLlONif45KWw2mvEbRgP/nNCkH/dMhIQ=
github.com/aditya-K2/tview v0.0.0-20211115161300-6b99c2c2658c h1:UG9OxyXccanOiC83FEonB3+iLYvA3aAYgiX1N7Vurm0=
github.com/aditya-K2/tview v0.0.0-20211115161300-6b99c2c2658c/go.mod h1:nPfBlFYx4SBcLlONif45KWw2mvEbRgP/nNCkH/dMhIQ=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -255,8 +259,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/tview v0.0.0-20211001102648-5508f4b00266 h1:UrmGSzDIp4gfkDuMLdXk1Tx4FjS8GTWrWJjHfnS6GmY=
github.com/rivo/tview v0.0.0-20211001102648-5508f4b00266/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@ -471,12 +473,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 h1:7NCfEGl0sfUojmX78nK9pBJuUlSZWEJA/TwASvfiPLo=
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -484,8 +488,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

109
main.go
View File

@ -1,22 +1,28 @@
package main package main
import ( import (
"sort"
"strconv" "strconv"
"time" "time"
"github.com/aditya-K2/goMP/config" "github.com/aditya-K2/goMP/config"
"github.com/aditya-K2/goMP/search"
"github.com/fhs/gompd/mpd" "github.com/fhs/gompd/mpd"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var CONN *mpd.Client var (
var UI *Application CONN *mpd.Client
var NOTIFICATION_SERVER *NotificationServer UI *Application
var Volume int64 NOTIFICATION_SERVER *NotificationServer
var Random bool Volume int64
var Repeat bool Random bool
var InsidePlaylist bool = true Repeat bool
InsidePlaylist bool = true
InsideSearchView bool = false
ARTIST_TREE map[string]map[string]map[string]string
)
func main() { func main() {
config.ReadConfig() config.ReadConfig()
@ -48,22 +54,27 @@ func main() {
Random, _ = strconv.ParseBool(_v["random"]) Random, _ = strconv.ParseBool(_v["random"])
Repeat, _ = strconv.ParseBool(_v["repeat"]) Repeat, _ = strconv.ParseBool(_v["repeat"])
ARTIST_TREE, err = GenerateArtistTree()
NOTIFICATION_SERVER = NewNotificationServer()
NOTIFICATION_SERVER.Start()
var SEARCH_CONTENT_SLICE []interface{}
UI.ExpandedView.SetDrawFunc(func(s tcell.Screen, x, y, width, height int) (int, int, int, int) { UI.ExpandedView.SetDrawFunc(func(s tcell.Screen, x, y, width, height int) (int, int, int, int) {
if InsidePlaylist { if InsidePlaylist {
UpdatePlaylist(UI.ExpandedView) UpdatePlaylist(UI.ExpandedView)
} else if InsideSearchView {
UpdateSearchView(UI.ExpandedView, SEARCH_CONTENT_SLICE)
} else { } else {
Update(dirTree.children, UI.ExpandedView) Update(dirTree.children, UI.ExpandedView)
} }
return UI.ExpandedView.GetInnerRect() return UI.ExpandedView.GetInnerRect()
}) })
NOTIFICATION_SERVER = NewNotificationServer()
NOTIFICATION_SERVER.Start()
var FUNC_MAP = map[string]func(){ var FUNC_MAP = map[string]func(){
"showChildrenContent": func() { "showChildrenContent": func() {
r, _ := UI.ExpandedView.GetSelection() r, _ := UI.ExpandedView.GetSelection()
if !InsidePlaylist { if !InsidePlaylist && !InsideSearchView {
if len(dirTree.children[r].children) == 0 { if len(dirTree.children[r].children) == 0 {
id, _ := CONN.AddId(dirTree.children[r].absolutePath, -1) id, _ := CONN.AddId(dirTree.children[r].absolutePath, -1)
CONN.PlayId(id) CONN.PlayId(id)
@ -71,15 +82,18 @@ func main() {
Update(dirTree.children[r].children, UI.ExpandedView) Update(dirTree.children[r].children, UI.ExpandedView)
dirTree = &dirTree.children[r] dirTree = &dirTree.children[r]
} }
} else { } else if InsidePlaylist {
CONN.Play(r) CONN.Play(r)
} else if InsideSearchView {
r, _ := UI.ExpandedView.GetSelection()
AddToPlaylist(SEARCH_CONTENT_SLICE[r], true)
} }
}, },
"togglePlayBack": func() { "togglePlayBack": func() {
togglePlayBack() togglePlayBack()
}, },
"showParentContent": func() { "showParentContent": func() {
if !InsidePlaylist { if !InsidePlaylist && !InsideSearchView {
if dirTree.parent != nil { if dirTree.parent != nil {
Update(dirTree.parent.children, UI.ExpandedView) Update(dirTree.parent.children, UI.ExpandedView)
dirTree = dirTree.parent dirTree = dirTree.parent
@ -91,18 +105,18 @@ func main() {
}, },
"clearPlaylist": func() { "clearPlaylist": func() {
CONN.Clear() CONN.Clear()
if InsidePlaylist {
UpdatePlaylist(UI.ExpandedView)
}
NOTIFICATION_SERVER.Send("PlayList Cleared") NOTIFICATION_SERVER.Send("PlayList Cleared")
}, },
"previousSong": func() { "previousSong": func() {
CONN.Previous() CONN.Previous()
}, },
"addToPlaylist": func() { "addToPlaylist": func() {
if !InsidePlaylist { if !InsidePlaylist && !InsideSearchView {
r, _ := UI.ExpandedView.GetSelection() r, _ := UI.ExpandedView.GetSelection()
CONN.Add(dirTree.children[r].absolutePath) CONN.Add(dirTree.children[r].absolutePath)
} else if InsideSearchView {
r, _ := UI.ExpandedView.GetSelection()
AddToPlaylist(SEARCH_CONTENT_SLICE[r], false)
} }
}, },
"toggleRandom": func() { "toggleRandom": func() {
@ -135,18 +149,26 @@ func main() {
}, },
"navigateToFiles": func() { "navigateToFiles": func() {
InsidePlaylist = false InsidePlaylist = false
InsideSearchView = false
UI.Navbar.Select(1, 0) UI.Navbar.Select(1, 0)
Update(dirTree.children, UI.ExpandedView) Update(dirTree.children, UI.ExpandedView)
}, },
"navigateToPlaylist": func() { "navigateToPlaylist": func() {
InsidePlaylist = true InsidePlaylist = true
InsideSearchView = false
UI.Navbar.Select(0, 0) UI.Navbar.Select(0, 0)
UpdatePlaylist(UI.ExpandedView) UpdatePlaylist(UI.ExpandedView)
}, },
"navigateToMostPlayed": func() { "navigateToMostPlayed": func() {
InsideSearchView = false
InsidePlaylist = false InsidePlaylist = false
UI.Navbar.Select(2, 0) UI.Navbar.Select(2, 0)
}, },
"navigateToSearch": func() {
InsideSearchView = true
InsidePlaylist = false
UI.Navbar.Select(3, 0)
},
"quit": func() { "quit": func() {
UI.App.Stop() UI.App.Stop()
}, },
@ -167,10 +189,42 @@ func main() {
CONN.Delete(r, -1) CONN.Delete(r, -1)
} }
}, },
"FocusSearch": func() {
UI.App.SetFocus(UI.SearchBar)
},
} }
config.GenerateKeyMap(FUNC_MAP) config.GenerateKeyMap(FUNC_MAP)
UI.SearchBar.SetAutocompleteFunc(func(c string) []string {
if c != "" && c != " " && c != " " {
var p search.PairList
for k2, v := range ARTIST_TREE {
p = append(p, search.Pair{Key: k2, Value: search.GetLevenshteinDistance(c, k2)})
for k1, v1 := range v {
p = append(p, search.Pair{Key: k1, Value: search.GetLevenshteinDistance(c, k1)})
for k := range v1 {
p = append(p, search.Pair{Key: k, Value: search.GetLevenshteinDistance(c, k)})
}
}
}
sort.Sort(p)
var suggestions []string
i := 0
for _, k := range p {
if i == 10 {
break
}
_, _, w, _ := UI.SearchBar.GetRect()
suggestions = append(suggestions, getFormattedString(k.Key, w-2))
i++
}
return suggestions
} else {
return make([]string, 0)
}
})
UI.ExpandedView.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey { UI.ExpandedView.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
if val, ok := config.KEY_MAP[int(e.Rune())]; ok { if val, ok := config.KEY_MAP[int(e.Rune())]; ok {
FUNC_MAP[val]() FUNC_MAP[val]()
@ -180,6 +234,27 @@ func main() {
} }
}) })
UI.SearchBar.SetDoneFunc(func(e tcell.Key) {
if e == tcell.KeyEnter {
UI.ExpandedView.Select(0, 0)
InsideSearchView = true
InsidePlaylist = false
SEARCH_CONTENT_SLICE = nil
SEARCH_CONTENT_SLICE, err = GenerateContentSlice(UI.SearchBar.GetText())
if err != nil {
NOTIFICATION_SERVER.Send("Could Not Retrieve the Results")
} else {
UI.SearchBar.SetText("")
UI.App.SetFocus(UI.ExpandedView)
UI.Navbar.Select(3, 0)
}
}
if e == tcell.KeyEscape {
InsideSearchView = false
UI.App.SetFocus(UI.ExpandedView)
}
})
go func() { go func() {
for { for {
UI.App.Draw() UI.App.Draw()

View File

@ -3,8 +3,8 @@ package main
import ( import (
"time" "time"
"github.com/aditya-K2/tview"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
) )
/* Notification Primitive */ /* Notification Primitive */

View File

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/aditya-K2/tview"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
) )
var CurrentSong string var CurrentSong string

90
search/searchUtils.go Normal file
View File

@ -0,0 +1,90 @@
package search
import (
"fmt"
"math"
"math/bits"
"strings"
)
type Pair struct {
Key string
Value int
}
type PairList []Pair
func (p PairList) Len() int { return len(p) }
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func ConvertToBitMap(b string) uint64 {
b = strings.ToLower(b)
var a uint64 = 0
for _, i := range b {
asc := (int(i) - 97)
if asc >= 97 && asc <= 122 {
a |= 1 << asc
}
}
return a
}
func CalculateUpperBound(a, b string) float64 {
aB := ConvertToBitMap(a)
bB := ConvertToBitMap(b)
lengthAf64 := float64(len(a))
lengthBf64 := float64(len(b))
m := math.Min(lengthAf64, lengthBf64) - float64(bits.OnesCount64(aB&(^bB)))
return (1.0 / 3.0) * ((float64(m) / float64(len(a))) + (float64(m) / float64(len(b))) + 1)
}
func min(a, b, c int) int {
if a > b && b < c {
return b
} else if b > a && a < c {
return a
} else {
return c
}
}
func cost(i, j rune) int {
if i != j {
return 1
} else {
return 0
}
}
func GetLevenshteinDistance(a, b string) int {
c := []rune(a)
e := []rune(b)
m, n := len(c), len(e)
d := make([][]int, m+1)
for i := range d {
d[i] = make([]int, n+1)
}
for i := range d {
for j := range d[i] {
if j == 0 {
d[i][j] = i
} else if i == 0 {
d[i][j] = j
} else {
d[i][j] = 0
}
}
}
for j := 1; j < n+1; j++ {
for i := 1; i < m+1; i++ {
d[i][j] = min(d[i-1][j-1]+cost(c[i-1], e[j-1]),
d[i][j-1]+1,
d[i-1][j]+1)
}
}
return d[m][n]
}
func main() {
fmt.Println(GetLevenshteinDistance("cat", "wildcat"))
}