tstor/pkg/ytdlp/download.go

106 lines
2.7 KiB
Go
Raw Permalink Normal View History

2024-05-13 16:56:20 +00:00
package ytdlp
import (
"context"
"encoding/json"
2024-06-14 22:14:44 +00:00
"io"
2024-05-13 16:56:20 +00:00
"os/exec"
"strings"
"github.com/royalcat/ctxprogress"
"golang.org/x/sync/errgroup"
)
type DownloadStatus string
const (
StatusDownloading DownloadStatus = "downloading"
StatusFinished DownloadStatus = "finished"
StatusErrored DownloadStatus = "error"
)
// Progress for the Running call
type DownloadProgress struct {
Status DownloadStatus `json:"status"`
Filename string `json:"filename"`
TmpFilename string `json:"tmpfilename"`
DownloadedBytes int64 `json:"downloaded_bytes"`
TotalBytes int64 `json:"total_bytes"`
TotalBytesEstimate float64 `json:"total_bytes_estimate"`
Elapsed float64 `json:"elapsed"`
ETA float64 `json:"eta"`
Speed float64 `json:"speed"`
FragmentIndex int64 `json:"fragment_index"`
FragmentCount int64 `json:"fragment_count"`
}
// Current implements ctxprogress.Progress.
func (d DownloadProgress) Progress() (int, int) {
if d.TotalBytes != -1 && d.TotalBytes != 0 && d.DownloadedBytes != -1 {
return int(d.DownloadedBytes), int(d.TotalBytes)
}
if d.TotalBytesEstimate != -1 && d.TotalBytesEstimate != 0 && d.DownloadedBytes != -1 {
return int(d.DownloadedBytes), int(d.TotalBytesEstimate)
}
return int(d.FragmentIndex), int(d.FragmentCount)
}
const rawProgressTemplate = `download:
%{
"status":"%(progress.status)s",
"eta":%(progress.eta|-1)s,
"speed":%(progress.speed|0)s,
"downloaded_bytes":%(progress.downloaded_bytes|-1)s,
"total_bytes": %(progress.total_bytes|-1)s,
"total_bytes_estimate": %(progress.total_bytes_estimate|-1)s,
"fragment_index":%(progress.fragment_index|-1)s,
"fragment_count":%(progress.fragment_count|-1)s
}`
var progressTemplate = strings.NewReplacer("\n", "", "\t", "", " ", "").Replace(rawProgressTemplate)
2024-06-14 22:14:44 +00:00
func (c *Client) Download(ctx context.Context, url string, w io.Writer) error {
2024-05-13 16:56:20 +00:00
args := []string{
"--progress", "--newline", "--progress-template", progressTemplate,
2024-06-14 22:14:44 +00:00
"-o", "-",
2024-05-13 16:56:20 +00:00
url,
}
group, ctx := errgroup.WithContext(ctx)
2024-06-14 22:14:44 +00:00
stderr, lines, err := lineReader(group)
2024-05-13 16:56:20 +00:00
if err != nil {
return err
}
cmd := exec.CommandContext(ctx, c.binary, args...)
cmd.Stdout = w
2024-06-14 22:14:44 +00:00
cmd.Stderr = stderr
2024-05-13 16:56:20 +00:00
group.Go(func() error {
err := cmd.Run()
2024-06-14 22:14:44 +00:00
stderr.Close()
2024-05-13 16:56:20 +00:00
if err != nil {
return err
}
return nil
})
for line := range lines {
if line, ok := strings.CutPrefix(line, "%"); ok {
p := DownloadProgress{}
err = json.Unmarshal([]byte(line), &p)
if err != nil {
//TODO: handle error
continue
}
ctxprogress.Set(ctx, p)
}
}
2024-06-14 22:14:44 +00:00
return group.Wait()
2024-05-13 16:56:20 +00:00
}