Telegram bot for downloading media from any site using yt-dlp. Supports audio/video/both, quality selection, language picker, container format selection, playlist pagination, progress updates, per-user quotas, user preferences, download history, cancel at any step, polling and webhook modes, proxy support, and SQLite persistence.
79 lines
1.8 KiB
Go
79 lines
1.8 KiB
Go
package dl
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"uptodownBot/internal/domain"
|
|
)
|
|
|
|
var progressRE = regexp.MustCompile(`\[\w+\]\s+([\d.]+)%\s+of\s+~?([\d.]+)(\w+)`)
|
|
|
|
type progressUpdate struct {
|
|
pct float64
|
|
speed string
|
|
eta string
|
|
}
|
|
|
|
// parseProgressLine parses a single yt-dlp stderr progress line.
|
|
// Returns nil if the line doesn't contain progress info.
|
|
func parseProgressLine(line string) *progressUpdate {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" || !strings.HasPrefix(line, "[download]") {
|
|
return nil
|
|
}
|
|
// Try to extract the simple percentage from the default format:
|
|
// [download] 45.2% of 123.45MiB at 2.34MiB/s ETA 00:45
|
|
m := progressRE.FindStringSubmatch(line)
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
pct, err := strconv.ParseFloat(m[1], 64)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
u := &progressUpdate{pct: pct}
|
|
|
|
// Extract speed and ETA from remaining parts
|
|
rest := line
|
|
if idx := strings.Index(rest, "%"); idx >= 0 {
|
|
rest = rest[idx+1:]
|
|
}
|
|
// Look for "at <speed>"
|
|
if atIdx := strings.Index(rest, " at "); atIdx >= 0 {
|
|
afterAt := rest[atIdx+4:]
|
|
if spaceIdx := strings.Index(afterAt, " "); spaceIdx >= 0 {
|
|
u.speed = strings.TrimSpace(afterAt[:spaceIdx])
|
|
rest = afterAt[spaceIdx:]
|
|
}
|
|
}
|
|
// Look for "ETA <duration>"
|
|
if etaIdx := strings.Index(rest, "ETA "); etaIdx >= 0 {
|
|
etaStr := strings.TrimSpace(rest[etaIdx+4:])
|
|
u.eta = etaStr
|
|
}
|
|
|
|
return u
|
|
}
|
|
|
|
// readProgress reads yt-dlp stderr and sends progress updates to the channel.
|
|
func readProgress(stderr io.ReadCloser, updates chan<- *domain.DownloadJob, job *domain.DownloadJob) {
|
|
defer close(updates)
|
|
scanner := bufio.NewScanner(stderr)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
parsed := parseProgressLine(line)
|
|
if parsed == nil {
|
|
continue
|
|
}
|
|
job.Progress = parsed.pct
|
|
job.Speed = parsed.speed
|
|
job.ETA = parsed.eta
|
|
updates <- job
|
|
}
|
|
}
|