fix: resolve 8 architecture flaws across download, playlist, and webhook
All checks were successful
CI / build (push) Successful in 52s
All checks were successful
CI / build (push) Successful in 52s
- Add --continue and --max-filesize to yt-dlp for download resilience - Route thumbnail downloads through HTTP_PROXY - Show per-video progress (i/N) with cancel for playlist downloads - Run playlist asynchronously so cancel callbacks can be processed - Guard handlePlaylistConfirm against concurrent active jobs - Close Done channel in playlist entry lifecycle - Add stepState TTL (30 min) to prevent stale states - Wrap webhook server for graceful shutdown on SIGTERM - Refactor formatBytes to loop-based implementation - Show first line of stderr in user-facing error messages - Fix format pointer reuse in playlist loop - Add CreatedAt field to stepState for TTL tracking - Add MaxFileSize and ProgressPrefix fields to DownloadJob
This commit is contained in:
@@ -31,12 +31,20 @@ type stepState struct {
|
||||
FormatIDs []string
|
||||
PlaylistState *domain.PlaylistState
|
||||
SearchState *domain.SearchState
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
const stepStateTTL = 30 * time.Minute
|
||||
|
||||
func (h *Handler) getUserStepState(chatID int64) *stepState {
|
||||
h.stepStatesMu.Lock()
|
||||
defer h.stepStatesMu.Unlock()
|
||||
return h.stepStates[chatID]
|
||||
ss := h.stepStates[chatID]
|
||||
if ss != nil && time.Since(ss.CreatedAt) > stepStateTTL {
|
||||
delete(h.stepStates, chatID)
|
||||
return nil
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (h *Handler) setUserStepState(chatID int64, ss *stepState) {
|
||||
@@ -133,6 +141,7 @@ func (h *Handler) handleURLInput(ctx context.Context, chatID int64, text string,
|
||||
URL: url,
|
||||
MediaInfo: info,
|
||||
Step: 0,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
h.sendMediaPreview(ctx, chatID, info)
|
||||
@@ -348,6 +357,7 @@ func (h *Handler) startDownload(ctx context.Context, chatID int64, msgID int, us
|
||||
Status: domain.StatusDownloading,
|
||||
Title: ss.MediaInfo.Title,
|
||||
FileSize: fileSize,
|
||||
MaxFileSize: h.maxFileSize,
|
||||
Done: make(chan struct{}),
|
||||
}
|
||||
|
||||
@@ -375,9 +385,13 @@ func (h *Handler) runDownload(ctx context.Context, job *domain.DownloadJob) {
|
||||
defer func() { <-h.downloadSem }()
|
||||
|
||||
kb := progressKeyboard(0, "", "")
|
||||
progressHeader := "Downloading: " + job.Title
|
||||
if job.ProgressPrefix != "" {
|
||||
progressHeader = job.ProgressPrefix + "\n" + progressHeader
|
||||
}
|
||||
msg, err := h.Bot.SendMessage(ctx, &tgbot.SendMessageParams{
|
||||
ChatID: job.ChatID,
|
||||
Text: fmt.Sprintf("Downloading: %s", job.Title),
|
||||
Text: progressHeader,
|
||||
ReplyMarkup: &kb,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -390,7 +404,9 @@ func (h *Handler) runDownload(ctx context.Context, job *domain.DownloadJob) {
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
if job.StderrLog != "" {
|
||||
errMsg += ": " + job.StderrLog
|
||||
if lines := strings.SplitN(job.StderrLog, "\n", 2); len(lines) > 0 && lines[0] != "" {
|
||||
errMsg = strings.TrimSpace(lines[0])
|
||||
}
|
||||
}
|
||||
h.editText(ctx, job.ChatID, job.MessageID, fmt.Sprintf("Download failed: %s", errMsg), nil)
|
||||
job.Status = domain.StatusFailed
|
||||
@@ -460,7 +476,10 @@ func (h *Handler) trackDownloadProgress(ctx context.Context, job *domain.Downloa
|
||||
}
|
||||
|
||||
kb := progressKeyboard(upd.Progress, upd.Speed, upd.ETA)
|
||||
text := fmt.Sprintf("Downloading: %s", job.Title)
|
||||
text := "Downloading: " + job.Title
|
||||
if job.ProgressPrefix != "" {
|
||||
text = job.ProgressPrefix + "\n" + text
|
||||
}
|
||||
h.editText(ctx, job.ChatID, job.MessageID, text, &kb)
|
||||
|
||||
case <-ctx.Done():
|
||||
@@ -732,7 +751,13 @@ func (h *Handler) sendMediaPreview(ctx context.Context, chatID int64, info *doma
|
||||
|
||||
// downloadFile downloads a URL to a temp file in the given directory and returns the path.
|
||||
func downloadFile(url, dir string) (string, error) {
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
tr := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
client := &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: tr,
|
||||
}
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("http get: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user