gomu/utils.go

227 lines
4.2 KiB
Go
Raw Normal View History

2020-06-22 00:05:56 +08:00
// Copyright (C) 2020 Raziman
2020-06-19 16:22:20 +08:00
package main
2020-06-24 11:34:20 +08:00
import (
2020-06-27 17:16:49 +08:00
"bytes"
"fmt"
2020-06-26 12:54:48 +08:00
"net/http"
2020-06-24 11:34:20 +08:00
"os"
2020-06-27 17:16:49 +08:00
"os/exec"
2020-06-25 14:12:23 +08:00
"path"
2020-06-27 17:16:49 +08:00
"regexp"
2020-06-24 11:34:20 +08:00
"strings"
"time"
2020-06-26 12:54:48 +08:00
"github.com/faiface/beep/mp3"
2020-06-27 17:16:49 +08:00
"github.com/rivo/tview"
"github.com/spf13/viper"
2020-06-24 11:34:20 +08:00
)
2020-06-19 16:22:20 +08:00
func log(text string) {
f, err := os.OpenFile("message.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
2020-06-21 23:47:12 +08:00
if _, err := f.Write([]byte(text + "\n")); err != nil {
2020-06-19 16:22:20 +08:00
panic(err)
}
if err := f.Close(); err != nil {
panic(err)
}
}
2020-06-24 11:34:20 +08:00
func fmtDuration(input time.Duration) string {
val := input.Round(time.Second).String()
if !strings.Contains(val, "m") {
val = "0m" + val
}
val = strings.ReplaceAll(val, "h", ":")
val = strings.ReplaceAll(val, "m", ":")
val = strings.ReplaceAll(val, "s", "")
var result []string
for _, v := range strings.Split(val, ":") {
2020-06-26 12:54:48 +08:00
2020-06-24 11:34:20 +08:00
if len(v) < 2 {
2020-06-26 12:54:48 +08:00
result = append(result, "0"+v)
2020-06-24 11:34:20 +08:00
} else {
result = append(result, v)
}
}
return strings.Join(result, ":")
}
2020-06-25 14:12:23 +08:00
func expandTilde(_path string) string {
if !strings.HasPrefix(_path, "~") {
return _path
}
2020-06-26 12:54:48 +08:00
home, err := os.UserHomeDir()
2020-06-25 14:12:23 +08:00
if err != nil {
log(err.Error())
}
return path.Join(home, strings.TrimPrefix(_path, "~"))
}
2020-06-26 12:54:48 +08:00
// gets the length of the song in the queue
func GetLength(audioPath string) (time.Duration, error) {
f, err := os.Open(audioPath)
defer f.Close()
if err != nil {
return 0, err
}
streamer, format, err := mp3.Decode(f)
defer streamer.Close()
if err != nil {
return 0, err
}
return format.SampleRate.D(streamer.Len()), nil
}
// detects the filetype of file
func GetFileContentType(out *os.File) (string, error) {
buffer := make([]byte, 512)
_, err := out.Read(buffer)
if err != nil {
return "", err
}
contentType := http.DetectContentType(buffer)
return strings.SplitAfter(contentType, "/")[1], nil
}
// gets the file name by removing extension and path
func GetName(fn string) string {
return strings.TrimSuffix(path.Base(fn), path.Ext(fn))
}
2020-06-27 17:16:49 +08:00
// download audio from youtube audio and adds the song to the selected playlist
func Ytdl(url string, selPlaylist *tview.TreeNode) {
2020-06-28 12:52:38 +08:00
// lookup if youtube-dl exists
_, err := exec.LookPath("youtube-dl")
if err != nil {
timeoutPopup(" Error ", "youtube-dl is not in your $PATH", time.Second*5)
return
}
2020-06-27 17:16:49 +08:00
dir := viper.GetString("music_dir")
selAudioFile := selPlaylist.GetReference().(*AudioFile)
selPlaylistName := selAudioFile.Name
timeoutPopup(" Ytdl ", "Downloading", time.Second*5)
// specify the output path for ytdl
outputDir := fmt.Sprintf(
2020-06-27 17:18:10 +08:00
"%s/%s/%%(artist)s - %%(track)s.%%(ext)s",
dir,
2020-06-27 17:16:49 +08:00
selPlaylistName)
args := []string{
"--extract-audio",
"--audio-format",
"mp3",
"--output",
outputDir,
url,
}
cmd := exec.Command("youtube-dl", args...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
go func() {
err := cmd.Run()
if err != nil {
timeoutPopup(" Error ", "An error occured when downloading", time.Second*5)
return
}
if cmd.Stderr != nil {
timeoutPopup(" Error ", "An error occured when downloading", time.Second*5)
return
}
playlistPath := fmt.Sprintf("%s/%s", expandTilde(dir), selPlaylistName)
downloadedAudioPath := downloadedFilePath(
stdout.Bytes(), playlistPath)
f, err := os.Open(downloadedAudioPath)
if err != nil {
log(err.Error())
}
defer f.Close()
node := tview.NewTreeNode(path.Base(f.Name()))
audioFile := &AudioFile{
Name: f.Name(),
Path: downloadedAudioPath,
IsAudioFile: true,
Parent: selPlaylist,
}
node.SetReference(audioFile)
selPlaylist.AddChild(node)
app.Draw()
timeoutPopup(
" Ytdl ",
2020-06-27 17:18:10 +08:00
fmt.Sprintf("Finished downloading\n%s",
path.Base(downloadedAudioPath)), time.Second*5)
2020-06-27 17:16:49 +08:00
}()
}
// this just parsing the output from the ytdl to get the audio path
// this is used because we need to get the song name
// example ~/path/to/song/song.mp3
func downloadedFilePath(output []byte, dir string) string {
regexSearch := fmt.Sprintf(`\[ffmpeg\] Destination: %s\/.*.mp3`,
escapeBackSlash(dir))
parseAudioPathOnly := regexp.MustCompile(`\/.*mp3$`)
re := regexp.MustCompile(regexSearch)
return string(parseAudioPathOnly.Find(re.Find(output)))
}
func escapeBackSlash(input string) string {
return strings.ReplaceAll(input, "/", `\/`)
}