From 5d46449ddd5155a773520201c836497f23a5d18a Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 30 Mar 2021 13:22:21 +0800 Subject: [PATCH 01/48] Fix data race in playingbar.go and player.go --- command.go | 48 ++++++++++++++++++++++++++++-------------------- hook/hook.go | 3 ++- player/player.go | 37 ++++++++++++++++++++++++++++--------- playingbar.go | 47 ++++++++++++++++++++++++++++++++++------------- playlist.go | 27 +++++++-------------------- queue.go | 34 ++++++++++++++++++++++------------ start.go | 12 ++++++++++-- tageditor.go | 36 ++++++++++++++++++++---------------- 8 files changed, 151 insertions(+), 93 deletions(-) diff --git a/command.go b/command.go index f993701..451e42c 100644 --- a/command.go +++ b/command.go @@ -307,64 +307,70 @@ func (c Command) defineCommands() { c.define("forward", func() { if gomu.player.IsRunning() && !gomu.player.IsPaused() { - position := gomu.playingBar.progress + 10 - if position < gomu.playingBar.full { + position := gomu.playingBar.getProgress() + 10 + if position < gomu.playingBar.getFull() { err := gomu.player.Seek(position) if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } - gomu.playingBar.progress = position + gomu.playingBar.setProgress(position) } } }) c.define("rewind", func() { if gomu.player.IsRunning() && !gomu.player.IsPaused() { - position := gomu.playingBar.progress - 10 + position := gomu.playingBar.getProgress() - 10 if position-1 > 0 { err := gomu.player.Seek(position) if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } - gomu.playingBar.progress = position + gomu.playingBar.setProgress(position) } else { err := gomu.player.Seek(0) if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } - gomu.playingBar.progress = 0 + gomu.playingBar.setProgress(0) } } }) c.define("forward_fast", func() { if gomu.player.IsRunning() && !gomu.player.IsPaused() { - position := gomu.playingBar.progress + 60 - if position < gomu.playingBar.full { + position := gomu.playingBar.getProgress() + 60 + if position < gomu.playingBar.getFull() { err := gomu.player.Seek(position) if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } - gomu.playingBar.progress = position + gomu.playingBar.setProgress(position) } } }) c.define("rewind_fast", func() { if gomu.player.IsRunning() && !gomu.player.IsPaused() { - position := gomu.playingBar.progress - 60 + position := gomu.playingBar.getProgress() - 60 if position-1 > 0 { err := gomu.player.Seek(position) if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } - gomu.playingBar.progress = position + gomu.playingBar.setProgress(position) } else { err := gomu.player.Seek(0) if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } - gomu.playingBar.progress = 0 + gomu.playingBar.setProgress(0) } } }) @@ -372,14 +378,16 @@ func (c Command) defineCommands() { c.define("yank", func() { err := gomu.playlist.yank() if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } }) c.define("paste", func() { err := gomu.playlist.paste() if err != nil { - logError(err) + errorPopup(err) + gomu.app.Draw() } }) diff --git a/hook/hook.go b/hook/hook.go index 5e70f74..647ec41 100644 --- a/hook/hook.go +++ b/hook/hook.go @@ -1,10 +1,11 @@ +// Package hook is handling event hookds package hook type EventHook struct { events map[string][]func() } -// NewNewEventHook returns new instance of EventHook +// NewEventHook returns new instance of EventHook func NewEventHook() *EventHook { return &EventHook{make(map[string][]func())} } diff --git a/player/player.go b/player/player.go index ee666a8..8d43f44 100644 --- a/player/player.go +++ b/player/player.go @@ -1,7 +1,9 @@ +// Package player is the place actually play the music package player import ( "os" + "sync" "time" "github.com/faiface/beep" @@ -31,6 +33,7 @@ type Player struct { songFinish func(Audio) songStart func(Audio) songSkip func(Audio) + mu sync.Mutex } // New returns new Player instance. @@ -100,15 +103,16 @@ func (p *Player) Run(currSong Audio) error { } p.streamSeekCloser = stream - p.format = &format // song duration - p.length = p.format.SampleRate.D(p.streamSeekCloser.Len()) + p.length = format.SampleRate.D(p.streamSeekCloser.Len()) sr := beep.SampleRate(48000) if !p.hasInit { + // p.mu.Lock() err := speaker.Init(sr, sr.N(time.Second/10)) + // p.mu.Unlock() if err != nil { return tracerr.Wrap(err) @@ -120,7 +124,7 @@ func (p *Player) Run(currSong Audio) error { p.currentSong = currSong // resample to adapt to sample rate of new songs - resampled := beep.Resample(4, p.format.SampleRate, sr, p.streamSeekCloser) + resampled := beep.Resample(4, format.SampleRate, sr, p.streamSeekCloser) sstreamer := beep.Seq(resampled, beep.Callback(func() { p.isRunning = false @@ -134,7 +138,10 @@ func (p *Player) Run(currSong Audio) error { Paused: false, } + p.mu.Lock() + p.format = &format p.ctrl = ctrl + p.mu.Unlock() resampler := beep.ResampleRatio(4, 1, ctrl) volume := &effects.Volume{ @@ -170,7 +177,7 @@ func (p *Player) Play() { speaker.Unlock() } -// Volume up and volume down using -0.5 or +0.5. +// SetVolume set volume up and volume down using -0.5 or +0.5. func (p *Player) SetVolume(v float64) float64 { // check if no songs playing currently @@ -186,7 +193,7 @@ func (p *Player) SetVolume(v float64) float64 { return p.volume } -// Toggles the pause state. +// TogglePause toggles the pause state. func (p *Player) TogglePause() { if p.ctrl == nil { @@ -200,7 +207,7 @@ func (p *Player) TogglePause() { } } -// Skips current song. +// Skip current song. func (p *Player) Skip() { p.execSongSkip(p.currentSong) @@ -210,17 +217,23 @@ func (p *Player) Skip() { } // drain the stream + speaker.Lock() p.ctrl.Streamer = nil - p.streamSeekCloser.Close() p.isRunning = false p.format = nil + speaker.Unlock() + p.execSongFinish(p.currentSong) } // GetPosition returns the current position of audio file. func (p *Player) GetPosition() time.Duration { + p.mu.Lock() + speaker.Lock() + defer speaker.Unlock() + defer p.mu.Unlock() if p.format == nil || p.streamSeekCloser == nil { return 1 } @@ -228,16 +241,22 @@ func (p *Player) GetPosition() time.Duration { return p.format.SampleRate.D(p.streamSeekCloser.Position()) } -// seek is the function to move forward and rewind +// Seek is the function to move forward and rewind func (p *Player) Seek(pos int) error { + p.mu.Lock() speaker.Lock() defer speaker.Unlock() + defer p.mu.Unlock() err := p.streamSeekCloser.Seek(pos * int(p.format.SampleRate)) return err } // IsPaused is used to distinguish the player between pause and stop func (p *Player) IsPaused() bool { + p.mu.Lock() + speaker.Lock() + defer speaker.Unlock() + defer p.mu.Unlock() if p.ctrl == nil { return false } @@ -266,7 +285,7 @@ func (p *Player) IsRunning() bool { return p.isRunning } -// Gets the length of the song in the queue +// GetLength return the length of the song in the queue func GetLength(audioPath string) (time.Duration, error) { f, err := os.Open(audioPath) diff --git a/playingbar.go b/playingbar.go index f86382f..2a5d022 100644 --- a/playingbar.go +++ b/playingbar.go @@ -7,6 +7,8 @@ import ( "fmt" "strconv" "strings" + "sync" + "sync/atomic" "time" "github.com/rivo/tview" @@ -19,15 +21,16 @@ import ( // PlayingBar shows song name, progress and lyric type PlayingBar struct { *tview.Frame - full int + full int32 update chan struct{} - progress int + progress int32 skip bool text *tview.TextView hasTag bool tag *id3v2.Tag subtitle *lyric.Lyric subtitles []*lyric.Lyric + mu sync.Mutex } func (p *PlayingBar) help() []string { @@ -60,9 +63,12 @@ func (p *PlayingBar) run() error { for { // stop progressing if song ends or skipped - if p.progress > p.full || p.skip { + progress := p.getProgress() + full := p.getFull() + + if progress > full || p.skip { p.skip = false - p.progress = 0 + p.setProgress(0) break } @@ -71,21 +77,25 @@ func (p *PlayingBar) run() error { continue } - p.progress = int(gomu.player.GetPosition().Seconds()) + // p.progress = int(gomu.player.GetPosition().Seconds()) + p.setProgress(int(gomu.player.GetPosition().Seconds())) - start, err := time.ParseDuration(strconv.Itoa(p.progress) + "s") + start, err := time.ParseDuration(strconv.Itoa(progress) + "s") if err != nil { return tracerr.Wrap(err) } - end, err := time.ParseDuration(strconv.Itoa(p.full) + "s") + end, err := time.ParseDuration(strconv.Itoa(full) + "s") if err != nil { return tracerr.Wrap(err) } + var width int + gomu.app.QueueUpdate(func() { + _, _, width, _ = p.GetInnerRect() + }) - _, _, width, _ := p.GetInnerRect() - progressBar := progresStr(p.progress, p.full, width/2, "█", "━") + progressBar := progresStr(progress, full, width/2, "█", "━") // our progress bar var lyricText string if p.subtitle != nil { @@ -96,11 +106,11 @@ func (p *PlayingBar) run() error { endTime = int32(p.subtitle.SyncedCaptions[i+1].Timestamp) } else { // Here we display the last lyric until the end of song - endTime = int32(p.full * 1000) + endTime = int32(full * 1000) } // here the currentTime is delayed 1 second because we want to show lyrics earlier - currentTime := int32(p.progress*1000) + 1000 + currentTime := int32(progress*1000) + 1000 if currentTime >= startTime && currentTime <= endTime { lyricText = p.subtitle.SyncedCaptions[i].Text break @@ -135,8 +145,8 @@ func (p *PlayingBar) setSongTitle(title string) { // Resets progress bar, ready for execution func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) { - p.full = full - p.progress = 0 + p.full = int32(full) + p.setProgress(0) p.setSongTitle(currentSong.name) p.hasTag = false p.tag = nil @@ -300,3 +310,14 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { return nil } + +func (p *PlayingBar) getProgress() int { + return int(atomic.LoadInt32(&p.progress)) +} + +func (p *PlayingBar) setProgress(progress int) { + atomic.StoreInt32(&p.progress, int32(progress)) +} +func (p *PlayingBar) getFull() int { + return int(atomic.LoadInt32(&p.full)) +} diff --git a/playlist.go b/playlist.go index 337f61e..7c84444 100644 --- a/playlist.go +++ b/playlist.go @@ -737,6 +737,13 @@ func populate(root *tview.TreeNode, rootPath string, sortMtime bool) error { parent: root, } + audioLength, err := getTagLength(audioFile.path) + if err != nil { + logError(err) + } + + audioFile.length = audioLength + displayText := setDisplayText(audioFile) child.SetReference(audioFile) @@ -842,23 +849,3 @@ func setDisplayText(audioFile *AudioFile) string { emojiDir := gomu.anko.GetString("Emoji.playlist") return fmt.Sprintf(" %s %s", emojiDir, audioFile.name) } - -// populateAudioLength is the most time consuming part of startup, -// so here we initialize it separately -func populateAudioLength(root *tview.TreeNode) error { - root.Walk(func(node *tview.TreeNode, _ *tview.TreeNode) bool { - audioFile := node.GetReference().(*AudioFile) - if audioFile.isAudioFile { - audioLength, err := getTagLength(audioFile.path) - if err != nil { - logError(err) - return false - } - audioFile.length = audioLength - } - return true - }) - - gomu.queue.updateTitle() - return nil -} diff --git a/queue.go b/queue.go index 0d2b6d5..7f2667b 100644 --- a/queue.go +++ b/queue.go @@ -155,18 +155,6 @@ func (q *Queue) dequeue() (*AudioFile, error) { // Add item to the list and returns the length of the queue func (q *Queue) enqueue(audioFile *AudioFile) (int, error) { - isTestEnv := os.Getenv("TEST") == "false" - - if !gomu.player.IsRunning() && !gomu.player.IsPaused() && isTestEnv { - - err := gomu.player.Run(audioFile) - if err != nil { - die(err) - } - - return q.GetItemCount(), nil - } - if !audioFile.isAudioFile { return q.GetItemCount(), nil } @@ -430,3 +418,25 @@ func (q *Queue) updateQueueNames() error { q.loadQueue() return nil } + +// playQueue play the first item in the queue +func (q *Queue) playQueue() error { + + isTestEnv := os.Getenv("TEST") == "false" + + if !gomu.player.IsRunning() && !gomu.player.IsPaused() && isTestEnv { + + audioFile, err := q.dequeue() + if err != nil { + return tracerr.Wrap(err) + } + err = gomu.player.Run(audioFile) + if err != nil { + return tracerr.Wrap(err) + } + + return nil + } + + return nil +} diff --git a/start.go b/start.go index 462c031..d73da69 100644 --- a/start.go +++ b/start.go @@ -71,7 +71,7 @@ func defineInternals() { playlist, _ := gomu.anko.NewModule("Playlist") playlist.Define("get_focused", gomu.playlist.getCurrentFile) playlist.Define("focus", func(filepath string) { - + root := gomu.playlist.GetRoot() root.Walk(func(node, _ *tview.TreeNode) bool { @@ -372,7 +372,9 @@ func start(application *tview.Application, args Args) { } audioFile := audio.(*AudioFile) + gomu.playingBar.newProgress(audioFile, int(duration.Seconds())) + name := audio.Name() var description string @@ -392,6 +394,7 @@ func start(application *tview.Application, args Args) { logError(err) } }() + }) gomu.player.SetSongFinish(func(currAudio player.Audio) { @@ -436,6 +439,12 @@ func start(application *tview.Application, args Args) { } } + if len(gomu.queue.items) > 0 { + if err := gomu.queue.playQueue(); err != nil { + logError(err) + } + } + sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) go func() { @@ -526,7 +535,6 @@ func start(application *tview.Application, args Args) { } }) - go populateAudioLength(gomu.playlist.GetRoot()) gomu.app.SetRoot(gomu.pages, true).SetFocus(gomu.playlist) // main loop diff --git a/tageditor.go b/tageditor.go index 15e0cec..46d7ffa 100644 --- a/tageditor.go +++ b/tageditor.go @@ -259,22 +259,24 @@ func tagPopup(node *AudioFile) (err error) { options = newOptions // Update dropdown options - lyricDropDown.SetOptions(newOptions, nil). - SetCurrentOption(0). - SetSelectedFunc(func(text string, _ int) { - lyricTextView.SetText(popupLyricMap[text]). - SetTitle(" " + text + " lyric preview ") - }) + gomu.app.QueueUpdateDraw(func() { + lyricDropDown.SetOptions(newOptions, nil). + SetCurrentOption(0). + SetSelectedFunc(func(text string, _ int) { + lyricTextView.SetText(popupLyricMap[text]). + SetTitle(" " + text + " lyric preview ") + }) - // Update lyric preview - if len(newOptions) > 0 { - _, langExt := lyricDropDown.GetCurrentOption() - lyricTextView.SetText(popupLyricMap[langExt]). - SetTitle(" " + langExt + " lyric preview ") - } else { - lyricTextView.SetText("No lyric embeded."). - SetTitle(" lyric preview ") - } + // Update lyric preview + if len(newOptions) > 0 { + _, langExt := lyricDropDown.GetCurrentOption() + lyricTextView.SetText(popupLyricMap[langExt]). + SetTitle(" " + langExt + " lyric preview ") + } else { + lyricTextView.SetText("No lyric embeded."). + SetTitle(" lyric preview ") + } + }) }() }). SetBackgroundColorActivated(gomu.colors.popup). @@ -304,7 +306,9 @@ func tagPopup(node *AudioFile) (err error) { SetWrap(true). SetBorder(true) lyricTextView.SetChangedFunc(func() { - lyricTextView.ScrollToBeginning() + gomu.app.QueueUpdate(func() { + lyricTextView.ScrollToBeginning() + }) }) leftGrid.SetRows(3, 1, 3, 3, 3, 3, 0, 3, 3, 1, 3, 3). From f597722f5236579fda5dabb4700cc6156d95f37e Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 30 Mar 2021 14:01:24 +0800 Subject: [PATCH 02/48] fix bug set playingbar.full --- playingbar.go | 21 ++++++++++++--------- tageditor.go | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/playingbar.go b/playingbar.go index 2a5d022..45ef756 100644 --- a/playingbar.go +++ b/playingbar.go @@ -7,7 +7,6 @@ import ( "fmt" "strconv" "strings" - "sync" "sync/atomic" "time" @@ -21,16 +20,15 @@ import ( // PlayingBar shows song name, progress and lyric type PlayingBar struct { *tview.Frame - full int32 + full int64 update chan struct{} - progress int32 + progress int64 skip bool text *tview.TextView hasTag bool tag *id3v2.Tag subtitle *lyric.Lyric subtitles []*lyric.Lyric - mu sync.Mutex } func (p *PlayingBar) help() []string { @@ -145,7 +143,7 @@ func (p *PlayingBar) setSongTitle(title string) { // Resets progress bar, ready for execution func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) { - p.full = int32(full) + p.setFull(full) p.setProgress(0) p.setSongTitle(currentSong.name) p.hasTag = false @@ -220,8 +218,8 @@ func (p *PlayingBar) switchLyrics() { // only 1 subtitle, prompt to the user and select this one if len(p.subtitles) == 1 { - defaultTimedPopup(" Warning ", p.subtitle.LangExt+" lyric is the only lyric available") p.subtitle = p.subtitles[0] + defaultTimedPopup(" Warning ", p.subtitle.LangExt+" lyric is the only lyric available") return } @@ -312,12 +310,17 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { } func (p *PlayingBar) getProgress() int { - return int(atomic.LoadInt32(&p.progress)) + return int(atomic.LoadInt64(&p.progress)) } func (p *PlayingBar) setProgress(progress int) { - atomic.StoreInt32(&p.progress, int32(progress)) + atomic.StoreInt64(&p.progress, int64(progress)) } + func (p *PlayingBar) getFull() int { - return int(atomic.LoadInt32(&p.full)) + return int(atomic.LoadInt64(&p.full)) +} + +func (p *PlayingBar) setFull(full int) { + atomic.StoreInt64(&p.full, int64(full)) } diff --git a/tageditor.go b/tageditor.go index 46d7ffa..0e78f65 100644 --- a/tageditor.go +++ b/tageditor.go @@ -366,9 +366,9 @@ func tagPopup(node *AudioFile) (err error) { case tcell.KeyEsc: gomu.pages.RemovePage(popupID) gomu.popups.pop() - case tcell.KeyTab: + case tcell.KeyTab, tcell.KeyCtrlN, tcell.KeyCtrlJ: lyricFlex.cycleFocus(gomu.app, false) - case tcell.KeyBacktab: + case tcell.KeyBacktab, tcell.KeyCtrlP, tcell.KeyCtrlK: lyricFlex.cycleFocus(gomu.app, true) case tcell.KeyRight: lyricFlex.cycleFocus(gomu.app, false) From 973be286907944844e2378a39294f7ff02173ca1 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 30 Mar 2021 15:03:56 +0800 Subject: [PATCH 03/48] minor fix test --- playingbar_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playingbar_test.go b/playingbar_test.go index d343b9c..c23bb0c 100644 --- a/playingbar_test.go +++ b/playingbar_test.go @@ -36,7 +36,7 @@ func Test_NewProgress(t *testing.T) { p.newProgress(&audio, full) - if p.full != full { + if p.full != int64(full) { t.Errorf("Expected %d; got %d", full, p.full) } From ff531589e5986d367c1ecad820ce1cc5394ac084 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 30 Mar 2021 15:17:21 +0800 Subject: [PATCH 04/48] minor fix warning for receiver name --- tageditor.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tageditor.go b/tageditor.go index 0e78f65..05f870e 100644 --- a/tageditor.go +++ b/tageditor.go @@ -437,12 +437,12 @@ func (f *lyricFlex) Focus(delegate func(p tview.Primitive)) { } // loadTagMap will load from tag and return a map of langExt to lyrics -func (node *AudioFile) loadTagMap() (tag *id3v2.Tag, popupLyricMap map[string]string, options []string, err error) { +func (a *AudioFile) loadTagMap() (tag *id3v2.Tag, popupLyricMap map[string]string, options []string, err error) { popupLyricMap = make(map[string]string) - if node.isAudioFile { - tag, err = id3v2.Open(node.path, id3v2.Options{Parse: true}) + if a.isAudioFile { + tag, err = id3v2.Open(a.path, id3v2.Options{Parse: true}) if err != nil { return nil, nil, nil, tracerr.Wrap(err) } From 887026a58ddda03a4f6dda942fc35f7a92c61188 Mon Sep 17 00:00:00 2001 From: tramhao Date: Wed, 31 Mar 2021 16:45:15 +0800 Subject: [PATCH 05/48] minor refactor tageditor.go --- tageditor.go | 33 ++++++++++++++++----------------- utils.go | 2 -- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/tageditor.go b/tageditor.go index 05f870e..55d0364 100644 --- a/tageditor.go +++ b/tageditor.go @@ -22,10 +22,9 @@ type lyricFlex struct { *tview.Flex FocusedItem tview.Primitive inputs []tview.Primitive + box *tview.Box } -var box *tview.Box = tview.NewBox() - // tagPopup is used to edit tag, delete and fetch lyrics func tagPopup(node *AudioFile) (err error) { @@ -45,9 +44,9 @@ func tagPopup(node *AudioFile) (err error) { deleteLyricButton *tview.Button = tview.NewButton("Delete Lyric") getLyricDropDown *tview.DropDown = tview.NewDropDown() getLyricButton *tview.Button = tview.NewButton("Fetch Lyric") - lyricTextView *tview.TextView - leftGrid *tview.Grid = tview.NewGrid() - rightFlex *tview.Flex = tview.NewFlex() + lyricTextView *tview.TextView = tview.NewTextView() + leftGrid *tview.Grid = tview.NewGrid() + rightFlex *tview.Flex = tview.NewFlex() ) artistInputField.SetLabel("Artist: "). @@ -292,7 +291,7 @@ func tagPopup(node *AudioFile) (err error) { lyricText = "No lyric embeded." langExt = "" } - lyricTextView = tview.NewTextView() + lyricTextView. SetDynamicColors(true). SetRegions(true). @@ -323,15 +322,6 @@ func tagPopup(node *AudioFile) (err error) { AddItem(lyricDropDown, 10, 0, 1, 3, 1, 10, true). AddItem(deleteLyricButton, 11, 0, 1, 3, 1, 10, true) - box.SetBorder(true). - SetTitle(node.name). - SetBackgroundColor(gomu.colors.popup). - SetBorderColor(gomu.colors.accent). - SetTitleColor(gomu.colors.accent). - SetBorderPadding(1, 1, 2, 2) - - leftGrid.Box = box - rightFlex.SetDirection(tview.FlexColumn). AddItem(lyricTextView, 0, 1, true) @@ -341,8 +331,17 @@ func tagPopup(node *AudioFile) (err error) { AddItem(rightFlex, 0, 3, true), nil, nil, + tview.NewBox(). + SetBorder(true). + SetTitle(node.name). + SetBackgroundColor(gomu.colors.popup). + SetBorderColor(gomu.colors.accent). + SetTitleColor(gomu.colors.accent). + SetBorderPadding(1, 1, 2, 2), } + leftGrid.Box = lyricFlex.box + lyricFlex.inputs = []tview.Primitive{ getTagButton, artistInputField, @@ -413,12 +412,12 @@ func (f *lyricFlex) cycleFocus(app *tview.Application, reverse bool) { if f.inputs[9].HasFocus() { f.inputs[9].(*tview.TextView).SetBorderColor(gomu.colors.accent). SetTitleColor(gomu.colors.accent) - box.SetBorderColor(gomu.colors.background). + f.box.SetBorderColor(gomu.colors.background). SetTitleColor(gomu.colors.background) } else { f.inputs[9].(*tview.TextView).SetBorderColor(gomu.colors.background). SetTitleColor(gomu.colors.background) - box.SetBorderColor(gomu.colors.accent). + f.box.SetBorderColor(gomu.colors.accent). SetTitleColor(gomu.colors.accent) } return diff --git a/utils.go b/utils.go index d9b0f5d..9bc34dd 100644 --- a/utils.go +++ b/utils.go @@ -6,8 +6,6 @@ import ( "bytes" "errors" "fmt" - - // "io" "log" "net/http" "os" From 993cb6d281e167e89192567ffcaa3e204a89764f Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 00:07:42 +0800 Subject: [PATCH 06/48] add config option rename_bytag, fix bug cannot play when queue is empty --- command.go | 6 +++++- start.go | 4 +++- tageditor.go | 50 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/command.go b/command.go index 451e42c..51f999b 100644 --- a/command.go +++ b/command.go @@ -205,7 +205,11 @@ func (c Command) defineCommands() { } gomu.queue.pushFront(a) - gomu.player.Skip() + if gomu.player.IsRunning() { + gomu.player.Skip() + } else { + gomu.queue.playQueue() + } } }) diff --git a/start.go b/start.go index d73da69..8b3c84f 100644 --- a/start.go +++ b/start.go @@ -241,9 +241,11 @@ module General { invidious_instance = "https://vid.puffyan.us" # Prefered language for lyrics to be displayed, if not available, english version # will be displayed. - # Available tags: en,el,ko,es,th,vi,zh-Hans,zh-Hant, and can be separated with comma. + # Available tags: en,el,ko,es,th,vi,zh-Hans,zh-Hant,zh-CN and can be separated with comma. # find more tags: youtube-dl --skip-download --list-subs "url" lang_lyric = "en" + # When save tag, could rename the file by tag info: artist-songname-album + rename_bytag = false } module Emoji { diff --git a/tageditor.go b/tageditor.go index 55d0364..814b0f8 100644 --- a/tageditor.go +++ b/tageditor.go @@ -64,6 +64,14 @@ func tagPopup(node *AudioFile) (err error) { SetText(tag.Album()). SetFieldBackgroundColor(gomu.colors.popup) + leftBox := tview.NewBox(). + SetBorder(true). + SetTitle(node.name). + SetBackgroundColor(gomu.colors.popup). + SetBorderColor(gomu.colors.accent). + SetTitleColor(gomu.colors.accent). + SetBorderPadding(1, 1, 2, 2) + getTagButton.SetSelectedFunc(func() { var titles []string audioFile := node @@ -111,6 +119,16 @@ func tagPopup(node *AudioFile) (err error) { errorPopup(err) return } + if gomu.anko.GetBool("General.rename_bytag") { + newName := fmt.Sprintf("%s-%s", newTag.Artist, newTag.Title) + err = gomu.playlist.rename(newName) + if err != nil { + errorPopup(err) + return + } + gomu.playlist.refresh() + leftBox.SetTitle(newName) + } defaultTimedPopup(" Success ", "Tag update successfully") }) }() @@ -129,14 +147,28 @@ func tagPopup(node *AudioFile) (err error) { return } defer tag.Close() - tag.SetArtist(artistInputField.GetText()) - tag.SetTitle(titleInputField.GetText()) - tag.SetAlbum(albumInputField.GetText()) + newArtist := artistInputField.GetText() + newTitle := titleInputField.GetText() + newAlbum := albumInputField.GetText() + tag.SetArtist(newArtist) + tag.SetTitle(newTitle) + tag.SetAlbum(newAlbum) err = tag.Save() if err != nil { errorPopup(err) return } + if gomu.anko.GetBool("General.rename_bytag") { + newName := fmt.Sprintf("%s-%s", newArtist, newTitle) + err = gomu.playlist.rename(newName) + if err != nil { + errorPopup(err) + return + } + gomu.playlist.refresh() + leftBox.SetTitle(newName) + } + defaultTimedPopup(" Success ", "Tag update successfully") }). @@ -331,13 +363,7 @@ func tagPopup(node *AudioFile) (err error) { AddItem(rightFlex, 0, 3, true), nil, nil, - tview.NewBox(). - SetBorder(true). - SetTitle(node.name). - SetBackgroundColor(gomu.colors.popup). - SetBorderColor(gomu.colors.accent). - SetTitleColor(gomu.colors.accent). - SetBorderPadding(1, 1, 2, 2), + leftBox, } leftGrid.Box = lyricFlex.box @@ -369,9 +395,9 @@ func tagPopup(node *AudioFile) (err error) { lyricFlex.cycleFocus(gomu.app, false) case tcell.KeyBacktab, tcell.KeyCtrlP, tcell.KeyCtrlK: lyricFlex.cycleFocus(gomu.app, true) - case tcell.KeyRight: + case tcell.KeyDown: lyricFlex.cycleFocus(gomu.app, false) - case tcell.KeyLeft: + case tcell.KeyUp: lyricFlex.cycleFocus(gomu.app, true) } From 21019e6884b21409c5140352094f0e9c05846e1e Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 00:23:51 +0800 Subject: [PATCH 07/48] minor fix: play selected when paused not working --- queue.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/queue.go b/queue.go index 7f2667b..682a7d1 100644 --- a/queue.go +++ b/queue.go @@ -422,20 +422,13 @@ func (q *Queue) updateQueueNames() error { // playQueue play the first item in the queue func (q *Queue) playQueue() error { - isTestEnv := os.Getenv("TEST") == "false" - - if !gomu.player.IsRunning() && !gomu.player.IsPaused() && isTestEnv { - - audioFile, err := q.dequeue() - if err != nil { - return tracerr.Wrap(err) - } - err = gomu.player.Run(audioFile) - if err != nil { - return tracerr.Wrap(err) - } - - return nil + audioFile, err := q.dequeue() + if err != nil { + return tracerr.Wrap(err) + } + err = gomu.player.Run(audioFile) + if err != nil { + return tracerr.Wrap(err) } return nil From 2c6690cf1e5665fde1e7122100c95f43e5e4ac8d Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 10:25:08 +0800 Subject: [PATCH 08/48] fix searchpopup not working --- popup.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/popup.go b/popup.go index 70d0396..506b2c6 100644 --- a/popup.go +++ b/popup.go @@ -436,7 +436,6 @@ func searchPopup(title string, stringsToMatch []string, handler func(selected st pattern := input.GetText() matches := fuzzy.Find(pattern, stringsToMatch) const highlight = "[red]%c[-]" - // const highlight = "[red]%s[-]" for _, match := range matches { var text strings.Builder @@ -515,8 +514,6 @@ func searchPopup(title string, stringsToMatch []string, handler func(selected st gomu.pages.AddPage("search-input-popup", center(popupFrame, 70, 40), true, true) gomu.popups.push(popup) - // This is to ensure the popup is shown even when paused - gomu.app.Draw() } // Creates new popup widget with default settings From fda946ae92a1daa9f9ca01aa8c6c634b5575d309 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 11:25:14 +0800 Subject: [PATCH 09/48] fix searchpopup not shown --- popup.go | 2 ++ tageditor.go | 1 + 2 files changed, 3 insertions(+) diff --git a/popup.go b/popup.go index 506b2c6..bdafc72 100644 --- a/popup.go +++ b/popup.go @@ -904,6 +904,8 @@ func lyricPopup(lang string, audioFile *AudioFile, wg *sync.WaitGroup) error { }() }) + gomu.app.Draw() + return nil } diff --git a/tageditor.go b/tageditor.go index 814b0f8..fae7ba7 100644 --- a/tageditor.go +++ b/tageditor.go @@ -131,6 +131,7 @@ func tagPopup(node *AudioFile) (err error) { } defaultTimedPopup(" Success ", "Tag update successfully") }) + gomu.app.Draw() }() }() }). From 90cc3c49701f2f9c9b3c46c20f1867a4bd32606f Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 15:24:31 +0800 Subject: [PATCH 10/48] refactor yank paste and rename --- command.go | 12 ------- playlist.go | 94 ++++++++++++++++++++++------------------------------- popup.go | 16 +++++++-- queue.go | 21 +++++++++--- 4 files changed, 69 insertions(+), 74 deletions(-) diff --git a/command.go b/command.go index 51f999b..7ed9735 100644 --- a/command.go +++ b/command.go @@ -316,7 +316,6 @@ func (c Command) defineCommands() { err := gomu.player.Seek(position) if err != nil { errorPopup(err) - gomu.app.Draw() } gomu.playingBar.setProgress(position) } @@ -330,14 +329,12 @@ func (c Command) defineCommands() { err := gomu.player.Seek(position) if err != nil { errorPopup(err) - gomu.app.Draw() } gomu.playingBar.setProgress(position) } else { err := gomu.player.Seek(0) if err != nil { errorPopup(err) - gomu.app.Draw() } gomu.playingBar.setProgress(0) } @@ -351,7 +348,6 @@ func (c Command) defineCommands() { err := gomu.player.Seek(position) if err != nil { errorPopup(err) - gomu.app.Draw() } gomu.playingBar.setProgress(position) } @@ -365,14 +361,12 @@ func (c Command) defineCommands() { err := gomu.player.Seek(position) if err != nil { errorPopup(err) - gomu.app.Draw() } gomu.playingBar.setProgress(position) } else { err := gomu.player.Seek(0) if err != nil { errorPopup(err) - gomu.app.Draw() } gomu.playingBar.setProgress(0) } @@ -383,7 +377,6 @@ func (c Command) defineCommands() { err := gomu.playlist.yank() if err != nil { errorPopup(err) - gomu.app.Draw() } }) @@ -391,7 +384,6 @@ func (c Command) defineCommands() { err := gomu.playlist.paste() if err != nil { errorPopup(err) - gomu.app.Draw() } }) @@ -422,7 +414,6 @@ func (c Command) defineCommands() { err := lyricPopup(lang, audioFile, &wg) if err != nil { errorPopup(err) - gomu.app.Draw() } }() } @@ -439,7 +430,6 @@ func (c Command) defineCommands() { err := lyricPopup(lang, audioFile, &wg) if err != nil { errorPopup(err) - gomu.app.Draw() } }() } @@ -449,7 +439,6 @@ func (c Command) defineCommands() { err := gomu.playingBar.delayLyric(500) if err != nil { errorPopup(err) - gomu.app.Draw() } }) @@ -457,7 +446,6 @@ func (c Command) defineCommands() { err := gomu.playingBar.delayLyric(-500) if err != nil { errorPopup(err) - gomu.app.Draw() } }) diff --git a/playlist.go b/playlist.go index 7c84444..3ac00a2 100644 --- a/playlist.go +++ b/playlist.go @@ -4,6 +4,7 @@ package main import ( "bytes" + "errors" "fmt" "io/ioutil" "os" @@ -82,12 +83,8 @@ type Playlist struct { // number of downloads download int done chan struct{} -} - -var ( yankFile *AudioFile - isYanked bool -) +} func (p *Playlist) help() []string { @@ -488,6 +485,7 @@ func (p *Playlist) rename(newName string) error { currentNode := p.GetCurrentNode() audio := currentNode.GetReference().(*AudioFile) + pathToFile, _ := filepath.Split(audio.path) var newPath string if audio.isAudioFile { @@ -500,11 +498,6 @@ func (p *Playlist) rename(newName string) error { return tracerr.Wrap(err) } - audio.path = newPath - gomu.queue.saveQueue(false) - gomu.queue.clearQueue() - gomu.queue.loadQueue() - return nil } @@ -778,60 +771,51 @@ func populate(root *tview.TreeNode, rootPath string, sortMtime bool) error { } func (p *Playlist) yank() error { - yankFile = p.getCurrentFile() - if yankFile == nil { - isYanked = false - defaultTimedPopup(" Error! ", "No file has been yanked.") - return nil + p.yankFile = p.getCurrentFile() + if p.yankFile == nil { + return errors.New("no file has been yanked") } - if yankFile.node == p.GetRoot() { - isYanked = false - defaultTimedPopup(" Error! ", "Please don't yank the root directory.") - return nil + if p.yankFile.node == p.GetRoot() { + return errors.New("please don't yank the root directory") } - isYanked = true - defaultTimedPopup(" Success ", yankFile.name+"\n has been yanked successfully.") + defaultTimedPopup(" Success ", p.yankFile.name+"\n has been yanked successfully.") return nil } func (p *Playlist) paste() error { - if isYanked { - isYanked = false - oldPathDir, oldPathFileName := filepath.Split(yankFile.path) - pasteFile := p.getCurrentFile() - if pasteFile.isAudioFile { - newPathDir, _ := filepath.Split(pasteFile.path) - if oldPathDir == newPathDir { - return nil - } - newPathFull := filepath.Join(newPathDir, oldPathFileName) - err := os.Rename(yankFile.path, newPathFull) - if err != nil { - defaultTimedPopup(" Error ", yankFile.name+"\n has not been pasted.") - return tracerr.Wrap(err) - } - defaultTimedPopup(" Success ", yankFile.name+"\n has been pasted to\n"+pasteFile.name) - - } else { - newPathDir := pasteFile.path - if oldPathDir == newPathDir { - return nil - } - newPathFull := filepath.Join(newPathDir, oldPathFileName) - err := os.Rename(yankFile.path, newPathFull) - if err != nil { - defaultTimedPopup(" Error ", yankFile.name+"\n has not been pasted.") - return tracerr.Wrap(err) - } - defaultTimedPopup(" Success ", yankFile.name+"\n has been pasted to\n"+pasteFile.name) - - } - - p.refresh() - gomu.queue.updateQueueNames() + if p.yankFile == nil { + return errors.New("no file has been yanked") } + oldPathDir, oldPathFileName := filepath.Split(p.yankFile.path) + pasteFile := p.getCurrentFile() + var newPathDir string + if pasteFile.isAudioFile { + newPathDir, _ = filepath.Split(pasteFile.path) + } else { + newPathDir = pasteFile.path + } + + if oldPathDir == newPathDir { + return nil + } + + newPathFull := filepath.Join(newPathDir, oldPathFileName) + err := os.Rename(p.yankFile.path, newPathFull) + if err != nil { + return tracerr.Wrap(err) + } + + defaultTimedPopup(" Success ", p.yankFile.name+"\n has been pasted to\n"+newPathDir) + p.yankFile = nil + + // keep queue references updated + p.refresh() + gomu.queue.saveQueue(false) + gomu.queue.clearQueue() + gomu.queue.loadQueue() + return nil } diff --git a/popup.go b/popup.go index bdafc72..f72e846 100644 --- a/popup.go +++ b/popup.go @@ -555,14 +555,12 @@ func renamePopup(node *AudioFile) { } err := gomu.playlist.rename(newName) if err != nil { - defaultTimedPopup(" Error ", err.Error()) - logError(err) + errorPopup(err) } gomu.pages.RemovePage(popupID) gomu.popups.pop() gomu.playlist.refresh() - gomu.queue.updateQueueNames() gomu.setFocusPanel(gomu.playlist) gomu.prevPanel = gomu.playlist @@ -573,6 +571,18 @@ func renamePopup(node *AudioFile) { } return true }) + // update queue + if node.isAudioFile { + newNode := gomu.playlist.getCurrentFile() + err = gomu.queue.rename(node, newNode) + if err != nil { + errorPopup(err) + } + } else { + gomu.queue.saveQueue(false) + gomu.queue.clearQueue() + gomu.queue.loadQueue() + } case tcell.KeyEsc: gomu.pages.RemovePage(popupID) diff --git a/queue.go b/queue.go index 682a7d1..89c503f 100644 --- a/queue.go +++ b/queue.go @@ -412,10 +412,23 @@ func sha1Hex(input string) string { } // Modify the title of songs in queue -func (q *Queue) updateQueueNames() error { - q.saveQueue(false) - q.clearQueue() - q.loadQueue() +func (q *Queue) rename(oldAudio *AudioFile, newAudio *AudioFile) error { + for i, v := range q.items { + if v.name != oldAudio.name { + continue + } + + q.items[i] = newAudio + songLength, err := getTagLength(newAudio.path) + if err != nil { + return tracerr.Wrap(err) + } + queueItemView := fmt.Sprintf( + "[ %s ] %s", fmtDuration(songLength), getName(newAudio.name), + ) + q.SetItemText(i, queueItemView, "") + + } return nil } From 5593afd7854a4e93cfff15793574b249940d7ad1 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 16:09:09 +0800 Subject: [PATCH 11/48] refactor rename and tageditor rename --- playlist.go | 24 ++++++++++++++++++++++++ popup.go | 21 +++------------------ tageditor.go | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/playlist.go b/playlist.go index 3ac00a2..e01171c 100644 --- a/playlist.go +++ b/playlist.go @@ -833,3 +833,27 @@ func setDisplayText(audioFile *AudioFile) string { emojiDir := gomu.anko.GetString("Emoji.playlist") return fmt.Sprintf(" %s %s", emojiDir, audioFile.name) } + +func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { + + root := p.GetRoot() + root.Walk(func(node, _ *tview.TreeNode) bool { + if strings.Contains(node.GetText(), newName) { + p.setHighlight(node) + } + return true + }) + // update queue + if node.isAudioFile { + newNode := p.getCurrentFile() + err := gomu.queue.rename(node, newNode) + if err != nil { + return tracerr.Wrap(err) + } + } else { + gomu.queue.saveQueue(false) + gomu.queue.clearQueue() + gomu.queue.loadQueue() + } + return nil +} diff --git a/popup.go b/popup.go index f72e846..e2a4003 100644 --- a/popup.go +++ b/popup.go @@ -564,24 +564,9 @@ func renamePopup(node *AudioFile) { gomu.setFocusPanel(gomu.playlist) gomu.prevPanel = gomu.playlist - root := gomu.playlist.GetRoot() - root.Walk(func(node, _ *tview.TreeNode) bool { - if strings.Contains(node.GetText(), newName) { - gomu.playlist.setHighlight(node) - } - return true - }) - // update queue - if node.isAudioFile { - newNode := gomu.playlist.getCurrentFile() - err = gomu.queue.rename(node, newNode) - if err != nil { - errorPopup(err) - } - } else { - gomu.queue.saveQueue(false) - gomu.queue.clearQueue() - gomu.queue.loadQueue() + err = gomu.playlist.refreshByNode(node, newName) + if err != nil { + errorPopup(err) } case tcell.KeyEsc: diff --git a/tageditor.go b/tageditor.go index fae7ba7..f27ebf7 100644 --- a/tageditor.go +++ b/tageditor.go @@ -128,6 +128,13 @@ func tagPopup(node *AudioFile) (err error) { } gomu.playlist.refresh() leftBox.SetTitle(newName) + + // update queue + err = gomu.playlist.refreshByNode(node, newName) + if err != nil { + errorPopup(err) + return + } } defaultTimedPopup(" Success ", "Tag update successfully") }) @@ -168,6 +175,14 @@ func tagPopup(node *AudioFile) (err error) { } gomu.playlist.refresh() leftBox.SetTitle(newName) + + // update queue + err = gomu.playlist.refreshByNode(node, newName) + if err != nil { + errorPopup(err) + return + } + } defaultTimedPopup(" Success ", "Tag update successfully") From c91f95678cbc512720f94759f3c6fa0fe0792340 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 1 Apr 2021 23:54:38 +0800 Subject: [PATCH 12/48] Fix paly new added song when queue is empty and no song is playing --- command.go | 6 ++++++ playlist.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 7ed9735..da26cf0 100644 --- a/command.go +++ b/command.go @@ -95,6 +95,12 @@ func (c Command) defineCommands() { currNode := gomu.playlist.GetCurrentNode() if audioFile.isAudioFile { gomu.queue.enqueue(audioFile) + if len(gomu.queue.items) == 1 && !gomu.player.IsRunning() { + err := gomu.queue.playQueue() + if err != nil { + errorPopup(err) + } + } } else { currNode.SetExpanded(true) } diff --git a/playlist.go b/playlist.go index e01171c..d0a4d6b 100644 --- a/playlist.go +++ b/playlist.go @@ -808,7 +808,6 @@ func (p *Playlist) paste() error { } defaultTimedPopup(" Success ", p.yankFile.name+"\n has been pasted to\n"+newPathDir) - p.yankFile = nil // keep queue references updated p.refresh() @@ -816,6 +815,23 @@ func (p *Playlist) paste() error { gomu.queue.clearQueue() gomu.queue.loadQueue() + currentSong := gomu.player.GetCurrentSong() + if p.yankFile.name == currentSong.Name() { + newNode := p.yankFile + newNode.path = newPathFull + gomu.queue.enqueue(newNode) + gomu.queue.pushFront(newNode) + gomu.player.Skip() + if gomu.anko.GetBool("General.queue_loop") { + _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) + if err != nil { + return tracerr.Wrap(err) + } + } + } + + p.yankFile = nil + return nil } @@ -850,6 +866,20 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { if err != nil { return tracerr.Wrap(err) } + + currentSong := gomu.player.GetCurrentSong() + if node.name == currentSong.Name() { + gomu.queue.enqueue(newNode) + gomu.queue.pushFront(newNode) + gomu.player.Skip() + if gomu.anko.GetBool("General.queue_loop") { + _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) + if err != nil { + return tracerr.Wrap(err) + } + } + } + } else { gomu.queue.saveQueue(false) gomu.queue.clearQueue() From d7c3cb3403b4d6209ed6cc1afc7f0b0a141ba398 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 00:13:20 +0800 Subject: [PATCH 13/48] change node after rename in tageditor.go --- tageditor.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tageditor.go b/tageditor.go index f27ebf7..0ad8cb6 100644 --- a/tageditor.go +++ b/tageditor.go @@ -135,6 +135,7 @@ func tagPopup(node *AudioFile) (err error) { errorPopup(err) return } + node = gomu.playlist.getCurrentFile() } defaultTimedPopup(" Success ", "Tag update successfully") }) @@ -182,7 +183,7 @@ func tagPopup(node *AudioFile) (err error) { errorPopup(err) return } - + node = gomu.playlist.getCurrentFile() } defaultTimedPopup(" Success ", "Tag update successfully") From c00bc9594b978285d3199684f816d9e7aa842df7 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 01:48:17 +0800 Subject: [PATCH 14/48] change logic of rename to insert new one and delete old one in queue.go --- queue.go | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/queue.go b/queue.go index 89c503f..f736518 100644 --- a/queue.go +++ b/queue.go @@ -417,16 +417,14 @@ func (q *Queue) rename(oldAudio *AudioFile, newAudio *AudioFile) error { if v.name != oldAudio.name { continue } - - q.items[i] = newAudio - songLength, err := getTagLength(newAudio.path) + err := q.insertItem(i, newAudio) + if err != nil { + return tracerr.Wrap(err) + } + _, err = q.deleteItem(i + 1) if err != nil { return tracerr.Wrap(err) } - queueItemView := fmt.Sprintf( - "[ %s ] %s", fmtDuration(songLength), getName(newAudio.name), - ) - q.SetItemText(i, queueItemView, "") } return nil @@ -446,3 +444,39 @@ func (q *Queue) playQueue() error { return nil } + +func (q *Queue) insertItem(index int, audioFile *AudioFile) error { + + if index > len(q.items)-1 { + return tracerr.New("Index out of range") + } + + if index != -1 { + songLength, err := getTagLength(audioFile.path) + if err != nil { + return tracerr.Wrap(err) + } + queueItemView := fmt.Sprintf( + "[ %s ] %s", fmtDuration(songLength), getName(audioFile.name), + ) + + q.InsertItem(index, queueItemView, audioFile.path, 0, nil) + + var nItems []*AudioFile + + for i, v := range q.items { + + if i == index { + nItems = append(nItems, audioFile) + } + + nItems = append(nItems, v) + } + + q.items = nItems + q.updateTitle() + + } + + return nil +} From dde95bba2082adb9c9ed721c4a5b4fd20aae6584 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 09:44:47 +0800 Subject: [PATCH 15/48] minor fix: start playing after buld_add --- command.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/command.go b/command.go index da26cf0..e34e45d 100644 --- a/command.go +++ b/command.go @@ -127,6 +127,12 @@ func (c Command) defineCommands() { if !bulkAdd { gomu.playlist.addAllToQueue(currNode) + if len(gomu.queue.items) > 0 && !gomu.player.IsRunning() { + err := gomu.queue.playQueue() + if err != nil { + errorPopup(err) + } + } return } @@ -136,6 +142,12 @@ func (c Command) defineCommands() { if label == "yes" { gomu.playlist.addAllToQueue(currNode) + if len(gomu.queue.items) > 0 && !gomu.player.IsRunning() { + err := gomu.queue.playQueue() + if err != nil { + errorPopup(err) + } + } } }) From 45b2957417c79ae4ded4816ac25c4fcc2a2ea235 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 15:28:04 +0800 Subject: [PATCH 16/48] minor fix: after a song finish, enqueue it first then playqueue --- start.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/start.go b/start.go index 8b3c84f..b5b3a0f 100644 --- a/start.go +++ b/start.go @@ -400,16 +400,6 @@ func start(application *tview.Application, args Args) { }) gomu.player.SetSongFinish(func(currAudio player.Audio) { - audio, err := gomu.queue.dequeue() - if err != nil { - gomu.playingBar.setDefault() - return - } - - err = gomu.player.Run(audio) - if err != nil { - die(err) - } if gomu.queue.isLoop { _, err = gomu.queue.enqueue(currAudio.(*AudioFile)) @@ -417,6 +407,13 @@ func start(application *tview.Application, args Args) { logError(err) } } + + if len(gomu.queue.items) > 0 { + err := gomu.queue.playQueue() + if err != nil { + logError(err) + } + } }) flex := layout(gomu) From 93dc45dbb29a40ea8eb816c4bbd43ec69336c9f3 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 15:51:56 +0800 Subject: [PATCH 17/48] minor fix: playingbar is not set correctly when queue is empty --- playingbar.go | 1 + start.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/playingbar.go b/playingbar.go index 45ef756..73635ce 100644 --- a/playingbar.go +++ b/playingbar.go @@ -67,6 +67,7 @@ func (p *PlayingBar) run() error { if progress > full || p.skip { p.skip = false p.setProgress(0) + p.subtitle = nil break } diff --git a/start.go b/start.go index b5b3a0f..1df03fc 100644 --- a/start.go +++ b/start.go @@ -413,6 +413,8 @@ func start(application *tview.Application, args Args) { if err != nil { logError(err) } + } else { + gomu.playingBar.setDefault() } }) From a6ba20b54a917bbd70eda1a2d8540a47811bcc17 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 16:14:16 +0800 Subject: [PATCH 18/48] minor fix: subtitle is still showing when queue is empty and stopped --- playingbar.go | 1 - start.go | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/playingbar.go b/playingbar.go index 73635ce..45ef756 100644 --- a/playingbar.go +++ b/playingbar.go @@ -67,7 +67,6 @@ func (p *PlayingBar) run() error { if progress > full || p.skip { p.skip = false p.setProgress(0) - p.subtitle = nil break } diff --git a/start.go b/start.go index 1df03fc..b7e5b3f 100644 --- a/start.go +++ b/start.go @@ -401,6 +401,8 @@ func start(application *tview.Application, args Args) { gomu.player.SetSongFinish(func(currAudio player.Audio) { + gomu.playingBar.subtitles = nil + gomu.playingBar.subtitle = nil if gomu.queue.isLoop { _, err = gomu.queue.enqueue(currAudio.(*AudioFile)) if err != nil { From 6976541a2a65776e8cbce0d1c5dd3b907f629b17 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 17:14:42 +0800 Subject: [PATCH 19/48] minor fix: panic when rename sone and not playing --- playlist.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/playlist.go b/playlist.go index d0a4d6b..b2557d2 100644 --- a/playlist.go +++ b/playlist.go @@ -868,6 +868,9 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { } currentSong := gomu.player.GetCurrentSong() + if currentSong == nil { + return nil + } if node.name == currentSong.Name() { gomu.queue.enqueue(newNode) gomu.queue.pushFront(newNode) From 8db110282ad1ca2c29b7a5715bb6a9d792d6a2b1 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 2 Apr 2021 17:21:39 +0800 Subject: [PATCH 20/48] minor fix: panic when rename sone and not playing --- playlist.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/playlist.go b/playlist.go index b2557d2..100dfc1 100644 --- a/playlist.go +++ b/playlist.go @@ -867,10 +867,11 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { return tracerr.Wrap(err) } - currentSong := gomu.player.GetCurrentSong() - if currentSong == nil { + if !gomu.player.IsRunning() { return nil } + + currentSong := gomu.player.GetCurrentSong() if node.name == currentSong.Name() { gomu.queue.enqueue(newNode) gomu.queue.pushFront(newNode) From 42160b621342717965f906baf2e662d030061cda Mon Sep 17 00:00:00 2001 From: tramhao Date: Sat, 3 Apr 2021 01:38:01 +0800 Subject: [PATCH 21/48] in progress: try to fix paste function but failed --- playlist.go | 36 +++++++++++++++++++++--------------- queue.go | 6 ++++-- start.go | 4 +--- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/playlist.go b/playlist.go index 100dfc1..44f4c0d 100644 --- a/playlist.go +++ b/playlist.go @@ -814,21 +814,27 @@ func (p *Playlist) paste() error { gomu.queue.saveQueue(false) gomu.queue.clearQueue() gomu.queue.loadQueue() + gomu.player.Skip() + // if gomu.queue.isLoop { + // _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) + // if err != nil { + // return tracerr.Wrap(err) + // } + // } - currentSong := gomu.player.GetCurrentSong() - if p.yankFile.name == currentSong.Name() { - newNode := p.yankFile - newNode.path = newPathFull - gomu.queue.enqueue(newNode) - gomu.queue.pushFront(newNode) - gomu.player.Skip() - if gomu.anko.GetBool("General.queue_loop") { - _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) - if err != nil { - return tracerr.Wrap(err) - } - } - } + // currentSong := gomu.player.GetCurrentSong() + // if p.yankFile.name == currentSong.Name() { + // newNode := p.yankFile + // newNode.path = newPathFull + // gomu.queue.pushFront(newNode) + // gomu.player.Skip() + // if gomu.queue.isLoop { + // _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) + // if err != nil { + // return tracerr.Wrap(err) + // } + // } + // } p.yankFile = nil @@ -876,7 +882,7 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { gomu.queue.enqueue(newNode) gomu.queue.pushFront(newNode) gomu.player.Skip() - if gomu.anko.GetBool("General.queue_loop") { + if gomu.queue.isLoop { _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) if err != nil { return tracerr.Wrap(err) diff --git a/queue.go b/queue.go index f736518..ba4bf65 100644 --- a/queue.go +++ b/queue.go @@ -198,7 +198,8 @@ func (q *Queue) saveQueue(isQuit bool) error { songPaths := q.getItems() var content strings.Builder - if gomu.player.HasInit() && isQuit && gomu.player.GetCurrentSong() != nil { + if gomu.player.HasInit() && gomu.player.GetCurrentSong() != nil { + // if gomu.player.HasInit() && isQuit && gomu.player.GetCurrentSong() != nil { currentSongPath := gomu.player.GetCurrentSong().Path() currentSongInQueue := false for _, songPath := range songPaths { @@ -206,7 +207,8 @@ func (q *Queue) saveQueue(isQuit bool) error { currentSongInQueue = true } } - if !currentSongInQueue && len(q.items) != 0 { + if !currentSongInQueue { + // if !currentSongInQueue && len(q.items) != 0 { hashed := sha1Hex(getName(currentSongPath)) content.WriteString(hashed + "\n") } diff --git a/start.go b/start.go index b7e5b3f..1c14b14 100644 --- a/start.go +++ b/start.go @@ -429,9 +429,7 @@ func start(application *tview.Application, args Args) { gomu.playingBar.setDefault() - isQueueLoop := gomu.anko.GetBool("General.queue_loop") - - gomu.queue.isLoop = isQueueLoop + gomu.queue.isLoop = gomu.anko.GetBool("General.queue_loop") loadQueue := gomu.anko.GetBool("General.load_prev_queue") From 88d72ed2f796eddc210162c9154500a412f5c574 Mon Sep 17 00:00:00 2001 From: tramhao Date: Sat, 3 Apr 2021 18:55:54 +0800 Subject: [PATCH 22/48] in progress: add updateQueuePath --- command.go | 1 + playlist.go | 15 +++++---------- queue.go | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/command.go b/command.go index e34e45d..36e6102 100644 --- a/command.go +++ b/command.go @@ -223,6 +223,7 @@ func (c Command) defineCommands() { } gomu.queue.pushFront(a) + if gomu.player.IsRunning() { gomu.player.Skip() } else { diff --git a/playlist.go b/playlist.go index 44f4c0d..bca106c 100644 --- a/playlist.go +++ b/playlist.go @@ -811,21 +811,16 @@ func (p *Playlist) paste() error { // keep queue references updated p.refresh() - gomu.queue.saveQueue(false) - gomu.queue.clearQueue() - gomu.queue.loadQueue() - gomu.player.Skip() - // if gomu.queue.isLoop { - // _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) - // if err != nil { - // return tracerr.Wrap(err) - // } - // } + err = gomu.queue.updateQueuePath() + if err != nil { + return tracerr.Wrap(err) + } // currentSong := gomu.player.GetCurrentSong() // if p.yankFile.name == currentSong.Name() { // newNode := p.yankFile // newNode.path = newPathFull + // gomu.queue.enqueue(newNode) // gomu.queue.pushFront(newNode) // gomu.player.Skip() // if gomu.queue.isLoop { diff --git a/queue.go b/queue.go index ba4bf65..6ab85b0 100644 --- a/queue.go +++ b/queue.go @@ -482,3 +482,30 @@ func (q *Queue) insertItem(index int, audioFile *AudioFile) error { return nil } + +//update the path information in queue +func (q *Queue) updateQueuePath() error { + + var songs []string + if len(q.items) < 1 { + return nil + } + for _, v := range q.items { + song := sha1Hex(getName(v.name)) + songs = append(songs, song) + } + + q.clearQueue() + for _, v := range songs { + + audioFile, err := gomu.playlist.findAudioFile(v) + + if err != nil { + logError(err) + continue + } + q.enqueue(audioFile) + } + + return nil +} From 464b64c9c18fd1f013e14ed32d5f3264e503f3ce Mon Sep 17 00:00:00 2001 From: tramhao Date: Sun, 4 Apr 2021 00:01:50 +0800 Subject: [PATCH 23/48] Fixed: paste with duplicated items and rename with duplicated itmes --- playlist.go | 46 +++++++++++++++------------------------------- queue.go | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/playlist.go b/playlist.go index bca106c..0600c6b 100644 --- a/playlist.go +++ b/playlist.go @@ -788,6 +788,7 @@ func (p *Playlist) paste() error { return errors.New("no file has been yanked") } + oldAudio := p.yankFile oldPathDir, oldPathFileName := filepath.Split(p.yankFile.path) pasteFile := p.getCurrentFile() var newPathDir string @@ -816,20 +817,12 @@ func (p *Playlist) paste() error { return tracerr.Wrap(err) } - // currentSong := gomu.player.GetCurrentSong() - // if p.yankFile.name == currentSong.Name() { - // newNode := p.yankFile - // newNode.path = newPathFull - // gomu.queue.enqueue(newNode) - // gomu.queue.pushFront(newNode) - // gomu.player.Skip() - // if gomu.queue.isLoop { - // _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) - // if err != nil { - // return tracerr.Wrap(err) - // } - // } - // } + newAudio := oldAudio + newAudio.path = newPathFull + err = gomu.queue.updateCurrentSong(oldAudio, newAudio) + if err != nil { + return tracerr.Wrap(err) + } p.yankFile = nil @@ -867,28 +860,19 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { if err != nil { return tracerr.Wrap(err) } - - if !gomu.player.IsRunning() { - return nil + err = gomu.queue.updateCurrentSong(node, newNode) + if err != nil { + return tracerr.Wrap(err) } - - currentSong := gomu.player.GetCurrentSong() - if node.name == currentSong.Name() { - gomu.queue.enqueue(newNode) - gomu.queue.pushFront(newNode) - gomu.player.Skip() - if gomu.queue.isLoop { - _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) - if err != nil { - return tracerr.Wrap(err) - } - } - } - } else { gomu.queue.saveQueue(false) gomu.queue.clearQueue() gomu.queue.loadQueue() + tmpLoop := gomu.queue.isLoop + gomu.queue.isLoop = false + gomu.player.Skip() + gomu.queue.isLoop = tmpLoop } + return nil } diff --git a/queue.go b/queue.go index 6ab85b0..f048806 100644 --- a/queue.go +++ b/queue.go @@ -509,3 +509,29 @@ func (q *Queue) updateQueuePath() error { return nil } + +func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) (err error) { + + var tmpLoop bool + if !gomu.player.IsRunning() && !gomu.player.IsPaused() { + return nil + } + + currentSong := gomu.player.GetCurrentSong() + + if oldAudio.name == currentSong.Name() { + gomu.queue.pushFront(newAudio) + tmpLoop = q.isLoop + q.isLoop = false + gomu.player.Skip() + q.isLoop = tmpLoop + // if gomu.queue.isLoop { + // _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) + // if err != nil { + // return tracerr.Wrap(err) + // } + // } + } + + return nil +} From 5435979dc94bacc3578ca60d429c3e61fc8cbaae Mon Sep 17 00:00:00 2001 From: tramhao Date: Sun, 4 Apr 2021 00:17:27 +0800 Subject: [PATCH 24/48] remove comments --- queue.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/queue.go b/queue.go index f048806..e9a2eed 100644 --- a/queue.go +++ b/queue.go @@ -512,7 +512,6 @@ func (q *Queue) updateQueuePath() error { func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) (err error) { - var tmpLoop bool if !gomu.player.IsRunning() && !gomu.player.IsPaused() { return nil } @@ -521,16 +520,10 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) (err if oldAudio.name == currentSong.Name() { gomu.queue.pushFront(newAudio) - tmpLoop = q.isLoop + tmpLoop := q.isLoop q.isLoop = false gomu.player.Skip() q.isLoop = tmpLoop - // if gomu.queue.isLoop { - // _, err = gomu.queue.deleteItem(len(gomu.queue.items) - 1) - // if err != nil { - // return tracerr.Wrap(err) - // } - // } } return nil From e0ed6b3b173ca7d2d0ab76b12a978b34e76db9f5 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 6 Apr 2021 11:38:57 +0800 Subject: [PATCH 25/48] Minor Fix: remove unnecessary error returned. --- playlist.go | 15 +++------------ queue.go | 16 ++++++++++------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/playlist.go b/playlist.go index 0600c6b..723b00f 100644 --- a/playlist.go +++ b/playlist.go @@ -812,17 +812,11 @@ func (p *Playlist) paste() error { // keep queue references updated p.refresh() - err = gomu.queue.updateQueuePath() - if err != nil { - return tracerr.Wrap(err) - } + gomu.queue.updateQueuePath() newAudio := oldAudio newAudio.path = newPathFull - err = gomu.queue.updateCurrentSong(oldAudio, newAudio) - if err != nil { - return tracerr.Wrap(err) - } + gomu.queue.updateCurrentSong(oldAudio, newAudio) p.yankFile = nil @@ -860,10 +854,7 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { if err != nil { return tracerr.Wrap(err) } - err = gomu.queue.updateCurrentSong(node, newNode) - if err != nil { - return tracerr.Wrap(err) - } + gomu.queue.updateCurrentSong(node, newNode) } else { gomu.queue.saveQueue(false) gomu.queue.clearQueue() diff --git a/queue.go b/queue.go index e9a2eed..5df4aed 100644 --- a/queue.go +++ b/queue.go @@ -484,11 +484,11 @@ func (q *Queue) insertItem(index int, audioFile *AudioFile) error { } //update the path information in queue -func (q *Queue) updateQueuePath() error { +func (q *Queue) updateQueuePath() { var songs []string if len(q.items) < 1 { - return nil + return } for _, v := range q.items { song := sha1Hex(getName(v.name)) @@ -507,24 +507,28 @@ func (q *Queue) updateQueuePath() error { q.enqueue(audioFile) } - return nil } -func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) (err error) { +func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) { if !gomu.player.IsRunning() && !gomu.player.IsPaused() { - return nil + return } currentSong := gomu.player.GetCurrentSong() + position := gomu.playingBar.getProgress() + paused := gomu.player.IsPaused() if oldAudio.name == currentSong.Name() { gomu.queue.pushFront(newAudio) tmpLoop := q.isLoop q.isLoop = false gomu.player.Skip() + gomu.player.Seek(position) + if paused { + gomu.player.TogglePause() + } q.isLoop = tmpLoop } - return nil } From 010dcc9258a36b1e663972a459759d16112aedd7 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 6 Apr 2021 12:18:45 +0800 Subject: [PATCH 26/48] minor fix: refreshbynode fix a bug for rename folders --- playlist.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/playlist.go b/playlist.go index 723b00f..d927350 100644 --- a/playlist.go +++ b/playlist.go @@ -838,6 +838,7 @@ func setDisplayText(audioFile *AudioFile) string { return fmt.Sprintf(" %s %s", emojiDir, audioFile.name) } +// refreshByNode is called after rename of file or folder, to refresh queue info func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { root := p.GetRoot() @@ -848,22 +849,18 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { return true }) // update queue + newNode := p.getCurrentFile() if node.isAudioFile { - newNode := p.getCurrentFile() err := gomu.queue.rename(node, newNode) if err != nil { return tracerr.Wrap(err) } - gomu.queue.updateCurrentSong(node, newNode) } else { gomu.queue.saveQueue(false) gomu.queue.clearQueue() gomu.queue.loadQueue() - tmpLoop := gomu.queue.isLoop - gomu.queue.isLoop = false - gomu.player.Skip() - gomu.queue.isLoop = tmpLoop } + gomu.queue.updateCurrentSong(node, newNode) return nil } From 7695bc6a7f4afdc7755adbb8fddd6a783211b652 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 6 Apr 2021 13:34:41 +0800 Subject: [PATCH 27/48] minor fix: updateCurrentSong check for situation of delete song and playlists --- playlist.go | 17 ++++------------- popup.go | 2 ++ queue.go | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/playlist.go b/playlist.go index d927350..c33993e 100644 --- a/playlist.go +++ b/playlist.go @@ -253,7 +253,7 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { return } - audioName := getName(audioFile.path) + // audioName := getName(audioFile.path) err := os.Remove(audioFile.path) if err != nil { @@ -269,15 +269,8 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { p.refresh() // Here we remove the song from queue - songPaths := gomu.queue.getItems() - if audioName == getName(gomu.player.GetCurrentSong().Name()) { - gomu.player.Skip() - } - for i, songPath := range songPaths { - if strings.Contains(songPath, audioName) { - gomu.queue.deleteItem(i) - } - } + gomu.queue.updateQueuePath() + gomu.queue.updateCurrentSong(audioFile, nil) } }) @@ -856,9 +849,7 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { return tracerr.Wrap(err) } } else { - gomu.queue.saveQueue(false) - gomu.queue.clearQueue() - gomu.queue.loadQueue() + gomu.queue.updateQueuePath() } gomu.queue.updateCurrentSong(node, newNode) diff --git a/popup.go b/popup.go index e2a4003..41e05b7 100644 --- a/popup.go +++ b/popup.go @@ -145,6 +145,8 @@ func confirmDeleteAllPopup(selPlaylist *tview.TreeNode) (err error) { if err != nil { errorPopup(err) } + gomu.queue.updateQueuePath() + gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil) } case tcell.KeyEscape: diff --git a/queue.go b/queue.go index 5df4aed..5001ddf 100644 --- a/queue.go +++ b/queue.go @@ -507,6 +507,7 @@ func (q *Queue) updateQueuePath() { q.enqueue(audioFile) } + q.updateTitle() } func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) { @@ -519,16 +520,48 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) { position := gomu.playingBar.getProgress() paused := gomu.player.IsPaused() - if oldAudio.name == currentSong.Name() { - gomu.queue.pushFront(newAudio) + if !oldAudio.isAudioFile { + //Here we check the situation when currentsong is under oldAudio folder + if strings.Contains(currentSong.Path(), oldAudio.path) { + + tmpLoop := q.isLoop + q.isLoop = false + gomu.player.Skip() + if paused { + gomu.player.TogglePause() + } + q.isLoop = tmpLoop + return + + } + } + + if oldAudio.name != currentSong.Name() { + return + } + + // if newAudio is empty, we simply skip current song + if newAudio == nil { tmpLoop := q.isLoop q.isLoop = false gomu.player.Skip() - gomu.player.Seek(position) if paused { gomu.player.TogglePause() } q.isLoop = tmpLoop + return } + // if newAudio is not empty, we insert it in the first of queue, then play it + gomu.queue.pushFront(newAudio) + tmpLoop := q.isLoop + q.isLoop = false + gomu.player.Skip() + gomu.player.Seek(position) + if paused { + gomu.player.TogglePause() + } + q.isLoop = tmpLoop + q.updateTitle() + } From 126f7f7820ca438ce918fcf61307e32a1a7f859b Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 6 Apr 2021 14:55:40 +0800 Subject: [PATCH 28/48] fix updateCurrentSong to handle delete --- gomu.go | 2 +- playlist.go | 8 ++++---- popup.go | 2 +- queue.go | 11 ++++------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/gomu.go b/gomu.go index 78607bd..25d40d9 100644 --- a/gomu.go +++ b/gomu.go @@ -140,7 +140,7 @@ func (g *Gomu) setUnfocusPanel(panel Panel) { func (g *Gomu) quit(args Args) error { if !*args.empty { - err := gomu.queue.saveQueue(true) + err := gomu.queue.saveQueue() if err != nil { return tracerr.Wrap(err) } diff --git a/playlist.go b/playlist.go index c33993e..9260240 100644 --- a/playlist.go +++ b/playlist.go @@ -270,7 +270,7 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { // Here we remove the song from queue gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSong(audioFile, nil) + gomu.queue.updateCurrentSong(audioFile, nil, true) } }) @@ -809,7 +809,7 @@ func (p *Playlist) paste() error { newAudio := oldAudio newAudio.path = newPathFull - gomu.queue.updateCurrentSong(oldAudio, newAudio) + gomu.queue.updateCurrentSong(oldAudio, newAudio, false) p.yankFile = nil @@ -844,14 +844,14 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { // update queue newNode := p.getCurrentFile() if node.isAudioFile { - err := gomu.queue.rename(node, newNode) + err := gomu.queue.renameItem(node, newNode) if err != nil { return tracerr.Wrap(err) } } else { gomu.queue.updateQueuePath() } - gomu.queue.updateCurrentSong(node, newNode) + gomu.queue.updateCurrentSong(node, newNode, false) return nil } diff --git a/popup.go b/popup.go index 41e05b7..298e41e 100644 --- a/popup.go +++ b/popup.go @@ -146,7 +146,7 @@ func confirmDeleteAllPopup(selPlaylist *tview.TreeNode) (err error) { errorPopup(err) } gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil) + gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil, true) } case tcell.KeyEscape: diff --git a/queue.go b/queue.go index 5001ddf..1fd249e 100644 --- a/queue.go +++ b/queue.go @@ -193,13 +193,12 @@ func (q *Queue) getItems() []string { } // Save the current queue -func (q *Queue) saveQueue(isQuit bool) error { +func (q *Queue) saveQueue() error { songPaths := q.getItems() var content strings.Builder if gomu.player.HasInit() && gomu.player.GetCurrentSong() != nil { - // if gomu.player.HasInit() && isQuit && gomu.player.GetCurrentSong() != nil { currentSongPath := gomu.player.GetCurrentSong().Path() currentSongInQueue := false for _, songPath := range songPaths { @@ -414,7 +413,7 @@ func sha1Hex(input string) string { } // Modify the title of songs in queue -func (q *Queue) rename(oldAudio *AudioFile, newAudio *AudioFile) error { +func (q *Queue) renameItem(oldAudio *AudioFile, newAudio *AudioFile) error { for i, v := range q.items { if v.name != oldAudio.name { continue @@ -510,7 +509,7 @@ func (q *Queue) updateQueuePath() { q.updateTitle() } -func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) { +func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDelete bool) { if !gomu.player.IsRunning() && !gomu.player.IsPaused() { return @@ -520,10 +519,9 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) { position := gomu.playingBar.getProgress() paused := gomu.player.IsPaused() - if !oldAudio.isAudioFile { + if !oldAudio.isAudioFile && isDelete { //Here we check the situation when currentsong is under oldAudio folder if strings.Contains(currentSong.Path(), oldAudio.path) { - tmpLoop := q.isLoop q.isLoop = false gomu.player.Skip() @@ -532,7 +530,6 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile) { } q.isLoop = tmpLoop return - } } From a7ba78fed26320f0a6b55d45f5e0bd878e21bbfc Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 6 Apr 2021 17:18:11 +0800 Subject: [PATCH 29/48] Minor Fix: updateCurrentSong handles folder rename and paste --- playlist.go | 12 +++++++++--- popup.go | 4 ++-- queue.go | 51 +++++++++++++++++++++++++++++++++++++-------------- tageditor.go | 4 ++-- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/playlist.go b/playlist.go index 9260240..a7a4fbc 100644 --- a/playlist.go +++ b/playlist.go @@ -809,7 +809,10 @@ func (p *Playlist) paste() error { newAudio := oldAudio newAudio.path = newPathFull - gomu.queue.updateCurrentSong(oldAudio, newAudio, false) + err = gomu.queue.updateCurrentSong(oldAudio, newAudio, false) + if err != nil { + return tracerr.Wrap(err) + } p.yankFile = nil @@ -832,7 +835,7 @@ func setDisplayText(audioFile *AudioFile) string { } // refreshByNode is called after rename of file or folder, to refresh queue info -func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { +func (p *Playlist) refreshAfterRename(node *AudioFile, newName string) error { root := p.GetRoot() root.Walk(func(node, _ *tview.TreeNode) bool { @@ -851,7 +854,10 @@ func (p *Playlist) refreshByNode(node *AudioFile, newName string) error { } else { gomu.queue.updateQueuePath() } - gomu.queue.updateCurrentSong(node, newNode, false) + err := gomu.queue.updateCurrentSong(node, newNode, false) + if err != nil { + return tracerr.Wrap(err) + } return nil } diff --git a/popup.go b/popup.go index 298e41e..98168f4 100644 --- a/popup.go +++ b/popup.go @@ -146,7 +146,7 @@ func confirmDeleteAllPopup(selPlaylist *tview.TreeNode) (err error) { errorPopup(err) } gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil, true) + err = gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil, true) } case tcell.KeyEscape: @@ -566,7 +566,7 @@ func renamePopup(node *AudioFile) { gomu.setFocusPanel(gomu.playlist) gomu.prevPanel = gomu.playlist - err = gomu.playlist.refreshByNode(node, newName) + err = gomu.playlist.refreshAfterRename(node, newName) if err != nil { errorPopup(err) } diff --git a/queue.go b/queue.go index 1fd249e..5cc3d66 100644 --- a/queue.go +++ b/queue.go @@ -509,32 +509,54 @@ func (q *Queue) updateQueuePath() { q.updateTitle() } -func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDelete bool) { +// update current playing song info to reflect the changes during rename and paste +func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDelete bool) error { if !gomu.player.IsRunning() && !gomu.player.IsPaused() { - return + return nil } currentSong := gomu.player.GetCurrentSong() position := gomu.playingBar.getProgress() paused := gomu.player.IsPaused() - if !oldAudio.isAudioFile && isDelete { - //Here we check the situation when currentsong is under oldAudio folder - if strings.Contains(currentSong.Path(), oldAudio.path) { - tmpLoop := q.isLoop - q.isLoop = false - gomu.player.Skip() - if paused { - gomu.player.TogglePause() + if !oldAudio.isAudioFile { + // Here we check the situation when currentsong is under oldAudio folder + if strings.Contains(currentSong.Path(), oldAudio.path) || strings.Contains(currentSong.Path(), oldAudio.name) { + if isDelete { + tmpLoop := q.isLoop + q.isLoop = false + gomu.player.Skip() + if paused { + gomu.player.TogglePause() + } + q.isLoop = tmpLoop + return nil + } else { + // Here is the handling of folder rename and paste + currentSong, err := gomu.playlist.findAudioFile(sha1Hex(getName(currentSong.Name()))) + if err != nil { + return tracerr.Wrap(err) + } + gomu.queue.pushFront(currentSong) + tmpLoop := q.isLoop + q.isLoop = false + gomu.player.Skip() + gomu.player.Seek(position) + if paused { + gomu.player.TogglePause() + } + q.isLoop = tmpLoop + q.updateTitle() + + return nil } - q.isLoop = tmpLoop - return + } } if oldAudio.name != currentSong.Name() { - return + return nil } // if newAudio is empty, we simply skip current song @@ -546,7 +568,7 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDe gomu.player.TogglePause() } q.isLoop = tmpLoop - return + return nil } // if newAudio is not empty, we insert it in the first of queue, then play it @@ -561,4 +583,5 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDe q.isLoop = tmpLoop q.updateTitle() + return nil } diff --git a/tageditor.go b/tageditor.go index 0ad8cb6..f547207 100644 --- a/tageditor.go +++ b/tageditor.go @@ -130,7 +130,7 @@ func tagPopup(node *AudioFile) (err error) { leftBox.SetTitle(newName) // update queue - err = gomu.playlist.refreshByNode(node, newName) + err = gomu.playlist.refreshAfterRename(node, newName) if err != nil { errorPopup(err) return @@ -178,7 +178,7 @@ func tagPopup(node *AudioFile) (err error) { leftBox.SetTitle(newName) // update queue - err = gomu.playlist.refreshByNode(node, newName) + err = gomu.playlist.refreshAfterRename(node, newName) if err != nil { errorPopup(err) return From d5ee151c348ac485a5c66642b250c70c3133b961 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 6 Apr 2021 23:32:40 +0800 Subject: [PATCH 30/48] minor fix updatecurrentSong --- popup.go | 2 +- queue.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/popup.go b/popup.go index 98168f4..92574ee 100644 --- a/popup.go +++ b/popup.go @@ -146,7 +146,7 @@ func confirmDeleteAllPopup(selPlaylist *tview.TreeNode) (err error) { errorPopup(err) } gomu.queue.updateQueuePath() - err = gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil, true) + gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil, true) } case tcell.KeyEscape: diff --git a/queue.go b/queue.go index 5cc3d66..6fde674 100644 --- a/queue.go +++ b/queue.go @@ -522,7 +522,8 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDe if !oldAudio.isAudioFile { // Here we check the situation when currentsong is under oldAudio folder - if strings.Contains(currentSong.Path(), oldAudio.path) || strings.Contains(currentSong.Path(), oldAudio.name) { + // if strings.Contains(currentSong.Path(), oldAudio.path) || strings.Contains(currentSong.Path(), oldAudio.name) { + if strings.Contains(currentSong.Path(), oldAudio.path) { if isDelete { tmpLoop := q.isLoop q.isLoop = false @@ -531,6 +532,8 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDe gomu.player.TogglePause() } q.isLoop = tmpLoop + q.updateTitle() + return nil } else { // Here is the handling of folder rename and paste From a30e18c7878865a4755eded8ede43d3bf3cc273d Mon Sep 17 00:00:00 2001 From: tramhao Date: Wed, 7 Apr 2021 11:52:13 +0800 Subject: [PATCH 31/48] Refactor updateCurrentSong into 3 different functions and now it's more clear --- playlist.go | 33 +++++++++----- popup.go | 5 +- queue.go | 128 ++++++++++++++++++++++++++++++---------------------- 3 files changed, 99 insertions(+), 67 deletions(-) diff --git a/playlist.go b/playlist.go index a7a4fbc..5c3526f 100644 --- a/playlist.go +++ b/playlist.go @@ -270,7 +270,7 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { // Here we remove the song from queue gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSong(audioFile, nil, true) + gomu.queue.updateCurrentSongDelete(audioFile) } }) @@ -804,14 +804,21 @@ func (p *Playlist) paste() error { defaultTimedPopup(" Success ", p.yankFile.name+"\n has been pasted to\n"+newPathDir) // keep queue references updated - p.refresh() - gomu.queue.updateQueuePath() - newAudio := oldAudio newAudio.path = newPathFull - err = gomu.queue.updateCurrentSong(oldAudio, newAudio, false) - if err != nil { - return tracerr.Wrap(err) + + p.refresh() + gomu.queue.updateQueuePath() + if p.yankFile.isAudioFile { + err = gomu.queue.updateCurrentSongName(oldAudio, newAudio) + if err != nil { + return tracerr.Wrap(err) + } + } else { + err = gomu.queue.updateCurrentSongPath(oldAudio, newAudio) + if err != nil { + return tracerr.Wrap(err) + } } p.yankFile = nil @@ -851,12 +858,16 @@ func (p *Playlist) refreshAfterRename(node *AudioFile, newName string) error { if err != nil { return tracerr.Wrap(err) } + err = gomu.queue.updateCurrentSongName(node, newNode) + if err != nil { + return tracerr.Wrap(err) + } } else { gomu.queue.updateQueuePath() - } - err := gomu.queue.updateCurrentSong(node, newNode, false) - if err != nil { - return tracerr.Wrap(err) + err := gomu.queue.updateCurrentSongPath(node, newNode) + if err != nil { + return tracerr.Wrap(err) + } } return nil diff --git a/popup.go b/popup.go index 92574ee..0942d9d 100644 --- a/popup.go +++ b/popup.go @@ -141,12 +141,13 @@ func confirmDeleteAllPopup(selPlaylist *tview.TreeNode) (err error) { gomu.popups.pop() if confirmationText == "DELETE" { - err = gomu.playlist.deletePlaylist(selPlaylist.GetReference().(*AudioFile)) + audioFile := selPlaylist.GetReference().(*AudioFile) + err = gomu.playlist.deletePlaylist(audioFile) if err != nil { errorPopup(err) } gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSong(selPlaylist.GetReference().(*AudioFile), nil, true) + gomu.queue.updateCurrentSongDelete(audioFile) } case tcell.KeyEscape: diff --git a/queue.go b/queue.go index 6fde674..d5306d3 100644 --- a/queue.go +++ b/queue.go @@ -500,7 +500,6 @@ func (q *Queue) updateQueuePath() { audioFile, err := gomu.playlist.findAudioFile(v) if err != nil { - logError(err) continue } q.enqueue(audioFile) @@ -509,8 +508,8 @@ func (q *Queue) updateQueuePath() { q.updateTitle() } -// update current playing song info to reflect the changes during rename and paste -func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDelete bool) error { +// update current playing song name to reflect the changes during rename and paste +func (q *Queue) updateCurrentSongName(oldAudio *AudioFile, newAudio *AudioFile) error { if !gomu.player.IsRunning() && !gomu.player.IsPaused() { return nil @@ -520,61 +519,11 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDe position := gomu.playingBar.getProgress() paused := gomu.player.IsPaused() - if !oldAudio.isAudioFile { - // Here we check the situation when currentsong is under oldAudio folder - // if strings.Contains(currentSong.Path(), oldAudio.path) || strings.Contains(currentSong.Path(), oldAudio.name) { - if strings.Contains(currentSong.Path(), oldAudio.path) { - if isDelete { - tmpLoop := q.isLoop - q.isLoop = false - gomu.player.Skip() - if paused { - gomu.player.TogglePause() - } - q.isLoop = tmpLoop - q.updateTitle() - - return nil - } else { - // Here is the handling of folder rename and paste - currentSong, err := gomu.playlist.findAudioFile(sha1Hex(getName(currentSong.Name()))) - if err != nil { - return tracerr.Wrap(err) - } - gomu.queue.pushFront(currentSong) - tmpLoop := q.isLoop - q.isLoop = false - gomu.player.Skip() - gomu.player.Seek(position) - if paused { - gomu.player.TogglePause() - } - q.isLoop = tmpLoop - q.updateTitle() - - return nil - } - - } - } - if oldAudio.name != currentSong.Name() { return nil } - // if newAudio is empty, we simply skip current song - if newAudio == nil { - tmpLoop := q.isLoop - q.isLoop = false - gomu.player.Skip() - if paused { - gomu.player.TogglePause() - } - q.isLoop = tmpLoop - return nil - } - - // if newAudio is not empty, we insert it in the first of queue, then play it + // we insert it in the first of queue, then play it gomu.queue.pushFront(newAudio) tmpLoop := q.isLoop q.isLoop = false @@ -588,3 +537,74 @@ func (q *Queue) updateCurrentSong(oldAudio *AudioFile, newAudio *AudioFile, isDe return nil } + +// update current playing song path to reflect the changes during rename and paste +func (q *Queue) updateCurrentSongPath(oldAudio *AudioFile, newAudio *AudioFile) error { + + if !gomu.player.IsRunning() && !gomu.player.IsPaused() { + return nil + } + + currentSong := gomu.player.GetCurrentSong() + position := gomu.playingBar.getProgress() + paused := gomu.player.IsPaused() + + // Here we check the situation when currentsong is under oldAudio folder + if !strings.Contains(currentSong.Path(), oldAudio.path) { + return nil + } + + // Here is the handling of folder rename and paste + currentSongAudioFile, err := gomu.playlist.findAudioFile(sha1Hex(getName(currentSong.Name()))) + if err != nil { + return tracerr.Wrap(err) + } + gomu.queue.pushFront(currentSongAudioFile) + tmpLoop := q.isLoop + q.isLoop = false + gomu.player.Skip() + gomu.player.Seek(position) + if paused { + gomu.player.TogglePause() + } + q.isLoop = tmpLoop + + q.updateTitle() + return nil + +} + +// update current playing song simply delete it +func (q *Queue) updateCurrentSongDelete(oldAudio *AudioFile) { + if !gomu.player.IsRunning() && !gomu.player.IsPaused() { + return + } + + currentSong := gomu.player.GetCurrentSong() + paused := gomu.player.IsPaused() + + var delete bool + if oldAudio.isAudioFile { + if oldAudio.name == currentSong.Name() { + delete = true + } + } else { + if strings.Contains(currentSong.Path(), oldAudio.path) { + delete = true + } + } + + if !delete { + return + } + + tmpLoop := q.isLoop + q.isLoop = false + gomu.player.Skip() + if paused { + gomu.player.TogglePause() + } + q.isLoop = tmpLoop + q.updateTitle() + +} From c916a2f9149d3d3c19d7ef2b89ac27bc74e5e756 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 02:24:52 +0800 Subject: [PATCH 32/48] minor fix: move to next node before delete file and folder, not to go back root every time --- playlist.go | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/playlist.go b/playlist.go index 5c3526f..61c0293 100644 --- a/playlist.go +++ b/playlist.go @@ -253,34 +253,35 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { return } - // audioName := getName(audioFile.path) + // hehe we need to move focus to next node before delete it + p.InputHandler()(tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone), nil) + err := os.Remove(audioFile.path) - if err != nil { - - defaultTimedPopup(" Error ", "Unable to delete "+audioFile.name) - - err = tracerr.Wrap(err) - + errorPopup(err) } else { - defaultTimedPopup(" Success ", audioFile.name+"\nhas been deleted successfully") - p.refresh() - - // Here we remove the song from queue - gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSongDelete(audioFile) + go gomu.app.QueueUpdateDraw(func() { + p.refresh() + // Here we remove the song from queue + gomu.queue.updateQueuePath() + gomu.queue.updateCurrentSongDelete(audioFile) + }) } }) - return nil + return err } // Deletes playlist/dir from filesystem func (p *Playlist) deletePlaylist(audioFile *AudioFile) (err error) { + // here we close the node and then move to next folder before delete + p.InputHandler()(tcell.NewEventKey(tcell.KeyRune, 'h', tcell.ModNone), nil) + p.InputHandler()(tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone), nil) + err = os.RemoveAll(audioFile.path) if err != nil { err = tracerr.Wrap(err) @@ -288,7 +289,13 @@ func (p *Playlist) deletePlaylist(audioFile *AudioFile) (err error) { defaultTimedPopup( " Success ", audioFile.name+"\nhas been deleted successfully") - p.refresh() + go gomu.app.QueueUpdateDraw(func() { + p.refresh() + // Here we remove the song from queue + gomu.queue.updateQueuePath() + gomu.queue.updateCurrentSongDelete(audioFile) + + }) } return err From f229a8b97ac43fdd65df1d8611f0dacc50c851ec Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 02:36:47 +0800 Subject: [PATCH 33/48] minor fix:1.deleteitem in queue move more natural. 2. Add queue to the first item so that it can be played with skip --- command.go | 2 +- queue.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 36e6102..630a730 100644 --- a/command.go +++ b/command.go @@ -94,7 +94,7 @@ func (c Command) defineCommands() { audioFile := gomu.playlist.getCurrentFile() currNode := gomu.playlist.GetCurrentNode() if audioFile.isAudioFile { - gomu.queue.enqueue(audioFile) + gomu.queue.pushFront(audioFile) if len(gomu.queue.items) == 1 && !gomu.player.IsRunning() { err := gomu.queue.playQueue() if err != nil { diff --git a/queue.go b/queue.go index d5306d3..624ca34 100644 --- a/queue.go +++ b/queue.go @@ -70,6 +70,10 @@ func (q *Queue) deleteItem(index int) (*AudioFile, error) { } q.items = nItems + // here we move to next item if not at the end + if index < len(q.items) { + q.next() + } q.updateTitle() } From 5a3f418a2cea793de3623aad142d97ef198dea54 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 03:04:39 +0800 Subject: [PATCH 34/48] error handling for deleteSong and deletePlaylist --- command.go | 6 ++---- playlist.go | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/command.go b/command.go index 630a730..cccd917 100644 --- a/command.go +++ b/command.go @@ -62,10 +62,8 @@ func (c Command) defineCommands() { return } - err := gomu.playlist.deleteSong(audioFile) - if err != nil { - logError(err) - } + gomu.playlist.deleteSong(audioFile) + }) c.define("youtube_search", func() { diff --git a/playlist.go b/playlist.go index 61c0293..eca12de 100644 --- a/playlist.go +++ b/playlist.go @@ -244,7 +244,7 @@ func (p Playlist) getCurrentFile() *AudioFile { } // Deletes song from filesystem -func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { +func (p *Playlist) deleteSong(audioFile *AudioFile) { confirmationPopup( "Are you sure to delete this audio file?", func(_ int, buttonName string) { @@ -259,20 +259,20 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) (err error) { err := os.Remove(audioFile.path) if err != nil { errorPopup(err) - } else { - defaultTimedPopup(" Success ", - audioFile.name+"\nhas been deleted successfully") - go gomu.app.QueueUpdateDraw(func() { - p.refresh() - // Here we remove the song from queue - gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSongDelete(audioFile) - }) + return } + defaultTimedPopup(" Success ", + audioFile.name+"\nhas been deleted successfully") + go gomu.app.QueueUpdateDraw(func() { + p.refresh() + // Here we remove the song from queue + gomu.queue.updateQueuePath() + gomu.queue.updateCurrentSongDelete(audioFile) + }) + }) - return err } // Deletes playlist/dir from filesystem @@ -284,21 +284,21 @@ func (p *Playlist) deletePlaylist(audioFile *AudioFile) (err error) { err = os.RemoveAll(audioFile.path) if err != nil { - err = tracerr.Wrap(err) - } else { - defaultTimedPopup( - " Success ", - audioFile.name+"\nhas been deleted successfully") - go gomu.app.QueueUpdateDraw(func() { - p.refresh() - // Here we remove the song from queue - gomu.queue.updateQueuePath() - gomu.queue.updateCurrentSongDelete(audioFile) - - }) + return tracerr.Wrap(err) } - return err + defaultTimedPopup( + " Success ", + audioFile.name+"\nhas been deleted successfully") + go gomu.app.QueueUpdateDraw(func() { + p.refresh() + // Here we remove the song from queue + gomu.queue.updateQueuePath() + gomu.queue.updateCurrentSongDelete(audioFile) + + }) + + return nil } // Bulk add a playlist to queue From 904c89562e7ca2d2d841c8c4a04cec18ea78863d Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 12:05:48 +0800 Subject: [PATCH 35/48] testing ueberzug --- go.mod | 2 ++ go.sum | 11 +++++++++++ playingbar.go | 54 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 32f7520..b27e423 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/PuerkitoBio/goquery v1.6.1 // indirect github.com/antchfx/htmlquery v1.2.3 // indirect github.com/antchfx/xmlquery v1.3.4 // indirect + github.com/bogem/id3v2 v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/faiface/beep v1.0.2 github.com/gdamore/tcell/v2 v2.2.0 @@ -26,6 +27,7 @@ require ( github.com/tj/go-spin v1.1.0 github.com/tramhao/id3v2 v1.2.1 github.com/ztrue/tracerr v0.3.0 + gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06 golang.org/x/exp v0.0.0-20201229011636-eab1b5eb1a03 // indirect golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect diff --git a/go.sum b/go.sum index f315220..1d48838 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,12 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +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/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk= github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= @@ -66,6 +73,8 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mewkiz/flac v1.0.5/go.mod h1:EHZNU32dMF6alpurYyKHDLYpW1lYpBZ5WrXi/VuNIGs= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -92,6 +101,8 @@ github.com/tramhao/id3v2 v1.2.1 h1:h8tXj1ReHoGwIIZEDp+fiDhGhf2wpCyrxEKLKVmhQw8= github.com/tramhao/id3v2 v1.2.1/go.mod h1:4jmC9bwoDhtGTsDkEBwSUlUgJq/D+8w4626jvM1Oo1k= github.com/ztrue/tracerr v0.3.0 h1:lDi6EgEYhPYPnKcjsYzmWw4EkFEoA/gfe+I9Y5f+h6Y= github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= +gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06 h1:lGu8YGHgq9ABb00JDQewrqhKIvku+/1uFsnq/QUeiU8= +gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06/go.mod h1:UKSsoWKXcGQXxWYkXFB4z/UqJtIF3pZT6fHm6beLPKM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/playingbar.go b/playingbar.go index 45ef756..d051a46 100644 --- a/playingbar.go +++ b/playingbar.go @@ -5,6 +5,10 @@ package main import ( "errors" "fmt" + "image" + "io/ioutil" + "log" + "os" "strconv" "strings" "sync/atomic" @@ -13,6 +17,7 @@ import ( "github.com/rivo/tview" "github.com/tramhao/id3v2" "github.com/ztrue/tracerr" + ugo "gitlab.com/diamondburned/ueberzug-go" "github.com/issadarkthing/gomu/lyric" ) @@ -20,15 +25,16 @@ import ( // PlayingBar shows song name, progress and lyric type PlayingBar struct { *tview.Frame - full int64 - update chan struct{} - progress int64 - skip bool - text *tview.TextView - hasTag bool - tag *id3v2.Tag - subtitle *lyric.Lyric - subtitles []*lyric.Lyric + full int64 + update chan struct{} + progress int64 + skip bool + text *tview.TextView + hasTag bool + tag *id3v2.Tag + subtitle *lyric.Lyric + subtitles []*lyric.Lyric + albumPhoto []byte } func (p *PlayingBar) help() []string { @@ -139,6 +145,20 @@ func (p *PlayingBar) setSongTitle(title string) { p.Clear() titleColor := gomu.colors.title p.AddText(title, true, tview.AlignCenter, titleColor) + reader, err := os.Open("/home/tramhao/.local/src/gomu/1.jpg") + if err != nil { + log.Fatal(err) + } + defer reader.Close() + img1, _, _ := image.Decode(reader) + _, err = ugo.NewImage(img1, 0, 0) + if err != nil { + log.Fatal(err) + } + + // defer i.Clear() + // defer i.Destroy() + } // Resets progress bar, ready for execution @@ -306,6 +326,22 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { } } + pictures := tag.GetFrames(tag.CommonID("Attached picture")) + for _, f := range pictures { + pic, ok := f.(id3v2.PictureFrame) + if !ok { + return errors.New("picture frame error") + } + + // Do something with picture frame. + fmt.Println(pic.Description) + p.albumPhoto = pic.Picture + } + + err = ioutil.WriteFile("/home/tramhao/.local/src/gomu/1.jpg", p.albumPhoto, 0644) + if err != nil { + return tracerr.Wrap(err) + } return nil } From 34ec606f819cbf212e8067746481c504723a555b Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 15:14:37 +0800 Subject: [PATCH 36/48] show photo of embeded --- go.mod | 1 + go.sum | 3 +++ playingbar.go | 48 ++++++++++++++++++++++++------------------------ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index b27e423..0231dbf 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/antchfx/xmlquery v1.3.4 // indirect github.com/bogem/id3v2 v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/disintegration/imaging v1.6.2 github.com/faiface/beep v1.0.2 github.com/gdamore/tcell/v2 v2.2.0 github.com/gobwas/glob v0.2.3 // indirect diff --git a/go.sum b/go.sum index 1d48838..980800d 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/bogem/id3v2 v1.2.0/go.mod h1:t78PK5AQ56Q47kizpYiV6gtjj3jfxlz87oFpty8D github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/faiface/beep v1.0.2 h1:UB5DiRNmA4erfUYnHbgU4UB6DlBOrsdEFRtcc8sCkdQ= github.com/faiface/beep v1.0.2/go.mod h1:1yLb5yRdHMsovYYWVqYLioXkVuziCSITW1oarTeduQM= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= @@ -115,6 +117,7 @@ golang.org/x/exp v0.0.0-20201229011636-eab1b5eb1a03/go.mod h1:I6l2HNBLBZEcrOoCpy golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20180806140643-507816974b79/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= diff --git a/playingbar.go b/playingbar.go index d051a46..d9716e1 100644 --- a/playingbar.go +++ b/playingbar.go @@ -3,17 +3,16 @@ package main import ( + "bytes" "errors" "fmt" - "image" - "io/ioutil" "log" - "os" "strconv" "strings" "sync/atomic" "time" + "github.com/disintegration/imaging" "github.com/rivo/tview" "github.com/tramhao/id3v2" "github.com/ztrue/tracerr" @@ -34,7 +33,7 @@ type PlayingBar struct { tag *id3v2.Tag subtitle *lyric.Lyric subtitles []*lyric.Lyric - albumPhoto []byte + albumPhoto *ugo.Image } func (p *PlayingBar) help() []string { @@ -145,19 +144,6 @@ func (p *PlayingBar) setSongTitle(title string) { p.Clear() titleColor := gomu.colors.title p.AddText(title, true, tview.AlignCenter, titleColor) - reader, err := os.Open("/home/tramhao/.local/src/gomu/1.jpg") - if err != nil { - log.Fatal(err) - } - defer reader.Close() - img1, _, _ := image.Decode(reader) - _, err = ugo.NewImage(img1, 0, 0) - if err != nil { - log.Fatal(err) - } - - // defer i.Clear() - // defer i.Destroy() } @@ -165,11 +151,16 @@ func (p *PlayingBar) setSongTitle(title string) { func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) { p.setFull(full) p.setProgress(0) - p.setSongTitle(currentSong.name) p.hasTag = false p.tag = nil p.subtitles = nil p.subtitle = nil + if p.albumPhoto != nil { + p.albumPhoto.Clear() + p.albumPhoto.Destroy() + ugo.Close() + } + p.albumPhoto = nil err := p.loadLyrics(currentSong.path) if err != nil { @@ -204,6 +195,8 @@ func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) { p.subtitle = p.subtitles[0] } } + p.setSongTitle(currentSong.name) + } // Sets default title and progress bar @@ -334,14 +327,21 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { } // Do something with picture frame. - fmt.Println(pic.Description) - p.albumPhoto = pic.Picture + img1, err := imaging.Decode(bytes.NewReader(pic.Picture)) + if err != nil { + return tracerr.Wrap(err) + } + dstImage128 := imaging.Fit(img1, 128, 128, imaging.Lanczos) + + go gomu.app.QueueUpdateDraw(func() { + x, y, _, _ := p.GetInnerRect() + _, err := ugo.NewImage(dstImage128, x*16, y*31) + if err != nil { + log.Fatal(err) + } + }) } - err = ioutil.WriteFile("/home/tramhao/.local/src/gomu/1.jpg", p.albumPhoto, 0644) - if err != nil { - return tracerr.Wrap(err) - } return nil } From 4313d904da8aa2a9d55adb3ea564affa137e6014 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 15:21:09 +0800 Subject: [PATCH 37/48] don't quit when error happens --- playingbar.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/playingbar.go b/playingbar.go index d9716e1..d0454fa 100644 --- a/playingbar.go +++ b/playingbar.go @@ -6,7 +6,6 @@ import ( "bytes" "errors" "fmt" - "log" "strconv" "strings" "sync/atomic" @@ -337,7 +336,7 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { x, y, _, _ := p.GetInnerRect() _, err := ugo.NewImage(dstImage128, x*16, y*31) if err != nil { - log.Fatal(err) + logError(err) } }) } From f8c1d36c3e226ce8bd138703e145c47bf12e06e2 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 17:13:51 +0800 Subject: [PATCH 38/48] not fully fixed: position of ueberzug window --- go.mod | 1 + go.sum | 1 + playingbar.go | 17 +++++++++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 0231dbf..c563d0e 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/tramhao/id3v2 v1.2.1 github.com/ztrue/tracerr v0.3.0 gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/exp v0.0.0-20201229011636-eab1b5eb1a03 // indirect golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect diff --git a/go.sum b/go.sum index 980800d..69861a4 100644 --- a/go.sum +++ b/go.sum @@ -108,6 +108,7 @@ gitlab.com/diamondburned/ueberzug-go v0.0.0-20190521043425-7c15a5f63b06/go.mod h golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180710024300-14dda7b62fcd/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/playingbar.go b/playingbar.go index d0454fa..21f72a0 100644 --- a/playingbar.go +++ b/playingbar.go @@ -6,6 +6,7 @@ import ( "bytes" "errors" "fmt" + "os" "strconv" "strings" "sync/atomic" @@ -16,6 +17,7 @@ import ( "github.com/tramhao/id3v2" "github.com/ztrue/tracerr" ugo "gitlab.com/diamondburned/ueberzug-go" + "golang.org/x/crypto/ssh/terminal" "github.com/issadarkthing/gomu/lyric" ) @@ -157,9 +159,8 @@ func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) { if p.albumPhoto != nil { p.albumPhoto.Clear() p.albumPhoto.Destroy() - ugo.Close() + p.albumPhoto = nil } - p.albumPhoto = nil err := p.loadLyrics(currentSong.path) if err != nil { @@ -334,10 +335,18 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { go gomu.app.QueueUpdateDraw(func() { x, y, _, _ := p.GetInnerRect() - _, err := ugo.NewImage(dstImage128, x*16, y*31) + width, height, err := terminal.GetSize(int(os.Stdin.Fd())) if err != nil { - logError(err) + errorPopup(err) } + + windowWidth := 1920 + windowHeight := 1200 + p.albumPhoto, err = ugo.NewImage(dstImage128, x*windowWidth/width, y*windowHeight/height) + if err != nil { + errorPopup(err) + } + p.albumPhoto.Show() }) } From ca5051fa13fb4631a76fda9a72af6274e8a66d2d Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 22:54:22 +0800 Subject: [PATCH 39/48] Fixed:position of photo window --- playingbar.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/playingbar.go b/playingbar.go index 21f72a0..0106c70 100644 --- a/playingbar.go +++ b/playingbar.go @@ -3,10 +3,10 @@ package main import ( + "C" "bytes" "errors" "fmt" - "os" "strconv" "strings" "sync/atomic" @@ -17,10 +17,13 @@ import ( "github.com/tramhao/id3v2" "github.com/ztrue/tracerr" ugo "gitlab.com/diamondburned/ueberzug-go" - "golang.org/x/crypto/ssh/terminal" "github.com/issadarkthing/gomu/lyric" ) +import ( + "syscall" + "unsafe" +) // PlayingBar shows song name, progress and lyric type PlayingBar struct { @@ -335,14 +338,12 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { go gomu.app.QueueUpdateDraw(func() { x, y, _, _ := p.GetInnerRect() - width, height, err := terminal.GetSize(int(os.Stdin.Fd())) + width, height, windowWidth, windowHeight := getConsoleSize() if err != nil { errorPopup(err) } - windowWidth := 1920 - windowHeight := 1200 - p.albumPhoto, err = ugo.NewImage(dstImage128, x*windowWidth/width, y*windowHeight/height) + p.albumPhoto, err = ugo.NewImage(dstImage128, (x+3)*windowWidth/width, (y+2)*windowHeight/height) if err != nil { errorPopup(err) } @@ -368,3 +369,15 @@ func (p *PlayingBar) getFull() int { func (p *PlayingBar) setFull(full int) { atomic.StoreInt64(&p.full, int64(full)) } + +func getConsoleSize() (int, int, int, int) { + var sz struct { + rows uint16 + cols uint16 + xpixels uint16 + ypixels uint16 + } + _, _, _ = syscall.Syscall(syscall.SYS_IOCTL, + uintptr(syscall.Stdout), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz))) + return int(sz.cols), int(sz.rows), int(sz.xpixels), int(sz.ypixels) +} From ae123b4db19b2fb088bb0e4b709c713ec5728ee9 Mon Sep 17 00:00:00 2001 From: tramhao Date: Thu, 8 Apr 2021 23:11:37 +0800 Subject: [PATCH 40/48] fix: playing song is saved when queue is empty --- playingbar.go | 3 +++ queue.go | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/playingbar.go b/playingbar.go index 0106c70..6461f9d 100644 --- a/playingbar.go +++ b/playingbar.go @@ -210,6 +210,9 @@ func (p *PlayingBar) setDefault() { "%s ┣%s┫ %s", "00:00", strings.Repeat("━", width/2), "00:00", ) p.text.SetText(text) + if p.albumPhoto != nil { + p.albumPhoto.Clear() + } } // Skips the current playing song diff --git a/queue.go b/queue.go index 624ca34..d75db00 100644 --- a/queue.go +++ b/queue.go @@ -210,8 +210,7 @@ func (q *Queue) saveQueue() error { currentSongInQueue = true } } - if !currentSongInQueue { - // if !currentSongInQueue && len(q.items) != 0 { + if !currentSongInQueue && len(q.items) != 0 { hashed := sha1Hex(getName(currentSongPath)) content.WriteString(hashed + "\n") } From 037aacdd486e28db6b62e4eb5afce51a553b0e7e Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 9 Apr 2021 10:33:29 +0800 Subject: [PATCH 41/48] remove unnecessary import C --- playingbar.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/playingbar.go b/playingbar.go index 6461f9d..7554fa9 100644 --- a/playingbar.go +++ b/playingbar.go @@ -3,7 +3,6 @@ package main import ( - "C" "bytes" "errors" "fmt" @@ -18,11 +17,10 @@ import ( "github.com/ztrue/tracerr" ugo "gitlab.com/diamondburned/ueberzug-go" - "github.com/issadarkthing/gomu/lyric" -) -import ( "syscall" "unsafe" + + "github.com/issadarkthing/gomu/lyric" ) // PlayingBar shows song name, progress and lyric From 17b91bd9ff1e56f6dac77e4696a7def09e8051e2 Mon Sep 17 00:00:00 2001 From: tramhao Date: Fri, 9 Apr 2021 10:42:45 +0800 Subject: [PATCH 42/48] adjust order of imports in playingbar.go --- playingbar.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/playingbar.go b/playingbar.go index 7554fa9..c14862e 100644 --- a/playingbar.go +++ b/playingbar.go @@ -9,7 +9,9 @@ import ( "strconv" "strings" "sync/atomic" + "syscall" "time" + "unsafe" "github.com/disintegration/imaging" "github.com/rivo/tview" @@ -17,9 +19,6 @@ import ( "github.com/ztrue/tracerr" ugo "gitlab.com/diamondburned/ueberzug-go" - "syscall" - "unsafe" - "github.com/issadarkthing/gomu/lyric" ) From fca438aa326372898a6dceb6b1ff4486817a1065 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 13 Apr 2021 13:22:17 +0800 Subject: [PATCH 43/48] 1. Fix: lyric negative offset means timestamp add. 2. Refactor: add lyric.GetText() and hide details from gomu --- lyric/lrc.go | 34 ++++++++++++++++++++++++++++++---- playingbar.go | 23 ++++------------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/lyric/lrc.go b/lyric/lrc.go index cfb80f0..0990dcd 100644 --- a/lyric/lrc.go +++ b/lyric/lrc.go @@ -16,6 +16,7 @@ package lyric import ( + "errors" "fmt" "regexp" "runtime" @@ -127,11 +128,11 @@ func (lyric *Lyric) NewFromLRC(s string) (err error) { for _, v := range lyric.UnsyncedCaptions { var s id3v2.SyncedText s.Text = v.Text - if lyric.Offset >= 0 { - s.Timestamp = v.Timestamp + uint32(lyric.Offset) + if lyric.Offset <= 0 { + s.Timestamp = v.Timestamp + uint32(-lyric.Offset) } else { - if v.Timestamp > uint32(-lyric.Offset) { - s.Timestamp = v.Timestamp - uint32(-lyric.Offset) + if v.Timestamp > uint32(lyric.Offset) { + s.Timestamp = v.Timestamp - uint32(lyric.Offset) } else { s.Timestamp = 0 } @@ -265,3 +266,28 @@ func timeLRC(t uint32) string { res := fmt.Sprintf("%02d:%02d.%03d", m, s, ms) return res } + +// GetText will fetch lyric by time in seconds and mode, mode=0 means fetch current line, +// mode =1 means fetch next line, mode=-1 means fetch previous line +func (lyric *Lyric) GetText(time int) (string, error) { + if lyric.SyncedCaptions == nil { + return "", errors.New("no synced lyric found") + } + + for i, v := range lyric.SyncedCaptions { + // here we want to show lyric 1 second earlier + if int(v.Timestamp) <= time*1000+1000 { + if i < len(lyric.SyncedCaptions)-1 { + next := lyric.SyncedCaptions[i+1] + if int(next.Timestamp) > time*1000+1000 { + return v.Text, nil + } else { + continue + } + } + return v.Text, nil + } + } + + return "", nil +} diff --git a/playingbar.go b/playingbar.go index c14862e..89fd3ca 100644 --- a/playingbar.go +++ b/playingbar.go @@ -103,24 +103,9 @@ func (p *PlayingBar) run() error { // our progress bar var lyricText string if p.subtitle != nil { - for i := range p.subtitle.SyncedCaptions { - startTime := int32(p.subtitle.SyncedCaptions[i].Timestamp) - var endTime int32 - if i < len(p.subtitle.SyncedCaptions)-1 { - endTime = int32(p.subtitle.SyncedCaptions[i+1].Timestamp) - } else { - // Here we display the last lyric until the end of song - endTime = int32(full * 1000) - } - - // here the currentTime is delayed 1 second because we want to show lyrics earlier - currentTime := int32(progress*1000) + 1000 - if currentTime >= startTime && currentTime <= endTime { - lyricText = p.subtitle.SyncedCaptions[i].Text - break - } else { - lyricText = "" - } + lyricText, err = p.subtitle.GetText(progress) + if err != nil { + return tracerr.Wrap(err) } } @@ -260,7 +245,7 @@ func (p *PlayingBar) switchLyrics() { func (p *PlayingBar) delayLyric(lyricDelay int) (err error) { if p.subtitle != nil { - p.subtitle.Offset += int32(lyricDelay) + p.subtitle.Offset -= int32(lyricDelay) err = embedLyric(gomu.player.GetCurrentSong().Path(), p.subtitle, false) if err != nil { return tracerr.Wrap(err) From 29894636cc7c2119f2f412d0bf805dd41506b1e7 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 13 Apr 2021 14:02:34 +0800 Subject: [PATCH 44/48] Refactor lrc.go:GetText() --- lyric/lrc.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lyric/lrc.go b/lyric/lrc.go index 0990dcd..ea0c69a 100644 --- a/lyric/lrc.go +++ b/lyric/lrc.go @@ -267,27 +267,25 @@ func timeLRC(t uint32) string { return res } -// GetText will fetch lyric by time in seconds and mode, mode=0 means fetch current line, -// mode =1 means fetch next line, mode=-1 means fetch previous line +// GetText will fetch lyric by time in seconds func (lyric *Lyric) GetText(time int) (string, error) { + if lyric.SyncedCaptions == nil { return "", errors.New("no synced lyric found") } - for i, v := range lyric.SyncedCaptions { - // here we want to show lyric 1 second earlier - if int(v.Timestamp) <= time*1000+1000 { - if i < len(lyric.SyncedCaptions)-1 { - next := lyric.SyncedCaptions[i+1] - if int(next.Timestamp) > time*1000+1000 { - return v.Text, nil - } else { - continue - } - } - return v.Text, nil + // here we want to show lyric 1 second earlier + time = time*1000 + 1000 + + text := lyric.SyncedCaptions[0].Text + + for _, v := range lyric.SyncedCaptions { + if time >= int(v.Timestamp) { + text = v.Text + } else { + break } } - return "", nil + return text, nil } From 5b5c6be65135102dfeb3ff6cc8f9f4b6b32bb84c Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 13 Apr 2021 14:20:00 +0800 Subject: [PATCH 45/48] Minor Fix: clear album photo if no photo embeded --- lyric/lrc.go | 2 +- playingbar.go | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lyric/lrc.go b/lyric/lrc.go index ea0c69a..4f55611 100644 --- a/lyric/lrc.go +++ b/lyric/lrc.go @@ -270,7 +270,7 @@ func timeLRC(t uint32) string { // GetText will fetch lyric by time in seconds func (lyric *Lyric) GetText(time int) (string, error) { - if lyric.SyncedCaptions == nil { + if len(lyric.SyncedCaptions) == 0 { return "", errors.New("no synced lyric found") } diff --git a/playingbar.go b/playingbar.go index 89fd3ca..a8fb15a 100644 --- a/playingbar.go +++ b/playingbar.go @@ -281,6 +281,12 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { p.hasTag = true p.tag = tag + if p.albumPhoto != nil { + p.albumPhoto.Clear() + p.albumPhoto.Destroy() + p.albumPhoto = nil + } + syltFrames := tag.GetFrames(tag.CommonID("Synchronised lyrics/text")) usltFrames := tag.GetFrames(tag.CommonID("Unsynchronised lyrics/text transcription")) @@ -324,9 +330,6 @@ func (p *PlayingBar) loadLyrics(currentSongPath string) error { go gomu.app.QueueUpdateDraw(func() { x, y, _, _ := p.GetInnerRect() width, height, windowWidth, windowHeight := getConsoleSize() - if err != nil { - errorPopup(err) - } p.albumPhoto, err = ugo.NewImage(dstImage128, (x+3)*windowWidth/width, (y+2)*windowHeight/height) if err != nil { From a740744d2763ddff77a60f16daeb180502d5cf59 Mon Sep 17 00:00:00 2001 From: tramhao Date: Tue, 13 Apr 2021 23:34:30 +0800 Subject: [PATCH 46/48] hide album photo when big popup on top --- popup.go | 15 +++++++++++++++ tageditor.go | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/popup.go b/popup.go index 0942d9d..d09df8f 100644 --- a/popup.go +++ b/popup.go @@ -67,6 +67,9 @@ func (s *Stack) pop() tview.Primitive { } else { // focus the panel if no popup left gomu.app.SetFocus(gomu.prevPanel.(tview.Primitive)) + if gomu.playingBar.albumPhoto != nil { + gomu.playingBar.albumPhoto.Show() + } } return last @@ -323,6 +326,10 @@ func helpPopup(panel Panel) { return nil }) + if gomu.playingBar.albumPhoto != nil { + gomu.playingBar.albumPhoto.Clear() + } + gomu.pages.AddPage("help-page", center(list, 50, 32), true, true) gomu.popups.push(list) } @@ -515,6 +522,10 @@ func searchPopup(title string, stringsToMatch []string, handler func(selected st // this is to fix the left border of search popup popupFrame := tview.NewFrame(popup) + if gomu.playingBar.albumPhoto != nil { + gomu.playingBar.albumPhoto.Clear() + } + gomu.pages.AddPage("search-input-popup", center(popupFrame, 70, 40), true, true) gomu.popups.push(popup) } @@ -723,6 +734,10 @@ func replPopup() { flex.Box = flexBox + if gomu.playingBar.albumPhoto != nil { + gomu.playingBar.albumPhoto.Clear() + } + gomu.pages.AddPage(popupID, center(flex, 90, 30), true, true) gomu.popups.push(flex) } diff --git a/tageditor.go b/tageditor.go index f547207..8a2d749 100644 --- a/tageditor.go +++ b/tageditor.go @@ -398,6 +398,10 @@ func tagPopup(node *AudioFile) (err error) { lyricTextView, } + if gomu.playingBar.albumPhoto != nil { + gomu.playingBar.albumPhoto.Clear() + } + gomu.pages.AddPage(popupID, center(lyricFlex, 90, 36), true, true) gomu.popups.push(lyricFlex) From 008c39e9256d6fdf1d885a76e84a85d66b3663cc Mon Sep 17 00:00:00 2001 From: tramhao Date: Wed, 14 Apr 2021 11:11:47 +0800 Subject: [PATCH 47/48] Rename GetLyric interface to conform to standard naming conventions --- lyric/lyric.go | 7 ++++--- lyric/lyric_cn.go | 10 +++++----- lyric/lyric_en.go | 10 +++++----- popup.go | 16 ++++++++-------- tageditor.go | 4 ++-- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/lyric/lyric.go b/lyric/lyric.go index 16a2d02..2e7f5c7 100644 --- a/lyric/lyric.go +++ b/lyric/lyric.go @@ -19,9 +19,10 @@ type SongTag struct { LyricID string } -type GetLyrics interface { - GetLyric(songTag *SongTag) (string, error) - GetLyricOptions(search string) ([]*SongTag, error) +// LyricFetcher is the interface to get lyrics via different language +type LyricFetcher interface { + LyricFetch(songTag *SongTag) (string, error) + LyricOptions(search string) ([]*SongTag, error) } // cleanHTML parses html text to valid utf-8 text diff --git a/lyric/lyric_cn.go b/lyric/lyric_cn.go index 6c8172b..5bdb59a 100644 --- a/lyric/lyric_cn.go +++ b/lyric/lyric_cn.go @@ -42,10 +42,10 @@ type tagLyric struct { Tlyric string `json:"tlyric"` } -type GetLyricCn struct{} +type LyricFetcherCn struct{} -// GetLyricOptions queries available song lyrics. It returns slice of SongTag -func (cn GetLyricCn) GetLyricOptions(search string) ([]*SongTag, error) { +// LyricOptions queries available song lyrics. It returns slice of SongTag +func (cn LyricFetcherCn) LyricOptions(search string) ([]*SongTag, error) { serviceProvider := "netease" results, err := getLyricOptionsCnByProvider(search, serviceProvider) @@ -63,9 +63,9 @@ func (cn GetLyricCn) GetLyricOptions(search string) ([]*SongTag, error) { return results, err } -// GetLyric should receive songTag that was returned from getLyricOptions +// LyricFetch should receive songTag that was returned from getLyricOptions // and returns lyric of the queried song. -func (cn GetLyricCn) GetLyric(songTag *SongTag) (lyricString string, err error) { +func (cn LyricFetcherCn) LyricFetch(songTag *SongTag) (lyricString string, err error) { urlSearch := "http://api.sunyj.xyz" diff --git a/lyric/lyric_en.go b/lyric/lyric_en.go index 510b8d5..1e8c547 100644 --- a/lyric/lyric_en.go +++ b/lyric/lyric_en.go @@ -8,11 +8,11 @@ import ( "github.com/gocolly/colly" ) -type GetLyricEn struct{} +type LyricFetcherEn struct{} -// GetLyric should receive SongTag that was returned from GetLyricOptions, and +// LyricFetch should receive SongTag that was returned from GetLyricOptions, and // returns lyric of the queried song. -func (en GetLyricEn) GetLyric(songTag *SongTag) (string, error) { +func (en LyricFetcherEn) LyricFetch(songTag *SongTag) (string, error) { var lyric string c := colly.NewCollector() @@ -39,8 +39,8 @@ func (en GetLyricEn) GetLyric(songTag *SongTag) (string, error) { return "", fmt.Errorf("lyric not compatible") } -// GetLyricOptions queries available song lyrics. It returns slice of SongTag -func (en GetLyricEn) GetLyricOptions(search string) ([]*SongTag, error) { +// LyricOptions queries available song lyrics. It returns slice of SongTag +func (en LyricFetcherEn) LyricOptions(search string) ([]*SongTag, error) { var songTags []*SongTag diff --git a/popup.go b/popup.go index d09df8f..b22591e 100644 --- a/popup.go +++ b/popup.go @@ -864,10 +864,10 @@ func lyricPopup(lang string, audioFile *AudioFile, wg *sync.WaitGroup) error { var titles []string - // below we chose languages with GetLyrics interfaces - getLyric := lyricLang(lang) + // below we chose LyricFetcher interfaces by language + lyricFetcher := lyricLang(lang) - results, err := getLyric.GetLyricOptions(audioFile.name) + results, err := lyricFetcher.LyricOptions(audioFile.name) if err != nil { return tracerr.Wrap(err) } @@ -890,7 +890,7 @@ func lyricPopup(lang string, audioFile *AudioFile, wg *sync.WaitGroup) error { break } } - lyricContent, err := getLyric.GetLyric(results[selectedIndex]) + lyricContent, err := lyricFetcher.LyricFetch(results[selectedIndex]) if err != nil { errorPopup(err) gomu.app.Draw() @@ -922,15 +922,15 @@ func lyricPopup(lang string, audioFile *AudioFile, wg *sync.WaitGroup) error { return nil } -func lyricLang(lang string) lyric.GetLyrics { +func lyricLang(lang string) lyric.LyricFetcher { switch lang { case "en": - return lyric.GetLyricEn{} + return lyric.LyricFetcherEn{} case "zh-CN": - return lyric.GetLyricCn{} + return lyric.LyricFetcherCn{} default: - return lyric.GetLyricEn{} + return lyric.LyricFetcherEn{} } } diff --git a/tageditor.go b/tageditor.go index 8a2d749..0910338 100644 --- a/tageditor.go +++ b/tageditor.go @@ -76,8 +76,8 @@ func tagPopup(node *AudioFile) (err error) { var titles []string audioFile := node go func() { - var getLyric lyric.GetLyricCn - results, err := getLyric.GetLyricOptions(audioFile.name) + var lyricFetcher lyric.LyricFetcherCn + results, err := lyricFetcher.LyricOptions(audioFile.name) if err != nil { errorPopup(err) return From e2853a94f4a3ef7d0037df0018f56555c905a820 Mon Sep 17 00:00:00 2001 From: tramhao Date: Sat, 17 Apr 2021 01:12:20 +0800 Subject: [PATCH 48/48] README update info about album cover --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cebd1bf..253cdea 100644 --- a/README.md +++ b/README.md @@ -133,4 +133,7 @@ I just wanted to implement my own music player with a programming language i'm c - pause - forward and rewind +### Album Photo +For songs downloaded by Gomu, the thumbnail will be embeded as Album cover. If you're not satisfied with the cover, you can edit it with kid3 and attach an image as album cover. Jpeg is tested, but other formats should work as well. + Seeking and more advanced stuff has not yet been implemented; feel free to contribute :)