async IAsyncEnumerable <ParsedImage> IImageParsingStrategy.ParseAsync(ParseRequest request, IProgress <ParseProgress> progress) { if (request is null) { ThrowHelper.ArgumentNull(nameof(request)); } if (request.EstimatedCount < 1) { ThrowHelper.ArgumentOutOfRange(nameof(request.EstimatedCount), request.EstimatedCount, "Value must be 1 or greater!"); } var imagesPerCategory = (double)request.EstimatedCount / request.Categories.Count(); var capacity = (int)Math.Ceiling(imagesPerCategory / _pageSize) * request.Categories.Sum(x => x.Keywords.Count()); var throttler = new SemaphoreSlim(MaxThreads); var currentCount = 0; foreach (var category in request.Categories) { var keywordsCount = category.Keywords.Count(); var imagesPerKeyword = (int)Math.Ceiling(imagesPerCategory / keywordsCount); var allTasks = new List <Task>(); var parsedImages = new List <ParsedImage>(imagesPerKeyword); var total = (int)Math.Ceiling((double)imagesPerKeyword / _pageSize); var pages = Enumerable.Range(_startFrom, total); var disposables = new List <IDisposable>(); foreach (var keyword in category.Keywords) { await throttler.WaitAsync(); allTasks.Add( Task.Run(async() => { try { var uri = new Uri(_url).AddParameter("query", keyword); foreach (var page in pages) { var pageUri = uri.AddParameter("per_page", _pageSize) .AddParameter("page", page); var take = imagesPerKeyword - (page - 1) * _pageSize; var response = await _httpClient.GetAsync <Response>(pageUri); disposables.Add(response.Disposable); var results = response.Result.Results.Take(take); foreach (var result in results) { var download = await _httpClient.GetAsync(result.Links.Download); var stream = await download.Content.ReadAsStreamAsync(); var image = Image.FromStream(stream); var parsedImage = new ParsedImage { Category = category.Name, Image = image, Keyword = keyword }; parsedImages.Add(parsedImage); disposables.Add(download); disposables.Add(stream); } } } finally { throttler.Release(); } })); } await Task.WhenAll(allTasks); foreach (var parsedImage in parsedImages) { var data = new ParseProgress { CurrentCount = ++currentCount, EstimatedCount = request.EstimatedCount }; progress?.Report(data); yield return(parsedImage); } foreach (var disposable in disposables) { disposable.Dispose(); } } }
private Task PrintResponse(AvatarLocation location, Context ctx, PKMember target, ParsedImage avatar, MemberGuildSettings?targetGuildData) { var typeFrag = location switch { AvatarLocation.Server => "server avatar", AvatarLocation.Member => "avatar", _ => throw new ArgumentOutOfRangeException(nameof(location)) }; var serverFrag = location switch { AvatarLocation.Server => $" This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**).", AvatarLocation.Member when targetGuildData?.AvatarUrl != null => $"\n{Emojis.Note} Note that this member *also* has a server-specific avatar set in this server (**{ctx.Guild.Name}**), and thus changing the global avatar will have no effect here.", _ => "" }; var msg = avatar.Source switch { AvatarSource.User => $"{Emojis.Success} Member {typeFrag} changed to {avatar.SourceUser?.Username}'s avatar!{serverFrag}\n{Emojis.Warn} If {avatar.SourceUser?.Username} changes their avatar, the member's avatar will need to be re-set.", AvatarSource.Url => $"{Emojis.Success} Member {typeFrag} changed to the image at the given URL.{serverFrag}", AvatarSource.Attachment => $"{Emojis.Success} Member {typeFrag} changed to attached image.{serverFrag}\n{Emojis.Warn} If you delete the message containing the attachment, the avatar will stop working.", _ => throw new ArgumentOutOfRangeException() }; // The attachment's already right there, no need to preview it. var hasEmbed = avatar.Source != AvatarSource.Attachment; return(hasEmbed ? ctx.Reply(msg, new EmbedBuilder().Image(new Embed.EmbedImage(avatar.Url)).Build()) : ctx.Reply(msg)); }