Merge pull request #8 from aditya-K2/search
Implementing Search Functionality Seems Stable. So merging.
This commit is contained in:
commit
ab8753c741
31
App.go
31
App.go
@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/aditya-K2/tview"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
var IMG_X, IMG_Y, IMG_W, IMG_H int
|
||||
@ -11,7 +11,7 @@ type Application struct {
|
||||
App *tview.Application
|
||||
ExpandedView *tview.Table
|
||||
Navbar *tview.Table
|
||||
SearchBar *tview.Table
|
||||
SearchBar *tview.InputField
|
||||
ProgressBar *progressBar
|
||||
Pages *tview.Pages
|
||||
}
|
||||
@ -21,7 +21,8 @@ func newApplication(r *Renderer) *Application {
|
||||
var pBar *progressBar = newProgressBar(r)
|
||||
expandedView := tview.NewTable()
|
||||
Navbar := tview.NewTable()
|
||||
searchBar := tview.NewTable()
|
||||
searchBar := tview.NewInputField()
|
||||
searchBar.SetFieldBackgroundColor(tcell.ColorDefault)
|
||||
imagePreviewer := tview.NewBox()
|
||||
imagePreviewer.SetBorder(true)
|
||||
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()
|
||||
})
|
||||
|
||||
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.SetSelectable(true, false)
|
||||
Navbar.SetCell(0, 0, tview.NewTableCell("PlayList"))
|
||||
Navbar.SetCell(1, 0, tview.NewTableCell("Files"))
|
||||
Navbar.SetCell(2, 0, tview.NewTableCell("Most Played"))
|
||||
Navbar.SetCell(3, 0, tview.NewTableCell("Search"))
|
||||
|
||||
searchNavFlex := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(searchBar, 3, 1, false).
|
||||
AddItem(Navbar, 0, 4, false).
|
||||
AddItem(imagePreviewer, 9, 3, false)
|
||||
|
||||
@ -45,8 +50,13 @@ func newApplication(r *Renderer) *Application {
|
||||
AddItem(searchNavFlex, 17, 1, 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).
|
||||
AddItem(sNavExpViewFlex, 0, 8, false).
|
||||
AddItem(searchBarFlex, 0, 8, false).
|
||||
AddItem(pBar.t, 5, 1, false)
|
||||
|
||||
expandedView.SetBorderPadding(1, 1, 1, 1).SetBorder(true)
|
||||
@ -58,6 +68,15 @@ func newApplication(r *Renderer) *Application {
|
||||
App := tview.NewApplication()
|
||||
App.SetRoot(rootPages, true).SetFocus(expandedView)
|
||||
|
||||
searchBar.SetDoneFunc(func(k tcell.Key) {
|
||||
switch k {
|
||||
case tcell.KeyEscape:
|
||||
{
|
||||
App.SetFocus(expandedView)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return &Application{
|
||||
App: App,
|
||||
ExpandedView: expandedView,
|
||||
|
224
client.go
224
client.go
@ -1,9 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/rivo/tview"
|
||||
"github.com/aditya-K2/tview"
|
||||
)
|
||||
|
||||
var (
|
||||
WHITE_AND_BOLD string = "[#ffffff::b]"
|
||||
)
|
||||
|
||||
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 {
|
||||
var _s string = stringSlice[0]
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,10 +43,12 @@ var (
|
||||
50: "navigateToFiles",
|
||||
49: "navigateToPlaylist",
|
||||
51: "navigateToMostPlayed",
|
||||
52: "navigateToSearch",
|
||||
113: "quit",
|
||||
115: "stop",
|
||||
117: "updateDB",
|
||||
100: "deleteSongFromPlaylist",
|
||||
47: "FocusSearch",
|
||||
}
|
||||
)
|
||||
|
||||
|
10
go.mod
10
go.mod
@ -1,16 +1,18 @@
|
||||
module github.com/aditya-K2/goMP
|
||||
|
||||
go 1.17
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/aditya-K2/tview v0.0.0-20211115161300-6b99c2c2658c
|
||||
github.com/bogem/id3v2 v1.2.0
|
||||
github.com/fhs/gompd v1.0.1
|
||||
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1
|
||||
github.com/mewkiz/flac v1.0.7
|
||||
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
|
||||
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
15
go.sum
@ -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/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
|
||||
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/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=
|
||||
@ -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.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
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/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
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-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-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-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-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-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.3.0/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.4/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.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-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
109
main.go
109
main.go
@ -1,22 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aditya-K2/goMP/config"
|
||||
"github.com/aditya-K2/goMP/search"
|
||||
"github.com/fhs/gompd/mpd"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var CONN *mpd.Client
|
||||
var UI *Application
|
||||
var NOTIFICATION_SERVER *NotificationServer
|
||||
var Volume int64
|
||||
var Random bool
|
||||
var Repeat bool
|
||||
var InsidePlaylist bool = true
|
||||
var (
|
||||
CONN *mpd.Client
|
||||
UI *Application
|
||||
NOTIFICATION_SERVER *NotificationServer
|
||||
Volume int64
|
||||
Random bool
|
||||
Repeat bool
|
||||
InsidePlaylist bool = true
|
||||
InsideSearchView bool = false
|
||||
ARTIST_TREE map[string]map[string]map[string]string
|
||||
)
|
||||
|
||||
func main() {
|
||||
config.ReadConfig()
|
||||
@ -48,22 +54,27 @@ func main() {
|
||||
Random, _ = strconv.ParseBool(_v["random"])
|
||||
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) {
|
||||
if InsidePlaylist {
|
||||
UpdatePlaylist(UI.ExpandedView)
|
||||
} else if InsideSearchView {
|
||||
UpdateSearchView(UI.ExpandedView, SEARCH_CONTENT_SLICE)
|
||||
} else {
|
||||
Update(dirTree.children, UI.ExpandedView)
|
||||
}
|
||||
return UI.ExpandedView.GetInnerRect()
|
||||
})
|
||||
|
||||
NOTIFICATION_SERVER = NewNotificationServer()
|
||||
NOTIFICATION_SERVER.Start()
|
||||
|
||||
var FUNC_MAP = map[string]func(){
|
||||
"showChildrenContent": func() {
|
||||
r, _ := UI.ExpandedView.GetSelection()
|
||||
if !InsidePlaylist {
|
||||
if !InsidePlaylist && !InsideSearchView {
|
||||
if len(dirTree.children[r].children) == 0 {
|
||||
id, _ := CONN.AddId(dirTree.children[r].absolutePath, -1)
|
||||
CONN.PlayId(id)
|
||||
@ -71,15 +82,18 @@ func main() {
|
||||
Update(dirTree.children[r].children, UI.ExpandedView)
|
||||
dirTree = &dirTree.children[r]
|
||||
}
|
||||
} else {
|
||||
} else if InsidePlaylist {
|
||||
CONN.Play(r)
|
||||
} else if InsideSearchView {
|
||||
r, _ := UI.ExpandedView.GetSelection()
|
||||
AddToPlaylist(SEARCH_CONTENT_SLICE[r], true)
|
||||
}
|
||||
},
|
||||
"togglePlayBack": func() {
|
||||
togglePlayBack()
|
||||
},
|
||||
"showParentContent": func() {
|
||||
if !InsidePlaylist {
|
||||
if !InsidePlaylist && !InsideSearchView {
|
||||
if dirTree.parent != nil {
|
||||
Update(dirTree.parent.children, UI.ExpandedView)
|
||||
dirTree = dirTree.parent
|
||||
@ -91,18 +105,18 @@ func main() {
|
||||
},
|
||||
"clearPlaylist": func() {
|
||||
CONN.Clear()
|
||||
if InsidePlaylist {
|
||||
UpdatePlaylist(UI.ExpandedView)
|
||||
}
|
||||
NOTIFICATION_SERVER.Send("PlayList Cleared")
|
||||
},
|
||||
"previousSong": func() {
|
||||
CONN.Previous()
|
||||
},
|
||||
"addToPlaylist": func() {
|
||||
if !InsidePlaylist {
|
||||
if !InsidePlaylist && !InsideSearchView {
|
||||
r, _ := UI.ExpandedView.GetSelection()
|
||||
CONN.Add(dirTree.children[r].absolutePath)
|
||||
} else if InsideSearchView {
|
||||
r, _ := UI.ExpandedView.GetSelection()
|
||||
AddToPlaylist(SEARCH_CONTENT_SLICE[r], false)
|
||||
}
|
||||
},
|
||||
"toggleRandom": func() {
|
||||
@ -135,18 +149,26 @@ func main() {
|
||||
},
|
||||
"navigateToFiles": func() {
|
||||
InsidePlaylist = false
|
||||
InsideSearchView = false
|
||||
UI.Navbar.Select(1, 0)
|
||||
Update(dirTree.children, UI.ExpandedView)
|
||||
},
|
||||
"navigateToPlaylist": func() {
|
||||
InsidePlaylist = true
|
||||
InsideSearchView = false
|
||||
UI.Navbar.Select(0, 0)
|
||||
UpdatePlaylist(UI.ExpandedView)
|
||||
},
|
||||
"navigateToMostPlayed": func() {
|
||||
InsideSearchView = false
|
||||
InsidePlaylist = false
|
||||
UI.Navbar.Select(2, 0)
|
||||
},
|
||||
"navigateToSearch": func() {
|
||||
InsideSearchView = true
|
||||
InsidePlaylist = false
|
||||
UI.Navbar.Select(3, 0)
|
||||
},
|
||||
"quit": func() {
|
||||
UI.App.Stop()
|
||||
},
|
||||
@ -167,10 +189,42 @@ func main() {
|
||||
CONN.Delete(r, -1)
|
||||
}
|
||||
},
|
||||
"FocusSearch": func() {
|
||||
UI.App.SetFocus(UI.SearchBar)
|
||||
},
|
||||
}
|
||||
|
||||
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 {
|
||||
if val, ok := config.KEY_MAP[int(e.Rune())]; ok {
|
||||
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() {
|
||||
for {
|
||||
UI.App.Draw()
|
||||
|
@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aditya-K2/tview"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
/* Notification Primitive */
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/aditya-K2/tview"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
var CurrentSong string
|
||||
|
90
search/searchUtils.go
Normal file
90
search/searchUtils.go
Normal 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"))
|
||||
}
|
Loading…
Reference in New Issue
Block a user