Commit fdfdcecc authored by Trevor Slocum's avatar Trevor Slocum

Initial commit

parents
arch: amd64
environment:
PROJECT_NAME: 'ditty'
CGO_ENABLED: '1'
GO111MODULE: 'on'
image: freebsd/latest
packages:
- go
sources:
- https://git.sr.ht/~tslocum/ditty
tasks:
- test: |
cd $PROJECT_NAME
go test ./...
arch: x86_64
environment:
PROJECT_NAME: 'ditty'
CGO_ENABLED: '1'
GO111MODULE: 'on'
image: alpine/edge
packages:
- go
sources:
- https://git.sr.ht/~tslocum/ditty
tasks:
- test: |
cd $PROJECT_NAME
go test ./...
.idea/
dist/
vendor/
*.sh
ditty
0.1.0:
- Initial release
This document covers the [ditty](https://git.sr.ht/~tslocum/ditty) command-line options.
# TODO
WIP
MIT License
Copyright (c) 2020 Trevor Slocum <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# ditty
[![GoDoc](https://godoc.org/git.sr.ht/~tslocum/ditty?status.svg)](https://godoc.org/git.sr.ht/~tslocum/ditty)
[![builds.sr.ht status](https://builds.sr.ht/~tslocum/ditty.svg)](https://builds.sr.ht/~tslocum/ditty)
[![Donate](https://img.shields.io/liberapay/receives/rocketnine.space.svg?logo=liberapay)](https://liberapay.com/rocketnine.space)
Audio player
## Screenshot
[![](https://ditty.rocketnine.space/static/screenshot1.png)](https://ditty.rocketnine.space/static/screenshot1.png)
## Install
Choose one of the following methods:
### Download
[**Download ditty**](https://ditty.rocketnine.space/download/?sort=name&order=desc)
### Compile
```
GO111MODULE=on go get git.sr.ht/~tslocum/ditty
```
## Configure
See [CONFIGURATION.md](https://man.sr.ht/~tslocum/ditty/CONFIGURATION.md)
## Support
Please share issues/suggestions [here](https://todo.sr.ht/~tslocum/ditty).
package main
import (
"fmt"
"io"
"log"
"math"
"os"
"path"
"strings"
"sync"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/effects"
"github.com/faiface/beep/flac"
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
"github.com/faiface/beep/vorbis"
"github.com/faiface/beep/wav"
)
var (
playingFileName string
playingFileInfo string
playingFileID int64
playingStreamer beep.StreamSeekCloser
playingFormat beep.Format
playingSampleRate beep.SampleRate
nextStreamer beep.StreamSeekCloser
nextFormat beep.Format
nextFileName string
volume *effects.Volume
ctrl *beep.Ctrl
audioLock = new(sync.Mutex)
)
type AudioFile struct {
File *os.File
Streamer beep.StreamSeekCloser
Format beep.Format
Metadata *Metadata
}
func openFile(filePath string) (*AudioFile, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
metadata := readMetadata(f)
_, err = f.Seek(0, io.SeekStart)
if err != nil {
log.Fatal(err)
}
var (
streamer beep.StreamSeekCloser
format beep.Format
)
switch strings.ToLower(path.Ext(filePath)) {
case ".wav":
streamer, format, err = wav.Decode(f)
case ".mp3":
streamer, format, err = mp3.Decode(f)
case ".ogg", ".weba", ".webm":
streamer, format, err = vorbis.Decode(f)
case ".flac":
streamer, format, err = flac.Decode(f)
default:
err = fmt.Errorf("unsupported file format")
}
if err != nil {
return nil, err
}
a := AudioFile{File: f, Streamer: streamer, Format: format, Metadata: metadata}
return &a, nil
}
func play(audioFile *AudioFile) {
audioLock.Lock()
defer audioLock.Unlock()
if playingStreamer != nil {
playingStreamer.Close()
}
thisFileID := time.Now().UnixNano()
playingFileID = thisFileID
playingStreamer = audioFile.Streamer
playingFormat = audioFile.Format
playingFileName = audioFile.File.Name()
playingFileInfo = ""
if audioFile.Metadata.Title != "" {
playingFileInfo = audioFile.Metadata.Title
if audioFile.Metadata.Artist != "" {
playingFileInfo = audioFile.Metadata.Artist + " - " + playingFileInfo
}
}
if audioFile.Format.SampleRate != playingSampleRate {
err := speaker.Init(audioFile.Format.SampleRate, audioFile.Format.SampleRate.N(time.Second/2))
if err != nil {
log.Fatalf("failed to initialize audio device: %s", err)
}
}
var (
vol float64
silent bool
)
speaker.Lock()
if volume != nil {
vol = volume.Volume
silent = volume.Silent
}
speaker.Unlock()
volume = &effects.Volume{
Streamer: beep.Seq(audioFile.Streamer, beep.Callback(func() {
if playingFileID != thisFileID {
return
}
go nextTrack()
})),
Base: volumeBase,
Volume: vol,
Silent: silent,
}
ctrl = &beep.Ctrl{
Streamer: volume,
Paused: false,
}
speaker.Clear()
speaker.Play(ctrl)
app.QueueUpdateDraw(func() {
updateMain()
updateQueue()
updateStatus()
})
}
func nextTrack() {
if mainBufferCursor-1 < len(mainBufferFiles)-1 {
mainBufferCursor++
audioFile, err := openFile(path.Join(mainBufferDirectory, mainBufferFiles[mainBufferCursor-1].File.Name()))
if err != nil {
return
}
play(audioFile)
app.QueueUpdateDraw(updateMain)
}
}
func roundUnit(x, unit float64) float64 {
return math.Round(x/unit) * unit
}
func supportedFormat(filePath string) bool {
switch strings.ToLower(path.Ext(filePath)) {
case ".wav":
return true
case ".mp3":
return true
case ".ogg", ".weba", ".webm":
return true
case ".flac":
return true
default:
return false
}
}
func fileFormat(fileName string) string {
switch strings.ToLower(path.Ext(fileName)) {
case ".wav":
return "WAV"
case ".mp3":
return "MP3"
case ".ogg", ".weba", ".webm":
return "OGG"
case ".flac":
return "FLAC"
default:
return "?"
}
}
module git.sr.ht/~tslocum/ditty
go 1.13
require (
git.sr.ht/~tslocum/cview v0.2.2
github.com/dhowden/tag v0.0.0-20191122115059-7e5c04feccd8
github.com/faiface/beep v1.0.2
github.com/gdamore/tcell v1.3.0
github.com/hajimehoshi/go-mp3 v0.2.1 // indirect
github.com/hajimehoshi/oto v0.5.4 // indirect
github.com/jfreymuth/oggvorbis v1.0.1 // indirect
github.com/mattn/go-runewidth v0.0.7
github.com/mewkiz/flac v1.0.6 // indirect
golang.org/x/exp v0.0.0-20191227195350-da58074b4299 // indirect
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 // indirect
golang.org/x/mobile v0.0.0-20191210151939-1a1fef82734d // indirect
golang.org/x/sys v0.0.0-20200107162124-548cf772de50 // indirect
)
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.sr.ht/~tslocum/cview v0.2.2 h1:eIN9Wy/DIHP9///qcz9Q7JkMP36duA5iyTP0GJ+WhvY=
git.sr.ht/~tslocum/cview v0.2.2/go.mod h1:TLTjvAd3pw6MqV6SaBMpxOdOdODW4O2gtQJ3B3H6PoU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
github.com/dhowden/tag v0.0.0-20191122115059-7e5c04feccd8 h1:nmFnmD8VZXkjDHIb1Gnfz50cgzUvGN72zLjPRXBW/hU=
github.com/dhowden/tag v0.0.0-20191122115059-7e5c04feccd8/go.mod h1:SniNVYuaD1jmdEEvi+7ywb1QFR7agjeTdGKyFb0p7Rw=
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=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ=
github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m256AsaDPQ+/4=
github.com/gopherjs/gopherwasm v1.0.0 h1:32nge/RlujS1Im4HNCJPp0NbBOAeBXFuT1KonUuLl+Y=
github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
github.com/hajimehoshi/go-mp3 v0.1.1 h1:Y33fAdTma70fkrxnc9u50Uq0lV6eZ+bkAlssdMmCwUc=
github.com/hajimehoshi/go-mp3 v0.1.1/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6gNhjCx9WBJw=
github.com/hajimehoshi/go-mp3 v0.2.1 h1:DH4ns3cPv39n3cs8MPcAlWqPeAwLCK8iNgqvg0QBWI8=
github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE=
github.com/hajimehoshi/oto v0.1.1/go.mod h1:hUiLWeBQnbDu4pZsAhOnGqMI1ZGibS6e2qhQdfpwz04=
github.com/hajimehoshi/oto v0.3.1 h1:cpf/uIv4Q0oc5uf9loQn7PIehv+mZerh+0KKma6gzMk=
github.com/hajimehoshi/oto v0.3.1/go.mod h1:e9eTLBB9iZto045HLbzfHJIc+jP3xaKrjZTghvb6fdM=
github.com/hajimehoshi/oto v0.3.4/go.mod h1:PgjqsBJff0efqL2nlMJidJgVJywLn6M4y8PI4TfeWfA=
github.com/hajimehoshi/oto v0.5.4 h1:Dn+WcYeF310xqStKm0tnvoruYUV5Sce8+sfUaIvWGkE=
github.com/hajimehoshi/oto v0.5.4/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/icza/bitio v1.0.0 h1:squ/m1SHyFeCA6+6Gyol1AxV9nmPPlJFT8c2vKdj3U8=
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k=
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
github.com/jfreymuth/oggvorbis v1.0.0 h1:aOpiihGrFLXpsh2osOlEvTcg5/aluzGQeC7m3uYWOZ0=
github.com/jfreymuth/oggvorbis v1.0.0/go.mod h1:abe6F9QRjuU9l+2jek3gj46lu40N4qlYxh2grqkLEDM=
github.com/jfreymuth/oggvorbis v1.0.1 h1:NT0eXBgE2WHzu6RT/6zcb2H10Kxj6Fm3PccT0LE6bqw=
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
github.com/jfreymuth/vorbis v1.0.0 h1:SmDf783s82lIjGZi8EGUUaS7YxPHgRj4ZXW/h7rUi7U=
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mewkiz/flac v1.0.5 h1:dHGW/2kf+/KZ2GGqSVayNEhL9pluKn/rr/h/QqD9Ogc=
github.com/mewkiz/flac v1.0.5/go.mod h1:EHZNU32dMF6alpurYyKHDLYpW1lYpBZ5WrXi/VuNIGs=
github.com/mewkiz/flac v1.0.6 h1:OnMwCWZPAnjDndjEzLynOZ71Y2U+/QYHoVI4JEKgKkk=
github.com/mewkiz/flac v1.0.6/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2 h1:EyTNMdePWaoWsRSGQnXiSoQu0r6RS1eA557AwJhlzHU=
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
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/exp v0.0.0-20180710024300-14dda7b62fcd h1:nLIcFw7GiqKXUS7HiChg6OAYWgASB2H97dZKd1GhDSs=
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=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
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-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20180806140643-507816974b79 h1:t2JRgCWkY7Qaa1J2jal+wqC9OjbyHCHwIA9rVlRUSMo=
golang.org/x/mobile v0.0.0-20180806140643-507816974b79/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20191210151939-1a1fef82734d h1:LlA9R5JFi974qK4gm9FRK1+qSkduxnQKcrimdzcidyc=
golang.org/x/mobile v0.0.0-20191210151939-1a1fef82734d/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 h1:/W9OPMnnpmFXHYkcp2rQsbFUbRlRzfECQjmAFiOyHE8=
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
project_name: ditty
builds:
-
id: ditty
binary: ditty
env:
- CGO_ENABLED=1
ldflags:
- -s -w -X git.sr.ht/~tslocum/ditty/version={{.Version}}
goos:
- linux
- windows
goarch:
- amd64
archives:
-
id: ditty
builds:
- ditty
format_overrides:
- goos: windows
format: zip
files:
- ./*.md
- CHANGELOG
- LICENSE
checksum:
name_template: 'checksums.txt'
package main
import (
"fmt"
"math"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/mattn/go-runewidth"
"github.com/gdamore/tcell"
"github.com/faiface/beep"
"git.sr.ht/~tslocum/cview"
"github.com/faiface/beep/speaker"
)
var (
app *cview.Application
mainbuf *cview.TextView
queuebuf *cview.TextView
topstatusbuf *cview.TextView
bottomstatusbuf *cview.TextView
mainBufferText string
mainBufferFiles []*LibraryEntry
mainBufferCursor int
mainBufferDirectory string
seekStart, seekEnd int
volumeStart, volumeEnd int
screenWidth, screenHeight int
mainBufHeight int
statusText string
)
func initTUI() error {
app = cview.NewApplication()
app.EnableMouse()
app.SetInputCapture(handleKeyPress)
app.SetAfterResizeFunc(handleResize)
app.SetMouseCapture(handleMouse)
grid := cview.NewGrid().SetRows(-2, -1, 1, 1).SetColumns(-1)
mainbuf = cview.NewTextView().SetDynamicColors(true).SetWrap(true).SetWordWrap(false)
queuebuf = cview.NewTextView().SetDynamicColors(true).SetWrap(true).SetWordWrap(false)
topstatusbuf = cview.NewTextView().SetWrap(false).SetWordWrap(false)
bottomstatusbuf = cview.NewTextView().SetWrap(false).SetWordWrap(false)
mainbuf.SetBorder(true).SetTitleAlign(cview.AlignLeft)
queuebuf.SetBorder(true).SetTitleAlign(cview.AlignLeft).SetTitle(" Queue ")
grid.AddItem(mainbuf, 0, 0, 1, 1, 0, 0, false)
grid.AddItem(queuebuf, 1, 0, 1, 1, 0, 0, false)
grid.AddItem(topstatusbuf, 2, 0, 1, 1, 0, 0, false)
grid.AddItem(bottomstatusbuf, 3, 0, 1, 1, 0, 0, false)
mainbuf.SetDrawFunc(func(screen tcell.Screen, x, y, width, height int) (i int, i2 int, i3 int, i4 int) {
mainBufHeight = height
return mainbuf.GetInnerRect()
})
app.SetRoot(grid, true)
return nil
}
func browseFolder(browse string) {
var err error
browse, err = filepath.Abs(browse)
if err != nil {
return
}
mainBufferFiles = scanFolder(browse)
var b strings.Builder
b.WriteString("..")
for _, entry := range mainBufferFiles {
b.WriteRune('\n')
b.WriteString(entry.String())
}
if len(mainBufferFiles) > 0 {
mainBufferCursor = 1
} else {
mainBufferCursor = 0
}
mainBufferDirectory = browse
mainBufferText = b.String()
app.QueueUpdateDraw(updateMain)
}
func updateMain() {
var titleText string
if statusText != "" {
titleText = statusText
} else {
titleText = mainBufferDirectory
truncated := false
widthRequirement := 4
for {
if runewidth.StringWidth(titleText) <= screenWidth-widthRequirement || !strings.ContainsRune(titleText, os.PathSeparator) {
break
}
titleText = titleText[strings.IndexRune(titleText, '/')+1:]
truncated = true
widthRequirement = 8
}
if truncated {
titleText = ".../" + titleText
}
titleText = runewidth.Truncate(titleText, screenWidth-4, "...")
}
mainbuf.SetTitle(" " + titleText + " ")
var printed int
var newBufferText string
if mainBufferCursor == 0 {
newBufferText += "[::r]"
}
var line string
if mainBufferDirectory == "/" {
line = "./"
} else {
line = "../"
}
newBufferText += line
for i := len(line); i < screenWidth-2; i++ {
newBufferText += " "