public async Task AddCachedImageAsync(R4UCard card) { try { var imgURL = card.Images.Last(); Log.Information("Caching [{serial}]: {imgURL}", card.Serial, imgURL); //using (System.IO.Stream netStream = await imgURL.WithImageHeaders().GetStreamAsync()) // card.GetImageStreamAsync()) var bytes = await imgURL.WithImageHeaders().GetAsync().WithRetries(10).ReceiveBytes(); using (Image img = Image.Load(bytes)) { var imageDirectoryPath = Path.Get(_IMAGE_CACHE_PATH); if (!imageDirectoryPath.Exists) { imageDirectoryPath.CreateDirectory(); } if (img.Width > img.Height) { Log.Debug("Rotating Image as it's Cached..."); img.Mutate(ctx => ctx.Rotate(RotateMode.Rotate90)); } img.Metadata.ExifProfile ??= new ExifProfile(); img.Metadata.ExifProfile.SetValue(SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag.Copyright, card.Images.Last().Authority); var savePath = Path.Get(_IMAGE_CACHE_PATH).Combine($"{card.Serial.Replace('-', '_').AsFileNameFriendly()}.jpg"); savePath.Open(img.SaveAsJpeg, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite); } } catch (InvalidOperationException e) when(e.Message == "Sequence contains no elements") { Log.Warning("Cannot be cached as no image URLs were found: {serial}", card.Serial); } }
private async Task <R4UCard> ParseOriginalCard(WikiCardContext context, WikiSetContext setContext) { var card = new R4UCard(); var mainInfoBox = context.MainInfoBox; var extraInfoBox = context.ExtraInfoBox; card.Name = new MultiLanguageString(); card.Name.JP = mainInfoBox.GetValueOrDefault("kanji", mainInfoBox["name"]); card.Name.EN = mainInfoBox["name"]; card.Type = TranslateType(mainInfoBox["card type"]); card.Set = setContext.Set; if (card.Type == CardType.Character) { card.Cost = int.Parse(mainInfoBox["cost"]); card.ATK = int.Parse(mainInfoBox["atk"]); card.DEF = int.Parse(mainInfoBox["def"]); card.Traits = await ParseTraits(context.RawMainInfoBox["trait"], setContext); } if (extraInfoBox.TryGetValue("card flavor(s)", out var flavor)) { card.Flavor = new MultiLanguageString(); card.Flavor.EN = flavor; } if (extraInfoBox.TryGetValue("card abilities", out var abilities)) { card.Effect = TranslateEffect(abilities); card.Color = InferFromEffect(card.Effect); } else { // Assumed. All Partner cards do not have effects, and vanilla cards have [Relaxing] card.Color = CardColor.Red; } return(card); }
private async Task <R4UCard> AddImageFromConsole(R4UCard card, InspectionOptions options) { var modifiedCard = card.Clone(); Log.Information("Please enter a new image URL: "); var newURIString = Console.ReadLine(); try { Log.Information("Validating URL..."); var newURI = new Uri(newURIString); Log.Information("Looks good; validating image... (will not check if the image itself is correct!)"); using (Image img = Image.Load(await newURI.WithImageHeaders().GetStreamAsync())) { Log.Information("Image can be loaded. Is the ratio reasonable?"); var aspectRatio = (img.Width * 1.0d) / img.Height; var flooredAspectRatio = Math.Floor(aspectRatio * 100); if (flooredAspectRatio < 70 || flooredAspectRatio > 72) { Log.Information("Image Ratio ({aspectRatio}) isn't correct (it must be approx. 0.71428571428); Failed inspection.", aspectRatio); return(null); } else { if (img.Width < 400) { Log.Warning("The image is of low quality; this may not be good for exporting purposes. Continue? [Y/N] (Default is N)"); if (!ConsoleUtils.Prompted(options.IsNonInteractive, options.NoWarning)) { return(null); } } modifiedCard.Images.Add(newURI); Log.Information("All preliminary tests passed. Modified {card}.", card.Serial); } } } catch (UnknownImageFormatException) { Log.Error("The URL does not point to a valid image. Inspection failed."); return(null); } catch (Exception e) { Log.Error("{e}", e); return(null); } return(modifiedCard); }
private async Task <R4UCard> Process(R4UCard card) { if (!HasMissingInformation(card)) { return(card); } var updatedCard = card.Clone(); var url = rebirthURLPrefix .SetQueryParam("cardno", updatedCard.Serial.Replace("+", "+")) // ; Log.Debug("Opening Link: {url}", url); var document = await url.WithReferrer("https://rebirth-fy.com/cardlist/").GetHTMLAsync(); var flavorJPText = document.QuerySelector(".cardlist-flavor").GetInnerText(); var rulesTextJPText = document.QuerySelector(".cardlist-free").GetInnerText().Trim(); var imageLink = document.QuerySelector(".cardlist-img").FindChild <IHtmlImageElement>().Source; var rulesTextEnumerable = effectMatcher.Matches(rulesTextJPText); Log.Information("Flavor JP: {jp}", flavorJPText); Log.Information("Rules Text JP: {jp}", rulesTextJPText); if (!String.IsNullOrWhiteSpace(flavorJPText)) { updatedCard.Flavor ??= new MultiLanguageString(); updatedCard.Flavor.JP = flavorJPText; } updatedCard.Effect = rulesTextEnumerable.Select((m, i) => { var result = card.Effect[i].Clone(); result.JP = m.Value; return(result); }).ToArray(); updatedCard.Images.Add(new Uri(imageLink)); Log.Information("After editing: {@card}", updatedCard); return(updatedCard); }
private bool HasMissingInformation(R4UCard card) { return((card?.Effect?.Any(mls => mls.JP == null) ?? false) || (card?.Name.JP == null) || (card?.Traits?.Any(mls => mls.JP == null) ?? false) || ((card.Images?.Count ?? 0) < 1)); }
private IEnumerable <R4UCard> CreateBaseCards(IHtmlParagraphElement paragraph, Dictionary <string, R4UReleaseSet> setMap) { List <R4UCard> cards = new List <R4UCard>(); cards.Add(new R4UCard()); var card = cards.First(); var content = paragraph.InnerHtml; var text = paragraph.GetInnerText(); var cursor = text.AsSpanCursor(); // var space = " "; // Format: <Serial> <Rarity> <JP Name with Spaces> <strong>English Name with Spaces</strong><br> if (TryGetExceptionalSerialRarityName(cursor.CurrentLine.ToString(), out var exceptionalResults)) { cards = exceptionalResults.Select(res => { var card = new R4UCard(); card.Serial = res.Serial; card.Rarity = res.Rarity; card.Name = res.Name; return(card); }).ToList(); if (cards.Count < 1) { yield break; } card = cards.First(); } else if (serialRarityJPNameMatcher.IsMatch(content)) { var firstLineMatch = serialRarityJPNameMatcher.Match(content); card.Serial = firstLineMatch.Groups[1].Value.Trim(); card.Rarity = firstLineMatch.Groups[2].Value.Trim(); card.Name = new MultiLanguageString(); card.Name.JP = rubyMatcher.Replace(firstLineMatch.Groups[3].Value, "").Trim(); // TODO: Resolve <ruby>永<rt>えい</rt>遠<rt>えん</rt></ruby>の<ruby>巫<rt>み</rt>女<rt>こ</rt></ruby> <ruby>霊<rt>れい</rt>夢<rt>む</rt></ruby> card.Name.EN = firstLineMatch.Groups[4].Value.Trim(); } else { throw new NotImplementedException($"The serial/rarity/JP Name line cannot be parsed. Here's the offending line: {cursor.CurrentLine.ToString()}"); } var releaseID = releaseIDMatcher.Match(card.Serial).Groups[1].Value; card.Set = setMap.GetValueOrDefault(releaseID, null) ?? CreateTemporarySet(releaseID); // Format: Cost <Cost> / <Series Name> / <Traits> cursor.Next(); var secondLine = cursor.CurrentLine.ToString(); if (costSeriesTraitMatcher.IsMatch(secondLine)) { card.Type = CardType.Character; var secondLineMatch = costSeriesTraitMatcher.Match(cursor.CurrentLine.ToString()); card.Cost = int.Parse(secondLineMatch.Groups[1].Value); card.Traits = secondLineMatch.Groups[3].Value // .Split(" – ") // .Select(str => str.Trim()) // .Select(t => new MultiLanguageString() { EN = t }) // .ToList(); cursor.Next(); card.ATK = cursor.CurrentLine .Slice("ATK ".Length) .AsParsed <int>(int.TryParse); cursor.Next(); string defLine = cursor.CurrentLine.ToString(); card.DEF = cursor.CurrentLine .Slice("DEF ".Length) .AsParsed <int>(int.TryParse); Regex flavorTextMatcher = new Regex(@"" + defLine + @"<br><em>(.+)</em><br>"); if (flavorTextMatcher.IsMatch(content)) { cursor.Next(); card.Flavor = new MultiLanguageString(); card.Flavor.EN = cursor.CurrentLine.ToString(); // flavorTextMatcher.Match(content).Groups[1].Value; } } else if (seriesRebirthMatcher.IsMatch(secondLine)) { card.Type = CardType.Rebirth; var rebirthLine = secondLine; Regex flavorTextMatcher = new Regex(@"" + rebirthLine + @"<br><em>(.+)</em><br>"); if (flavorTextMatcher.IsMatch(content)) { cursor.Next(); card.Flavor = new MultiLanguageString(); card.Flavor.EN = cursor.CurrentLine.ToString(); // flavorTextMatcher.Match(content).Groups[1].Value; } } else { card.Type = CardType.Partner; card.Color = CardColor.Red; } if (card.Color != CardColor.Red) { List <MultiLanguageString> effects = new List <MultiLanguageString>(); while (cursor.Next()) { Log.Information("Adding Effect: {eff}", cursor.CurrentLine.ToString()); effects.Add(new MultiLanguageString() { EN = cursor.CurrentLine.ToString() }); } effects = Compress(effects); card.Effect = effects.ToArray(); card.Color = CardUtils.InferFromEffect(card.Effect); } yield return(card); foreach (var dupCard in cards.Skip(1)) { var detailedDupCard = card.Clone(); detailedDupCard.Serial = dupCard.Serial; detailedDupCard.Rarity = dupCard.Rarity; detailedDupCard.Name = dupCard.Name; yield return(detailedDupCard); } }