示例#1
0
 private async Task AddCachedImageAsync(WeissSchwarzCard card, Func <Flurl.Url, CookieSession> _cookieSession, CancellationToken ct = default)
 {
     try
     {
         var imgURL = card.Images.Last();
         Log.Information("Caching: {imgURL}", imgURL);
         var session = _cookieSession(imgURL);
         using (System.IO.Stream netStream = await card.GetImageStreamAsync(session, ct))
             using (Image img = Image.Load(netStream))
             {
                 var imageDirectoryPath = Path.Get(_IMAGE_CACHE_PATH);
                 if (!imageDirectoryPath.Exists)
                 {
                     imageDirectoryPath.CreateDirectory();
                 }
                 if (img.Height < img.Width)
                 {
                     Log.Debug("Image is probably incorrectly oriented, rotating it 90 degs. clockwise to compensate.");
                     img.Mutate(ipc => ipc.Rotate(90));
                 }
                 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");
                 await img.SaveAsPngAsync(savePath.FullPath, ct);
             }
     } catch (InvalidOperationException e) when(e.Message == "Sequence contains no elements")
     {
         Log.Warning("Cannot be cached as no image URLs were found: {serial}", card.Serial);
     }
 }
示例#2
0
        private async Task AddCachedImageAsync(WeissSchwarzCard card)
        {
            try
            {
                var imgURL = card.Images.Last();
                Log.Information("Caching: {imgURL}", imgURL);
                using (System.IO.Stream netStream = await card.GetImageStreamAsync())
                    using (Image img = Image.Load(netStream))
                    {
                        var imageDirectoryPath = Path.Get(_IMAGE_CACHE_PATH);
                        if (!imageDirectoryPath.Exists)
                        {
                            imageDirectoryPath.CreateDirectory();
                        }

                        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);
                    }
            } 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 <WeissSchwarzDeck> Parse(Uri uri)
        {
            var encoreDecksDeckAPIURL = "https://www.encoredecks.com/api/deck";

            var localPath = uri.LocalPath;
            var deckID    = localPath.Substring(localPath.LastIndexOf('/') + 1);

            Log.Information("Deck ID: {deckID}", deckID);

            dynamic deckJSON = await GetDeckJSON(encoreDecksDeckAPIURL, deckID);

            WeissSchwarzDeck res = new WeissSchwarzDeck();

            res.Name = deckJSON.name;

            using (var db = _database())
            {
                await db.Database.MigrateAsync();

                foreach (dynamic card in deckJSON.cards)
                {
                    string           serial = WeissSchwarzCard.GetSerial(card.set.ToString(), card.side.ToString(), card.lang.ToString(), card.release.ToString(), card.sid.ToString());
                    WeissSchwarzCard wscard = await db.WeissSchwarzCards.FindAsync(serial);

                    if (wscard == null)
                    {
                        string setID = card.series;
                        await _parse($"https://www.encoredecks.com/api/series/{setID}/cards");

                        wscard = await db.WeissSchwarzCards.FindAsync(serial);
                    }

                    if (res.Ratios.TryGetValue(wscard, out int quantity))
                    {
                        res.Ratios[wscard]++;
                    }
                    else
                    {
                        res.Ratios[wscard] = 1;
                    }

                    //Log.Information("Parsed: {@wscard}", wscard);
                }
            }
            var simpleRatios = res.AsSimpleDictionary();

            Log.Information("Deck Parsed: {@simpleRatios}", simpleRatios);
            Log.Information("Cards in Deck: {@count}", simpleRatios.Values.Sum());
            return(res);
        }
示例#4
0
        private async Task <WeissSchwarzCard> AddImageFromConsole(WeissSchwarzCard 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);
        }
        public async IAsyncEnumerable <WeissSchwarzCard> Parse(string urlOrFile)
        {
            if (encoreDecksSiteSetMatcher.IsMatch(urlOrFile))
            {
                urlOrFile = TransformIntoAPIFormat(urlOrFile);
            }

            IList <dynamic> setCards = null;

            do
            {
                try
                {
                    setCards = await urlOrFile.WithRESTHeaders().GetJsonListAsync();
                }
                catch (FlurlHttpException)
                {
                    // Do nothing
                }
            } while (setCards == null);
            foreach (var setCard in setCards)
            {
                WeissSchwarzCard result = new WeissSchwarzCard();
                result.Name = new MultiLanguageString();
                var enOptional = DynamicExtensions.AsOptional(setCard.locale.EN);
                var jpOptional = DynamicExtensions.AsOptional(setCard.locale.NP);
                result.Name.EN = enOptional.name;
                result.Name.JP = jpOptional.name;
                (List <object>, List <object>)attributes = (enOptional.attributes, jpOptional.attributes);
                result.Traits   = TranslateTraits(attributes).ToList();
                result.Effect   = ((List <object>)enOptional.ability)?.Cast <string>().ToArray();
                result.Rarity   = setCard.rarity;
                result.Side     = TranslateSide(setCard.side);
                result.Level    = (int?)setCard.level;
                result.Cost     = (int?)setCard.cost;
                result.Power    = (int?)setCard.power;
                result.Soul     = (int?)setCard.soul;
                result.Triggers = TranslateTriggers(setCard.trigger);

                result.Serial = WeissSchwarzCard.GetSerial(setCard.set.ToString(), setCard.side.ToString(), setCard.lang.ToString(), setCard.release.ToString(), setCard.sid.ToString());

                result.Type    = TranslateType(setCard.cardtype);
                result.Color   = TranslateColor(setCard.colour);
                result.Remarks = $"Parsed: {this.GetType().Name}";
                yield return(result);
            }
            // Get
            yield break;
        }
示例#6
0
        public async Task Run(IContainer ioc)
        {
            Log.Information("Starting.");
            var language = InterpretLanguage(Language);
            IAsyncEnumerable <WeissSchwarzCard> list = null;

            using (var db = ioc.GetInstance <CardDatabaseContext>())
            {
                await db.Database.MigrateAsync();

                if (language == null)
                {
                    try
                    {
                        var tuple = WeissSchwarzCard.ParseSerial(ReleaseIDorFullSerialID);
                    }
                    catch (Exception)
                    {
                        Log.Error("Serial cannot be parsed properly. Did you mean to cache a release set? If so, please indicate the language (EN/JP) as well.");
                        return;
                    }

                    var query = from card in db.WeissSchwarzCards.AsQueryable()
                                where card.Serial.ToLower() == ReleaseIDorFullSerialID.ToLower()
                                select card;
                    list = query.ToAsyncEnumerable().Take(1);
                }
                else
                {
                    var releaseID = ReleaseIDorFullSerialID.ToLower().Replace("%", "");
                    var query     = from card in db.WeissSchwarzCards.AsQueryable()
                                    where EF.Functions.Like(card.Serial.ToLower(), $"%/{releaseID}%")
                                    select card;
                    list = query.ToAsyncEnumerable().Where(c => c.Language == language.Value);
                }

                await foreach (var card in list)
                {
                    await AddCachedImageAsync(card);
                }

                Log.Information("Done.");
                Log.Information("PS: Please refrain from executing this command continuously as this may cause your IP address to get tagged as a DDoS bot.");
                Log.Information("    Only cache the images you may need.");
                Log.Information("    -ronelm2000");
            }
        }
示例#7
0
        private async IAsyncEnumerable <WeissSchwarzCard> Process(WeissSchwarzCard firstCard, IAsyncEnumerable <WeissSchwarzCard> originalCards)
        {
            Log.Information("Starting...");
            var menu = await "http://jktcg.com/MenuLeftEN.html"
                       .WithHTMLHeaders()
                       .GetHTMLAsync();
            var pair     = CardListURLFrom(menu, firstCard);
            var cardList = await pair.url
                           .WithHTMLHeaders()
                           .GetHTMLAsync();

            var releaseID  = firstCard.ReleaseID;
            var cardImages = cardList.QuerySelectorAll <IHtmlImageElement>("a > img")
                             .Select(ele => (
                                         Serial: GetSerial(ele),
                                         Source: ele.Source.Replace("\t", "")
                                         ))
                             .ToDictionary(p => p.Serial, p => p.Source);//(setID + "-" + str.AsSpan().Slice(c => c.LastIndexOf('_') + 1, c => c.LastIndexOf(".")).ToString()).ToLower());

//                .ToLookup(ele => ele

            await foreach (var card in originalCards)
            {
                var res = card.Clone();
                try
                {
                    var imgURL = cardImages[res.Serial.ToLower()];
                    res.Images.Add(new Uri(imgURL));
                    Log.Information("Attached image to {serial}: {imgURL}", res.Serial, imgURL);
                }
                catch (KeyNotFoundException)
                {
                    Log.Warning("Tried to post-process {serial} when the URL for {releaseID} was loaded. Skipping.", res.Serial, releaseID);
                }
                yield return(res);
            }

            Log.Information("Finished.");
            yield break;
        }
        private WeissSchwarzCard ParseHOTCText(string hotcText)
        {
            var cursor = hotcText.AsSpanCursor();
            var res    = new WeissSchwarzCard();

            var cardNoText    = "Card No.: ";
            var rarityText    = "Rarity:";
            var colorText     = "Color: ";
            var sideText      = "Side: ";
            var levelText     = "Level: ";
            var costText      = "Cost: ";
            var powerText     = "Power: ";
            var soulText      = "Soul: ";
            var traitsText    = "Traits: ";
            var trait1Text    = "Trait 1: ";
            var triggersText  = "Triggers: ";
            var flavorText    = "Flavor:";
            var rulesTextText = "TEXT:";

            while (!cursor.CurrentLine.StartsWith(cardNoText))
            {
                cursor.Next();
            }
            var linesToCardNoText = cursor.LineNumber;

            //            ReadOnlySpan<char> cardNoLine = cursor.CurrentLine;
            res.Serial = cursor.CurrentLine.Slice(
                c => c.IndexOf(cardNoText) + cardNoText.Length,
                c => c.IndexOf(rarityText)
                )
                         .Trim()
                         .ToString();
            try
            {
                res.Rarity = cursor.CurrentLine.Slice(c => c.IndexOf(rarityText) + rarityText.Length).Trim().ToString();
            } catch (Exception e)
            {
                res.Rarity = HandleRarityCorrections(res.Serial, e);
            }
            cursor.MoveUp();
            // Log.Information("+1 above Card No: {line}", cursor.CurrentLine.ToString());
            res.Name       = new MultiLanguageString();
            res.Name["jp"] = cursor.CurrentLine.ToString();
            if (cursor.LineNumber > 1)
            {
                cursor.MoveUp();
                res.Name["en"] = cursor.CurrentLine.ToString();
            }
            //cursor.Next(3);
            cursor.Next(linesToCardNoText - cursor.LineNumber + 1);
            try
            {
                res.Color = cursor.CurrentLine.Slice(
                    c => c.IndexOf(colorText) + colorText.Length,
                    c => c.IndexOf(sideText)
                    )
                            .Trim()
                            .ToEnum <CardColor>()
                            .Value;
            } catch (Exception e)
            {
                res.Color = HandleColorCorrections(res.Serial, e);
            }

            try
            {
                res.Side = cursor.CurrentLine.Slice(
                    c => c.IndexOf(sideText) + sideText.Length,
                    c => c.IndexOf(sideText) + sideText.Length + c.Slice(c.IndexOf(sideText) + sideText.Length).IndexOf(' ')
                    )
                           .Trim()
                           .ToEnum <CardSide>()
                           .Value;

                var sideString = res.Side.ToString();
                res.Type = cursor.CurrentLine.Slice(
                    c => c.IndexOf(sideString, StringComparison.CurrentCultureIgnoreCase) + sideString.Length
                    )
                           .Trim()
                           .ToEnum <CardType>()
                           .Value;
            } catch (Exception)
            {
                (res.Side, res.Type) = HandleCorrections(res.Serial);
            }

            cursor.Next();

            switch (res.Type)
            {
            case CardType.Character:
                res.Power = cursor.CurrentLine.Slice(
                    c => c.IndexOf(powerText) + powerText.Length,
                    c => c.IndexOf(soulText)
                    )
                            .Trim()
                            .AsParsed <int>(int.TryParse);

                res.Soul = cursor.CurrentLine.Slice(
                    c => c.IndexOf(soulText) + soulText.Length
                    )
                           .Trim()
                           .AsParsed <int>(int.TryParse);
                goto case CardType.Event;

            case CardType.Event:
                res.Level = cursor.CurrentLine.Slice(
                    c => c.IndexOf(levelText) + levelText.Length,
                    c => c.IndexOf(costText)
                    )
                            .Trim()
                            .AsParsed <int>(int.TryParse);

                res.Cost = cursor.CurrentLine.Slice(
                    c => c.IndexOf(costText) + costText.Length,
                    c => c.IndexOf(powerText)
                    )
                           .Trim()
                           .AsParsed <int>(int.TryParse);
                break;

            default:
                break;
            }

            cursor.Next();
            if (cursor.CurrentLine.ToString().Contains(traitsText))
            {
                res.Traits = cursor.CurrentLine
                             .Slice(c => c.IndexOf(traitsText) + traitsText.Length)
                             .Trim()
                             .ToString()
                             .SplitWithRegex(@"([^(]+)\(([^\)]+)\),{0,1}")
                             .Select(this.ParseTrait)
                             .Where(o => o != null)
                             .ToList();
            }
            else if (cursor.CurrentLine.ToString().Contains(trait1Text))
            {
                res.Traits = cursor.CurrentLine
                             .Slice(c => c.IndexOf(trait1Text) + trait1Text.Length)
                             .Trim()
                             .ToString()
                             .Split("Trait 2: ")
                             .SelectMany(s => s.Trim().SplitWithRegex(@"([^(]+)\(([^\)]+)\),{0,1}")) //TODO: Duplicate Regex, may be identical with traitMatcher.
                             .Select(this.ParseTrait)
                             .Where(o => o != null)
                             .ToList();
            }

            cursor.Next();

            var stringTriggers = cursor.CurrentLine
                                 .Slice(c => c.IndexOf(triggersText) + triggersText.Length)
                                 .ToString();

            res.Triggers = TranslateTriggers(stringTriggers.Trim());

            cursor.Next();

            res.Flavor = cursor.CurrentLine.Slice(c => c.IndexOf(flavorText) + flavorText.Length).ToString();
            while (cursor.Next() && !cursor.CurrentLine.StartsWith(rulesTextText))
            {
                res.Flavor += " " + cursor.CurrentLine.ToString();
            }

            var stringEffect = cursor.LinesUntilEOS
                               .Slice(c => c.IndexOf(rulesTextText) + rulesTextText.Length)
                               .Trim()
                               .ToString();

            // Divide the string into separate lines of actual effects.
            var effectSplit = stringEffect
                              .Replace("[A]", "[AUTO]")
                              .Replace("[C]", "[CONT]")
                              .Replace("[S]", "[ACT]")
                              .Replace("\n[AUTO]", "\n[A][AUTO]")
                              .Replace("\n[CONT]", "\n[A][CONT]")
                              .Replace("\n[ACT]", "\n[A][ACT]")
                              .Split("\n[A]", StringSplitOptions.RemoveEmptyEntries);

            res.Effect = effectSplit.Select(s => Clean(s)).ToArray();

            res.Remarks = $"Extractor: {this.GetType().Name}";
            //            foreach (var effect in effectSplit)
            //                Log.Information("Effect {serial}: {effect}", res.Serial, effect);

            Log.Information("Extracted: {serial}", res.Serial);
            return(res);
        }
示例#9
0
        private (string setLinkWithUnderscores, string url) CardListURLFrom(IDocument menu, WeissSchwarzCard firstCard)
        {
            var ogReleaseID = firstCard.ReleaseID;
            var releaseIDs  = new List <string>();

            releaseIDs.Add(firstCard.ReleaseID);
            if (ogReleaseID.StartsWith("EN-"))
            {
                releaseIDs.Add(ogReleaseID.Substring(3));
            }
            var setLink = menu.Links.Cast <IHtmlAnchorElement>()
                          .Where(ele => LinkMatcher.IsMatch(ele.Href))
                          .Where(ele => releaseIDs.Any(s => ele.Href.Contains(s)))
                          .FirstOrDefault();

            if (setLink == null)
            {
                Log.Error("Cannot find a link that matches {SID} using this list of links: {@items}", ogReleaseID, menu.Links.Cast <IHtmlAnchorElement>().Select(ele => ele.Href).ToList());
                throw new Exception();
            }

            var enPreString            = "EN-";
            var setLinkWithUnderscores = setLink.Href.AsSpan()
                                         .Slice(x => x.IndexOf(enPreString))
                                         .ToString()
                                         .Replace("-", "_");

            return(setLinkWithUnderscores, $"http://jktcg.com/WS_EN/{setLinkWithUnderscores}/{setLinkWithUnderscores}.html");
        }
示例#10
0
    public async Task Run(IContainer ioc, IProgress <CommandProgressReport> progress, CancellationToken cancellationToken = default)
    {
        Log.Information("Starting.");
        var report = CommandProgressReport.Starting(CommandProgressReportVerbType.Caching);

        progress.Report(report);

        var language = InterpretLanguage(Language);
        IAsyncEnumerable <WeissSchwarzCard> list = null;

        Func <Flurl.Url, CookieSession> _cookieSession = (url) => ioc.GetInstance <GlobalCookieJar>()[url.Root];


        using (var db = ioc.GetInstance <CardDatabaseContext>())
        {
            await db.Database.MigrateAsync(cancellationToken);

            if (language == null)
            {
                try
                {
                    var tuple = WeissSchwarzCard.ParseSerial(ReleaseIDorFullSerialID);
                }
                catch (Exception)
                {
                    Log.Error("Serial cannot be parsed properly. Did you mean to cache a release set? If so, please indicate the language (EN/JP) as well.");
                    return;
                }

                var query = from card in db.WeissSchwarzCards.AsQueryable()
                            where card.Serial.ToLower() == ReleaseIDorFullSerialID.ToLower()
                            select card;
                list = query.ToAsyncEnumerable().Take(1);
            }
            else
            {
                var releaseID = ReleaseIDorFullSerialID.ToLower().Replace("%", "");
                var query     = from card in db.WeissSchwarzCards.AsQueryable()
                                where EF.Functions.Like(card.Serial.ToLower(), $"%/{releaseID}%")
                                select card;
                list = query.ToAsyncEnumerable().Where(c => c.Language == language.Value);
            }



            await foreach (var card in list.WithCancellation(cancellationToken))
            {
                report = report with
                {
                    MessageType   = MessageType.InProgress,
                    ReportMessage = new Card.API.Entities.Impls.MultiLanguageString
                    {
                        EN = $"Caching [${card.Serial}]..."
                    },
                    Percentage = 50
                };
                progress.Report(report);
                await AddCachedImageAsync(card, _cookieSession, cancellationToken);
            }

            Log.Information("Done.");
            Log.Information("PS: Please refrain from executing this command continuously as this may cause your IP address to get tagged as a DDoS bot.");
            Log.Information("    Only cache the images you may need.");
            Log.Information("    -ronelm2000");
        }

        report = report.AsDone();
        progress.Report(report);
    }
示例#11
0
        private async IAsyncEnumerable <WeissSchwarzCard> Process(WeissSchwarzCard firstCard, IAsyncEnumerable <WeissSchwarzCard> originalCards)
        {
            Log.Information("Starting...");
            var menu = await "http://jktcg.com/MenuLeftEN.html"
                       .WithHTMLHeaders()
                       .GetHTMLAsync();
            var pair     = CardListURLFrom(menu, firstCard);
            var cardList = await pair.url
                           .WithHTMLHeaders()
                           .GetHTMLAsync();

            var releaseID  = firstCard.ReleaseID;
            var cardImages = cardList.QuerySelectorAll <IHtmlImageElement>("a > img")
                             .Select(ele => (
                                         Serial: GetSerial(ele),
                                         Source: ele.Source.Replace("\t", "")
                                         ))
                             .ToDictionary(p => p.Serial, p => p.Source);//(setID + "-" + str.AsSpan().Slice(c => c.LastIndexOf('_') + 1, c => c.LastIndexOf(".")).ToString()).ToLower());

            /* Commented for future use
             * Log.Information("Getting all PRs on card database without a YYT image link...");
             * using (var db = _database())
             * {
             *  var prCards = db.WeissSchwarzCards.AsAsyncEnumerable()
             *      .Where(c => c.ReleaseID == releaseID
             *                  && c.Language == CardLanguage.English
             *                  && c.Rarity == "PR"
             *                  && !c.Images.Any(u => u.Authority == "jktcg.com")
             *      );
             *  await foreach (var prCard in prCards)
             *  {
             *      if (cardImages.TryGetValue(prCard.Serial, out var urlLink))
             *      {
             *          var imgUrl = new Uri(urlLink);
             *          prCard.Images.Add(imgUrl);
             *          db.Update(prCard);
             *          Log.Information("Attached to {serial}: {imgUrl}", prCard.Serial, urlLink);
             *      }
             *  }
             *  await db.SaveChangesAsync();
             * }
             */

            await foreach (var card in originalCards)
            {
                var res = card.Clone();
                try
                {
                    var imgURL = cardImages[res.Serial.ToLower()];
                    res.Images.Add(new Uri(imgURL));
                    Log.Information("Attached image to {serial}: {imgURL}", res.Serial, imgURL);
                }
                catch (KeyNotFoundException)
                {
                    Log.Warning("Tried to post-process {serial} when the URL for {releaseID} was loaded. Skipping.", res.Serial, releaseID);
                }
                yield return(res);
            }

            Log.Information("Finished.");
            yield break;
        }