replace progress percentage with actual file size

This commit is contained in:
2026-03-04 23:58:11 -05:00
parent 3cac63ba82
commit 481c8d9bb6
4 changed files with 93 additions and 68 deletions

View File

@@ -48,9 +48,8 @@ func startAsyncDownload(s *discordgo.Session, i *discordgo.InteractionCreate, ur
// Handle progress and results asynchronously
go func() {
// First update the original ephemeral message with "Processing..."
_, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: ptr(fmt.Sprintf("Downloading video: %s", renderProgressBar(0))),
Content: ptr("⏬ downloading: starting..."),
})
if err != nil {
log.Printf("Error updating interaction: %v", err)
@@ -63,35 +62,28 @@ func startAsyncDownload(s *discordgo.Session, i *discordgo.InteractionCreate, ur
progressChan = nil
continue
}
var content string
if prog.Phase == "post-processing" {
phaseEmoji := "⚙️"
content := fmt.Sprintf("%s %s",
phaseEmoji,
prog.Phase)
_, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: ptr(content),
})
if err != nil {
log.Printf("Error updating progress: %v", err)
}
content = "⚙️ post-processing"
} else {
phaseEmoji := "⏬"
content := fmt.Sprintf("%s %s: %s [eta: %s]\n📄 %s",
phaseEmoji,
prog.Phase,
renderProgressBar(prog.PercentFloat),
prog.ETA,
prog.Filename)
_, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: ptr(content),
})
if err != nil {
log.Printf("Error updating progress: %v", err)
var progressStr string
if prog.DownloadedBytes > 0 {
progressStr = formatBytes(prog.DownloadedBytes) + " downloaded"
} else {
progressStr = "starting..."
}
content = fmt.Sprintf("Downloading Video: %s", progressStr)
}
_, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: ptr(content),
})
if err != nil {
log.Printf("Error updating progress: %v", err)
}
case result := <-resultChan:
// Handle completion
if result.Success {
_, err = s.FollowupMessageCreate(i.Interaction, false, &discordgo.WebhookParams{
Content: "📥 Video downloaded: " + result.URL,

View File

@@ -1,19 +1,21 @@
package main
import (
"fmt"
"strings"
)
import "fmt"
// Helper function to create string pointer
func ptr(s string) *string {
return &s
}
const progressBarLength = 20
func renderProgressBar(percent float64) string {
filled := int(float64(progressBarLength) * percent / 100)
bar := strings.Repeat("█", filled) + strings.Repeat(" ", progressBarLength-filled)
return fmt.Sprintf("[%s] %.0f%%", bar, percent)
func formatBytes(b int) string {
switch {
case b >= 1<<30:
return fmt.Sprintf("%.1f GB", float64(b)/float64(1<<30))
case b >= 1<<20:
return fmt.Sprintf("%.1f MB", float64(b)/float64(1<<20))
case b >= 1<<10:
return fmt.Sprintf("%.1f KB", float64(b)/float64(1<<10))
default:
return fmt.Sprintf("%d B", b)
}
}

View File

@@ -1,11 +1,5 @@
package main
import (
"time"
"github.com/lrstanley/go-ytdlp"
)
type DownloadOptions struct {
EmbedThumbnail bool
IncludeSubtitles bool
@@ -35,12 +29,9 @@ type FormatOptions struct {
}
type ProgressUpdate struct {
Status ytdlp.ProgressStatus
Percent string
PercentFloat float64
ETA time.Duration
Filename string
Phase string
Phase string
DownloadedBytes int
Filename string
}
// InteractionState holds the state for a specific interaction

View File

@@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"os"
"path/filepath"
"sync"
"time"
"github.com/lrstanley/go-ytdlp"
@@ -95,42 +97,80 @@ func GetFormats(url string) (*FormatOptions, error) {
return formatOpts, nil
}
func dirSize(path string) int {
var total int64
filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if err == nil && !info.IsDir() {
total += info.Size()
}
return nil
})
return int(total)
}
func DownloadVideo(out_dir, temp_dir, url string, opts DownloadOptions, progressChan chan<- ProgressUpdate) {
defer close(progressChan)
var mu sync.Mutex
currentPhase := "downloading"
currentFilename := ""
// Poll the temp directory for actual bytes-on-disk progress.
// The yt-dlp progress callback only tracks phase/filename since
// DownloadedBytes from the callback is unreliable for DASH streams.
var wg sync.WaitGroup
wg.Add(1)
stopPoll := make(chan struct{})
go func() {
defer wg.Done()
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-stopPoll:
return
case <-ticker.C:
mu.Lock()
phase := currentPhase
filename := currentFilename
mu.Unlock()
progressChan <- ProgressUpdate{
Phase: phase,
DownloadedBytes: dirSize(temp_dir),
Filename: filename,
}
}
}
}()
defer func() {
close(stopPoll)
wg.Wait()
close(progressChan)
}()
homePath := "home:" + out_dir
tempPath := "temp:" + temp_dir
var lastPhase string
dl := ytdlp.New().
SetExecutable(ytdlpBinary).
Paths(homePath).
Paths(tempPath).
RecodeVideo("mp4").
ProgressFunc(100*time.Millisecond, func(prog ytdlp.ProgressUpdate) {
// Detect phase transition -- differentiate "downloading" as the main download
// and "post processing" when the file name changes, preventing it from appearing "reset"
if prog.Status == ytdlp.ProgressStatusFinished ||
prog.Status == ytdlp.ProgressStatusStarting ||
prog.Status == ytdlp.ProgressStatusError {
return
}
phase := "downloading"
if prog.Status == ytdlp.ProgressStatusDownloading && prog.Percent() == 0.0 {
// If we already had progress, it's likely post-processing
if lastPhase == "downloading" {
phase = "post-processing"
}
} else if prog.Status != ytdlp.ProgressStatusDownloading {
if prog.Status == ytdlp.ProgressStatusPostProcessing {
phase = "post-processing"
}
lastPhase = phase
progressChan <- ProgressUpdate{
Status: prog.Status,
Percent: prog.PercentString(),
PercentFloat: prog.Percent(),
ETA: prog.ETA(),
Filename: prog.Filename,
Phase: phase,
}
mu.Lock()
currentPhase = phase
currentFilename = prog.Filename
mu.Unlock()
}).
Output("%(title)s.%(ext)s")