private async Task StartDownloadAsync(DownloadItem downloadInfo) { downloadInfo.Status = DownloadStatus.Initializing; // Query the download URL for the right file. var VTask = GetDownloadUrlsAsync(downloadInfo.Request.DownloadUrl); var VideoList = await VTask.ConfigureAwait(false); if (VideoList == null) { downloadInfo.Status = DownloadStatus.Failed; RaiseCallback(downloadInfo); return; } if (!downloadInfo.UpgradeAudio || downloadInfo.Request.FileName == null || !File.Exists(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName)) { downloadInfo.UpgradeAudio = false; // Get the highest resolution format. BestFormatInfo BestFile = SelectBestFormat(VideoList); if (BestFile.BestVideo.AdaptiveType == AdaptiveType.None) { // Download single file. downloadInfo.Files.Add(new DownloadItem.FileProgress() { Source = BestFile.BestVideo, Destination = downloadInfo.Destination + BestFile.BestVideo.VideoExtension }); } else { // Download audio and video separately. downloadInfo.Files.Add(new DownloadItem.FileProgress() { Source = BestFile.BestVideo, Destination = downloadInfo.Destination + GetCodecExtension(BestFile.BestVideo.VideoType) }); if (BestFile.BestAudio != null) { downloadInfo.Files.Add(new DownloadItem.FileProgress() { Source = BestFile.BestAudio, Destination = downloadInfo.Destination + GetAudioExtension(BestFile.BestAudio.AudioType) }); } } } else if (File.Exists(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName)) { // Keep local video and upgrade audio. VideoInfo AudioFile = SelectBestAudio(from v in VideoList where (v.CanExtractAudio || v.AdaptiveType == AdaptiveType.Audio) orderby v.AudioBitrate descending select v); downloadInfo.Files.Add(new DownloadItem.FileProgress() { Source = AudioFile, Destination = downloadInfo.Destination + GetAudioExtension(AudioFile.AudioType) }); } await DownloadFilesAsync(downloadInfo, downloadInfo.Callback).ConfigureAwait(false); }
/// <summary> /// Downloads specified video from YouTube. /// </summary> /// <param name="video">The video to download.</param> /// <param name="queuePos">The position in the queue to auto-play, or -1.</param> /// <param name="upgradeAudio">If true, only the audio will be downloaded and it will be merged with the local video file.</param> /// <param name="callback">The method to call once download is completed.</param> public async Task DownloadVideoAsync(Media video, int queuePos, bool upgradeAudio, EventHandler<DownloadCompletedEventArgs> callback) { if (video == null || string.IsNullOrEmpty(video.DownloadUrl)) throw new ArgumentException("Video object is null or doesn't contain a valid YouTube URL."); if (IsDownloadDuplicate(video)) return; // Store in the Temp folder. DefaultMediaPath PathCalc = new DefaultMediaPath(Settings.TempFilesPath.Substring(Settings.NaturalGroundingFolder.Length)); string Destination = Settings.NaturalGroundingFolder + PathCalc.GetDefaultFileName(video.Artist, video.Title, null, (MediaType)video.MediaTypeId); string DownloadDesc = Path.GetFileName(Destination); Directory.CreateDirectory(Settings.TempFilesPath); // Add DownloadItem right away before doing any async work. DownloadItem DownloadInfo = new DownloadItem(video, Destination, DownloadDesc, queuePos, upgradeAudio, callback); Application.Current.Dispatcher.Invoke(() => downloadsList.Insert(0, DownloadInfo)); // Notify UI of new download to show window. if (DownloadAdded != null) DownloadAdded(this, new EventArgs()); if (downloadsList.Where(d => d.Status == DownloadStatus.Downloading || d.Status == DownloadStatus.Initializing).Count() < Settings.SimultaneousDownloads) await StartDownloadAsync(DownloadInfo).ConfigureAwait(false); }
public DownloadCompletedEventArgs(DownloadItem downloadInfo) { this.DownloadInfo = downloadInfo; }
private void RaiseCallback(DownloadItem downloadInfo) { if (downloadInfo.Callback != null) Application.Current.Dispatcher.Invoke(() => downloadInfo.Callback(this, new DownloadCompletedEventArgs(downloadInfo))); }
private void DownloadCanceled(DownloadItem downloadInfo) { if (downloadInfo.Status == DownloadStatus.Canceled) downloadInfo.Status = DownloadStatus.Canceled; else if (downloadInfo.Status == DownloadStatus.Failed) downloadInfo.Status = DownloadStatus.Failed; Thread.Sleep(100); // Delete partially-downloaded files. foreach (var item in downloadInfo.Files) { File.Delete(item.Destination); } }
private async Task DownloadCompletedAsync(DownloadItem downloadInfo) { // Separate file extension. string Destination = downloadInfo.Files[0].Destination; string DestinationExt = Path.GetExtension(Destination); Destination = Destination.Substring(0, Destination.Length - Path.GetExtension(Destination).Length); Media video = downloadInfo.Request; if (downloadInfo.Files.Count > 1) { VideoType File1Type = downloadInfo.Files[0].Source.VideoType; AudioType File2Type = downloadInfo.Files[1].Source.AudioType; string File1Ext = GetCodecExtension(File1Type); string File2Ext = GetAudioExtension(File2Type); DestinationExt = GetFinalExtension(File1Type, File2Type); // Make sure output file doesn't already exist. File.Delete(Destination + DestinationExt); // Merge audio and video files. await Task.Run(() => FfmpegBusiness.JoinAudioVideo(Destination + File1Ext, Destination + File2Ext, Destination + DestinationExt, true)); // Delete source files File.Delete(Destination + File1Ext); File.Delete(Destination + File2Ext); } else if (downloadInfo.UpgradeAudio) { // Get original video format. MediaInfoReader MediaReader = new MediaInfoReader(); //await MediaReader.LoadInfoAsync(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName); string VideoDestExt = ".mkv"; //if (MediaReader.VideoFormat == "VP8" || MediaReader.VideoFormat == "VP9") // VideoDestExt = ".webm"; string VideoDest = downloadInfo.Destination + " (extract)" + VideoDestExt; // Keep existing video and upgrade audio. string AudioExt = GetAudioExtension(downloadInfo.Files[0].Source.AudioType); DestinationExt = GetFinalExtension(Path.GetExtension(downloadInfo.Request.FileName), AudioExt); // Merge audio and video files. await Task.Run(() => { FfmpegBusiness.ExtractVideo(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName, VideoDest, true); if (FileHasContent(VideoDest)) FfmpegBusiness.JoinAudioVideo(VideoDest, Destination + AudioExt, Destination + DestinationExt, true); }); // Delete source files File.Delete(VideoDest); File.Delete(Destination + AudioExt); if (FileHasContent(Destination + DestinationExt) && File.Exists(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName)) FileOperationAPIWrapper.MoveToRecycleBin(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName); } // Ensure download and merge succeeded. if (FileHasContent(Destination + DestinationExt)) { // Get final file name. DefaultMediaPath PathCalc = new DefaultMediaPath(); string NewFileName = PathCalc.GetDefaultFileName(video.Artist, video.Title, video.MediaCategoryId, (MediaType)video.MediaTypeId); Directory.CreateDirectory(Path.GetDirectoryName(Settings.NaturalGroundingFolder + NewFileName)); video.FileName = NewFileName + DestinationExt; // Move file and overwrite. if (downloadInfo.Request.FileName != null && File.Exists(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName)) FileOperationAPIWrapper.MoveToRecycleBin(Settings.NaturalGroundingFolder + downloadInfo.Request.FileName); File.Move(Destination + DestinationExt, Settings.NaturalGroundingFolder + video.FileName); } else throw new FileNotFoundException("Audio and video streams could not be merged."); // Add to database EditVideoBusiness Business = new EditVideoBusiness(); Media ExistingData = Business.GetVideoById(video.MediaId); if (ExistingData != null) { // Edit video info. ExistingData.FileName = video.FileName; ExistingData.Length = null; ExistingData.Height = null; Business.Save(); } else { // Add new video info. Business.AddVideo(video); Business.Save(); } downloadInfo.Request.FileName = video.FileName; downloadInfo.Status = DownloadStatus.Done; }
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); // Detect whether this is the last file. fileInfo.Done = true; if (downloadInfo.Files.Any(d => !d.Done) == false) { var NextDownload = StartNextDownloadAsync().ConfigureAwait(false); // Raise events for the last file part only. if (downloadInfo.IsCompleted) { try { await DownloadCompletedAsync(downloadInfo).ConfigureAwait(false); } catch { downloadInfo.Status = DownloadStatus.Failed; } } else if (downloadInfo.IsCanceled) DownloadCanceled(downloadInfo); RaiseCallback(downloadInfo); await NextDownload; } }
/// <summary> /// Downloads the specified list of files. /// </summary> /// <param name="downloadInfo">The information about the files to download.</param> /// <param name="callback">The function to call when download is completed.</param> private async Task DownloadFilesAsync(DownloadItem downloadInfo, EventHandler<DownloadCompletedEventArgs> callback) { // Decrypt all URLs at the same time without waiting. List<Task> DecryptTasks = new List<Task>(); foreach (var item in downloadInfo.Files) { DecryptTasks.Add(Task.Run(() => DownloadUrlResolver.DecryptDownloadUrl(item.Source))); } await Task.WhenAll(DecryptTasks.ToArray()).ConfigureAwait(false); if (!downloadInfo.IsCanceled) { // Download all files. List<Task> DownloadTasks = new List<Task>(); foreach (var item in downloadInfo.Files) { DownloadTasks.Add(DownloadVideoAsync(downloadInfo, item, callback)); } await Task.WhenAll(DownloadTasks.ToArray()).ConfigureAwait(false); } else RaiseCallback(downloadInfo); }