/// <summary> /// Transfer the downloaded song to the given <see cref="SongTarget"/>s. /// </summary> /// <param name="targets"></param> /// <param name="downloadContainer"></param> /// <param name="cancellationToken"></param> /// <returns></returns> /// <exception cref="OperationCanceledException"></exception> protected async Task <TargetResult[]> TransferToTargets(IEnumerable <SongTarget> targets, DownloadContainer downloadContainer, CancellationToken cancellationToken) { List <TargetResult> completedTargets = new List <TargetResult>(_targets.Length); foreach (SongTarget?target in targets) { cancellationToken.ThrowIfCancellationRequested(); TargetResult result = await target.TransferAsync(Song, downloadContainer.GetResultStream(), cancellationToken).ConfigureAwait(false); completedTargets.Add(result); _stageIndex++; ReportProgress(JobProgress.CreateTargetCompletion(CurrentProgress, result)); } return(completedTargets.ToArray()); }
/// <summary> /// Starts the job. /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task RunAsync(CancellationToken cancellationToken) { JobState = JobState.Running; if (CancellationToken.CanBeCanceled) { CancellationTokenSource?cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, CancellationToken); cancellationToken = cts.Token; cts.Dispose(); cts = null; } JobStage = JobStage.Downloading; Exception? exception = null; bool canceled = false; EventHandler?handler = JobStarted; handler?.Invoke(this, null); DownloadContainer? downloadContainer = null; List <TargetResult> completedTargets = new List <TargetResult>(_targets.Length); List <SongTarget> pendingTargets = new List <SongTarget>(_targets.Length); foreach (SongTarget?target in _targets) { SongState songState = await target.CheckSongExistsAsync(Song).ConfigureAwait(false); if (songState != SongState.Wanted) { completedTargets.Add(new TargetResult(target, songState, true, null)); } else { pendingTargets.Add(target); } } try { cancellationToken.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(Song.Key)) { SongInfoResponse?result = await WebUtils.SongInfoManager.GetSongByHashAsync(Song.Hash, cancellationToken).ConfigureAwait(false); if (result.Success) { Song.Key = result.Song?.Key; } } if (pendingTargets.Count > 0) { _downloadJob.JobProgressChanged += _downloadJob_JobProgressChanged; DownloadResult = await _downloadJob.RunAsync(cancellationToken).ConfigureAwait(false); downloadContainer = DownloadResult.DownloadContainer; if (DownloadResult.Exception != null) { throw DownloadResult.Exception; } _stageIndex = 1; ReportProgress(JobProgress.CreateDownloadCompletion(CurrentProgress, DownloadResult)); JobStage = JobStage.TransferringToTargets; foreach (TargetResult?targetResult in completedTargets) { _stageIndex++; ReportProgress(JobProgress.CreateTargetCompletion(CurrentProgress, targetResult)); } completedTargets.AddRange(await TransferToTargets(pendingTargets, downloadContainer, cancellationToken).ConfigureAwait(false)); } else { DownloadResult = new DownloadResult(null, DownloadResultStatus.Skipped, 0); _stageIndex = 1; _downloadJob_JobProgressChanged(this, new DownloadJobProgressChangedEventArgs(DownloadJobStatus.Finished)); JobStage = JobStage.TransferringToTargets; foreach (TargetResult?targetResult in completedTargets) { _stageIndex++; ReportProgress(JobProgress.CreateTargetCompletion(CurrentProgress, targetResult)); } } TargetResult[] failedTargets = completedTargets.Where(t => !t.Success).ToArray(); if (failedTargets.Length > 0) { Exception?firstException = failedTargets.FirstOrDefault(t => t.Exception != null)?.Exception; if (firstException != null) { throw firstException; } } TargetResults = completedTargets.ToArray(); } catch (OperationCanceledException ex) { if (TargetResults == null && completedTargets.Count > 0) { TargetResults = completedTargets.ToArray(); } else { TargetResults = Array.Empty <TargetResult>(); } canceled = true; exception = ex; ReportProgress(JobProgress.CreateFromFault(JobProgressType.Cancellation, JobStage, CurrentProgress)); JobState = JobState.Cancelled; } catch (Exception ex) { if (TargetResults == null && completedTargets.Count > 0) { TargetResults = completedTargets.ToArray(); } else { TargetResults = Array.Empty <TargetResult>(); } exception = ex; JobState = JobState.Error; ReportProgress(JobProgress.CreateFromFault(JobProgressType.Error, JobStage, CurrentProgress)); } finally { JobStage = JobStage.Finishing; try { downloadContainer?.Dispose(); } catch { } if (DownloadResult == null) { // TODO: What was this for? } FinishJob(canceled, exception); JobFinishedAsyncCallback?asyncCallback = JobFinishedAsyncCallback; if (asyncCallback != null) { await asyncCallback(Result).ConfigureAwait(false); } } }