public async Task <string[]> Download(params string[] uris) { if (Downloading) { throw new NotSupportedException("This downloader is already downloading!"); } Downloading = true; var files = new List <string>(); try { DownloadStarted?.Invoke(this, EventArgs.Empty); foreach (var uri in uris) { files.Add(await DownloadFile(uri)); } DownloadCompleted?.Invoke(this, EventArgs.Empty); } catch { files.Clear(); DownloadFailed?.Invoke(this, EventArgs.Empty); } Downloading = false; return(files.ToArray()); }
private void WebClientOnDownloadFileCompleted(object sender, AsyncCompletedEventArgs asyncCompletedEventArgs) { if (asyncCompletedEventArgs.Cancelled) { if (Exists()) { Delete(); } Cancelled?.Invoke(this, new EventArgs()); return; } if (asyncCompletedEventArgs.Error != null) { DownloadFailed?.Invoke(this, new DownloadFailEvent(asyncCompletedEventArgs.Error)); AppLogger.Log.Error("Problem downloading file ", asyncCompletedEventArgs.Error); return; } if (Exists()) { Downloaded?.Invoke(this, new EventArgs()); } }
public override void ResponseReceived(byte[] parameter) { switch ((FileExplorerCommunication)parameter[0]) { case FileExplorerCommunication.ResponseDtpPackage: _dtpFactory.Receive(parameter, 1); break; case FileExplorerCommunication.ResponseDownloadPackage: DownloadPackageReceived?.Invoke(this, parameter); break; case FileExplorerCommunication.ResponsePackagingDirectory: break; case FileExplorerCommunication.ResponseCopyingFile: break; case FileExplorerCommunication.ResponseProcessingEntryChanged: ProcessingEntryUpdateReceived?.Invoke(this, _processingEntryUpdateSerializer.Value.Deserialize <ProcessingEntryUpdate>(parameter, 1)); break; case FileExplorerCommunication.ResponseDownloadFailed: DownloadFailed?.Invoke(this, new Guid(parameter.Skip(1).ToArray())); break; case FileExplorerCommunication.ResponseProcessingEntryAdded: ProcessingEntryAdded?.Invoke(this, _processingEntrySerializer.Value.Deserialize <ProcessingEntry>(parameter, 1).Unpack(null)); break; } }
private void RunTask(BeatmapDownloadTask task) { Log.Verbose("Detect {name} {id} downloading", task.IsBeatmapSet ? "beatmapset" : "beatmap", task.Id); IBeatmapInfo beatmapInfo = null; try { Task <IBeatmapInfo> infoTask = task.IsBeatmapSet ? _beatmapProvider.LookupBySetIdAsync(task.Id) : _beatmapProvider.LookupByIdAsync(task.Id); beatmapInfo = infoTask.Result ?? throw new BeatmapNotFoundException(task.Id); DownloadStarted?.Invoke(this, new BeatmapDownloadEventArgs(beatmapInfo)); var option = new BeatmapDownloadOption(); if (DownloadProgressChanged != null) { option.Progress = new PropagateHandler(this, beatmapInfo); } var result = _beatmapProvider.DownloadAsync(beatmapInfo, option).Result; if (result.Exception != null) { throw result.Exception; } if (File.Exists(_process.MainModule?.FileName)) { Process.Start(_process.MainModule !.FileName, result.FilePath); } DownloadCompleted?.Invoke(this, new BeatmapDownloadEventArgs(beatmapInfo)); } catch (Exception e) { DownloadFailed?.Invoke(this, new BeatmapDownloadFailedEventArgs(beatmapInfo, e)); var fallbackUrl = task.IsBeatmapSet ? $"https://osu.ppy.sh/beatmapsets/{task.Id}" : $"https://osu.ppy.sh/b/{task.Id}"; Process.Start(fallbackUrl); } }
public async void ReceiveData(byte[] data) { _fileStream.Write(data, 0, data.Length); if (_fileStream.Length == TotalBytesToReceive) { if (_fileHash == null) { await Task.Run(() => _autoResetEvent.WaitOne()); } _fileStream.Position = 0; byte[] fileHash; using (var sha256 = new SHA256Managed()) fileHash = sha256.ComputeHash(_fileStream); _fileStream.Close(); _fileStream = null; if (fileHash.SequenceEqual(_fileHash)) { ViewData.DataManagerType.ModifyDownloadedFile(FileName); DownloadFinished?.Invoke(this, EventArgs.Empty); } else { File.Delete(FileName); DownloadFailed?.Invoke(this, null); } IsFinished = true; return; } BytesReceived = _fileStream.Length; Progress = BytesReceived / TotalBytesToReceive; ProgressChanged?.Invoke(this, EventArgs.Empty); }
/// <summary> /// Event invocator for the <see cref="DownloadFailed"/> event /// </summary> /// <param name="e">Event arguments for the <see cref="DownloadFailed"/> event</param> protected virtual void OnDownloadFailed(DownloadFailedEventArgs e) { DownloadFailed?.Invoke(this, e); }
/// <summary> /// Begin a download for the requested <typeparamref name="TModel"/>. /// </summary> /// <param name="model">The <typeparamref name="TModel"/> to be downloaded.</param> /// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param> /// <returns>Whether the download was started.</returns> public bool Download(TModel model, bool minimiseDownloadSize = false) { if (!canDownload(model)) { return(false); } var request = CreateDownloadRequest(model, minimiseDownloadSize); DownloadNotification notification = new DownloadNotification { Text = $"Downloading {request.Model}", }; request.DownloadProgressed += progress => { notification.State = ProgressNotificationState.Active; notification.Progress = progress; }; request.Success += filename => { Task.Factory.StartNew(async() => { // This gets scheduled back to the update thread, but we want the import to run in the background. var imported = await Import(notification, filename); // for now a failed import will be marked as a failed download for simplicity. if (!imported.Any()) { DownloadFailed?.Invoke(request); } currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; request.Failure += triggerFailure; notification.CancelRequested += () => { request.Cancel(); currentDownloads.Remove(request); notification.State = ProgressNotificationState.Cancelled; return(true); }; currentDownloads.Add(request); PostNotification?.Invoke(notification); api.PerformAsync(request); DownloadBegan?.Invoke(request); return(true); void triggerFailure(Exception error) { DownloadFailed?.Invoke(request); if (error is OperationCanceledException) { return; } notification.State = ProgressNotificationState.Cancelled; Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!"); currentDownloads.Remove(request); } }
/// <summary> /// Handles incoming messages. /// </summary> /// <param name="sender">The <see cref="IMessageConnection"/> instance from which the message originated.</param> /// <param name="message">The message.</param> public async void HandleMessageRead(object sender, byte[] message) { var connection = (IMessageConnection)sender; var code = new MessageReader <MessageCode.Peer>(message).ReadCode(); Diagnostic.Debug($"Peer message received: {code} from {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})"); try { switch (code) { case MessageCode.Peer.SearchResponse: var searchResponse = SearchResponseFactory.FromByteArray(message); if (SoulseekClient.Searches.TryGetValue(searchResponse.Token, out var search)) { search.TryAddResponse(searchResponse); } break; case MessageCode.Peer.BrowseResponse: var browseWaitKey = new WaitKey(MessageCode.Peer.BrowseResponse, connection.Username); try { SoulseekClient.Waiter.Complete(browseWaitKey, BrowseResponseFactory.FromByteArray(message)); } catch (Exception ex) { SoulseekClient.Waiter.Throw(browseWaitKey, new MessageReadException("The peer returned an invalid browse response", ex)); throw; } break; case MessageCode.Peer.InfoRequest: UserInfo outgoingInfo; try { outgoingInfo = await SoulseekClient.Options .UserInfoResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false); } catch (Exception ex) { outgoingInfo = await new SoulseekClientOptions() .UserInfoResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false); Diagnostic.Warning($"Failed to resolve user info response: {ex.Message}", ex); } await connection.WriteAsync(outgoingInfo.ToByteArray()).ConfigureAwait(false); break; case MessageCode.Peer.SearchRequest: var searchRequest = PeerSearchRequest.FromByteArray(message); if (SoulseekClient.Options.SearchResponseResolver == default) { break; } try { var peerSearchResponse = await SoulseekClient.Options.SearchResponseResolver(connection.Username, searchRequest.Token, SearchQuery.FromText(searchRequest.Query)).ConfigureAwait(false); if (peerSearchResponse != null && peerSearchResponse.FileCount + peerSearchResponse.LockedFileCount > 0) { await connection.WriteAsync(peerSearchResponse.ToByteArray()).ConfigureAwait(false); } } catch (Exception ex) { Diagnostic.Warning($"Error resolving search response for query '{searchRequest.Query}' requested by {connection.Username} with token {searchRequest.Token}: {ex.Message}", ex); } break; case MessageCode.Peer.BrowseRequest: BrowseResponse browseResponse; try { browseResponse = await SoulseekClient.Options.BrowseResponseResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false); } catch (Exception ex) { browseResponse = await new SoulseekClientOptions() .BrowseResponseResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false); Diagnostic.Warning($"Failed to resolve browse response: {ex.Message}", ex); } await connection.WriteAsync(browseResponse.ToByteArray()).ConfigureAwait(false); break; case MessageCode.Peer.FolderContentsRequest: var folderContentsRequest = FolderContentsRequest.FromByteArray(message); Directory outgoingFolderContents = null; try { outgoingFolderContents = await SoulseekClient.Options.DirectoryContentsResolver( connection.Username, connection.IPEndPoint, folderContentsRequest.Token, folderContentsRequest.DirectoryName).ConfigureAwait(false); } catch (Exception ex) { Diagnostic.Warning($"Failed to resolve directory contents response: {ex.Message}", ex); } if (outgoingFolderContents != null) { var folderContentsResponseMessage = new FolderContentsResponse(folderContentsRequest.Token, outgoingFolderContents); await connection.WriteAsync(folderContentsResponseMessage).ConfigureAwait(false); } break; case MessageCode.Peer.FolderContentsResponse: var folderContentsResponse = FolderContentsResponse.FromByteArray(message); SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.FolderContentsResponse, connection.Username, folderContentsResponse.Token), folderContentsResponse.Directory); break; case MessageCode.Peer.InfoResponse: var incomingInfo = UserInfoResponseFactory.FromByteArray(message); SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.InfoResponse, connection.Username), incomingInfo); break; case MessageCode.Peer.TransferResponse: var transferResponse = TransferResponse.FromByteArray(message); SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferResponse, connection.Username, transferResponse.Token), transferResponse); break; case MessageCode.Peer.QueueDownload: var queueDownloadRequest = QueueDownloadRequest.FromByteArray(message); var(queueRejected, queueRejectionMessage) = await TryEnqueueDownloadAsync(connection.Username, connection.IPEndPoint, queueDownloadRequest.Filename).ConfigureAwait(false); if (queueRejected) { await connection.WriteAsync(new UploadDenied(queueDownloadRequest.Filename, queueRejectionMessage)).ConfigureAwait(false); } else { await TrySendPlaceInQueueAsync(connection, queueDownloadRequest.Filename).ConfigureAwait(false); } break; case MessageCode.Peer.TransferRequest: var transferRequest = TransferRequest.FromByteArray(message); if (transferRequest.Direction == TransferDirection.Upload) { if (!SoulseekClient.DownloadDictionary.IsEmpty && SoulseekClient.DownloadDictionary.Values.Any(d => d.Username == connection.Username && d.Filename == transferRequest.Filename)) { SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, transferRequest.Filename), transferRequest); } else { // reject the transfer with an empty reason. it was probably cancelled, but we can't be sure. Diagnostic.Debug($"Rejecting unknown upload from {connection.Username} for {transferRequest.Filename} with token {transferRequest.Token}"); await connection.WriteAsync(new TransferResponse(transferRequest.Token, "Cancelled")).ConfigureAwait(false); } } else { var(transferRejected, transferRejectionMessage) = await TryEnqueueDownloadAsync(connection.Username, connection.IPEndPoint, transferRequest.Filename).ConfigureAwait(false); if (transferRejected) { await connection.WriteAsync(new TransferResponse(transferRequest.Token, transferRejectionMessage)).ConfigureAwait(false); await connection.WriteAsync(new UploadDenied(transferRequest.Filename, transferRejectionMessage)).ConfigureAwait(false); } else { await connection.WriteAsync(new TransferResponse(transferRequest.Token, "Queued")).ConfigureAwait(false); await TrySendPlaceInQueueAsync(connection, transferRequest.Filename).ConfigureAwait(false); } } break; case MessageCode.Peer.UploadDenied: var uploadDeniedResponse = UploadDenied.FromByteArray(message); Diagnostic.Debug($"Download of {uploadDeniedResponse.Filename} from {connection.Username} was denied: {uploadDeniedResponse.Message}"); SoulseekClient.Waiter.Throw(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, uploadDeniedResponse.Filename), new TransferRejectedException(uploadDeniedResponse.Message)); DownloadDenied?.Invoke(this, new DownloadDeniedEventArgs(connection.Username, uploadDeniedResponse.Filename, uploadDeniedResponse.Message)); break; case MessageCode.Peer.PlaceInQueueResponse: var placeInQueueResponse = PlaceInQueueResponse.FromByteArray(message); SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.PlaceInQueueResponse, connection.Username, placeInQueueResponse.Filename), placeInQueueResponse); break; case MessageCode.Peer.PlaceInQueueRequest: var placeInQueueRequest = PlaceInQueueRequest.FromByteArray(message); await TrySendPlaceInQueueAsync(connection, placeInQueueRequest.Filename).ConfigureAwait(false); break; case MessageCode.Peer.UploadFailed: var uploadFailedResponse = UploadFailed.FromByteArray(message); var msg = $"Download of {uploadFailedResponse.Filename} reported as failed by {connection.Username}"; var download = SoulseekClient.DownloadDictionary.Values.FirstOrDefault(d => d.Username == connection.Username && d.Filename == uploadFailedResponse.Filename); if (download != null) { SoulseekClient.Waiter.Throw(new WaitKey(MessageCode.Peer.TransferRequest, download.Username, download.Filename), new TransferException(msg)); } Diagnostic.Debug(msg); DownloadFailed?.Invoke(this, new DownloadFailedEventArgs(connection.Username, uploadFailedResponse.Filename)); break; default: Diagnostic.Debug($"Unhandled peer message: {code} from {connection.Username} ({connection.IPEndPoint}); {message.Length} bytes"); break; } } catch (Exception ex) { Diagnostic.Warning($"Error handling peer message: {code} from {connection.Username} ({connection.IPEndPoint}); {ex.Message}", ex); } }
/// <summary> /// Passes the internal event in the protocol handler to the outward-facing /// event. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">E.</param> private void OnModuleInstallationFailed(object sender, ModuleInstallationFailedArgs e) { DownloadFailed?.Invoke(sender, e); }
private static IEnumerator UpdateModCoroutine(DependencyObject item, DownloadStart downloadStart, DownloadProgress progress, DownloadFailed dlFail, DownloadFinish finish, InstallFailed installFail, InstallFinish installFinish) { // (3.2) Logger.updater.Debug($"Release: {BeatSaber.ReleaseType}"); var mod = new Ref <ApiEndpoint.Mod>(null); yield return(GetModInfo(item.Name, item.ResolvedVersion.ToString(), mod)); try { mod.Verify(); } catch (Exception e) { Logger.updater.Error($"Error occurred while trying to get information for {item}"); Logger.updater.Error(e); yield break; } var releaseName = BeatSaber.ReleaseType == BeatSaber.Release.Steam ? ApiEndpoint.Mod.DownloadsObject.TypeSteam : ApiEndpoint.Mod.DownloadsObject.TypeOculus; var platformFile = mod.Value.Downloads.First(f => f.Type == ApiEndpoint.Mod.DownloadsObject.TypeUniversal || f.Type == releaseName); string url = ApiEndpoint.BeatModBase + platformFile.Path; Logger.updater.Debug($"URL = {url}"); const int maxTries = 3; int tries = maxTries; while (tries > 0) { if (tries-- != maxTries) { Logger.updater.Debug("Re-trying download..."); } using (var stream = new MemoryStream()) using (var request = UnityWebRequest.Get(url)) using (var taskTokenSource = new CancellationTokenSource()) { var dlh = new StreamDownloadHandler(stream, (int i1, int i2, double d) => progress?.Invoke(item, i1, i2, d)); request.downloadHandler = dlh; downloadStart?.Invoke(item); Logger.updater.Debug("Sending request"); //Logger.updater.Debug(request?.downloadHandler?.ToString() ?? "DLH==NULL"); yield return(request.SendWebRequest()); Logger.updater.Debug("Download finished"); if (request.isNetworkError) { Logger.updater.Error("Network error while trying to update mod"); Logger.updater.Error(request.error); dlFail?.Invoke(item, request.error); taskTokenSource.Cancel(); continue; } if (request.isHttpError) { Logger.updater.Error("Server returned an error code while trying to update mod"); Logger.updater.Error(request.error); dlFail?.Invoke(item, request.error); taskTokenSource.Cancel(); continue; } finish?.Invoke(item); stream.Seek(0, SeekOrigin.Begin); // reset to beginning var downloadTask = Task.Run(() => { // use slightly more multi threaded approach than co-routines // ReSharper disable once AccessToDisposedClosure ExtractPluginAsync(stream, item, platformFile); }, taskTokenSource.Token); while (!(downloadTask.IsCompleted || downloadTask.IsCanceled || downloadTask.IsFaulted)) { yield return(null); // pause co-routine until task is done } if (downloadTask.IsFaulted) { if (downloadTask.Exception != null && downloadTask.Exception.InnerExceptions.Any(e => e is BeatmodsInterceptException)) { // any exception is an intercept exception Logger.updater.Error($"BeatMods did not return expected data for {item.Name}"); } Logger.updater.Error($"Error downloading mod {item.Name}"); Logger.updater.Error(downloadTask.Exception); installFail?.Invoke(item, downloadTask.Exception); continue; } break; } } if (tries == 0) { Logger.updater.Warn($"Plugin download failed {maxTries} times, not re-trying"); installFinish?.Invoke(item, true); } else { Logger.updater.Debug("Download complete"); installFinish?.Invoke(item, false); } }