public static bool TryGetGenre(int id, out DLSiteGenre genre)
        {
            genre = null;

            var first = _genres.FirstOrDefault(x => x.ID == id);

            if (first == null)
            {
                return(false);
            }

            genre = first;
            return(true);
        }
        public static string ConvertTo(DLSiteGenre genre, ILogger logger, bool english)
        {
            var url = english ? genre.GetJPNLink : genre.GetENGLink;

            var web      = new HtmlWeb();
            var document = web.Load(url);

            if (document == null)
            {
                logger.Error($"Document for {url} is null!");
                return(null);
            }

            var node = document.DocumentNode;
            var id   = genre.ID.ToString();

            if (node.IsNull(logger, "Document Node", id))
            {
                return(null);
            }

            var titleNode = node.SelectSingleNode("//div[@id='container']/div[@id='wrapper']/div[@id='main']/div[@id='main_inner']/div[@class='base_title_br clearfix']/h1/span[@class='original_name']");

            if (titleNode.IsNull(logger, "Title Node", id))
            {
                return(null);
            }

            var title = titleNode.DecodeInnerText();

            if (string.IsNullOrEmpty(title))
            {
                return(null);
            }

            return(DLSiteGenre.CleanName(title, !english));
        }
        public static async Task <DLSiteGame> LoadGame(string id, ILogger logger)
        {
            var isEnglish = id.StartsWith("RE");
            var url       = Consts.GetWorkURL(id, isEnglish);

            var web      = new HtmlWeb();
            var document = await web.LoadFromWebAsync(url);

            if (document == null)
            {
                return(null);
            }

            var node = document.DocumentNode;

            //logger.Info(node.InnerHtml);

            var game = new DLSiteGame {
                DLSiteLink = url
            };

            if (node.TryGetInnerText(
                    "//div[@id='top_wrapper']/div[@class='base_title_br clearfix']/h1[@id='work_name']/a", logger, "Name",
                    id, out var name))
            {
                game.Name = name;
            }

            var imageNodes =
                node.SelectNodes(
                    "//div[@id='work_header']/div[@id='work_left']/div/div[@class='product-slider']/div[@class='product-slider-data']/div");

            if (!imageNodes.IsNullOrEmpty(logger, "Images", id))
            {
                game.ImageURLs = imageNodes.Select(x =>
                {
                    var src = x.GetValue("data-src");
                    if (src.StartsWith("//"))
                    {
                        src = $"https:{src}";
                    }
                    return(src);
                }).NotNull().ToList();
            }

            //TODO: ratings
            //ratings require script execution

            /*var ratingsNode =
             *  node.SelectSingleNode(
             *      "//div[@id='work_right']/div[@class='work_right_info']/div[@class='work_right_info_item'][2]/dl[@class='work_right_info_title']//span[@class='point average_count']");
             * if(ratingsNode == null)
             *  logger.Warn($"Found no ratings node for {id}!");
             * else
             * {
             *  var sRating = ratingsNode.DecodeInnerText();
             *  if(sRating.IsEmpty())
             *      logger.Warn($"Rating for {id} is empty!");
             *  else
             *  {
             *      if(!double.TryParse(sRating, out var rating))
             *          logger.Warn($"Unable to parse rating {sRating} to double!");
             *      else
             *          game.Rating = rating;
             *  }
             * }*/

            var workRightNode = node.SelectSingleNode("//div[@id='work_right']/div[@id='work_right_inner']");

            if (workRightNode.IsNull(logger, "Work Right Div", id))
            {
                return(game);
            }

            var circleNode = workRightNode.SelectSingleNode("//div[@id='work_right_name']/table[@id='work_maker']/tr/td/span[@class='maker_name']/a");

            if (!circleNode.IsNull(logger, "Circle", id))
            {
                var sCircle = circleNode.DecodeInnerText();
                if (!sCircle.IsEmpty(logger, "Circle", id))
                {
                    game.Circle = sCircle;
                }

                var sCircleLink = circleNode.GetValue("href");
                if (!sCircleLink.IsEmpty(logger, "Circle Link", id))
                {
                    game.CircleLink = sCircleLink;
                }
            }

            var descriptionNode = node.SelectSingleNode("//div[@class='work_parts_container']/div[@class='work_parts type_text']/div[@class='work_parts_area']/p");

            if (!descriptionNode.IsNull(logger, "Description", id))
            {
                var sDescription = descriptionNode.InnerHtml;
                if (!sDescription.IsEmpty(logger, "Description", id))
                {
                    game.Description = HttpUtility.HtmlDecode(sDescription);
                }
            }

            var tableChildren = workRightNode.SelectNodes("//table[@id='work_outline']//tr");

            if (tableChildren.IsNullOrEmpty(logger, "Table", id))
            {
                return(game);
            }

            Dictionary <string, HtmlNode> dic = tableChildren.ToDictionary(x =>
            {
                var th = x.SelectSingleNode("th");
                return(th.DecodeInnerText());
            }, x => x.SelectSingleNode("td"));

            dic.Do(x =>
            {
                var key = x.Key;
                var td  = x.Value;

                if (key == Consts.GetReleaseTranslation(isEnglish))
                {
                    var dateNode = td.SelectSingleNode("a");
                    if (!dateNode.IsNull(logger, "Release Date", id))
                    {
                        var sDate = dateNode.DecodeInnerText();
                        if (!sDate.IsEmpty(logger, "Release Date", id))
                        {
                            game.Release = sDate;
                        }
                    }

                    return;
                }

                if (key == Consts.GetLastModifiedTranslation(isEnglish))
                {
                    var sLastModified = td.DecodeInnerText();
                    if (!sLastModified.IsEmpty(logger, "Last Modified", id))
                    {
                        game.LastModified = sLastModified;
                    }
                    return;
                }

                if (key == Consts.GetAgeRatingsTranslation(isEnglish))
                {
                    var ratingNode = td.SelectSingleNode("div[@class='work_genre']/a/span");
                    if (!ratingNode.IsNull(logger, "Age Rating", id))
                    {
                        var sRating = ratingNode.DecodeInnerText();
                        if (!sRating.IsEmpty(logger, "Age Rating", id))
                        {
                            game.AgeRating = Utils.ToAgeRating(sRating);
                        }
                    }

                    return;
                }

                if (key == Consts.GetWorkFormatTranslation(isEnglish))
                {
                    var formatNodes = td.SelectNodes("div[@class='work_genre']/a/span");
                    if (!formatNodes.IsNullOrEmpty(logger, "Work Format", id))
                    {
                        game.WorkFormats = formatNodes.Select(y => y.DecodeInnerText()).NotNull().ToList();
                    }

                    return;
                }

                if (key == Consts.GetFileFormatTranslation(isEnglish))
                {
                    var fileFormatNode = td.SelectSingleNode("div[@class='work_genre']/a/span");
                    if (!fileFormatNode.IsNull(logger, "File Format", id))
                    {
                        var sFileFormat = fileFormatNode.DecodeInnerText();
                        if (!sFileFormat.IsEmpty(logger, "File Format", id))
                        {
                            game.FileFormat = sFileFormat;
                        }
                    }

                    return;
                }

                if (key == Consts.GetGenreTranslation(isEnglish))
                {
                    var genreNodes = td.SelectNodes("div[@class='main_genre']/a");
                    if (!genreNodes.IsNullOrEmpty(logger, "Genres", id))
                    {
                        game.Genres = genreNodes.Select(genreNode =>
                        {
                            var genreUrl = genreNode.GetValue("href");
                            var genreID  = isEnglish ? DLSiteGenre.GetENGID(genreUrl) : DLSiteGenre.GetJPNID(genreUrl);
                            if (genreID == -1)
                            {
                                logger.Error($"Could not get ID from {genreUrl}");
                                return(null);
                            }

                            if (DLSiteGenres.TryGetGenre(genreID, out var cachedGenre))
                            {
                                if (cachedGenre.ENG != null)
                                {
                                    return(cachedGenre);
                                }
                            }

                            var genre     = new DLSiteGenre(genreID);
                            var genreName = genreNode.DecodeInnerText();
                            if (isEnglish)
                            {
                                genre.ENG = genreName;
                            }
                            else
                            {
                                genre.JPN         = genreName;
                                var resultConvert = DLSiteGenres.ConvertTo(genre, logger, isEnglish);
                                if (string.IsNullOrEmpty(resultConvert))
                                {
                                    logger.Error($"Unable to convert {genreName} to English genre!");
                                    return(null);
                                }

                                genre.ENG = resultConvert;
                            }

                            return(genre);
                        }).NotNull().ToList();
                    }

                    return;
                }

                if (key == Consts.GetFileSizeTranslation(isEnglish))
                {
                    var fileSizeNode = td.SelectSingleNode("div[@class='main_genre']");
                    if (!fileSizeNode.IsNull(logger, "File Size", id))
                    {
                        var sFileSize = fileSizeNode.DecodeInnerText();
                        if (!sFileSize.IsEmpty(logger, "File Size", id))
                        {
                            game.FileSize = sFileSize;
                        }
                    }

                    return;
                }

                logger.Warn($"Unknown key: {key}");
            });

            return(game);
        }