private List <MetadataField> GetAvailableFields() { var game = _options.GameData; var list = new List <MetadataField>(); var name = game.Name; if (name.IsEmpty()) { var dlSiteLink = game.Links.FirstOrDefault(x => x.Name.Equals("DLSite", StringComparison.InvariantCultureIgnoreCase)); if (dlSiteLink == null) { return(list); } name = dlSiteLink.Url; } var id = name; IDCheck: if (!id.StartsWith("RJ", StringComparison.InvariantCultureIgnoreCase) && !id.StartsWith("RE", StringComparison.InvariantCultureIgnoreCase)) { if (id.StartsWith(Consts.RootENG) || id.StartsWith(Consts.RootJPN)) { //https://www.dlsite.com/ecchi-eng/work/=/product_id/RE234198.html var root = id.StartsWith(Consts.RootENG) ? Consts.RootENG : Consts.RootJPN; id = id.Replace(root, ""); //work/=/product_id/{id}.html id = id.Replace(".html", "").Replace("work/=/product_id/", ""); goto IDCheck; } var dlSiteLink = game.Links.FirstOrDefault(x => x.Name.Equals("DLSite", StringComparison.InvariantCultureIgnoreCase)); if (dlSiteLink != null) { id = dlSiteLink.Url; goto IDCheck; } Logger.Warn($"Trying to get metadata for {id} but it does not start with RJ/RE!"); return(list); } _game = DLSiteGame.LoadGame(id, Logger).Result; Task.Run(() => { DLSiteGenres.AddGenres(_game.Genres); var dataDir = Path.Combine(_plugin.PlayniteApi.Paths.ExtensionsDataPath, _plugin.Id.ToString()); DLSiteGenres.SaveGenres(dataDir); }); list.Add(MetadataField.Links); if (!_game.Name.IsEmpty()) { list.Add(MetadataField.Name); } if (!_game.Circle.IsEmpty()) { list.Add(MetadataField.Developers); list.Add(MetadataField.Publishers); } if (!_game.Description.IsEmpty()) { list.Add(MetadataField.Description); } if (!_game.Release.IsEmpty()) { list.Add(MetadataField.ReleaseDate); } if (_game.Genres != null && _game.Genres.Count > 0) { list.Add(MetadataField.Genres); } if (_game.ImageURLs != null && _game.ImageURLs.Count > 0) { list.Add(MetadataField.CoverImage); if (_game.ImageURLs.Count > 1) { list.Add(MetadataField.BackgroundImage); } } //TODO: Age Ratings //TODO: Platform return(list); }
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); }