diff --git a/internal/downloader/downloader.go b/internal/downloader/downloader.go index 0449fac..0d8d306 100644 --- a/internal/downloader/downloader.go +++ b/internal/downloader/downloader.go @@ -24,6 +24,7 @@ import ( const ( defaultClientTimeout = 30 * time.Second + vodSegmentTimeout = 5 * time.Minute // Longer timeout for VOD segments which can be large maxRedirects = 5 playlistPollInterval = 2 * time.Second // Poll interval for livestreams ) @@ -193,6 +194,20 @@ func downloadPlaylist(ctx context.Context, client *http.Client, parsed *url.URL, } // For VOD (Video on Demand), use temp file and convert to MP4 + // Create a context without deadline for VOD downloads to avoid premature cancellation + // Preserve cancellation from parent context but remove any deadline + vodCtx, vodCancel := context.WithCancel(context.Background()) + defer vodCancel() + + // If parent context is cancelled, cancel VOD context too + go func() { + <-ctx.Done() + vodCancel() + }() + + // Create a client with longer timeout for VOD segments + vodClient := &http.Client{Timeout: vodSegmentTimeout} + tempTS, err := os.CreateTemp("", "sdl-*.ts") if err != nil { return fmt.Errorf("create temp file: %w", err) @@ -208,12 +223,13 @@ func downloadPlaylist(ctx context.Context, client *http.Client, parsed *url.URL, fmt.Fprintf(os.Stderr, "Downloading %d segments...\n", totalSegments) for i, segment := range segments { - if err := downloadSegment(ctx, client, finalURL, segment, tempTS); err != nil { + if err := downloadSegment(vodCtx, vodClient, finalURL, segment, tempTS); err != nil { return fmt.Errorf("download segment %d: %w", i, err) } // Show progress: segment number, total, and percentage progress := float64(i+1) / float64(totalSegments) * 100 fmt.Fprintf(os.Stderr, "\rProgress: %d/%d segments (%.1f%%)", i+1, totalSegments, progress) + os.Stderr.Sync() // Flush progress output } fmt.Fprintf(os.Stderr, "\n") @@ -223,7 +239,7 @@ func downloadPlaylist(ctx context.Context, client *http.Client, parsed *url.URL, mp4Name := ensureMP4Extension(name) fmt.Fprintf(os.Stderr, "Converting to MP4...\n") - if err := transmuxToMP4(ctx, tempPath, mp4Name); err != nil { + if err := transmuxToMP4(vodCtx, tempPath, mp4Name); err != nil { return err } fmt.Fprintf(os.Stderr, "Complete: %s\n", mp4Name)