1
+ using AiServer . ServiceModel ;
2
+ using AiServer . ServiceModel . Types ;
3
+ using BlazorDiffusion . ServiceInterface ;
4
+ using BlazorDiffusion . ServiceModel ;
5
+ using ServiceStack . IO ;
6
+ using ServiceStack . Text ;
7
+ using JsonApiClient = ServiceStack . JsonApiClient ;
8
+
9
+ namespace BlazorDiffusion . AiServer ;
10
+ public class AiServerClient : IStableDiffusionClient
11
+ {
12
+ public JsonApiClient Client { get ; set ; }
13
+ public IVirtualFiles VirtualFiles { get ; set ; }
14
+
15
+ public string ? OutputPathPrefix { get ; set ; }
16
+
17
+ private object seedLock = new ( ) ;
18
+
19
+ public async Task < ImageGenerationResponse > GenerateImageAsync ( ImageGeneration request )
20
+ {
21
+ var req = request . ToComfy ( ) ;
22
+ var res = await Client . PostAsync ( req ) ;
23
+ if ( res == null )
24
+ throw new Exception ( "Failed to generate image." ) ;
25
+ var now = DateTime . UtcNow ;
26
+ var key = $ "{ now : yyyy/MM/dd} /{ ( long ) now . TimeOfDay . TotalMilliseconds } ";
27
+
28
+ var results = new List < ImageGenerationResult > ( ) ;
29
+ var seed = ( res ? . Request ? . Seed ?? 0 ) . ConvertTo < uint > ( ) ;
30
+ await Parallel . ForEachAsync ( res . Images , async ( item , token ) =>
31
+ {
32
+ var artifactUrl = $ "{ Client . BaseUri . TrimEnd ( '/' ) } /uploads{ item . Url } ";
33
+ var bytes = await artifactUrl . GetBytesFromUrlAsync ( token : token ) ;
34
+ var imageDetails = ImageDetails . Calculate ( bytes ) ;
35
+ var uuid = Guid . NewGuid ( ) . ToString ( "N" ) ;
36
+ lock ( seedLock )
37
+ {
38
+ results . Add ( new ( )
39
+ {
40
+ Prompt = request . Prompt ,
41
+ Seed = seed ,
42
+ AnswerId = res . PromptId ,
43
+ FilePath = $ "/artifacts/{ key } /output_{ uuid } .png",
44
+ FileName = $ "output_{ uuid } .png",
45
+ ContentLength = bytes . Length ,
46
+ Width = request . Width ,
47
+ Height = request . Height ,
48
+ ImageDetails = imageDetails ,
49
+ } ) ;
50
+ // Assume incremental seeds for multiple images as comfyui does not provide the specific image seed back
51
+ seed ++ ;
52
+ }
53
+ var output = Path . Join ( OutputPathPrefix , key , $ "output_{ uuid } .png") ;
54
+ await VirtualFiles . WriteFileAsync ( output , bytes , token ) ;
55
+ } ) ;
56
+
57
+ return new ImageGenerationResponse
58
+ {
59
+ RequestId = res . PromptId ,
60
+ EngineId = "comfy" ,
61
+ Key = key ,
62
+ Results = results ,
63
+ } ;
64
+ }
65
+
66
+ public string GetMetadataPath ( Creative creative ) => OutputPathPrefix . CombineWith ( creative . Key , "metadata.json" ) ;
67
+ public IVirtualFile GetMetadataFile ( Creative creative ) => VirtualFiles . GetFile ( GetMetadataPath ( creative ) ) ;
68
+
69
+ public async Task SaveMetadataAsync ( Creative creative )
70
+ {
71
+ var vfsPathSuffix = creative . Key ;
72
+ var outputDir = Path . Join ( OutputPathPrefix , vfsPathSuffix ) ;
73
+ await VirtualFiles . WriteFileAsync ( Path . Join ( outputDir , "metadata.json" ) , creative . ToJson ( ) . IndentJson ( ) ) ;
74
+ }
75
+
76
+ public Task DeleteFolderAsync ( Creative creative )
77
+ {
78
+ var vfsPathSuffix = creative . Key ;
79
+ var directory = VirtualFiles . GetDirectory ( Path . Join ( OutputPathPrefix , vfsPathSuffix ) ) ;
80
+ var allFiles = directory . GetAllFiles ( ) ;
81
+ VirtualFiles . DeleteFiles ( allFiles ) ;
82
+ return Task . CompletedTask ;
83
+ }
84
+ }
85
+
86
+ public static class StableDiffusionClientExtensions
87
+ {
88
+ public static ComfyTextToImage ToComfy ( this ImageGeneration request )
89
+ {
90
+ return new ComfyTextToImage
91
+ {
92
+ Height = request . Height ,
93
+ Width = request . Width ,
94
+ Seed = request . Seed ?? Random . Shared . Next ( ) ,
95
+ BatchSize = request . Images ,
96
+ PositivePrompt = request . Prompt ,
97
+ } ;
98
+ }
99
+ }
0 commit comments