refactor: extract audiofile.go from playlist.go

This commit is contained in:
tramhao 2021-04-30 15:33:53 +08:00
parent 05bf59e30a
commit dcb8fda7ba
10 changed files with 311 additions and 239 deletions

View File

@ -46,10 +46,10 @@ func (c Command) defineCommands() {
c.define("delete_playlist", func() {
audioFile := gomu.playlist.getCurrentFile()
if audioFile.isAudioFile {
if audioFile.IsAudioFile() {
return
}
err := confirmDeleteAllPopup(audioFile.node)
err := confirmDeleteAllPopup(audioFile.Node())
if err != nil {
errorPopup(err)
}
@ -58,7 +58,7 @@ func (c Command) defineCommands() {
c.define("delete_file", func() {
audioFile := gomu.playlist.getCurrentFile()
// prevent from deleting a directory
if !audioFile.isAudioFile {
if !audioFile.IsAudioFile() {
return
}
@ -81,8 +81,8 @@ func (c Command) defineCommands() {
}
// this ensures it downloads to
// the correct dir
if audioFile.isAudioFile {
downloadMusicPopup(audioFile.parent)
if audioFile.IsAudioFile() {
downloadMusicPopup(audioFile.ParentNode())
} else {
downloadMusicPopup(currNode)
}
@ -91,7 +91,7 @@ func (c Command) defineCommands() {
c.define("add_queue", func() {
audioFile := gomu.playlist.getCurrentFile()
currNode := gomu.playlist.GetCurrentNode()
if audioFile.isAudioFile {
if audioFile.IsAudioFile() {
gomu.queue.pushFront(audioFile)
if len(gomu.queue.items) == 1 && !gomu.player.IsRunning() {
err := gomu.queue.playQueue()
@ -111,8 +111,8 @@ func (c Command) defineCommands() {
// close the node's parent
// remove the color of the node
if audioFile.isAudioFile {
parent := audioFile.parent
if audioFile.IsAudioFile() {
parent := audioFile.ParentNode()
gomu.playlist.setHighlight(parent)
parent.SetExpanded(false)
}
@ -165,7 +165,7 @@ func (c Command) defineCommands() {
files := make([]string, len(gomu.playlist.getAudioFiles()))
for i, file := range gomu.playlist.getAudioFiles() {
files[i] = file.name
files[i] = file.Name()
}
searchPopup("Search", files, func(text string) {
@ -175,7 +175,7 @@ func (c Command) defineCommands() {
logError(err)
}
gomu.playlist.setHighlight(audio.node)
gomu.playlist.setHighlight(audio.Node())
gomu.playlist.refresh()
})
})
@ -245,14 +245,14 @@ func (c Command) defineCommands() {
audios := make([]string, 0, len(queue.items))
for _, file := range queue.items {
audios = append(audios, file.name)
audios = append(audios, file.Name())
}
searchPopup("Songs", audios, func(selected string) {
index := 0
for i, v := range queue.items {
if v.name == selected {
if v.Name() == selected {
index = i
}
}
@ -426,7 +426,7 @@ func (c Command) defineCommands() {
var wg sync.WaitGroup
wg.Add(1)
if audioFile.isAudioFile {
if audioFile.IsAudioFile() {
go func() {
err := lyricPopup(lang, audioFile, &wg)
if err != nil {
@ -442,7 +442,7 @@ func (c Command) defineCommands() {
var wg sync.WaitGroup
wg.Add(1)
if audioFile.isAudioFile {
if audioFile.IsAudioFile() {
go func() {
err := lyricPopup(lang, audioFile, &wg)
if err != nil {

3
go.mod
View File

@ -6,7 +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/bogem/id3v2 v1.2.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/disintegration/imaging v1.6.2
github.com/faiface/beep v1.0.2
@ -29,7 +29,6 @@ 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

1
go.sum
View File

@ -108,7 +108,6 @@ 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=

152
player/audiofile.go Normal file
View File

@ -0,0 +1,152 @@
// Copyright (C) 2020 Raziman
package player
import (
"errors"
"fmt"
"sort"
"time"
"github.com/rivo/tview"
"github.com/tramhao/id3v2"
"github.com/ztrue/tracerr"
)
var _ Audio = (*AudioFile)(nil)
// AudioFile represents directories and mp3 files
// isAudioFile equals to false if it is a directory
type AudioFile struct {
name string
path string
isAudioFile bool
length time.Duration
node *tview.TreeNode
parent *tview.TreeNode
}
// NewAudioFile return a new instance of Audiofile
func NewAudioFile() *AudioFile {
return &AudioFile{}
}
// Name return the name of AudioFile
func (a *AudioFile) Name() string {
return a.name
}
// SetName set the name of AudioFile
func (a *AudioFile) SetName(name string) {
if name == "" {
return
}
a.name = name
}
// Path return the path of AudioFile
func (a *AudioFile) Path() string {
return a.path
}
// SetPath return the path of AudioFile
func (a *AudioFile) SetPath(path string) {
a.path = path
}
// IsAudioFile check if the file is song or directory
func (a *AudioFile) IsAudioFile() bool {
return a.isAudioFile
}
// SetIsAudioFile check if the file is song or directory
func (a *AudioFile) SetIsAudioFile(isAudioFile bool) {
a.isAudioFile = isAudioFile
}
// Len return the length of AudioFile
func (a *AudioFile) Len() time.Duration {
return a.length
}
// SetLen set the length of AudioFile
func (a *AudioFile) SetLen(length time.Duration) {
a.length = length
}
// Parent return the parent directory of AudioFile
func (a *AudioFile) Parent() *AudioFile {
if a.parent == nil {
return nil
}
return a.parent.GetReference().(*AudioFile)
}
// SetParentNode return the parent directory of AudioFile
func (a *AudioFile) SetParentNode(parentNode *tview.TreeNode) {
if parentNode == nil {
return
}
a.parent = parentNode
}
// ParentNode return the parent node of AudioFile
func (a *AudioFile) ParentNode() *tview.TreeNode {
if a.parent == nil {
return nil
}
return a.parent
}
// Node return the current node of AudioFile
func (a *AudioFile) Node() *tview.TreeNode {
if a.node == nil {
return nil
}
return a.node
}
// SetNode return the current node of AudioFile
func (a *AudioFile) SetNode(node *tview.TreeNode) {
a.node = node
}
// String return the string of AudioFile
func (a *AudioFile) String() string {
if a == nil {
return "nil"
}
return fmt.Sprintf("%#v", a)
}
// LoadTagMap will load from tag and return a map of langExt to lyrics
func (a *AudioFile) LoadTagMap() (tag *id3v2.Tag, popupLyricMap map[string]string, options []string, err error) {
popupLyricMap = make(map[string]string)
if a.isAudioFile {
tag, err = id3v2.Open(a.path, id3v2.Options{Parse: true})
if err != nil {
return nil, nil, nil, tracerr.Wrap(err)
}
defer tag.Close()
} else {
return nil, nil, nil, fmt.Errorf("not an audio file")
}
usltFrames := tag.GetFrames(tag.CommonID("Unsynchronised lyrics/text transcription"))
for _, f := range usltFrames {
uslf, ok := f.(id3v2.UnsynchronisedLyricsFrame)
if !ok {
return nil, nil, nil, errors.New("USLT error")
}
res := uslf.Lyrics
popupLyricMap[uslf.ContentDescriptor] = res
}
for option := range popupLyricMap {
options = append(options, option)
}
sort.Strings(options)
return tag, popupLyricMap, options, err
}

View File

@ -21,6 +21,7 @@ import (
ugo "gitlab.com/diamondburned/ueberzug-go"
"github.com/issadarkthing/gomu/lyric"
"github.com/issadarkthing/gomu/player"
)
// PlayingBar shows song name, progress and lyric
@ -145,7 +146,7 @@ func (p *PlayingBar) setSongTitle(title string) {
}
// Resets progress bar, ready for execution
func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) {
func (p *PlayingBar) newProgress(currentSong *player.AudioFile, full int) {
p.setFull(full)
p.setProgress(0)
p.hasTag = false
@ -158,7 +159,7 @@ func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) {
p.albumPhoto = nil
}
err := p.loadLyrics(currentSong.path)
err := p.loadLyrics(currentSong.Path())
if err != nil {
errorPopup(err)
return
@ -191,7 +192,7 @@ func (p *PlayingBar) newProgress(currentSong *AudioFile, full int) {
p.subtitle = p.subtitles[0]
}
}
p.setSongTitle(currentSong.name)
p.setSongTitle(currentSong.Name())
}

View File

@ -26,54 +26,6 @@ import (
"github.com/issadarkthing/gomu/player"
)
var _ player.Audio = (*AudioFile)(nil)
// AudioFile represents directories and mp3 files
// isAudioFile equals to false if it is a directory
type AudioFile struct {
name string
path string
isAudioFile bool
length time.Duration
node *tview.TreeNode
parent *tview.TreeNode
}
// Name return the name of AudioFile
func (a *AudioFile) Name() string {
return a.name
}
// Path return the path of AudioFile
func (a *AudioFile) Path() string {
return a.path
}
// IsAudioFile check if the file is song or directory
func (a *AudioFile) IsAudioFile() bool {
return a.isAudioFile
}
// Len return the length of AudioFile
func (a *AudioFile) Len() time.Duration {
return a.length
}
// GetParent return the parent directory of AudioFile
func (a *AudioFile) GetParent() *AudioFile {
if a.parent == nil {
return nil
}
return a.parent.GetReference().(*AudioFile)
}
func (a *AudioFile) String() string {
if a == nil {
return "nil"
}
return fmt.Sprintf("%#v", a)
}
// Playlist struct represents playlist panel
// that shows the tree of the music directory
type Playlist struct {
@ -83,7 +35,7 @@ type Playlist struct {
// number of downloads
download int
done chan struct{}
yankFile *AudioFile
yankFile *player.AudioFile
}
func (p *Playlist) help() []string {
@ -152,11 +104,10 @@ func newPlaylist(args Args) *Playlist {
done: make(chan struct{}),
}
rootAudioFile := &AudioFile{
name: path.Base(rootDir),
node: root,
path: rootDir,
}
rootAudioFile := player.NewAudioFile()
rootAudioFile.SetName(path.Base(rootDir))
rootAudioFile.SetNode(root)
rootAudioFile.SetPath(rootDir)
root.SetReference(rootAudioFile)
root.SetColor(gomu.colors.playlistDir)
@ -235,16 +186,16 @@ func newPlaylist(args Args) *Playlist {
}
// Returns the current file highlighted in the playlist
func (p Playlist) getCurrentFile() *AudioFile {
func (p Playlist) getCurrentFile() *player.AudioFile {
node := p.GetCurrentNode()
if node == nil {
return nil
}
return node.GetReference().(*AudioFile)
return node.GetReference().(*player.AudioFile)
}
// Deletes song from filesystem
func (p *Playlist) deleteSong(audioFile *AudioFile) {
func (p *Playlist) deleteSong(audioFile *player.AudioFile) {
confirmationPopup(
"Are you sure to delete this audio file?", func(_ int, buttonName string) {
@ -256,14 +207,14 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) {
// 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)
err := os.Remove(audioFile.Path())
if err != nil {
errorPopup(err)
return
}
defaultTimedPopup(" Success ",
audioFile.name+"\nhas been deleted successfully")
audioFile.Name()+"\nhas been deleted successfully")
go gomu.app.QueueUpdateDraw(func() {
p.refresh()
// Here we remove the song from queue
@ -276,20 +227,20 @@ func (p *Playlist) deleteSong(audioFile *AudioFile) {
}
// Deletes playlist/dir from filesystem
func (p *Playlist) deletePlaylist(audioFile *AudioFile) (err error) {
func (p *Playlist) deletePlaylist(audioFile *player.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)
err = os.RemoveAll(audioFile.Path())
if err != nil {
return tracerr.Wrap(err)
}
defaultTimedPopup(
" Success ",
audioFile.name+"\nhas been deleted successfully")
audioFile.Name()+"\nhas been deleted successfully")
go gomu.app.QueueUpdateDraw(func() {
p.refresh()
// Here we remove the song from queue
@ -308,13 +259,13 @@ func (p *Playlist) addAllToQueue(root *tview.TreeNode) {
childrens = root.GetChildren()
// gets the parent if the highlighted item is a file
if root.GetReference().(*AudioFile).isAudioFile {
childrens = root.GetReference().(*AudioFile).parent.GetChildren()
if root.GetReference().(*player.AudioFile).IsAudioFile() {
childrens = root.GetReference().(*player.AudioFile).ParentNode().GetChildren()
}
for _, v := range childrens {
currNode := v.GetReference().(*AudioFile)
if currNode.isAudioFile {
currNode := v.GetReference().(*player.AudioFile)
if currNode.IsAudioFile() {
currSong := gomu.player.GetCurrentSong()
if currSong == nil || (currNode.Name() != currSong.Name()) {
@ -331,17 +282,17 @@ func (p *Playlist) refresh() {
root := gomu.playlist.GetRoot()
prevNode := gomu.playlist.GetCurrentNode()
prevFilepath := prevNode.GetReference().(*AudioFile).Path()
prevFilepath := prevNode.GetReference().(*player.AudioFile).Path()
root.ClearChildren()
node := root.GetReference().(*AudioFile)
node := root.GetReference().(*player.AudioFile)
populate(root, node.path, gomu.anko.GetBool("General.sort_by_mtime"))
populate(root, node.Path(), gomu.anko.GetBool("General.sort_by_mtime"))
root.Walk(func(node, _ *tview.TreeNode) bool {
// to preserve previously highlighted node
if node.GetReference().(*AudioFile).Path() == prevFilepath {
if node.GetReference().(*player.AudioFile).Path() == prevFilepath {
p.setHighlight(node)
return false
}
@ -371,14 +322,13 @@ func (p *Playlist) addSongToPlaylist(
return tracerr.Wrap(err)
}
audioFile := &AudioFile{
name: songName,
path: audioPath,
isAudioFile: true,
length: audioLength,
node: node,
parent: selPlaylist,
}
var audioFile *player.AudioFile
audioFile.SetName(songName)
audioFile.SetPath(audioPath)
audioFile.SetIsAudioFile(true)
audioFile.SetLen(audioLength)
audioFile.SetNode(node)
audioFile.SetParentNode(selPlaylist)
displayText := setDisplayText(audioFile)
@ -390,15 +340,15 @@ func (p *Playlist) addSongToPlaylist(
}
// Gets all audio files walks from music root directory
func (p *Playlist) getAudioFiles() []*AudioFile {
func (p *Playlist) getAudioFiles() []*player.AudioFile {
root := p.GetRoot()
audioFiles := []*AudioFile{}
audioFiles := []*player.AudioFile{}
root.Walk(func(node, _ *tview.TreeNode) bool {
audioFile := node.GetReference().(*AudioFile)
audioFile := node.GetReference().(*player.AudioFile)
audioFiles = append(audioFiles, audioFile)
return true
@ -412,7 +362,7 @@ func (p *Playlist) createPlaylist(name string) error {
selectedNode := p.GetCurrentNode()
parentNode := selectedNode.GetReference().(*AudioFile).parent
parentNode := selectedNode.GetReference().(*player.AudioFile).ParentNode()
// if the current node is the root
// sets the parent to itself
@ -420,9 +370,9 @@ func (p *Playlist) createPlaylist(name string) error {
parentNode = selectedNode
}
audioFile := parentNode.GetReference().(*AudioFile)
audioFile := parentNode.GetReference().(*player.AudioFile)
err := os.Mkdir(path.Join(audioFile.path, name), 0744)
err := os.Mkdir(path.Join(audioFile.Path(), name), 0744)
if err != nil {
return tracerr.Wrap(err)
@ -439,7 +389,7 @@ func (p *Playlist) createPlaylist(name string) error {
func (p *Playlist) setHighlight(currNode *tview.TreeNode) {
if p.prevNode != nil {
if p.prevNode.GetReference().(*AudioFile).isAudioFile {
if p.prevNode.GetReference().(*player.AudioFile).IsAudioFile() {
p.prevNode.SetColor(gomu.colors.foreground)
} else {
p.prevNode.SetColor(gomu.colors.playlistDir)
@ -454,17 +404,17 @@ func (p *Playlist) setHighlight(currNode *tview.TreeNode) {
// Traverses the playlist and finds the AudioFile struct
// audioName must be hashed with sha1 first
func (p *Playlist) findAudioFile(audioName string) (*AudioFile, error) {
func (p *Playlist) findAudioFile(audioName string) (*player.AudioFile, error) {
root := p.GetRoot()
var selNode *AudioFile
var selNode *player.AudioFile
root.Walk(func(node, _ *tview.TreeNode) bool {
audioFile := node.GetReference().(*AudioFile)
audioFile := node.GetReference().(*player.AudioFile)
hashed := sha1Hex(getName(audioFile.name))
hashed := sha1Hex(getName(audioFile.Name()))
if hashed == audioName {
selNode = audioFile
@ -484,16 +434,16 @@ func (p *Playlist) findAudioFile(audioName string) (*AudioFile, error) {
func (p *Playlist) rename(newName string) error {
currentNode := p.GetCurrentNode()
audio := currentNode.GetReference().(*AudioFile)
audio := currentNode.GetReference().(*player.AudioFile)
pathToFile, _ := filepath.Split(audio.path)
pathToFile, _ := filepath.Split(audio.Path())
var newPath string
if audio.isAudioFile {
if audio.IsAudioFile() {
newPath = pathToFile + newName + ".mp3"
} else {
newPath = pathToFile + newName
}
err := os.Rename(audio.path, newPath)
err := os.Rename(audio.Path(), newPath)
if err != nil {
return tracerr.Wrap(err)
}
@ -553,8 +503,8 @@ func ytdl(url string, selPlaylist *tview.TreeNode) error {
return tracerr.Wrap(err)
}
selAudioFile := selPlaylist.GetReference().(*AudioFile)
dir := selAudioFile.path
selAudioFile := selPlaylist.GetReference().(*player.AudioFile)
dir := selAudioFile.Path()
defaultTimedPopup(" Ytdl ", "Downloading")
@ -722,20 +672,19 @@ func populate(root *tview.TreeNode, rootPath string, sortMtime bool) error {
continue
}
audioFile := &AudioFile{
name: songName,
path: path,
isAudioFile: true,
node: child,
parent: root,
}
audioFile := player.NewAudioFile()
audioFile.SetName(songName)
audioFile.SetPath(path)
audioFile.SetIsAudioFile(true)
audioFile.SetNode(child)
audioFile.SetParentNode(root)
audioLength, err := getTagLength(audioFile.path)
audioLength, err := getTagLength(audioFile.Path())
if err != nil {
logError(err)
}
audioFile.length = audioLength
audioFile.SetLen(audioLength)
displayText := setDisplayText(audioFile)
@ -747,13 +696,12 @@ func populate(root *tview.TreeNode, rootPath string, sortMtime bool) error {
if file.IsDir() || file.Mode()&os.ModeSymlink != 0 {
audioFile := &AudioFile{
name: songName,
path: path,
isAudioFile: false,
node: child,
parent: root,
}
audioFile := player.NewAudioFile()
audioFile.SetName(songName)
audioFile.SetPath(path)
audioFile.SetIsAudioFile(false)
audioFile.SetNode(child)
audioFile.SetParentNode(root)
displayText := setDisplayText(audioFile)
@ -775,10 +723,10 @@ func (p *Playlist) yank() error {
if p.yankFile == nil {
return errors.New("no file has been yanked")
}
if p.yankFile.node == p.GetRoot() {
if p.yankFile.Node() == p.GetRoot() {
return errors.New("please don't yank the root directory")
}
defaultTimedPopup(" Success ", p.yankFile.name+"\n has been yanked successfully.")
defaultTimedPopup(" Success ", p.yankFile.Name()+"\n has been yanked successfully.")
return nil
}
@ -789,13 +737,13 @@ func (p *Playlist) paste() error {
}
oldAudio := p.yankFile
oldPathDir, oldPathFileName := filepath.Split(p.yankFile.path)
oldPathDir, oldPathFileName := filepath.Split(p.yankFile.Path())
pasteFile := p.getCurrentFile()
var newPathDir string
if pasteFile.isAudioFile {
newPathDir, _ = filepath.Split(pasteFile.path)
if pasteFile.IsAudioFile() {
newPathDir, _ = filepath.Split(pasteFile.Path())
} else {
newPathDir = pasteFile.path
newPathDir = pasteFile.Path()
}
if oldPathDir == newPathDir {
@ -803,20 +751,20 @@ func (p *Playlist) paste() error {
}
newPathFull := filepath.Join(newPathDir, oldPathFileName)
err := os.Rename(p.yankFile.path, newPathFull)
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)
defaultTimedPopup(" Success ", p.yankFile.Name()+"\n has been pasted to\n"+newPathDir)
// keep queue references updated
newAudio := oldAudio
newAudio.path = newPathFull
newAudio.SetPath(newPathFull)
p.refresh()
gomu.queue.updateQueuePath()
if p.yankFile.isAudioFile {
if p.yankFile.IsAudioFile() {
err = gomu.queue.updateCurrentSongName(oldAudio, newAudio)
if err != nil {
return tracerr.Wrap(err)
@ -833,23 +781,23 @@ func (p *Playlist) paste() error {
return nil
}
func setDisplayText(audioFile *AudioFile) string {
func setDisplayText(audioFile *player.AudioFile) string {
useEmoji := gomu.anko.GetBool("General.use_emoji")
if !useEmoji {
return audioFile.name
return audioFile.Name()
}
if audioFile.isAudioFile {
if audioFile.IsAudioFile() {
emojiFile := gomu.anko.GetString("Emoji.file")
return fmt.Sprintf(" %s %s", emojiFile, audioFile.name)
return fmt.Sprintf(" %s %s", emojiFile, audioFile.Name())
}
emojiDir := gomu.anko.GetString("Emoji.playlist")
return fmt.Sprintf(" %s %s", emojiDir, audioFile.name)
return fmt.Sprintf(" %s %s", emojiDir, audioFile.Name())
}
// refreshByNode is called after rename of file or folder, to refresh queue info
func (p *Playlist) refreshAfterRename(node *AudioFile, newName string) error {
func (p *Playlist) refreshAfterRename(node *player.AudioFile, newName string) error {
root := p.GetRoot()
root.Walk(func(node, _ *tview.TreeNode) bool {
@ -860,7 +808,7 @@ func (p *Playlist) refreshAfterRename(node *AudioFile, newName string) error {
})
// update queue
newNode := p.getCurrentFile()
if node.isAudioFile {
if node.IsAudioFile() {
err := gomu.queue.renameItem(node, newNode)
if err != nil {
return tracerr.Wrap(err)

View File

@ -144,7 +144,7 @@ func confirmDeleteAllPopup(selPlaylist *tview.TreeNode) (err error) {
gomu.popups.pop()
if confirmationText == "DELETE" {
audioFile := selPlaylist.GetReference().(*AudioFile)
audioFile := selPlaylist.GetReference().(*player.AudioFile)
err = gomu.playlist.deletePlaylist(audioFile)
if err != nil {
errorPopup(err)
@ -555,10 +555,10 @@ func newInputPopup(popupID, title, label string, text string) *tview.InputField
return inputField
}
func renamePopup(node *AudioFile) {
func renamePopup(node *player.AudioFile) {
popupID := "rename-input-popup"
input := newInputPopup(popupID, " Rename ", "New name: ", node.name)
input := newInputPopup(popupID, " Rename ", "New name: ", node.Name())
input.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
switch e.Key() {
@ -831,10 +831,10 @@ func ytSearchPopup() {
var dir *tview.TreeNode
if audioFile.isAudioFile {
dir = audioFile.parent
if audioFile.IsAudioFile() {
dir = audioFile.ParentNode()
} else {
dir = audioFile.node
dir = audioFile.Node()
}
go func() {
@ -860,14 +860,14 @@ func ytSearchPopup() {
})
}
func lyricPopup(lang string, audioFile *AudioFile, wg *sync.WaitGroup) error {
func lyricPopup(lang string, audioFile *player.AudioFile, wg *sync.WaitGroup) error {
var titles []string
// below we chose LyricFetcher interfaces by language
lyricFetcher := lyricLang(lang)
results, err := lyricFetcher.LyricOptions(audioFile.name)
results, err := lyricFetcher.LyricOptions(audioFile.Name())
if err != nil {
return tracerr.Wrap(err)
}
@ -905,7 +905,7 @@ func lyricPopup(lang string, audioFile *AudioFile, wg *sync.WaitGroup) error {
return
}
lyric.LangExt = lang
err = embedLyric(audioFile.path, &lyric, false)
err = embedLyric(audioFile.Path(), &lyric, false)
if err != nil {
errorPopup(err)
gomu.app.Draw()

View File

@ -17,13 +17,15 @@ import (
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/ztrue/tracerr"
"github.com/issadarkthing/gomu/player"
)
// Queue shows queued songs for playing
type Queue struct {
*tview.List
savedQueuePath string
items []*AudioFile
items []*player.AudioFile
isLoop bool
}
@ -45,19 +47,19 @@ func (q *Queue) prev() {
// Usually used with GetCurrentItem which can return -1 if
// no item highlighted
func (q *Queue) deleteItem(index int) (*AudioFile, error) {
func (q *Queue) deleteItem(index int) (*player.AudioFile, error) {
if index > len(q.items)-1 {
return nil, tracerr.New("Index out of range")
}
// deleted audio file
var dAudio *AudioFile
var dAudio *player.AudioFile
if index != -1 {
q.RemoveItem(index)
var nItems []*AudioFile
var nItems []*player.AudioFile
for i, v := range q.items {
@ -87,7 +89,7 @@ func (q *Queue) updateTitle() string {
var totalLength time.Duration
for _, v := range q.items {
totalLength += v.length
totalLength += v.Len()
}
fmtTime := fmtDurationH(totalLength)
@ -127,23 +129,23 @@ func (q *Queue) updateTitle() string {
}
// Add item to the front of the queue
func (q *Queue) pushFront(audioFile *AudioFile) {
func (q *Queue) pushFront(audioFile *player.AudioFile) {
q.items = append([]*AudioFile{audioFile}, q.items...)
q.items = append([]*player.AudioFile{audioFile}, q.items...)
songLength := audioFile.length
songLength := audioFile.Len()
queueItemView := fmt.Sprintf(
"[ %s ] %s", fmtDuration(songLength), getName(audioFile.name),
"[ %s ] %s", fmtDuration(songLength), getName(audioFile.Name()),
)
q.InsertItem(0, queueItemView, audioFile.path, 0, nil)
q.InsertItem(0, queueItemView, audioFile.Path(), 0, nil)
q.updateTitle()
}
// gets the first item and remove it from the queue
// app.Draw() must be called after calling this function
func (q *Queue) dequeue() (*AudioFile, error) {
func (q *Queue) dequeue() (*player.AudioFile, error) {
if q.GetItemCount() == 0 {
return nil, tracerr.New("Empty list")
@ -157,23 +159,23 @@ 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) {
func (q *Queue) enqueue(audioFile *player.AudioFile) (int, error) {
if !audioFile.isAudioFile {
if !audioFile.IsAudioFile() {
return q.GetItemCount(), nil
}
q.items = append(q.items, audioFile)
songLength, err := getTagLength(audioFile.path)
songLength, err := getTagLength(audioFile.Path())
if err != nil {
return 0, tracerr.Wrap(err)
}
queueItemView := fmt.Sprintf(
"[ %s ] %s", fmtDuration(songLength), getName(audioFile.name),
"[ %s ] %s", fmtDuration(songLength), getName(audioFile.Name()),
)
q.AddItem(queueItemView, audioFile.path, 0, nil)
q.AddItem(queueItemView, audioFile.Path(), 0, nil)
q.updateTitle()
return q.GetItemCount(), nil
@ -236,7 +238,7 @@ func (q *Queue) saveQueue() error {
// Clears current queue
func (q *Queue) clearQueue() {
q.items = []*AudioFile{}
q.items = []*player.AudioFile{}
q.Clear()
q.updateTitle()
@ -335,13 +337,13 @@ func (q *Queue) shuffle() {
q.Clear()
for _, v := range q.items {
audioLen, err := getTagLength(v.path)
audioLen, err := getTagLength(v.Path())
if err != nil {
logError(err)
}
queueText := fmt.Sprintf("[ %s ] %s", fmtDuration(audioLen), v.name)
q.AddItem(queueText, v.path, 0, nil)
queueText := fmt.Sprintf("[ %s ] %s", fmtDuration(audioLen), v.Name())
q.AddItem(queueText, v.Path(), 0, nil)
}
// q.updateTitle()
@ -416,9 +418,9 @@ func sha1Hex(input string) string {
}
// Modify the title of songs in queue
func (q *Queue) renameItem(oldAudio *AudioFile, newAudio *AudioFile) error {
func (q *Queue) renameItem(oldAudio *player.AudioFile, newAudio *player.AudioFile) error {
for i, v := range q.items {
if v.name != oldAudio.name {
if v.Name() != oldAudio.Name() {
continue
}
err := q.insertItem(i, newAudio)
@ -449,24 +451,24 @@ func (q *Queue) playQueue() error {
return nil
}
func (q *Queue) insertItem(index int, audioFile *AudioFile) error {
func (q *Queue) insertItem(index int, audioFile *player.AudioFile) error {
if index > len(q.items)-1 {
return tracerr.New("Index out of range")
}
if index != -1 {
songLength, err := getTagLength(audioFile.path)
songLength, err := getTagLength(audioFile.Path())
if err != nil {
return tracerr.Wrap(err)
}
queueItemView := fmt.Sprintf(
"[ %s ] %s", fmtDuration(songLength), getName(audioFile.name),
"[ %s ] %s", fmtDuration(songLength), getName(audioFile.Name()),
)
q.InsertItem(index, queueItemView, audioFile.path, 0, nil)
q.InsertItem(index, queueItemView, audioFile.Path(), 0, nil)
var nItems []*AudioFile
var nItems []*player.AudioFile
for i, v := range q.items {
@ -493,7 +495,7 @@ func (q *Queue) updateQueuePath() {
return
}
for _, v := range q.items {
song := sha1Hex(getName(v.name))
song := sha1Hex(getName(v.Name()))
songs = append(songs, song)
}
@ -512,7 +514,7 @@ func (q *Queue) updateQueuePath() {
}
// update current playing song name to reflect the changes during rename and paste
func (q *Queue) updateCurrentSongName(oldAudio *AudioFile, newAudio *AudioFile) error {
func (q *Queue) updateCurrentSongName(oldAudio *player.AudioFile, newAudio *player.AudioFile) error {
if !gomu.player.IsRunning() && !gomu.player.IsPaused() {
return nil
@ -522,7 +524,7 @@ func (q *Queue) updateCurrentSongName(oldAudio *AudioFile, newAudio *AudioFile)
position := gomu.playingBar.getProgress()
paused := gomu.player.IsPaused()
if oldAudio.name != currentSong.Name() {
if oldAudio.Name() != currentSong.Name() {
return nil
}
@ -542,7 +544,7 @@ func (q *Queue) updateCurrentSongName(oldAudio *AudioFile, newAudio *AudioFile)
}
// update current playing song path to reflect the changes during rename and paste
func (q *Queue) updateCurrentSongPath(oldAudio *AudioFile, newAudio *AudioFile) error {
func (q *Queue) updateCurrentSongPath(oldAudio *player.AudioFile, newAudio *player.AudioFile) error {
if !gomu.player.IsRunning() && !gomu.player.IsPaused() {
return nil
@ -553,7 +555,7 @@ func (q *Queue) updateCurrentSongPath(oldAudio *AudioFile, newAudio *AudioFile)
paused := gomu.player.IsPaused()
// Here we check the situation when currentsong is under oldAudio folder
if !strings.Contains(currentSong.Path(), oldAudio.path) {
if !strings.Contains(currentSong.Path(), oldAudio.Path()) {
return nil
}
@ -578,7 +580,7 @@ func (q *Queue) updateCurrentSongPath(oldAudio *AudioFile, newAudio *AudioFile)
}
// update current playing song simply delete it
func (q *Queue) updateCurrentSongDelete(oldAudio *AudioFile) {
func (q *Queue) updateCurrentSongDelete(oldAudio *player.AudioFile) {
if !gomu.player.IsRunning() && !gomu.player.IsPaused() {
return
}
@ -587,12 +589,12 @@ func (q *Queue) updateCurrentSongDelete(oldAudio *AudioFile) {
paused := gomu.player.IsPaused()
var delete bool
if oldAudio.isAudioFile {
if oldAudio.name == currentSong.Name() {
if oldAudio.IsAudioFile() {
if oldAudio.Name() == currentSong.Name() {
delete = true
}
} else {
if strings.Contains(currentSong.Path(), oldAudio.path) {
if strings.Contains(currentSong.Path(), oldAudio.Path()) {
delete = true
}
}

View File

@ -75,7 +75,7 @@ func defineInternals() {
root := gomu.playlist.GetRoot()
root.Walk(func(node, _ *tview.TreeNode) bool {
if node.GetReference().(*AudioFile).Path() == filepath {
if node.GetReference().(*player.AudioFile).Path() == filepath {
gomu.playlist.setHighlight(node)
return false
}
@ -85,7 +85,7 @@ func defineInternals() {
})
queue, _ := gomu.anko.NewModule("Queue")
queue.Define("get_focused", func() *AudioFile {
queue.Define("get_focused", func() *player.AudioFile {
index := gomu.queue.GetCurrentItem()
if index < 0 || index > len(gomu.queue.items)-1 {
return nil
@ -373,7 +373,7 @@ func start(application *tview.Application, args Args) {
}
}
audioFile := audio.(*AudioFile)
audioFile := audio.(*player.AudioFile)
gomu.playingBar.newProgress(audioFile, int(duration.Seconds()))
@ -404,7 +404,7 @@ func start(application *tview.Application, args Args) {
gomu.playingBar.subtitles = nil
gomu.playingBar.subtitle = nil
if gomu.queue.isLoop {
_, err = gomu.queue.enqueue(currAudio.(*AudioFile))
_, err = gomu.queue.enqueue(currAudio.(*player.AudioFile))
if err != nil {
logError(err)
}

View File

@ -5,7 +5,6 @@ package main
import (
"errors"
"fmt"
"sort"
"strings"
"sync"
@ -15,6 +14,7 @@ import (
"github.com/ztrue/tracerr"
"github.com/issadarkthing/gomu/lyric"
"github.com/issadarkthing/gomu/player"
)
// lyricFlex extend the flex control to modify the Focus item
@ -26,10 +26,10 @@ type lyricFlex struct {
}
// tagPopup is used to edit tag, delete and fetch lyrics
func tagPopup(node *AudioFile) (err error) {
func tagPopup(node *player.AudioFile) (err error) {
popupID := "tag-editor-input-popup"
tag, popupLyricMap, options, err := node.loadTagMap()
tag, popupLyricMap, options, err := node.LoadTagMap()
if err != nil {
return tracerr.Wrap(err)
}
@ -66,7 +66,7 @@ func tagPopup(node *AudioFile) (err error) {
leftBox := tview.NewBox().
SetBorder(true).
SetTitle(node.name).
SetTitle(node.Name()).
SetBackgroundColor(gomu.colors.popup).
SetBorderColor(gomu.colors.accent).
SetTitleColor(gomu.colors.accent).
@ -77,7 +77,7 @@ func tagPopup(node *AudioFile) (err error) {
audioFile := node
go func() {
var lyricFetcher lyric.LyricFetcherCn
results, err := lyricFetcher.LyricOptions(audioFile.name)
results, err := lyricFetcher.LyricOptions(audioFile.Name())
if err != nil {
errorPopup(err)
return
@ -105,7 +105,7 @@ func tagPopup(node *AudioFile) (err error) {
titleInputField.SetText(newTag.Title)
albumInputField.SetText(newTag.Album)
tag, err = id3v2.Open(node.path, id3v2.Options{Parse: true})
tag, err = id3v2.Open(node.Path(), id3v2.Options{Parse: true})
if err != nil {
errorPopup(err)
return
@ -150,7 +150,10 @@ func tagPopup(node *AudioFile) (err error) {
SetTitleColor(gomu.colors.accent)
saveTagButton.SetSelectedFunc(func() {
tag, err = id3v2.Open(node.path, id3v2.Options{Parse: true})
tag, err = id3v2.Open(node.Path(), id3v2.Options{
Parse: true,
ParseFrames: []string{},
})
if err != nil {
errorPopup(err)
return
@ -213,7 +216,7 @@ func tagPopup(node *AudioFile) (err error) {
LangExt: langExt,
}
if len(options) > 0 {
err := embedLyric(node.path, lyric, true)
err := embedLyric(node.Path(), lyric, true)
if err != nil {
errorPopup(err)
return
@ -278,7 +281,7 @@ func tagPopup(node *AudioFile) (err error) {
audioFile := gomu.playlist.getCurrentFile()
_, lang := getLyricDropDown.GetCurrentOption()
if !audioFile.isAudioFile {
if !audioFile.IsAudioFile() {
errorPopup(errors.New("not an audio file"))
return
}
@ -298,7 +301,7 @@ func tagPopup(node *AudioFile) (err error) {
go func() {
// This is to ensure that the above go routine finish.
wg.Wait()
_, popupLyricMap, newOptions, err := audioFile.loadTagMap()
_, popupLyricMap, newOptions, err := audioFile.LoadTagMap()
if err != nil {
errorPopup(err)
gomu.app.Draw()
@ -481,35 +484,3 @@ func (f *lyricFlex) Focus(delegate func(p tview.Primitive)) {
f.Flex.Focus(delegate)
}
}
// loadTagMap will load from tag and return a map of langExt to lyrics
func (a *AudioFile) loadTagMap() (tag *id3v2.Tag, popupLyricMap map[string]string, options []string, err error) {
popupLyricMap = make(map[string]string)
if a.isAudioFile {
tag, err = id3v2.Open(a.path, id3v2.Options{Parse: true})
if err != nil {
return nil, nil, nil, tracerr.Wrap(err)
}
defer tag.Close()
} else {
return nil, nil, nil, fmt.Errorf("not an audio file")
}
usltFrames := tag.GetFrames(tag.CommonID("Unsynchronised lyrics/text transcription"))
for _, f := range usltFrames {
uslf, ok := f.(id3v2.UnsynchronisedLyricsFrame)
if !ok {
die(errors.New("USLT error"))
}
res := uslf.Lyrics
popupLyricMap[uslf.ContentDescriptor] = res
}
for option := range popupLyricMap {
options = append(options, option)
}
sort.Strings(options)
return tag, popupLyricMap, options, err
}