package main import ( "bufio" "encoding/json" "fmt" "log" "math" "net/url" "os" "os/exec" "strconv" "time" "golang.org/x/time/rate" ) var prate = rate.Sometimes{Interval: 1 * time.Second} var yturl = "https://youtu.be/%s" func NewYTdlUrl(vid string) ([]byte, error) { ytdl := config.GetString("youtube.ytdl") yt := exec.Command( ytdl, fmt.Sprintf(yturl, vid), "--cookies", "./cookies.txt", "--no-call-home", "--no-cache-dir", "--ignore-errors", "--newline", "--restrict-filenames", "-f", "234", "--get-url", ) yt.Stderr = os.Stderr uri, err := yt.Output() if err != nil { return nil, err } return uri[:len(uri)-1], nil } func NewYTdl(vid string) ([]byte, error) { tmpfile, err := exec.Command("mktemp", "/tmp/dnd_ytdlp_XXXXXXXXXXXX.m4a").Output() if err != nil { return nil, err } tmpfile = tmpfile[:len(tmpfile)-1] ytdl := config.GetString("youtube.ytdl") yt := exec.Command( ytdl, fmt.Sprintf(yturl, vid), "-q", "--cookies", "./cookies.txt", "--no-call-home", "--no-cache-dir", "--ignore-errors", "--newline", "--restrict-filenames", "--force-overwrites", "--progress", "--progress-template", "download:{ \"dl_bytes\": \"%(progress.downloaded_bytes)s\", \"total_bytes\": \"%(progress.total_bytes)s\" }", "-f", "251", "-o", string(tmpfile), ) yt.Stderr = os.Stderr ytprogress, err := yt.StdoutPipe() if err != nil { return nil, err } err = yt.Start() if err != nil { return nil, err } log.Printf("Start ffmpeg to extract audio to %s", string(tmpfile)) msg := make(map[string]interface{}) msg["event"] = "ambiance_download_start" data := make(map[string]string) data["name"] = fmt.Sprintf(yturl, vid) msg["payload"] = data ws_msg <- msg msg = make(map[string]interface{}) msg["event"] = "ambiance_download_progress" data = make(map[string]string) data["name"] = fmt.Sprintf(yturl, vid) scanner := bufio.NewScanner(ytprogress) for scanner.Scan() { err := json.Unmarshal(scanner.Bytes(), &data) if err != nil { log.Println(err) continue } dl, _ := strconv.ParseFloat(data["dl_bytes"], 64) total, _ := strconv.ParseFloat(data["total_bytes"], 64) percent := math.Floor((dl / total) * 100) data["percent"] = strconv.FormatInt(int64(percent), 10) prate.Do(func() { msg["payload"] = data ws_msg <- msg }) } if err := scanner.Err(); err != nil { return nil, err } err = yt.Wait() if err != nil { return nil, err } msg = make(map[string]interface{}) msg["event"] = "ambiance_download_complete" data = make(map[string]string) data["name"] = fmt.Sprintf(yturl, vid) msg["payload"] = data ws_msg <- msg return tmpfile, nil } func YTUrl(uri string) (vid string, err error) { u, err := url.Parse(uri) if err != nil { return "", err } switch u.Host { case "youtu.be": vid = u.Path[1:] case "m.youtube.com": fallthrough case "youtube.com": fallthrough case "www.youtube.com": vid = u.Query().Get("v") } if vid == "" { return vid, fmt.Errorf("unable to parse vid") } return } /* func DownloadAmbiance(uri string, name string) error { ytdl := config.GetString("youtube.ytdl") cmd := exec.Command( ytdl, uri, "--no-call-home", "--no-cache-dir", "-f", "140", "--cookies", "../cookies.txt", "-o", "-", "--force-overwrites", "-q", "--progress", "--progress-template", "%(progress)j", "--no-colors", "--newline", "--fixup", "never", ) cmdout, err := cmd.StdoutPipe() if err != nil { return err } err = cmd.Start() if err != nil { return err } msg := make(map[string]interface{}) msg["event"] = "ambiance_download_start" data := make(map[string]string) data["name"] = name msg["payload"] = data ws_msg <- msg msg = make(map[string]interface{}) msg["event"] = "ambiance_download_progress" data = make(map[string]string) scanner := bufio.NewScanner(cmdout) for scanner.Scan() { prate.Do(func() { js := gjson.GetManyBytes(scanner.Bytes(), "status", "downloaded_bytes", "total_bytes", "eta", "speed") data["name"] = name data["status"] = js[0].String() data["downloaded_bytes"] = js[1].String() data["total_bytes"] = js[2].String() data["eta"] = js[3].String() data["speed"] = js[4].String() msg["payload"] = data ws_msg <- msg }) } if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "reading output:", err) } err = cmd.Wait() if err != nil { return err } msg = make(map[string]interface{}) msg["event"] = "ambiance_download_complete" data = make(map[string]string) data["name"] = name msg["payload"] = data ws_msg <- msg ff := exec.Command( "ffmpeg", "-i", string(tmpfile), "-v", "error", // "-stats", "-progress", "pipe:1", "-ar", strconv.Itoa(sampleRate), "-ac", strconv.Itoa(channels), "-af", "loudnorm=I=-16:LRA=11:TP=-1.5", filepath.Join("./ambiance/", name+".mp3"), ) ffprogress, err := ff.StdoutPipe() if err != nil { return err } err = ff.Start() if err != nil { return err } msg = make(map[string]interface{}) msg["event"] = "ambiance_encode_start" data = make(map[string]string) data["name"] = name msg["payload"] = data ws_msg <- msg msg = make(map[string]interface{}) msg["event"] = "ambiance_encode_progress" data = make(map[string]string) data["name"] = name scanner = bufio.NewScanner(ffprogress) for scanner.Scan() { p := strings.Split(scanner.Text(), "=") if len(p) == 2 { data[p[0]] = p[1] } prate.Do(func() { msg["payload"] = data ws_msg <- msg }) } if err := scanner.Err(); err != nil { return err } err = ff.Wait() if err != nil { return err } msg = make(map[string]interface{}) msg["event"] = "ambiance_encode_complete" data = make(map[string]string) data["name"] = name msg["payload"] = data ws_msg <- msg return nil } */