/// <summary>Construct an instance.</summary> /// <param name="wiki">The mod metadata from the wiki (if available).</param> /// <param name="db">The mod metadata from SMAPI's internal DB (if available).</param> public ModExtendedMetadataModel(WikiCompatibilityEntry wiki, ModDataRecord db) { // wiki data if (wiki != null) { this.ID = wiki.ID; this.Name = wiki.Name; this.NexusID = wiki.NexusID; this.ChucklefishID = wiki.ChucklefishID; this.GitHubRepo = wiki.GitHubRepo; this.CustomSourceUrl = wiki.CustomSourceUrl; this.CustomUrl = wiki.CustomUrl; this.CompatibilityStatus = wiki.Status; this.CompatibilitySummary = wiki.Summary; } // internal DB data if (db != null) { this.ID = this.ID.Union(db.FormerIDs).ToArray(); this.Name = this.Name ?? db.DisplayName; } }
/********* ** Private methods *********/ /// <summary>Get the metadata for a mod.</summary> /// <param name="search">The mod data to match.</param> /// <param name="wikiData">The wiki data.</param> /// <param name="includeExtendedMetadata">Whether to include extended metadata for each mod.</param> /// <returns>Returns the mod data if found, else <c>null</c>.</returns> private async Task <ModEntryModel> GetModData(ModSearchEntryModel search, WikiCompatibilityEntry[] wikiData, bool includeExtendedMetadata) { // resolve update keys var updateKeys = new HashSet <string>(search.UpdateKeys ?? new string[0], StringComparer.InvariantCultureIgnoreCase); ModDataRecord record = this.ModDatabase.Get(search.ID); if (record?.Fields != null) { string defaultUpdateKey = record.Fields.FirstOrDefault(p => p.Key == ModDataFieldKey.UpdateKey && p.IsDefault)?.Value; if (!string.IsNullOrWhiteSpace(defaultUpdateKey)) { updateKeys.Add(defaultUpdateKey); } } // get latest versions ModEntryModel result = new ModEntryModel { ID = search.ID }; IList <string> errors = new List <string>(); foreach (string updateKey in updateKeys) { // fetch data ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey); if (data.Error != null) { errors.Add(data.Error); continue; } // handle main version if (data.Version != null) { if (!SemanticVersion.TryParse(data.Version, out ISemanticVersion version)) { errors.Add($"The update key '{updateKey}' matches a mod with invalid semantic version '{data.Version}'."); continue; } if (this.IsNewer(version, result.Main?.Version)) { result.Main = new ModEntryVersionModel(version, data.Url); } } // handle optional version if (data.PreviewVersion != null) { if (!SemanticVersion.TryParse(data.PreviewVersion, out ISemanticVersion version)) { errors.Add($"The update key '{updateKey}' matches a mod with invalid optional semantic version '{data.PreviewVersion}'."); continue; } if (this.IsNewer(version, result.Optional?.Version)) { result.Optional = new ModEntryVersionModel(version, data.Url); } } } // get unofficial version WikiCompatibilityEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(result.ID.Trim(), StringComparer.InvariantCultureIgnoreCase)); if (wikiEntry?.UnofficialVersion != null && this.IsNewer(wikiEntry.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.UnofficialVersion, result.Optional?.Version)) { result.Unofficial = new ModEntryVersionModel(wikiEntry.UnofficialVersion, this.WikiCompatibilityPageUrl); } // fallback to preview if latest is invalid if (result.Main == null && result.Optional != null) { result.Main = result.Optional; result.Optional = null; } // special cases if (result.ID == "Pathoschild.SMAPI") { if (result.Main != null) { result.Main.Url = "https://smapi.io/"; } if (result.Optional != null) { result.Optional.Url = "https://smapi.io/"; } } // add extended metadata if (includeExtendedMetadata && (wikiEntry != null || record != null)) { result.Metadata = new ModExtendedMetadataModel(wikiEntry, record); } // add result result.Errors = errors.ToArray(); return(result); }
/// <summary>Get update keys based on the available mod metadata, while maintaining the precedence order.</summary> /// <param name="specifiedKeys">The specified update keys.</param> /// <param name="record">The mod's entry in SMAPI's internal database.</param> /// <param name="entry">The mod's entry in the wiki list.</param> public IEnumerable <string> GetUpdateKeys(string[] specifiedKeys, ModDataRecord record, WikiCompatibilityEntry entry) { IEnumerable <string> GetRaw() { // specified update keys if (specifiedKeys != null) { foreach (string key in specifiedKeys) { yield return(key?.Trim()); } } // default update key string defaultKey = record?.GetDefaultUpdateKey(); if (defaultKey != null) { yield return(defaultKey); } // wiki metadata if (entry != null) { if (entry.NexusID.HasValue) { yield return($"Nexus:{entry.NexusID}"); } if (entry.ChucklefishID.HasValue) { yield return($"Chucklefish:{entry.ChucklefishID}"); } } } HashSet <string> seen = new HashSet <string>(StringComparer.InvariantCulture); foreach (string key in GetRaw()) { if (!string.IsNullOrWhiteSpace(key) && seen.Add(key)) { yield return(key); } } }