diff --git a/ComfySharp.sln.DotSettings b/ComfySharp.sln.DotSettings new file mode 100644 index 0000000..792e9ca --- /dev/null +++ b/ComfySharp.sln.DotSettings @@ -0,0 +1,2 @@ + + PackageReference \ No newline at end of file diff --git a/ComfySharp/ComfyClient.cs b/ComfySharp/ComfyClient.cs index c493e15..b85dc69 100644 --- a/ComfySharp/ComfyClient.cs +++ b/ComfySharp/ComfyClient.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Dynamic; using System.Net; using System.Net.Http.Json; using System.Text.Json; @@ -8,7 +9,7 @@ namespace ComfySharp; public class ComfyClient { private HttpClient client; - private List nodes; + private List nodes; public string BaseUrl { get; set; } @@ -18,7 +19,7 @@ public class ComfyClient { BaseAddress = new Uri(baseUrl), DefaultRequestHeaders = { { "User-Agent", "ComfySharp" } } }; - nodes= new List(); + nodes= new(); } public async Task GetEmbeddings() { @@ -49,10 +50,10 @@ public class ComfyClient { if (req is { IsSuccessStatusCode: true, Content: not null }) { var doc = await req.Content.ReadFromJsonAsync(); ObjectInfoParser.Parse(doc, out nodes); - } - - throw new NotImplementedException(); + + return null; + //throw new NotImplementedException(); } public async Task GetImage(string filename) { diff --git a/ComfySharp/ObjectInfoParser.cs b/ComfySharp/ObjectInfoParser.cs index 1f5580a..58e9bda 100644 --- a/ComfySharp/ObjectInfoParser.cs +++ b/ComfySharp/ObjectInfoParser.cs @@ -1,75 +1,21 @@ -using System.Text.Json; +using System.Dynamic; +using System.Text.Json; using ComfySharp.Types; namespace ComfySharp; public static class ObjectInfoParser { - public static void Parse(JsonDocument document, out List nodes) { - nodes = new List(); - foreach (var node in document.RootElement.EnumerateObject()) { - Node n = new(); - n.Name = node.Name; - - foreach (var prop in node.Value.EnumerateObject()) { - switch (prop.Name) { - case "input": - n.Input = new(); - - foreach (var input in prop.Value.EnumerateObject()) { - switch (input.Name) { - case "required": - foreach (var field in input.Value.EnumerateObject()) { - InputField f = new(); - f.Name = field.Name; - f.Type = Enum.Parse(field.Value.GetString() ?? ""); - n.Input.Required.Add(f); - } - break; - case "optional": - foreach (var field in input.Value.EnumerateObject()) { - InputField f = new(); - f.Name = field.Name; - f.Type = Enum.Parse(field.Value.GetString() ?? ""); - n.Input.Optional.Add(f); - } - break; - case "hidden": - foreach (var field in input.Value.EnumerateObject()) { - InputField f = new(); - f.Name = field.Name; - f.Type = Enum.Parse(field.Value.GetString() ?? ""); - n.Input.Hidden.Add(f); - } - break; - } - } - break; - case "output": - foreach (var output in prop.Value.EnumerateObject()) { - n.Outputs.Add(Enum.Parse(output.Value.GetString() ?? "")); - n.OutputIsList.Add(output.Value.GetBoolean()); - n.OutputNames.Add(output.Name); - } - break; - case "display_name": - n.DisplayName = prop.Value.GetString() ?? ""; - break; - case "description": - n.Description = prop.Value.GetString() ?? ""; - break; - case "category": - n.Category = prop.Value.GetString() ?? ""; - break; - case "output_node": - n.IsOutputNode = prop.Value.GetBoolean(); - break; - } - } - } + + public static void Parse(JsonDocument document, out List nodes) { + NodeDBGenerator dbGenerator = new(); + dbGenerator.GenerateNodes(document); + nodes = dbGenerator.GetNodes(); + dbGenerator.GenerateClasses(document); } private static void ParseNode(JsonElement node, out Node n) { n = new(); + n.Name = node.GetProperty("name").GetString() ?? ""; n.Input = ParseInput(node.GetProperty("input")); n.Outputs = ParseOutputs(node.GetProperty("output")); @@ -80,4 +26,20 @@ public static class ObjectInfoParser { n.Category = node.GetProperty("category").GetString() ?? ""; n.IsOutputNode = node.GetProperty("output_node").GetBoolean(); } + static private List ParseOutputNames(JsonElement getProperty) { + List outputNames = new(); + foreach (var output in getProperty.EnumerateArray()) { + outputNames.Add(output.GetProperty("name").GetString() ?? ""); + } + return outputNames; + } + static private List ParseOutputIsList(JsonElement getProperty) { + throw new NotImplementedException(); + } + static private List ParseOutputs(JsonElement getProperty) { + throw new NotImplementedException(); + } + static private Input ParseInput(JsonElement getProperty) { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/ComfySharp/Types/NodeDBGenerator.cs b/ComfySharp/Types/NodeDBGenerator.cs new file mode 100644 index 0000000..c0ea89b --- /dev/null +++ b/ComfySharp/Types/NodeDBGenerator.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Dynamic; +using System.Runtime.CompilerServices; + +namespace ComfySharp.Types; + +public class NodeDBGenerator { + private List nodes; + + private List knownTypes = new(); + public List GetKnownTypes() => knownTypes; + + private Dictionary> knownEnums = new(); + public Dictionary> GetKnownEnums() => knownEnums; + + public int Count => nodes.Count; + private int typeFields = 0; + private int enumFields = 0; + + public NodeDBGenerator() { + nodes = new(); + } + + public void ResetDb() => nodes.Clear(); + + /// + /// Adds a new type to the list of known types, avoids duplicates + /// + /// type name as string + private void AddKnownType(string type) { + if (!knownTypes.Contains(type)) { + knownTypes.Add(type); + Console.WriteLine("Added new known type: {0}", type); + } else { + Console.WriteLine("Type {0} already known, skipped.", type); + } + typeFields++; + } + + /// + /// Adds or updates a known enum, avoids duplicates. + /// + /// enum type name as string + /// Collection of valid string values for this enum + private void AddKnownEnum(string type, ICollection values) { + if (!knownEnums.ContainsKey(type)) { + knownEnums.Add(type, values.ToList()); + Console.WriteLine("Added new known enum: {0}", type); + } + else { + values.ToList().ForEach(value => { + if (!knownEnums[type].Contains(value)) { + knownEnums[type].Add(value); + Console.WriteLine("Added new value to known enum: {0}", value); + } + }); + } + enumFields++; + } + + public void GenerateClasses(JsonDocument document) { + foreach (var node in document.RootElement.EnumerateObject()) ScanNode(node); + + Console.WriteLine("List of recognized Types:"); + foreach (var knownType in knownTypes) { + Console.WriteLine(knownType); + } + Console.WriteLine("\nTotal amount of types iterated: {0}\n", typeFields); + Console.WriteLine("List of recognized Enums:"); + foreach (var knownEnum in knownEnums) { + Console.WriteLine(knownEnum.Key); + foreach (var value in knownEnum.Value) { + Console.Write("\t{0}", value); + } + Console.WriteLine(); + } + Console.WriteLine("\nTotal amount of enums iterated: {0}\n", enumFields); + } + + /// + /// executed for a single node, progresses through the properties of the node + /// + private void ScanNode(JsonProperty node) { + // each of this are top level properties of the node + foreach (var property in node.Value.EnumerateObject()) { + //if this is a list of input properties: + if (property.Name == "input" && property.Value.ValueKind == JsonValueKind.Object) ScanInputs(property); + else if (property.Name == "output" && property.Value.ValueKind == JsonValueKind.Array) ScanOutputs(property); + } + } + + /// + /// Executed on the input property inside a node + /// + private void ScanInputs(JsonProperty input) { + foreach (var inputType in input.Value.EnumerateObject()) { + //these are related to the nodes themselves and useless for us + if (inputType.Name == "hidden") continue; + // required and optionals have the same structure, so we can parse them the same way + if (inputType.Name == "required" || inputType.Name == "optional") + foreach (var inputProperty in inputType.Value.EnumerateObject()) ScanInputField(inputProperty); + } + } + + /// + /// Executed for each of the elements inside a required or optional input + /// + private void ScanInputField(JsonProperty inputProperty) { + // if element 0 is a string, this is a type + if (inputProperty.Value[0].ValueKind == JsonValueKind.String) + AddKnownType(inputProperty.Value[0].ToString()); + // else, if element 0 is an array, this is an enum + else if (inputProperty.Value[0].ValueKind == JsonValueKind.Array) { + List enumValues = new(); + inputProperty.Value[0].EnumerateArray().ToList().ForEach(value => enumValues.Add(value.ToString())); + AddKnownEnum(inputProperty.Name, enumValues); + + } + } + + private void ScanOutputs(JsonProperty output) { } + + + + + + + + + + + + + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GenerateNode(JsonElement data) { + var node = new ExpandoObject(); + Console.WriteLine("Generating node: {0}", data.GetProperty("name").GetString() ?? ""); + foreach (var property in data.EnumerateObject()) { + Console.WriteLine("Adding new property: {0}\nType: {2}\nValue: {1}\n", property.Name, property.Value, property.Value.GetType()); + node.TryAdd(property.Name, property.Value); + } + nodes.Add(node); + } + + public void GenerateNode(JsonProperty node) { + GenerateNode(node.Value); + } + + public void GenerateNodes(JsonDocument document) { + foreach (var node in document.RootElement.EnumerateObject()) { + GenerateNode(node.Value); + } + } + + public void GenerateNodes(string json) { + var document = JsonDocument.Parse(json); + GenerateNodes(document); + } + + public void GenerateNodes(ICollection elements) { + foreach (var element in elements) { + GenerateNode(element); + } + } + + public void GenerateNodes(ICollection elements) { + foreach (var element in elements) { + GenerateNode(element); + } + } + + public List GetNodes() => nodes; + + +}