Skip to content

Commit

Permalink
can launch song from terminal
Browse files Browse the repository at this point in the history
  • Loading branch information
oct2pus committed Apr 2, 2019
1 parent 1b155a5 commit 56b184a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 58 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Listen
Simple Music Player; built with Gotk3.
Simple MP3-only Music Player; built with Gotk3.

## Building

```git clone https://github.com/oct2pus/listen; cd listen; go build .```
```git clone https://github.com/oct2pus/listen; cd listen; go get -u; go build .```

## Feature Checklist for 1.0

- [x] .mp3, .flac, .ogg format support
- [x] displays embedded track art
- [x] pausing and resuming
- [x] displays unembedded album art
- [x] volume control
- [x] free move
- [x] restart song after completed
- [ ] launch song from terminal
- [x] launch song from terminal
64 changes: 39 additions & 25 deletions gui/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gui

import (
"listen/logic"

"github.com/faiface/beep/speaker"
"github.com/gotk3/gotk3/gtk"
)
Expand Down Expand Up @@ -38,6 +39,7 @@ func (a Actions) PlayPressed() Actions {
// a music stream and sets the trackart.
func (a Actions) FilePressed() Actions {
// create temporary holder window
println("emitted")
win, err := gtk.WindowNew(gtk.WINDOW_POPUP)
if err != nil {
logic.SendError(err, "holder window")
Expand All @@ -53,33 +55,19 @@ func (a Actions) FilePressed() Actions {
if err != nil {
logic.SendError(err, "file dialog")
}

// filter choices
filt, err := gtk.FileFilterNew()
if err != nil {
logic.SendError(err, "file filter")
}
filt.AddPattern("*.mp3")
filt.AddPattern("*.flac")
filt.AddPattern("*.ogg") // could possibly break if not a vorbis file.
// filt.AddPattern("*.flac") // cannot seek
// filt.AddPattern("*.ogg") // could possibly break if not a vorbis file.
diag.SetFilter(filt)

// if option is selected
if diag.Run() == int(gtk.RESPONSE_ACCEPT) {
a.Audio = logic.Read(diag.GetFilename())
a.GUI.ImgPlay.SetFromIconName("media-playback-stop-symbolic",
gtk.ICON_SIZE_BUTTON)
a.GUI.ProgScale.SetSensitive(true)
a.GUI.PlayButt.SetSensitive(true)
a.GUI.VolButt.SetSensitive(true)
a.GUI.ProgScale.SetRange(0, float64((*a.Audio.Stream).Len()))
a = a.VolumeSlid()
if a.Audio.Art != nil {
a.GUI.ImgTrack.SetFromPixbuf(a.Audio.Art)
} else {
a.GUI.ImgTrack.SetFromIconName("action-unavailable-symbolic",
gtk.ICON_SIZE_DND)
}
a = a.setup(diag.GetFilename())
}

return a
Expand All @@ -105,8 +93,8 @@ func (a Actions) VolumeSlid() Actions {
// released.
func (a Actions) MoveProg() Actions {
speaker.Lock()
if (int(a.GUI.ProgScale.GetValue()) == (*a.Audio.Stream).Len()) {
a.GUI.ProgScale.SetValue(float64(((*a.Audio.Stream)).Len()-1))
if int(a.GUI.ProgScale.GetValue()) == (*a.Audio.Stream).Len() {
a.GUI.ProgScale.SetValue(float64((*a.Audio.Stream).Len() - 1))
}
err := (*a.Audio.Stream).Seek(int(a.GUI.ProgScale.GetValue()))
if err != nil {
Expand All @@ -131,12 +119,16 @@ func (a Actions) DrawProg() Actions {
func (a Actions) isEnd() Actions {
if a.Audio.Stream != nil &&
a.GUI.ProgScale.GetValue() == float64((*a.Audio.Stream).Len()) {


var err error
speaker.Lock()
path := a.Audio.Path
a.Audio = logic.AudioData{}
speaker.Unlock() // speaker must be unlocked to play a stream
a.Audio = logic.Read(path)
a.Audio, err = logic.Read(path)
if err != nil {
return a
}
a.GUI.ProgScale.SetValue(0)
a.startStop()
a.GUI.ImgPlay.SetFromIconName(START,
Expand All @@ -150,10 +142,32 @@ func (a Actions) Block() {
block = true
}

// ParseArgs parses commandline arguments to launch a song.
func ParseArgs(args []string) {
// TODO
// This should probably be moved as well.
// LoadFromCMD occurs when a file is supplied before openning the program.
func (a Actions) LoadFromCMD(path string) Actions {
a = a.setup(path)
return a
}

func (a Actions) setup(path string) Actions {
var err error
a.Audio, err = logic.Read(path)
if err != nil {
return a
}
a.GUI.ImgPlay.SetFromIconName(STOP,
gtk.ICON_SIZE_BUTTON)
a.GUI.ProgScale.SetSensitive(true)
a.GUI.PlayButt.SetSensitive(true)
a.GUI.VolButt.SetSensitive(true)
a.GUI.ProgScale.SetRange(0, float64((*a.Audio.Stream).Len()))
a = a.VolumeSlid()
if a.Audio.Art != nil {
a.GUI.ImgTrack.SetFromPixbuf(a.Audio.Art)
} else {
a.GUI.ImgTrack.SetFromIconName("action-unavailable-symbolic",
gtk.ICON_SIZE_DND)
}
return a
}

func (a Actions) startStop() {
Expand Down
18 changes: 8 additions & 10 deletions gui/widgets.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
)

// InitWidgets calls and initalizes the starting values of all widge.ts
func InitWidgets(window Elements) Elements {
func InitWidgets(window Elements, cmdLaunch bool) Elements {
window = setAppWindowValues(window)
window = setHeaderValues(window)
window = setHeaderValues(window, cmdLaunch)
window = setTrackBoxValues(window)
// window = SetPopMenuValues(window)
return window
Expand All @@ -21,30 +21,28 @@ func setAppWindowValues(w Elements) Elements {
w.AppWindow.Add(w.TrackBox)
w.AppWindow.SetTitlebar(w.Header)
w.AppWindow.SetDefaultSize(logic.ArtSize, logic.ArtSize)
w.AppWindow.SetResizable(false) // may consider changing this one day
// don't think this application is ever
// supposed to go full screen tho
w.AppWindow.SetResizable(false) // may consider changing this one day
w.AppWindow.Show()
return w
}

// setHeaderValues initalizes all widget values that belong to GUI.Header.
func setHeaderValues(w Elements) Elements {
func setHeaderValues(w Elements, c bool) Elements {
// Buttons
//w.ImgMenu.SetFromIconName("open-menu-symbolic", gtk.ICON_SIZE_BUTTON)
w.ImgFile.SetFromIconName("document-open-symbolic", gtk.ICON_SIZE_BUTTON)
w.ImgPlay.SetFromIconName(START,
gtk.ICON_SIZE_BUTTON)
//w.MenuButt.SetImage(w.ImgMenu)
w.PlayButt.SetImage(w.ImgPlay)
w.PlayButt.SetSensitive(false)
w.PlayButt.SetSensitive(c)
w.FileButt.SetImage(w.ImgFile)
w.VolButt.SetValue(1)
w.VolButt.SetSensitive(false)
w.VolButt.SetSensitive(c)
//w.MenuButt.SetPopover(w.PopMenu)

// ProgScale
w.ProgScale.SetSensitive(false)
w.ProgScale.SetSensitive(c)
w.ProgScale.SetDrawValue(false)
w.ProgScale.SetHExpand(true)

Expand All @@ -67,7 +65,7 @@ func setTrackBoxValues(w Elements) Elements {
w.TrackBox.Add(w.ImgTrack)
w.ImgTrack.SetHExpand(true)
w.ImgTrack.SetVExpand(true)
w.ImgTrack.SetHAlign(gtk.ALIGN_CENTER)
w.ImgTrack.SetHAlign(gtk.ALIGN_BASELINE)
w.TrackBox.ShowAll()

return w
Expand Down
15 changes: 10 additions & 5 deletions logic/art.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@ import (
"path"
"strings"

"github.com/dhowden/tag"
"github.com/gotk3/gotk3/gdk"
)

// FindArt takes the art from the track and returns a pixbuf of it.
func FindArt(a AudioData) (*gdk.Pixbuf, error) {
// process picture
var pic *tag.Picture
mus, meta := a.openMusic()
defer mus.Close()

pic := meta.Picture()

if meta != nil {
pic = meta.Picture()
} else {
pic = nil
}
// todo (possibly maybe) wrap pixbuf_new_from_bytes for gotk3
f, err := os.Create("./.temp_cover")
if err != nil {
Expand All @@ -31,7 +36,7 @@ func FindArt(a AudioData) (*gdk.Pixbuf, error) {
if pic == nil {
cf, err := readFromCoverImg(mus)
if err != nil {
SendError(err, "no cover image, ignoring")
SendError(err, "image loading")
return nil, err
}
defer cf.Close()
Expand Down Expand Up @@ -73,7 +78,7 @@ func readFromCoverImg(mus *os.File) (*os.File, error) {
dir, _ := path.Split(mus.Name())
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, errors.New("Woah baby")
return nil, errors.New("cannot read directory")
}
for _, file := range files {
for _, format := range imgFormats {
Expand All @@ -83,5 +88,5 @@ func readFromCoverImg(mus *os.File) (*os.File, error) {
}
}

return nil, errors.New("No cover image found")
return nil, errors.New("no cover image found")
}
34 changes: 28 additions & 6 deletions logic/audio.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package logic
import (
"errors"
"os"
"strings"
"time"

"github.com/dhowden/tag"
Expand All @@ -14,38 +15,59 @@ import (
)

// Read reads and creates an Audio file
func Read(path string) AudioData {
func Read(path string) (AudioData, error) {
// Open file
println("here")
f, err := os.Open(path)
if err != nil {
SendError(err, "cannot open file")
return AudioData{}, err
}
var a AudioData
var ssc beep.StreamSeekCloser
var format beep.Format
a.Path = path
switch findFileType(a) {
musTag := findFileType(a)
switch musTag {
// TODO: FLAC needs to be able to seek.
case tag.FLAC:
ssc, format, err = flac.Decode(f)
case tag.MP3:
ssc, format, err = mp3.Decode(f)
// TODO: I need to detect vorbis encoding.
case tag.OGG:
ssc, format, err = vorbis.Decode(f)
default:
SendError(errors.New("Invalid stream type"), "cannot read file")
return AudioData{}
err = errors.New("invalid stream type")
SendError(err, "cannot read file")
return AudioData{}, err
}
if err != nil {
SendError(err, "cannot read file")
return AudioData{}, err
}

a = NewAudioData(&ssc, path)
// Play Audio
speaker.Init(format.SampleRate, format.SampleRate.N((time.Second / 10)))
speaker.Play(a.Vol)
return a
return a, nil
}

func findFileType(a AudioData) tag.FileType {
f, meta := a.openMusic()
defer f.Close() // better safe than sorry
if meta == nil || meta.FileType() == tag.UnknownFileType {
return noID3Search(f.Name())
}
return meta.FileType()
}

func noID3Search(s string) tag.FileType {
exts := []string{".mp3", ".ogg", ".flac"}
for _, ext := range exts {
if strings.Contains(s, ext) {
return tag.FileType(strings.ToUpper(strings.TrimPrefix(ext, ".")))
}
}
return tag.FileType("")
}
1 change: 1 addition & 0 deletions logic/audiodata.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func (a AudioData) openMusic() (*os.File, tag.Metadata) {
meta, err := tag.ReadFrom(mus)
if err != nil {
SendError(err, "writing file to metadata")
return mus, nil
}

return mus, meta
Expand Down
34 changes: 26 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,38 @@ import (
"listen/gui"
"listen/logic"
"os"
"strings"

"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
)

func main() {
const appID = "moe.jade.listen"
args := os.Args
app, err := gtk.ApplicationNew(appID, glib.APPLICATION_FLAGS_NONE)
app, err := gtk.ApplicationNew(appID, glib.APPLICATION_HANDLES_OPEN)

if err != nil {
logic.SendError(err, "application")
}
if args != nil {
// actions.ParseArgs(args)
}
app.Connect("activate", func() { activateConnect(app) })
app.Connect("open", func() { attemptOpen(app) })
app.Connect("activate", func() { activateConnect(app, false) })
app.Run(os.Args)
}

func activateConnect(app *gtk.Application) {
func activateConnect(app *gtk.Application, cmdLaunch bool) {

//create widgets
var window gui.Elements
window = gui.Elements.New(window, app)

//define widgets
window = gui.InitWidgets(window)
window = gui.InitWidgets(window, cmdLaunch)

// define actions
actions := gui.Actions{GUI: window}
if cmdLaunch {
actions = actions.LoadFromCMD(os.Args[1])
}

window.PlayButt.Connect("clicked",
func() { actions = actions.PlayPressed() })
Expand All @@ -51,3 +52,20 @@ func activateConnect(app *gtk.Application) {
window.ProgScale.ConnectAfter("draw",
func() { actions = actions.DrawProg() })
}

func attemptOpen(app *gtk.Application) {
isMP3 := false
if len(os.Args) > 1 {
_, err := os.Stat(os.Args[1])
if err == nil {
if strings.HasSuffix(strings.ToLower(os.Args[1]), ".mp3") {
isMP3 = true
} else {
logic.SendError(err, "not a mp3 file")
}
} else {
logic.SendError(err, "invalid file, ignoring")
}
}
activateConnect(app, isMP3)
}

0 comments on commit 56b184a

Please sign in to comment.