flagbug / YoutubeExtractor

A .NET library, that allows to download videos from YouTube and/or extract their audio track (currently only for flash videos).
814 stars 375 forks source link

YoutubeExtractor + Async BackgroundWorker. How to cancel download?.. #173

Open Gennady-G opened 8 years ago

Gennady-G commented 8 years ago

Hi guys!

Im. trying to use this lib in WinForm GUI app. Works cool, the only one problem - it cannot stop :-)

BackgroundWorker control allows to set CancellationPending flag, and then manually check it in DoWork function(while processing).

By instance:

    // BackgroundWorker launches Calculate function
    private void MyBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        // not UI thread
        e.Result = Calculate(sender as BackgroundWorker, e);
    }

    // some work in separate thread, where we check CancellationPending flag
    private long Calculate(BackgroundWorker instance, DoWorkEventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            if (instance.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
            else
            {
                System.Threading.Thread.Sleep(100);
                instance.ReportProgress(i);
            }
        }
        return 0L;
    }

    // click Cancel button:
   private void buttonCancel_Click(object sender, EventArgs e)
    {
        // set 'CancellationPending' flag to true, calling CancelAsync();
        MyBackgroundWorker.CancelAsync();
        buttonCancel.Enabled = false;
    }

How to realize that with Your library? I was triyng to cancel in DownloadProgressChanged, but app hangs. I think it could be done only in DoWork() function.. Here is big listing what I'm trying to do:

        // show progress in some visual controls..
        private void BackgroundWorker1ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // HANGS, .NET error 
            if (this.backgroundWorker1.CancellationPending)
            {
                this.Logger("Catched CancellationPending!");
                backgroundWorker1.ReportProgress(0);
                return;
            }
        }

        // Background worker job. Code is same as in Your dicumentation(Well, I renamed sender2 and args2)
        // Your Execute is synchronous, but I launch it in Asynchronous backgroundWorker
        private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
        {
            // it should be done during downloadin, BUT How?..
            if (backgroundWorker1.CancellationPending)
            {
                e.Cancel = true;
                return;
            }

            // Our test youtube link
            // todo: get from GUI
            string link = "https://www.youtube.com/watch?v=H7HmzwI67ec";

            // Get the available video formats.
            IEnumerable<VideoInfo> videoInfos = DownloadUrlResolver.GetDownloadUrls(link);

            // todo: try get the best first, then worst
            // Select the first .mp4 video with 360p resolution
            VideoInfo video = videoInfos.First(info => info.VideoType == VideoType.Mp4 && info.Resolution == 360);

            if (video.RequiresDecryption)
            {
                // If the video has a decrypted signature, decipher it
                DownloadUrlResolver.DecryptDownloadUrl(video);
            }

            var videoDownloader = new VideoDownloader(video, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), RemoveIllegalPathCharacters(video.Title) + video.VideoExtension));

            // Register the ProgressChanged event and print the current progress
            videoDownloader.DownloadProgressChanged += (sender2, args2)
                => backgroundWorker1.ReportProgress((int)args2.ProgressPercentage);

            // Execute the video downloader. For GUI applications note, that this method runs synchronously.
            videoDownloader.Execute();
        }

        // Cancel download by pressing button
        private void buttonCancel_Click(object sender, EventArgs e)
        {
            Logger("Cancel Pressed");

            if (backgroundWorker1.IsBusy)
            {
                // Notify the worker thread that a cancel has been requested.
                // The cancel will not actually happen until the thread in the
                // DoWork checks the m_oWorker.CancellationPending flag. 
                backgroundWorker1.CancelAsync();    
            }
        }
mysteryx93 commented 8 years ago

I cancel in DownloadProgressChanged. If app hangs, it's probably a multi-threading issue.

mysteryx93 commented 7 years ago

For reference, here is my own code

        private async Task DownloadVideoAsync(DownloadItem downloadInfo, DownloadItem.FileProgress fileInfo, EventHandler<DownloadCompletedEventArgs> callback) {
            downloadInfo.Status = DownloadStatus.Downloading;
            VideoDownloader YTD = new VideoDownloader(fileInfo.Source, fileInfo.Destination);
            YTD.DownloadProgressChanged += (sender, e) => {
                if (downloadInfo.IsCanceled)
                    e.Cancel = true;
                else {
                    fileInfo.BytesTotal = YTD.DownloadSize;
                    fileInfo.BytesDownloaded = e.ProgressBytes;
                    downloadInfo.UpdateProgress();
                }
            };

            // Run downloader task.
            await Task.Run(() => {
                try {
                    YTD.Execute();
                } catch {
                    downloadInfo.Status = DownloadStatus.Failed;
                }
            }).ConfigureAwait(false);
        }
Gennady-G commented 7 years ago

@mysteryx93 , thank You much!

Best regards,
Gennady