├── .gitignore ├── LICENSE ├── README.md ├── api.go ├── config.go ├── extra-batch-resp.go ├── extra-batch.go ├── extra-single-resp.go ├── extra-single.go ├── extra ├── batch.go └── const.go ├── go.mod ├── go.sum ├── img2img-resp.go ├── img2img.go ├── img2img ├── inpaint-masked-content.go ├── inpaint-mode.go └── resize-mode.go ├── interrogate.go ├── interrogate └── model.go ├── interrupt.go ├── options.go ├── png-info.go ├── progress.go ├── sampler └── const.go ├── scripts └── mod.go ├── sd-models.go ├── skip.go ├── txt2img-resp.go ├── txt2img.go ├── upscaler └── const.go └── utils └── base64.go /.gitignore: -------------------------------------------------------------------------------- 1 | /*-resp.json 2 | /EXAMPLE.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Meonako 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This project is unmaintained. Looking for a new owner or you should use fork instead. 2 | 3 | I'm not using GO anymore. While GO is definitely fun to use, it's "a bit **verbose**" and 4 | I think Rust is more enjoyable with something like `Option` enum. 5 | 6 | GO is a great language and maybe one of the first "***Errors are values***" and even with garbage collector, it is pretty fast. 7 | but for me, Rust is just more powerful with all these macro stuff 8 | (I can't write/create one obviously but I can't express enough how easy it is to use library like Tauri, Serde, poise-rs) 9 | 10 | # AUTOMATIC1111's Webui API 11 | 12 | AUTOMATIC1111's Webui **API** for **GO**. So you don't have to do it yourself. 13 | Aim to be as easy to use as possible ***without*** performance in mind. 14 | 15 | ## Currently Support (And also roadmap) 16 | 17 | - [x] Auth Related ( **DIDNT TEST** | Please [open an issue](https://github.com/Meonako/webui-api/issues/new) if you have encounter any problem ) 18 | - [x] Txt2Img 19 | - [x] Img2Img 20 | - [x] Extras (Single) 21 | - [x] Extras (Batch) 22 | - [x] PNG Info 23 | - [x] Progress 24 | - [x] Interrogate 25 | - [x] Interrupt 26 | - [x] Skip 27 | - [x] Options 28 | - [x] Get Available Model(s) 29 | 30 | ## Getting Started 31 | 32 | ***Required [Stable Diffusion Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) running with `--api` argument*** 33 | 34 | ``` 35 | go get github.com/Meonako/webui-api 36 | ``` 37 | Then Import 38 | ```go 39 | import ( 40 | ... 41 | "github.com/Meonako/webui-api" 42 | ) 43 | ``` 44 | 45 | _OR_ 46 | 47 | Simply add package to import like this 48 | ```go 49 | import ( 50 | ... 51 | "github.com/Meonako/webui-api" 52 | ) 53 | ``` 54 | 55 | Then run `go mod tidy` 56 | 57 | --- 58 | 59 | Initialize it like this 60 | 61 | ```go 62 | API := api.New() 63 | ``` 64 | 65 | Without passing anything it'll return `http://127.0.0.1:7860` and all `V1 API path` as default. 66 | If you wanna change it, just pass in a new config like this 67 | 68 | ```go 69 | API := api.New(api.Config{ 70 | BaseURL: "colab.google.link", 71 | Path: &api.APIPath{ 72 | Txt2Img: "/new/api/path/txt2img", 73 | }, 74 | }) 75 | ``` 76 | **Be aware, if you change `Path` field, you'll have to manually add all other path.** 77 | > Say for above example, it'll be only `Txt2Img` path in there. When you call `Img2Img`, you'll get an `unexpected response`/`error` or worse like `panic` 78 | 79 | --- 80 | 81 | Now that finished, we can start using it now. Let's say we'll do `TXT2IMG` 82 | ```go 83 | resp, err := API.Text2Image(&api.Txt2Image{ 84 | Prompt: "masterpiece, best quality, solo, cute, blue hair, purple eyes", 85 | NegativePrompt: "lowres, bad anatomy, low quality, normal quality, worst quality", 86 | }) 87 | ``` 88 | > **Keep in mind that this will block your app until API done generating image(s)** 89 | 90 | --- 91 | 92 | When it's done, check for the `error` and then we can do 93 | 94 | ```go 95 | imageList, err := resp.DecodeAllImages() 96 | ``` 97 | Check for the `error` and then we can save it to disk 98 | 99 | ```go 100 | for index, image := range imageList { 101 | file, err := os.OpenFile( 102 | fmt.Sprintf("txt2img-result %v.png", index), 103 | os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 104 | 0777, 105 | ) 106 | if err != nil { 107 | panic(err) 108 | } 109 | 110 | file.Write(image) 111 | file.Close() 112 | } 113 | ``` 114 | 115 | **Hol'up, one tip tho. If you really care about performance, you can decode it yourself like this** 116 | > Before called `resp.DecodeAllImages()` 117 | 118 | ```go 119 | for index := range resp.Images { 120 | decoded, err := resp.DecodeImage(index) 121 | if err != nil { 122 | panic(err) 123 | } 124 | 125 | file, err := os.OpenFile( 126 | fmt.Sprintf("txt2img-result %v.png", index), 127 | os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 128 | 0777, 129 | ) 130 | if err != nil { 131 | panic(err) 132 | } 133 | 134 | file.Write(image) 135 | file.Close() 136 | } 137 | ``` 138 | 139 | ## Example 140 | 141 | Move [HERE](https://github.com/Meonako/webui-api/wiki/Example) 142 | 143 | ## Default Value 144 | ```go 145 | var DefaultConfig = Config{ 146 | BaseURL: "http://127.0.0.1:7860", 147 | Path: &APIPath{ 148 | // Don't change any of these unless you know what you're doing. 149 | // I purposely exported this as I don't know If I'll still maintain this pkg in the future 150 | Txt2Img: "/sdapi/v1/txt2img", 151 | Img2Img: "/sdapi/v1/img2img", 152 | ExtraSingle: "/sdapi/v1/extra-single-image", 153 | ExtraBatch: "/sdapi/v1/extra-batch-images", 154 | PNGInfo: "/sdapi/v1/png-info", 155 | Progress: "/sdapi/v1/progress", 156 | Interrogate: "/sdapi/v1/interrogate", 157 | Interrupt: "/sdapi/v1/interrupt", 158 | Skip: "/sdapi/v1/skip", 159 | Options: "/sdapi/v1/options", 160 | SDModels: "/sdapi/v1/sd-models", 161 | }, 162 | } 163 | ``` 164 | 165 | ## Credits 166 | - [go-json](https://github.com/goccy/go-json) / [goccy](https://github.com/goccy) | for fast JSON encode/decode 167 | -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | type api struct { 12 | Config Config 13 | 14 | // Auth contains username and password like this: 15 | // 16 | // "username, password" or you can use Username && Password field instead. 17 | // 18 | // But do keep in mind that if Auth is not empty string, it'll use Auth and not Username && Password 19 | Auth string 20 | Username string 21 | Password string 22 | } 23 | 24 | var ( 25 | httpClient = &http.Client{} 26 | API = &api{ 27 | Config: setDefault(), 28 | } 29 | ) 30 | 31 | func New(newConfig ...Config) *api { 32 | API = &api{ 33 | Config: setDefault(newConfig...), 34 | } 35 | return API 36 | } 37 | 38 | // Set username and password for use when making request. Equivalent to 39 | // 40 | // api.Username = username 41 | // api.Password = password 42 | // 43 | // Either username or password should not be empty string 44 | func (a *api) SetAuth(username, password string) { 45 | a.Username = username 46 | a.Password = password 47 | } 48 | 49 | // Convenience function to build prompt. 50 | // 51 | // BuildPrompt("masterpiece", "best quality", "solo") -> "masterpiece, best quality, solo" 52 | func BuildPrompt(args ...string) string { 53 | return strings.Join(args, ", ") 54 | } 55 | 56 | // Send Get Request. 57 | func (a *api) get(path string) (body []byte, err error) { 58 | req, err := http.NewRequest("GET", a.Config.BaseURL+path, nil) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | return a.exec(req) 64 | } 65 | 66 | // Send Post Request. 67 | func (a *api) post(path string, data []byte) (body []byte, err error) { 68 | req, err := http.NewRequest("POST", a.Config.BaseURL+path, bytes.NewReader(data)) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | req.Header.Set("Content-Type", "application/json") 74 | 75 | return a.exec(req) 76 | } 77 | 78 | // Send Request. 79 | func (a *api) exec(req *http.Request) ([]byte, error) { 80 | a.setAuth(req) 81 | 82 | resp, err := httpClient.Do(req) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | return readBody(resp) 88 | } 89 | 90 | // Set HTTP Basic Auth. 91 | func (a *api) setAuth(req *http.Request) { 92 | if a.Auth != "" { 93 | credit := strings.Split(a.Auth, ", ") 94 | req.SetBasicAuth(credit[0], credit[1]) 95 | } else if a.Username != "" && a.Password != "" { 96 | req.SetBasicAuth(a.Username, a.Password) 97 | } 98 | } 99 | 100 | // Read response body. 101 | func readBody(resp *http.Response) (body []byte, err error) { 102 | defer resp.Body.Close() 103 | body, err = io.ReadAll(resp.Body) 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | if resp.StatusCode != 200 { 109 | return nil, fmt.Errorf("%v", string(body)) 110 | } 111 | 112 | return 113 | } 114 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Meonako/webui-api/sampler" 7 | ) 8 | 9 | type Config struct { 10 | // URL to API endpoint (e.g. http://127.0.0.1:7860, https://b645a912.gradio.app) 11 | // 12 | // - Default: http://127.0.0.1:7860 13 | BaseURL string 14 | 15 | // API path is stored here 16 | Path *APIPath 17 | 18 | // Default Value are store here. 19 | Default *Default 20 | } 21 | 22 | type Default struct { 23 | // Sampling Method or Sampler (e.g. Euler a, DPM++ 2M Karras). You can type it in yourself or use built-in Helper Package: sampler 24 | // 25 | // Default: "Euler a" 26 | Sampler string 27 | 28 | // Sampling Steps (e.g. 20, 120) 29 | // 30 | // Default: 20 31 | Steps int 32 | 33 | // Classifier-Free Guidance Scale (e.g. 7, 12.0) 34 | // 35 | // Default: 7.0 36 | CFGScale float64 37 | 38 | // Width of the image (e.g. 512, 1024) 39 | // 40 | // Default: 512 41 | Width int 42 | 43 | // Height of the image (e.g. 512, 1024) 44 | // 45 | // Default: 512 46 | Height int 47 | } 48 | 49 | type APIPath struct { 50 | // Path to txt2img API 51 | // 52 | // - Default: /sdapi/v1/txt2img 53 | Txt2Img string 54 | 55 | // Path to img2img API 56 | // 57 | // - Default: /sdapi/v1/img2img 58 | Img2Img string 59 | 60 | // Path to extra single image API 61 | // 62 | // - Default: /sdapi/v1/extra-single-image 63 | ExtraSingle string 64 | 65 | // Path to extra batch images API 66 | // 67 | // - Default: /sdapi/v1/extra-batch-images 68 | ExtraBatch string 69 | 70 | // Path to png info API 71 | // 72 | // - Default: /sdapi/v1/png-info 73 | PNGInfo string 74 | 75 | // Path to progress API 76 | // 77 | // - Default: /sdapi/v1/progress 78 | Progress string 79 | 80 | // Path to interrogate API 81 | // 82 | // - Default: /sdapi/v1/interrogate 83 | Interrogate string 84 | 85 | // Path to interrupt API 86 | // 87 | // - Default: /sdapi/v1/interrupt 88 | Interrupt string 89 | 90 | // Path to skip API 91 | // 92 | // - Default: /sdapi/v1/skip 93 | Skip string 94 | 95 | // Path to options API 96 | // 97 | // - Default: /sdapi/v1/options 98 | Options string 99 | 100 | // Path to sd-models API 101 | // 102 | // - Default: /sdapi/v1/sd-models 103 | SDModels string 104 | } 105 | 106 | var DefaultConfig = Config{ 107 | BaseURL: "http://127.0.0.1:7860", 108 | Path: &APIPath{ 109 | Txt2Img: "/sdapi/v1/txt2img", 110 | Img2Img: "/sdapi/v1/img2img", 111 | ExtraSingle: "/sdapi/v1/extra-single-image", 112 | ExtraBatch: "/sdapi/v1/extra-batch-images", 113 | PNGInfo: "/sdapi/v1/png-info", 114 | Progress: "/sdapi/v1/progress", 115 | Interrogate: "/sdapi/v1/interrogate", 116 | Interrupt: "/sdapi/v1/interrupt", 117 | Skip: "/sdapi/v1/skip", 118 | Options: "/sdapi/v1/options", 119 | SDModels: "/sdapi/v1/sd-models", 120 | }, 121 | } 122 | 123 | /* 124 | Default Values. 125 | 126 | Sampler = sampler.EULER_A, 127 | Steps = 28, 128 | CFGScale = 7, 129 | Width = 512, 130 | Height = 512, 131 | */ 132 | var DefaultValue = &Default{ 133 | Sampler: sampler.EULER_A, 134 | Steps: 20, 135 | CFGScale: 7, 136 | Width: 512, 137 | Height: 512, 138 | } 139 | 140 | func setDefault(conf ...Config) Config { 141 | if len(conf) <= 0 { 142 | return DefaultConfig 143 | } 144 | 145 | config := conf[0] 146 | 147 | if config.BaseURL == "" { 148 | config.BaseURL = DefaultConfig.BaseURL 149 | } 150 | 151 | config.BaseURL = strings.TrimSuffix(config.BaseURL, "/") 152 | 153 | if config.Path == nil { 154 | config.Path = DefaultConfig.Path 155 | } 156 | 157 | return config 158 | } 159 | -------------------------------------------------------------------------------- /extra-batch-resp.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/Meonako/webui-api/utils" 8 | ) 9 | 10 | type extraBatchImagesRespond struct { 11 | HTMLInfo string `json:"html_info"` // Upscaler info in HTML format. I don't even know why they return HTML format for API 12 | Images []string `json:"images"` // Base64-encoded image 13 | DecodedImages [][]byte // Base64-decoded image data in byte 14 | } 15 | 16 | // Decode data at index store in "Images" field and return it. 17 | func (er *extraBatchImagesRespond) DecodeImage(index int) ([]byte, error) { 18 | if len(er.Images) <= index { 19 | return []byte{}, fmt.Errorf("%v", "Out of bound. Provided Index > len(Images) of struct.") 20 | } 21 | 22 | return utils.DecodeBase64(er.Images[index]) 23 | } 24 | 25 | // Decode all data store in "Images" field and return it. You can access this later in "DecodedImages" Field. 26 | func (er *extraBatchImagesRespond) DecodeAllImages() ([][]byte, error) { 27 | if er.DecodedImages == nil || len(er.DecodedImages) > 0 { 28 | er.DecodedImages = [][]byte{} 29 | } 30 | 31 | for index := range er.Images { 32 | imageData, err := er.DecodeImage(index) 33 | if err != nil { 34 | return [][]byte{}, err 35 | } 36 | 37 | er.DecodedImages = append(er.DecodedImages, imageData) 38 | } 39 | 40 | return er.DecodedImages, nil 41 | } 42 | 43 | // Make bytes.Reader from "DecodedImages" field. 44 | // 45 | // - It'll call "DecodeAllImages()" if "len(DecodedImages) <= 0" then continue to proceed. 46 | // 47 | // This is ready to send to discord. Or ready to png.Decode and save. 48 | func (er *extraBatchImagesRespond) MakeBytesReader() (reader []*bytes.Reader, err error) { 49 | if er.DecodedImages == nil || len(er.DecodedImages) > 0 { 50 | er.DecodedImages = [][]byte{} 51 | } 52 | 53 | for index := range er.Images { 54 | imageData, err := er.DecodeImage(index) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | reader = append(reader, bytes.NewReader(imageData)) 60 | er.DecodedImages = append(er.DecodedImages, imageData) 61 | } 62 | 63 | return reader, nil 64 | } 65 | -------------------------------------------------------------------------------- /extra-batch.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/goccy/go-json" 5 | ) 6 | 7 | type ExtraBatchImages struct { 8 | // Resize Mode: Scale By || Scale To. See: webup-api/extra package for helper. 9 | // 10 | // Default: 0 (Scale By) 11 | ResizeMode int `json:"resize_mode,omitempty"` 12 | 13 | // Don't even know what this is. 14 | // 15 | // Original field was `ShowExtrasResults` but since the default value is `true`. it's quite tricky to do this in GO 16 | // 17 | // So I decided to reverse it. This set to true and "show_extras_results": false and vice versa 18 | // 19 | // Default: true 20 | DoNotShowExtrasResults bool `json:"show_extras_results,omitempty"` 21 | 22 | // GFPGAN Face restoration. Value must be between 0.0 - 1.0 23 | // 24 | // Default: 0.0 25 | GfpganVisibility float64 `json:"gfpgan_visibility,omitempty"` 26 | 27 | // CodeFormer Face restoration. Value must be between 0.0 - 1.0 28 | // 29 | // Default: 0.0 30 | CodeformerVisibility float64 `json:"codeformer_visibility,omitempty"` 31 | 32 | // CodeFormer Face restoration weight. 0 = Maximum Effect, 1 = Minimum Effect. 33 | // 34 | // Default: 0.0 35 | CodeformerWeight float64 `json:"codeformer_weight,omitempty"` 36 | 37 | // Multiplier to width and height of the original image. 38 | // 39 | // NOTE: Will only work if ResizeMode is 0 40 | // Default: 2 41 | UpscalingResize int `json:"upscaling_resize,omitempty"` 42 | 43 | // Width of the result image. 44 | // 45 | // NOTE: Will only work if ResizeMode is 1 46 | // Default: 512 47 | UpscalingResizeW int `json:"upscaling_resize_w,omitempty"` 48 | 49 | // Height of the result image. 50 | // 51 | // NOTE: Will only work if ResizeMode is 1 52 | // Default: 512 53 | UpscalingResizeH int `json:"upscaling_resize_h,omitempty"` 54 | 55 | // Crop Image if the aspect ratio of original image and result image doesn't match. 56 | // 57 | // Original field was `UpscalingCrop` but since the default value is `true`. it's quite tricky to do this in GO 58 | // 59 | // So I decided to reverse it. This set to true and "upscaling_crop": false and vice versa 60 | // 61 | // NOTE: Will only work if ResizeMode is 1 62 | // Default: true 63 | DoNotUpscalingCrop bool `json:"upscaling_crop,omitempty"` 64 | 65 | // First Upscaler Model. See: webui-api/extra package for helper. 66 | // 67 | // Default: "None" 68 | Upscaler1 string `json:"upscaler_1,omitempty"` 69 | 70 | // Second Upscaler Model. See: webui-api/extra package for helper. 71 | // 72 | // Default: "None" 73 | Upscaler2 string `json:"upscaler_2,omitempty"` 74 | 75 | // Second Upscaler Model Visibility. See: webui-api/extra package for helper. 76 | // 77 | // Default: 0.0 78 | ExtrasUpscaler2Visibility float64 `json:"extras_upscaler_2_visibility,omitempty"` 79 | 80 | // Upscale first then do face restoration. 81 | // 82 | // Default: false 83 | UpscaleFirst bool `json:"upscale_first,omitempty"` 84 | 85 | // Base64-encoded image to be upscale. 86 | // 87 | // Default: Empty 88 | ImagesList []ImageData `json:"imageList,omitempty"` 89 | 90 | // If true, Will Decode Images after received response from API 91 | DecodeAfterResult bool `json:"-"` 92 | } 93 | 94 | type ImageData struct { 95 | // Base64-encoded image to be upscale. 96 | // 97 | // Default: "" 98 | Data string `json:"data"` 99 | 100 | // I don't know what this is. I tried to read the source code for what it does but I don't think I get it. 101 | // 102 | // **NOT CONFIRM** Perhaps it is the temp file name 103 | // Default: "" 104 | Name string `json:"name"` 105 | } 106 | 107 | func (p *ExtraBatchImages) correctParams() { 108 | p.DoNotShowExtrasResults = !p.DoNotShowExtrasResults 109 | p.DoNotUpscalingCrop = !p.DoNotUpscalingCrop 110 | } 111 | 112 | func (a *api) ExtraBatchImages(params *ExtraBatchImages) (*extraBatchImagesRespond, error) { 113 | params.correctParams() 114 | 115 | payload, err := json.Marshal(params) 116 | if err != nil { 117 | return &extraBatchImagesRespond{}, err 118 | } 119 | 120 | data, err := a.post(a.Config.Path.ExtraBatch, payload) 121 | if err != nil { 122 | return &extraBatchImagesRespond{}, err 123 | } 124 | 125 | apiResp := extraBatchImagesRespond{} 126 | err = json.Unmarshal(data, &apiResp) 127 | if err != nil { 128 | return &extraBatchImagesRespond{}, err 129 | } 130 | 131 | if params.DecodeAfterResult { 132 | _, err := apiResp.DecodeAllImages() 133 | if err != nil { 134 | return &extraBatchImagesRespond{}, err 135 | } 136 | } 137 | 138 | return &apiResp, nil 139 | } 140 | -------------------------------------------------------------------------------- /extra-single-resp.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/Meonako/webui-api/utils" 4 | 5 | type extraSingleImageRespond struct { 6 | HTMLInfo string `json:"html_info"` // Upscaler info in HTML format. I don't even know why they return HTML format for API 7 | Image string `json:"image"` // Base64-encoded image 8 | DecodedImage []byte // Base64-decoded image data in byte 9 | } 10 | 11 | func (er *extraSingleImageRespond) DecodeImage() (decoded []byte, err error) { 12 | decoded, err = utils.DecodeBase64(er.Image) 13 | er.DecodedImage = decoded 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /extra-single.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/goccy/go-json" 4 | 5 | type ExtraSingleImage struct { 6 | // Resize Mode: Scale By || Scale To. See: webup-api/extra package for helper. 7 | // 8 | // Default: 0 (Scale By) 9 | ResizeMode int `json:"resize_mode,omitempty"` 10 | 11 | // Don't even know what this is. 12 | // 13 | // Original field was `ShowExtrasResults` but since the default value is `true`. it's quite tricky to do this in GO 14 | // 15 | // So I decided to reverse it. This set to true and "show_extras_results": false and vice versa 16 | // 17 | // Default: true 18 | DoNotShowExtrasResults bool `json:"show_extras_results,omitempty"` 19 | 20 | // GFPGAN Face restoration. Value must be between 0.0 - 1.0 21 | // 22 | // Default: 0.0 23 | GfpganVisibility float64 `json:"gfpgan_visibility,omitempty"` 24 | 25 | // CodeFormer Face restoration. Value must be between 0.0 - 1.0 26 | // 27 | // Default: 0.0 28 | CodeformerVisibility float64 `json:"codeformer_visibility,omitempty"` 29 | 30 | // CodeFormer Face restoration weight. 0 = Maximum Effect, 1 = Minimum Effect. 31 | // 32 | // Default: 0.0 33 | CodeformerWeight float64 `json:"codeformer_weight,omitempty"` 34 | 35 | // Multiplier to width and height of the original image. 36 | // 37 | // NOTE: Will only work if ResizeMode is 0 38 | // Default: 2 39 | UpscalingResize int `json:"upscaling_resize,omitempty"` 40 | 41 | // Width of the result image. 42 | // 43 | // NOTE: Will only work if ResizeMode is 1 44 | // Default: 512 45 | UpscalingResizeW int `json:"upscaling_resize_w,omitempty"` 46 | 47 | // Height of the result image. 48 | // 49 | // NOTE: Will only work if ResizeMode is 1 50 | // Default: 512 51 | UpscalingResizeH int `json:"upscaling_resize_h,omitempty"` 52 | 53 | // Crop Image if the aspect ratio of original image and result image doesn't match. 54 | // 55 | // Original field was `UpscalingCrop` but since the default value is `true`. it's quite tricky to do this in GO 56 | // 57 | // So I decided to reverse it. This set to true and "upscaling_crop": false and vice versa 58 | // 59 | // NOTE: Will only work if ResizeMode is 1 60 | // Default: true 61 | DoNotUpscalingCrop bool `json:"upscaling_crop,omitempty"` 62 | 63 | // First Upscaler Model. See: webui-api/extra package for helper. 64 | // 65 | // Default: "None" 66 | Upscaler1 string `json:"upscaler_1,omitempty"` 67 | 68 | // Second Upscaler Model. See: webui-api/extra package for helper. 69 | // 70 | // Default: "None" 71 | Upscaler2 string `json:"upscaler_2,omitempty"` 72 | 73 | // Second Upscaler Model Visibility. See: webui-api/extra package for helper. 74 | // 75 | // Default: 0.0 76 | ExtrasUpscaler2Visibility float64 `json:"extras_upscaler_2_visibility,omitempty"` 77 | 78 | // Upscale first then do face restoration. 79 | // 80 | // Default: false 81 | UpscaleFirst bool `json:"upscale_first,omitempty"` 82 | 83 | // Base64-encoded image to be upscale. 84 | // 85 | // Default: "" 86 | Image string `json:"image,omitempty"` 87 | 88 | // If true, Will Decode Images after received response from API 89 | DecodeAfterResult bool `json:"-"` 90 | } 91 | 92 | func (p *ExtraSingleImage) correctParams() { 93 | p.DoNotShowExtrasResults = !p.DoNotShowExtrasResults 94 | p.DoNotUpscalingCrop = !p.DoNotUpscalingCrop 95 | } 96 | 97 | func (a *api) ExtraSingleImage(params ExtraSingleImage) (*extraSingleImageRespond, error) { 98 | params.correctParams() 99 | 100 | payload, err := json.Marshal(params) 101 | if err != nil { 102 | return &extraSingleImageRespond{}, err 103 | } 104 | 105 | data, err := a.post(a.Config.Path.ExtraSingle, payload) 106 | if err != nil { 107 | return &extraSingleImageRespond{}, err 108 | } 109 | 110 | apiResp := extraSingleImageRespond{} 111 | err = json.Unmarshal(data, &apiResp) 112 | if err != nil { 113 | return &extraSingleImageRespond{}, err 114 | } 115 | 116 | if params.DecodeAfterResult { 117 | _, err := apiResp.DecodeImage() 118 | if err != nil { 119 | return &extraSingleImageRespond{}, err 120 | } 121 | } 122 | 123 | return &apiResp, nil 124 | } 125 | -------------------------------------------------------------------------------- /extra/batch.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | 8 | "github.com/Meonako/webui-api" 9 | "github.com/Meonako/webui-api/utils" 10 | ) 11 | 12 | // Convenience function to build []ImageData from base64-encoded image 13 | func BuildBatch(imageList ...string) (res []api.ImageData) { 14 | for _, image := range imageList { 15 | res = append(res, api.ImageData{Data: "data:image/png;base64," + image}) 16 | } 17 | 18 | return 19 | } 20 | 21 | // Convenience function to build []ImageData from files 22 | func BuildBatchFromFiles(files ...string) (res []api.ImageData, err error) { 23 | for _, path := range files { 24 | encoded, err := utils.Base64FromFile(path) 25 | if err != nil { 26 | return []api.ImageData{}, err 27 | } 28 | res = append(res, api.ImageData{Data: "data:image/png;base64," + encoded}) 29 | } 30 | 31 | return 32 | } 33 | 34 | // Convenience function to build []ImageData from files and IGNORE ANY ERRORS that might occur. 35 | // 36 | // This may be helpful when don't want your app to crash when file doesn't exists. 37 | func BuildBatchFromFilesIgnore(files ...string) (res []api.ImageData) { 38 | for _, path := range files { 39 | encoded, _ := utils.Base64FromFile(path) 40 | res = append(res, api.ImageData{Data: "data:image/png;base64," + encoded}) 41 | } 42 | 43 | return 44 | } 45 | 46 | // EXP: Convenience function to build []ImageData from directory 47 | func BuildBatchFromDir(pathToDir string) (res []api.ImageData, err error) { 48 | files, readErr := os.ReadDir(pathToDir) 49 | if readErr != nil { 50 | return []api.ImageData{}, readErr 51 | } 52 | 53 | for _, file := range files { 54 | if file.IsDir() { 55 | continue 56 | } 57 | 58 | fileName := file.Name() 59 | ext := filepath.Ext(fileName) 60 | if ext == "" && strings.ToLower(ext) != "png" && strings.ToLower(ext) != "jpg" && strings.ToLower(ext) != "jpeg" { 61 | continue 62 | } 63 | 64 | encoded, encErr := utils.Base64FromFile(filepath.Join(pathToDir, fileName)) 65 | if encErr != nil { 66 | return []api.ImageData{}, encErr 67 | } 68 | res = append(res, api.ImageData{Data: "data:image/png;base64," + encoded}) 69 | } 70 | 71 | return 72 | } 73 | -------------------------------------------------------------------------------- /extra/const.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | const ( 4 | SCALE_BY = iota // Multiplier to width and height of the original image 5 | SCALE_TO // Scale to specify width and height 6 | ) 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Meonako/webui-api 2 | 3 | go 1.19 4 | 5 | require github.com/goccy/go-json v0.10.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= 2 | github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 3 | -------------------------------------------------------------------------------- /img2img-resp.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/Meonako/webui-api/utils" 8 | 9 | "github.com/goccy/go-json" 10 | ) 11 | 12 | type img2imgRespond struct { 13 | Images []string `json:"images"` 14 | DecodedImages [][]byte 15 | Parameters Img2Img `json:"parameters"` 16 | Info string `json:"info"` 17 | } 18 | 19 | func newImg2ImgResp() *img2imgRespond { 20 | return &img2imgRespond{ 21 | Images: []string{}, 22 | DecodedImages: [][]byte{}, 23 | } 24 | } 25 | 26 | // Decode data at index store in "Images" field and return it. 27 | func (tr *img2imgRespond) DecodeImage(index int) ([]byte, error) { 28 | if len(tr.Images) <= index { 29 | return []byte{}, fmt.Errorf("%v", "Out of bound. Provided Index > len(Images) of struct.") 30 | } 31 | 32 | return utils.DecodeBase64(tr.Images[index]) 33 | } 34 | 35 | // Decode all data store in "Images" field and return it. You can access this later in "DecodedImages" Field. 36 | func (tr *img2imgRespond) DecodeAllImages() ([][]byte, error) { 37 | if tr.DecodedImages == nil || len(tr.DecodedImages) > 0 { 38 | tr.DecodedImages = [][]byte{} 39 | } 40 | 41 | for index := range tr.Images { 42 | imageData, err := tr.DecodeImage(index) 43 | if err != nil { 44 | return [][]byte{}, err 45 | } 46 | 47 | tr.DecodedImages = append(tr.DecodedImages, imageData) 48 | } 49 | return tr.DecodedImages, nil 50 | } 51 | 52 | // Make bytes.Reader from "DecodedImages" field. 53 | // 54 | // - It'll call "DecodeAllImages()" if "len(DecodedImages) <= 0" then continue to proceed. 55 | // 56 | // This is ready to send to discord. Or ready to png.Decode and save. 57 | func (tr *img2imgRespond) MakeBytesReader() (reader []*bytes.Reader, err error) { 58 | if tr.DecodedImages == nil || len(tr.DecodedImages) > 0 { 59 | tr.DecodedImages = [][]byte{} 60 | } 61 | 62 | for index := range tr.Images { 63 | imageData, err := tr.DecodeImage(index) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | reader = append(reader, bytes.NewReader(imageData)) 69 | tr.DecodedImages = append(tr.DecodedImages, imageData) 70 | } 71 | 72 | return reader, nil 73 | } 74 | 75 | // Info field contains generation parameters like "Parameters" field but in long string instead. 76 | // - So I wouldn't recommend doing this as it intend to be use as long string *UNLESS* you know what you're doing. 77 | func (tr *img2imgRespond) DecodeInfo() (res map[string]any, err error) { 78 | err = json.Unmarshal([]byte(tr.Info), &res) 79 | return 80 | } 81 | 82 | // Upscale any images in "Images" field. 83 | // 84 | // Leave `params.ImagesList` field empty to upscale all images. 85 | func (tr *img2imgRespond) Upscale(params *ExtraBatchImages) (*extraBatchImagesRespond, error) { 86 | if params.ImagesList == nil || len(params.ImagesList) <= 0 { 87 | params.ImagesList = tr.buildBatch() 88 | } 89 | 90 | return API.ExtraBatchImages(params) 91 | } 92 | 93 | func (tr *img2imgRespond) buildBatch() (res []ImageData) { 94 | for _, image := range tr.Images { 95 | res = append(res, ImageData{Data: "data:image/png;base64," + image}) 96 | } 97 | return 98 | } 99 | -------------------------------------------------------------------------------- /img2img.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/goccy/go-json" 5 | ) 6 | 7 | type Img2Img struct { 8 | InitImages []string `json:"init_images,omitempty"` // List of base64-encoded images to send as base/original 9 | ResizeMode int `json:"resize_mode,omitempty"` // I don't know If I got it right or not. See: webui-api/img2img RESIZE_MODE helper package 10 | DenoisingStrength float64 `json:"denoising_strength,omitempty"` // Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. 11 | ImageCFGScale float64 `json:"image_cfg_scale"` 12 | Mask string `json:"mask,omitempty"` // base64-encoded mask image. What to put inside the masked area before processing it with Stable Diffusion. 13 | MaskBlur int `json:"mask_blur,omitempty"` // How much to blur the mask before processing, in pixels. 14 | InpaintingFill int `json:"inpainting_fill,omitempty"` // I don't know If I got it right or not. See: webui-api/img2img INPAINT_MASK_CONENT helper package 15 | 16 | // Upscale masked region to target resolution, do inpainting, downscale back and paste into original image 17 | // 18 | // Original field was `InpaintFullRes` but since the default value is `true`. it's quite tricky to do this in GO 19 | // 20 | // So I decided to reverse it. This set to true and "inpaint_full_res": false and vice versa 21 | DoNotInpaintFullRes bool `json:"inpaint_full_res,omitempty"` 22 | 23 | InpaintFullResPadding int `json:"inpaint_full_res_padding,omitempty"` // Amount of pixels to take as sample around the inpaint areas 24 | InpaintingMaskInvert int `json:"inpainting_mask_invert,omitempty"` // I don't know If I got it right or not. See: webui-api/img2img INPAINT_MODE helper package 25 | InitialNoiseMultiplier int `json:"initial_noise_multiplier,omitempty"` // I don't even see this on the UI itself. Please, if you know, tell me about it. 26 | Prompt string `json:"prompt,omitempty"` 27 | NegativePrompt string `json:"negative_prompt,omitempty"` 28 | Styles []string `json:"styles,omitempty"` 29 | Seed int `json:"seed,omitempty"` // A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result 30 | Subseed int `json:"subseed,omitempty"` 31 | SubseedStrength int `json:"subseed_strength,omitempty"` 32 | SeedResizeFromH int `json:"seed_resize_from_h,omitempty"` 33 | SeedResizeFromW int `json:"seed_resize_from_w,omitempty"` 34 | SamplerName string `json:"sampler_name,omitempty"` // Either SamplerName or SamplerIndex will be used. 35 | SamplerIndex string `json:"sampler_index,omitempty"` // Either SamplerName or SamplerIndex will be used. 36 | BatchSize int `json:"batch_size,omitempty"` // How many do you want to simultaneously generate. 37 | BatchCount int `json:"n_iter,omitempty"` // How many times do you want to generate. 38 | Steps int `json:"steps,omitempty"` // How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results 39 | CFGScale float64 `json:"cfg_scale,omitempty"` // Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results 40 | Width int `json:"width,omitempty"` 41 | Height int `json:"height,omitempty"` 42 | RestoreFaces bool `json:"restore_faces,omitempty"` 43 | Tiling bool `json:"tiling,omitempty"` 44 | DoNotSaveSamples bool `json:"do_not_save_samples,omitempty"` 45 | DoNotSaveGrid bool `json:"do_not_save_grid,omitempty"` 46 | Eta float64 `json:"eta,omitempty"` 47 | SChurn float64 `json:"s_churn,omitempty"` 48 | STmax float64 `json:"s_tmax,omitempty"` 49 | STmin float64 `json:"s_tmin,omitempty"` 50 | SNoise float64 `json:"s_noise,omitempty"` 51 | OverrideSettings map[string]any `json:"override_settings,omitempty"` 52 | 53 | // Original field was `OverrideSettingsRestoreAfterwards` but since the default value is `true`. it's quite tricky to do this in GO 54 | // 55 | // So I decided to reverse it. This set to true and "override_settings_restore_afterwards": false and vice versa 56 | DoNotOverrideSettingsRestoreAfterwards bool `json:"override_settings_restore_afterwards"` 57 | 58 | ScriptName string `json:"script_name,omitempty"` 59 | ScriptArgs []string `json:"script_args,omitempty"` 60 | 61 | IncludeInitImages bool `json:"include_init_images,omitempty"` // I don't even know what this is. But it has just a little impact on the result images 62 | 63 | // Original field was `SendImages` but since the default value is `true`. it's quite tricky to do this in GO 64 | // 65 | // So I decided to reverse it. This set to true and "send_images": false and vice versa 66 | DoNotSendImages bool `json:"send_images"` 67 | 68 | SaveImages bool `json:"save_iamges,omitempty"` 69 | 70 | AlwaysOnScripts map[string]any `json:"alwayson_scripts,omitempty"` 71 | 72 | // If true, Will Decode Images after received response from API 73 | DecodeAfterResult bool `json:"-"` 74 | } 75 | 76 | func (i *Img2Img) processDefault(a *api) { 77 | if a.Config.Default == nil { 78 | return 79 | } 80 | 81 | if i.Width == 0 { 82 | i.Width = a.Config.Default.Width 83 | } 84 | 85 | if i.Height == 0 { 86 | i.Height = a.Config.Default.Height 87 | } 88 | 89 | if i.CFGScale == 0 { 90 | i.CFGScale = a.Config.Default.CFGScale 91 | } 92 | 93 | if i.Steps == 0 { 94 | i.Steps = a.Config.Default.Steps 95 | } 96 | 97 | if i.SamplerName == "" { 98 | i.SamplerName = a.Config.Default.Sampler 99 | } 100 | 101 | if i.SamplerIndex == "" { 102 | i.SamplerIndex = a.Config.Default.Sampler 103 | } 104 | 105 | i.DoNotInpaintFullRes = !i.DoNotInpaintFullRes 106 | i.DoNotOverrideSettingsRestoreAfterwards = !i.DoNotOverrideSettingsRestoreAfterwards 107 | i.DoNotSendImages = !i.DoNotSendImages 108 | } 109 | 110 | func (a *api) Image2Image(i *Img2Img) (*img2imgRespond, error) { 111 | i.processDefault(a) 112 | 113 | payload, err := json.Marshal(i) 114 | if err != nil { 115 | return &img2imgRespond{}, err 116 | } 117 | 118 | data, err := a.post(a.Config.Path.Img2Img, payload) 119 | if err != nil { 120 | return &img2imgRespond{}, err 121 | } 122 | 123 | apiResp := newImg2ImgResp() 124 | err = json.Unmarshal(data, &apiResp) 125 | 126 | if i.DecodeAfterResult { 127 | _, err := apiResp.DecodeAllImages() 128 | if err != nil { 129 | return &img2imgRespond{}, err 130 | } 131 | } 132 | 133 | return apiResp, err 134 | } 135 | -------------------------------------------------------------------------------- /img2img/inpaint-masked-content.go: -------------------------------------------------------------------------------- 1 | package img2img 2 | 3 | const ( 4 | FILL = iota // fill it with colors of the image. 5 | ORIGINAL // keep whatever was there originally. 6 | LATENT_NOISE // fill it with latent space noise. 7 | LATNET_NOTHING // fill it with latent space zeroes. 8 | ) 9 | -------------------------------------------------------------------------------- /img2img/inpaint-mode.go: -------------------------------------------------------------------------------- 1 | package img2img 2 | 3 | const ( 4 | INPAINT_MASKED_AREA = iota // Inpaint the masked area. 5 | INPAINT_OUTSIDE_MASKED_AREA // Inpaint outside the masked area/everything except for masked area. 6 | ) 7 | -------------------------------------------------------------------------------- /img2img/resize-mode.go: -------------------------------------------------------------------------------- 1 | package img2img 2 | 3 | const ( 4 | JUST_RESIZE = iota // Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio. 5 | CROP_AND_RESIZE // Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out. 6 | RESIZE_AND_FILL // Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors. 7 | ) 8 | -------------------------------------------------------------------------------- /interrogate.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/goccy/go-json" 4 | 5 | type Interrogate struct { 6 | Image string `json:"image"` 7 | Model string `json:"model"` 8 | } 9 | 10 | // Get captions from an image. 11 | func (a *api) Interrogate(params *Interrogate) (string, error) { 12 | payload, err := json.Marshal(params) 13 | if err != nil { 14 | return "", err 15 | } 16 | 17 | resp, err := a.post(a.Config.Path.Interrogate, payload) 18 | if err != nil { 19 | return "", err 20 | } 21 | 22 | result := map[string]string{} 23 | json.Unmarshal(resp, &result) 24 | 25 | return result["caption"], nil 26 | } 27 | -------------------------------------------------------------------------------- /interrogate/model.go: -------------------------------------------------------------------------------- 1 | package interrogate 2 | 3 | const ( 4 | CLIP = "clip" // Human style 5 | DEEPBOORU = "deepdanbooru" // Booru tags style 6 | ) 7 | -------------------------------------------------------------------------------- /interrupt.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // Stop processing images and return any results accumulated so far. 4 | func (a *api) Interrupt() error { 5 | _, err := a.post(a.Config.Path.Interrupt, nil) 6 | return err 7 | } 8 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json" 7 | ) 8 | 9 | // Only contains original options (without extension options. Can't posssibly do that.) 10 | type Options struct { 11 | SamplesSave bool `json:"samples_save,omitempty"` 12 | SamplesFormat string `json:"samples_format,omitempty"` 13 | SamplesFilenamePattern string `json:"samples_filename_pattern,omitempty"` 14 | SaveImagesAddNumber bool `json:"save_images_add_number,omitempty"` 15 | GridSave bool `json:"grid_save,omitempty"` 16 | GridFormat string `json:"grid_format,omitempty"` 17 | GridExtendedFilename bool `json:"grid_extended_filename,omitempty"` 18 | GridOnlyIfMultiple bool `json:"grid_only_if_multiple,omitempty"` 19 | GridPreventEmptySpots bool `json:"grid_prevent_empty_spots,omitempty"` 20 | NRows float64 `json:"n_rows,omitempty"` 21 | EnablePnginfo bool `json:"enable_pnginfo,omitempty"` 22 | SaveTxt bool `json:"save_txt,omitempty"` 23 | SaveImagesBeforeFaceRestoration bool `json:"save_images_before_face_restoration,omitempty"` 24 | SaveImagesBeforeHighresFix bool `json:"save_images_before_highres_fix,omitempty"` 25 | SaveImagesBeforeColorCorrection bool `json:"save_images_before_color_correction,omitempty"` 26 | JpegQuality float64 `json:"jpeg_quality,omitempty"` 27 | WebpLossless bool `json:"webp_lossless,omitempty"` 28 | ExportFor4Chan bool `json:"export_for_4chan,omitempty"` 29 | ImgDownscaleThreshold float64 `json:"img_downscale_threshold,omitempty"` 30 | TargetSideLength float64 `json:"target_side_length,omitempty"` 31 | ImgMaxSizeMp float64 `json:"img_max_size_mp,omitempty"` 32 | UseOriginalNameBatch bool `json:"use_original_name_batch,omitempty"` 33 | UseUpscalerNameAsSuffix bool `json:"use_upscaler_name_as_suffix,omitempty"` 34 | SaveSelectedOnly bool `json:"save_selected_only,omitempty"` 35 | DoNotAddWatermark bool `json:"do_not_add_watermark,omitempty"` 36 | TempDir string `json:"temp_dir,omitempty"` 37 | CleanTempDirAtStart bool `json:"clean_temp_dir_at_start,omitempty"` 38 | OutdirSamples string `json:"outdir_samples,omitempty"` 39 | OutdirTxt2ImgSamples string `json:"outdir_txt2img_samples,omitempty"` 40 | OutdirImg2ImgSamples string `json:"outdir_img2img_samples,omitempty"` 41 | OutdirExtrasSamples string `json:"outdir_extras_samples,omitempty"` 42 | OutdirGrids string `json:"outdir_grids,omitempty"` 43 | OutdirTxt2ImgGrids string `json:"outdir_txt2img_grids,omitempty"` 44 | OutdirImg2ImgGrids string `json:"outdir_img2img_grids,omitempty"` 45 | OutdirSave string `json:"outdir_save,omitempty"` 46 | SaveToDirs bool `json:"save_to_dirs,omitempty"` 47 | GridSaveToDirs bool `json:"grid_save_to_dirs,omitempty"` 48 | UseSaveToDirsForUI bool `json:"use_save_to_dirs_for_ui,omitempty"` 49 | DirectoriesFilenamePattern string `json:"directories_filename_pattern,omitempty"` 50 | DirectoriesMaxPromptWords float64 `json:"directories_max_prompt_words,omitempty"` 51 | ESRGANTile float64 `json:"ESRGAN_tile,omitempty"` 52 | ESRGANTileOverlap float64 `json:"ESRGAN_tile_overlap,omitempty"` 53 | RealESRGanEnabledModels []string `json:"realesrgan_enabled_models,omitempty"` 54 | UpscalerForImg2Img string `json:"upscaler_for_img2img,omitempty"` 55 | LDSRSteps float64 `json:"ldsr_steps,omitempty"` 56 | LDSRCache bool `json:"ldsr_cached,omitempty"` 57 | SWINTile float64 `json:"SWIN_tile"` 58 | SWINTileOverlap float64 `json:"SWIN_tile_overlap"` 59 | FaceRestorationModel string `json:"face_restoration_model,omitempty"` 60 | CodeFormerWeight float64 `json:"code_former_weight,omitempty"` 61 | FaceRestorationUnload bool `json:"face_restoration_unload,omitempty"` 62 | ShowWarnings bool `json:"show_warnings,omitempty"` 63 | MemmonPollRate float64 `json:"memmon_poll_rate,omitempty"` 64 | SamplesLogStdout bool `json:"samples_log_stdout,omitempty"` 65 | MultipleTqdm bool `json:"multiple_tqdm,omitempty"` 66 | PrintHypernetExtra bool `json:"print_hypernet_extra,omitempty"` 67 | UnloadModelsWhenTraining bool `json:"unload_models_when_training,omitempty"` 68 | PinMemory bool `json:"pin_memory,omitempty"` 69 | SaveOptimizerState bool `json:"save_optimizer_state,omitempty"` 70 | SaveTrainingSettingsToTxt bool `json:"save_training_settings_to_txt,omitempty"` 71 | DatasetFilenameWordRegex string `json:"dataset_filename_word_regex,omitempty"` 72 | DatasetFilenameJoinString string `json:"dataset_filename_join_string,omitempty"` 73 | TrainingImageRepeatsPerEpoch float64 `json:"training_image_repeats_per_epoch,omitempty"` 74 | TrainingWriteCsvEvery float64 `json:"training_write_csv_every,omitempty"` 75 | TrainingXattentionOptimizations bool `json:"training_xattention_optimizations,omitempty"` 76 | TrainingEnableTensorboard bool `json:"training_enable_tensorboard,omitempty"` 77 | TrainingTensorboardSaveImages bool `json:"training_tensorboard_save_images,omitempty"` 78 | TrainingTensorboardFlushEvery float64 `json:"training_tensorboard_flush_every,omitempty"` 79 | SdModelCheckpoint string `json:"sd_model_checkpoint,omitempty"` 80 | SdCheckpointCache float64 `json:"sd_checkpoint_cache,omitempty"` 81 | SdVaeCheckpointCache float64 `json:"sd_vae_checkpoint_cache,omitempty"` 82 | SdVae string `json:"sd_vae,omitempty"` 83 | SdVaeAsDefault bool `json:"sd_vae_as_default,omitempty"` 84 | InpaintingMaskWeight float64 `json:"inpainting_mask_weight,omitempty"` 85 | InitialNoiseMultiplier float64 `json:"initial_noise_multiplier,omitempty"` 86 | Img2ImgColorCorrection bool `json:"img2img_color_correction,omitempty"` 87 | Img2ImgFixSteps bool `json:"img2img_fix_steps,omitempty"` 88 | Img2ImgBackgroundColor string `json:"img2img_background_color,omitempty"` 89 | EnableQuantization bool `json:"enable_quantization,omitempty"` 90 | EnableEmphasis bool `json:"enable_emphasis,omitempty"` 91 | EnableBatchSeeds bool `json:"enable_batch_seeds,omitempty"` 92 | CommaPaddingBacktrack float64 `json:"comma_padding_backtrack,omitempty"` 93 | CLIPStopAtLastLayers float64 `json:"CLIP_stop_at_last_layers,omitempty"` 94 | UpcastAttn bool `json:"upcast_attn,omitempty"` 95 | UseOldEmphasisImplementation bool `json:"use_old_emphasis_implementation,omitempty"` 96 | UseOldKarrasSchedulerSigmas bool `json:"use_old_karras_scheduler_sigmas,omitempty"` 97 | NoDpmppSdeBatchDeterminism bool `json:"no_dpmpp_sde_batch_determinism,omitempty"` 98 | UseOldHiresFixWidthHeight bool `json:"use_old_hires_fix_width_height,omitempty"` 99 | InterrogateKeepModelsInMemory bool `json:"interrogate_keep_models_in_memory,omitempty"` 100 | InterrogateReturnRanks bool `json:"interrogate_return_ranks,omitempty"` 101 | InterrogateClipNumBeams float64 `json:"interrogate_clip_num_beams,omitempty"` 102 | InterrogateClipMinLength float64 `json:"interrogate_clip_min_length,omitempty"` 103 | InterrogateClipMaxLength float64 `json:"interrogate_clip_max_length,omitempty"` 104 | InterrogateClipDictLimit float64 `json:"interrogate_clip_dict_limit,omitempty"` 105 | InterrogateClipSkipCategories []any `json:"interrogate_clip_skip_categories,omitempty"` 106 | InterrogateDeepbooruScoreThreshold float64 `json:"interrogate_deepbooru_score_threshold,omitempty"` 107 | DeepbooruSortAlpha bool `json:"deepbooru_sort_alpha,omitempty"` 108 | DeepbooruUseSpaces bool `json:"deepbooru_use_spaces,omitempty"` 109 | DeepbooruEscape bool `json:"deepbooru_escape,omitempty"` 110 | DeepbooruFilterTags string `json:"deepbooru_filter_tags,omitempty"` 111 | ExtraNetworksDefaultView string `json:"extra_networks_default_view,omitempty"` 112 | ExtraNetworksDefaultMultiplier float64 `json:"extra_networks_default_multiplier,omitempty"` 113 | ExtraNetworksAddTextSeparator string `json:"extra_networks_add_text_separator,omitempty"` 114 | SdHypernetwork string `json:"sd_hypernetwork,omitempty"` 115 | SdLora string `json:"sd_lora,omitempty"` 116 | LoraApplyToOutputs bool `json:"lora_apply_to_outputs,omitempty"` 117 | ReturnGrid bool `json:"return_grid,omitempty"` 118 | DoNotShowImages bool `json:"do_not_show_images,omitempty"` 119 | AddModelHashToInfo bool `json:"add_model_hash_to_info,omitempty"` 120 | AddModelNameToInfo bool `json:"add_model_name_to_info,omitempty"` 121 | DisableWeightsAutoSwap bool `json:"disable_weights_auto_swap,omitempty"` 122 | SendSeed bool `json:"send_seed,omitempty"` 123 | SendSize bool `json:"send_size,omitempty"` 124 | Font string `json:"font,omitempty"` 125 | JsModalLightbox bool `json:"js_modal_lightbox,omitempty"` 126 | JsModalLightboxInitiallyZoomed bool `json:"js_modal_lightbox_initially_zoomed,omitempty"` 127 | ShowProgressInTitle bool `json:"show_progress_in_title,omitempty"` 128 | SamplersInDropdown bool `json:"samplers_in_dropdown,omitempty"` 129 | DimensionsAndBatchTogether bool `json:"dimensions_and_batch_together,omitempty"` 130 | KeyeditPrecisionAttention float64 `json:"keyedit_precision_attention,omitempty"` 131 | KeyeditPrecisionExtra float64 `json:"keyedit_precision_extra,omitempty"` 132 | Quicksettings string `json:"quicksettings,omitempty"` 133 | HiddenTabs []any `json:"hidden_tabs,omitempty"` 134 | UIReorder string `json:"ui_reorder,omitempty"` 135 | UIExtraNetworksTabReorder string `json:"ui_extra_networks_tab_reorder,omitempty"` 136 | Localization string `json:"localization,omitempty"` 137 | ShowProgressbar bool `json:"show_progressbar,omitempty"` 138 | LivePreviewsEnable bool `json:"live_previews_enable,omitempty"` 139 | ShowProgressGrid bool `json:"show_progress_grid,omitempty"` 140 | ShowProgressEveryNSteps float64 `json:"show_progress_every_n_steps,omitempty"` 141 | ShowProgressType string `json:"show_progress_type,omitempty"` 142 | LivePreviewContent string `json:"live_preview_content,omitempty"` 143 | LivePreviewRefreshPeriod float64 `json:"live_preview_refresh_period,omitempty"` 144 | HideSamplers []any `json:"hide_samplers,omitempty"` 145 | EtaDdim float64 `json:"eta_ddim,omitempty"` 146 | EtaAncestral float64 `json:"eta_ancestral,omitempty"` 147 | DdimDiscretize string `json:"ddim_discretize,omitempty"` 148 | SChurn float64 `json:"s_churn,omitempty"` 149 | STmin float64 `json:"s_tmin,omitempty"` 150 | SNoise float64 `json:"s_noise,omitempty"` 151 | EtaNoiseSeedDelta float64 `json:"eta_noise_seed_delta,omitempty"` 152 | AlwaysDiscardNextToLastSigma bool `json:"always_discard_next_to_last_sigma,omitempty"` 153 | UniPcVariant string `json:"uni_pc_variant,omitempty"` 154 | UniPcSkipType string `json:"uni_pc_skip_type,omitempty"` 155 | UniPcOrder float64 `json:"uni_pc_order,omitempty"` 156 | UniPcLowerOrderFinal bool `json:"uni_pc_lower_order_final,omitempty"` 157 | PostprocessingEnableInMainUI []any `json:"postprocessing_enable_in_main_ui,omitempty"` 158 | PostprocessingOperationOrder []any `json:"postprocessing_operation_order,omitempty"` 159 | UpscalingMaxImagesInCache float64 `json:"upscaling_max_images_in_cache,omitempty"` 160 | DisabledExtensions []string `json:"disabled_extensions,omitempty"` 161 | SdCheckpointHash string `json:"sd_checkpoint_hash,omitempty"` 162 | } 163 | 164 | // Get Options. 165 | // 166 | // NOTE: "NOT" included "extension options". 167 | // SEE: OptionsWithExtensionOptions() for extension options 168 | func (a *api) Options() (result *Options, err error) { 169 | resp, erro := a.get(a.Config.Path.Options) 170 | if erro != nil { 171 | err = erro 172 | return 173 | } 174 | 175 | err = json.Unmarshal(resp, &result) 176 | if err != nil { 177 | err = fmt.Errorf("err: %v\nValue: %v", err, string(resp)) 178 | } 179 | return 180 | } 181 | 182 | // Get Options. 183 | // 184 | // NOTE: Return as a map. SO you need to know the key(s) and value(s) type. 185 | func (a *api) OptionsWithExtensionOptions() (result map[string]any, err error) { 186 | resp, erro := a.get(a.Config.Path.Options) 187 | if erro != nil { 188 | err = erro 189 | return 190 | } 191 | 192 | err = json.Unmarshal(resp, &result) 193 | return 194 | } 195 | 196 | // Set original options. 197 | // 198 | // SEE: SetOptionsWithExtensionOptions() for extension options settings. 199 | func (a *api) SetOptions(params *Options) error { 200 | payload, err := json.Marshal(params) 201 | if err != nil { 202 | return err 203 | } 204 | 205 | _, err = a.post(a.Config.Path.Options, payload) 206 | return err 207 | } 208 | 209 | // Shorthand for map[string]any 210 | type Opt map[string]any 211 | 212 | // Receive api.Opt (map[string]any) as argument. 213 | func (a *api) SetOptionsWithExtensionOptions(params Opt) error { 214 | payload, err := json.Marshal(params) 215 | if err != nil { 216 | return err 217 | } 218 | 219 | _, err = a.post(a.Config.Path.Options, payload) 220 | return err 221 | } 222 | -------------------------------------------------------------------------------- /png-info.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json" 7 | ) 8 | 9 | // Retrieve Generation Parameters from an image if any. 10 | // 11 | // - NOTE: the server will crash if the image has no generation parameters 12 | // 13 | // - SEE: https://github.com/Meonako/webui-api/wiki#png-info-api-fix for how to fix 14 | func (a *api) PNGInfo(image string) (string, error) { 15 | payload, err := json.Marshal(map[string]string{"image": image}) 16 | if err != nil { 17 | return "", err 18 | } 19 | 20 | data, err := a.post(a.Config.Path.PNGInfo, payload) 21 | if err != nil { 22 | return "", err 23 | } 24 | 25 | var result map[string]interface{} 26 | err = json.Unmarshal(data, &result) 27 | if err != nil { 28 | return "", fmt.Errorf("unmarshaling json: %v\nValue: %v", err, string(data)) 29 | } 30 | 31 | info, ok := result["info"].(string) 32 | if !ok { 33 | return "", fmt.Errorf("%v", string(data)) 34 | } 35 | 36 | return info, err 37 | } 38 | -------------------------------------------------------------------------------- /progress.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "math" 8 | 9 | "github.com/Meonako/webui-api/utils" 10 | ) 11 | 12 | type progressRespond struct { 13 | Progress float64 `json:"progress"` 14 | ETA float64 `json:"eta_relative"` 15 | State state `json:"state"` 16 | CurrentImage string `json:"current_image"` 17 | } 18 | 19 | type state struct { 20 | Skipped bool `json:"skipped"` 21 | Interrupted bool `json:"interrupted"` 22 | Job string `json:"job"` 23 | JobCount int `json:"job_count"` 24 | JobNo int `json:"job_no"` 25 | CurrentStep int `json:"sampling_step"` 26 | TargetSamplingSteps int `json:"sampling_steps"` 27 | } 28 | 29 | // Get Generation Progress Info. Return Respond struct and Error object. 30 | func (a *api) Progress() (*progressRespond, error) { 31 | resp, err := a.get(a.Config.Path.Progress) 32 | if err != nil { 33 | return &progressRespond{}, err 34 | } 35 | 36 | var result progressRespond 37 | err = json.Unmarshal(resp, &result) 38 | return &result, err 39 | } 40 | 41 | // Convert Progress field to percentage value (e.g. 83.57%). 42 | // 43 | // format argument is optional. pass emtpy string will round to interger in string format. 44 | // 45 | // Default: 2 (e.g. 83.57%) 46 | func (p *progressRespond) GetProgress(format ...string) string { 47 | deci := ".2" 48 | if len(format) > 0 { 49 | if format[0] == "" { 50 | deci = ".0" 51 | } else { 52 | deci = "." + format[0] 53 | } 54 | } 55 | 56 | return fmt.Sprintf("%"+deci+"f", p.Progress*100) + "%" 57 | } 58 | 59 | // "Estimated Time of Arrival" of images :) (e.g. 53 Minutes 10 Seconds) 60 | // 61 | // format argument is optional. pass "2" will round "Seconds" to 2 decimal places in string format. 62 | // 63 | // Default: 0 (e.g. 53 Minutes 10 Seconds) 64 | func (p *progressRespond) GetETA(format ...string) string { 65 | deci := ".0" 66 | if len(format) > 0 { 67 | if format[0] != "" { 68 | deci = "." + format[0] 69 | } 70 | } 71 | 72 | return fmt.Sprintf("%v Minutes %"+deci+"f Seconds", math.Floor(p.ETA/60), math.Mod(p.ETA, 60)) 73 | } 74 | 75 | // Get current image if not empty. Usually this is empty if you didn't change the value of the following setting. 76 | // 77 | // "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch." 78 | func (p *progressRespond) GetCurrentImage() (*bytes.Reader, error) { 79 | if p.CurrentImage == "" { 80 | return nil, nil 81 | } 82 | 83 | byte, err := utils.DecodeBase64(p.CurrentImage) 84 | 85 | return bytes.NewReader(byte), err 86 | } 87 | -------------------------------------------------------------------------------- /sampler/const.go: -------------------------------------------------------------------------------- 1 | package sampler 2 | 3 | const ( 4 | EULER = "Euler" 5 | EULER_A = "Euler a" 6 | EULER_ASCESTRAL = "Euler a" // EULER_A alias 7 | 8 | LMS = "LMS" 9 | LMS_KARRAS = "LMS Karras" 10 | 11 | HEUN = "Heun" 12 | 13 | DPM_FAST = "DPM fast" 14 | DPM_ADAPTIVE = "DPM adaptive" 15 | DPM_PLUS_PLUS = "DPM++" 16 | 17 | DPM2 = "DPM2" 18 | DPM2_KARRAS = "DPM2 Karras" 19 | DPM2_A = "DPM2 a" 20 | DPM2_A_KARRAS = "DPM2 a Karras" 21 | 22 | DPM_PLUS_PLUS_2S_A = "DPM++ 2S a" 23 | DPM_PLUS_PLUS_2S_A_KARRAS = "DPM++ 2S a Karras" 24 | 25 | DPM_PLUS_PLUS_2M = "DPM++ 2M" 26 | DPM_PLUS_PLUS_2M_KARRAS = "DPM++ 2M Karras" 27 | 28 | DPM_PLUS_PLUS_SDE = "DPM++ SDE" 29 | DPM_PLUS_PLUS_SDE_KARRAS = "DPM++ SDE Karras" 30 | 31 | DPM_PLUS_PLUS_2M_SDE_EXP = "DPM++ 2M SDE Exponential" 32 | DPM_PLUS_PLUS_2M_SDE_HEUN = "DPM++ 2M SDE Heun" 33 | DPM_PLUS_PLUS_2M_SDE_HEUN_KARRAS = "DPM++ 2M SDE Heun Karras" 34 | DPM_PLUS_PLUS_2M_SDE_HEUN_EXP = "DPM++ 2M SDE Heun Exponential" 35 | 36 | DPM_PLUS_PLUS_3M_SDE = "DPM++ 3M SDE" 37 | DPM_PLUS_PLUS_3M_SDE_KARRAS = "DPM++ 3M SDE Karras" 38 | DPM_PLUS_PLUS_3M_SDE_EXP = "DPM++ 3M SDE Exponential" 39 | 40 | RESTART = "Restart" 41 | DDIM = "DDIM" 42 | PLMS = "PLMS" 43 | UNI_PC = "UniPC" 44 | ) 45 | -------------------------------------------------------------------------------- /scripts/mod.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | const ( 4 | PROMPT_MATRIX = "prompt matrix" 5 | PROMPTS_FROM_FILE_OR_TEXTBOX = "prompts from file or textbox" 6 | XYZ_PLOT = "x/y/z plot" 7 | 8 | ADDITIONAL_NETWORKS = "additional networks for generating" 9 | ADD_NET = "additional networks for generating" // `ADDITIONAL_NETWORKS` alias 10 | ) 11 | -------------------------------------------------------------------------------- /sd-models.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/goccy/go-json" 5 | ) 6 | 7 | type stableDiffusionModels struct { 8 | Title string `json:"title"` 9 | ModelName string `json:"model_name"` 10 | Hash string `json:"hash"` 11 | Filename string `json:"filename"` 12 | Config string `json:"config"` 13 | } 14 | 15 | // Get available Stable Diffusion Models 16 | func (a *api) SDModels() (result []*stableDiffusionModels, err error) { 17 | resp, erro := a.get(a.Config.Path.SDModels) 18 | if erro != nil { 19 | err = erro 20 | return 21 | } 22 | 23 | err = json.Unmarshal(resp, &result) 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /skip.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // Stop processing current image and continue processing. 4 | func (a *api) Skip() error { 5 | _, err := a.post(a.Config.Path.Skip, nil) 6 | return err 7 | } 8 | -------------------------------------------------------------------------------- /txt2img-resp.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/Meonako/webui-api/utils" 8 | 9 | "github.com/goccy/go-json" 10 | ) 11 | 12 | type txt2ImageRespond struct { 13 | Images []string `json:"images"` // Base64-encoded Images Data 14 | DecodedImages [][]byte `json:"-"` // Base64-decoded Images Data store here after "DecodeAllImages()" called 15 | Parameters Txt2Image `json:"parameters"` // Generation Parameters. Should be the same value as the one you pass to generate. 16 | Info string `json:"info"` // Info field contains generation parameters like "parameters" field but in long string instead. 17 | } 18 | 19 | func newTxt2ImgResp() *txt2ImageRespond { 20 | return &txt2ImageRespond{ 21 | Images: []string{}, 22 | DecodedImages: [][]byte{}, 23 | } 24 | } 25 | 26 | // Decode data at index store in "Images" field and return it. 27 | func (tr *txt2ImageRespond) DecodeImage(index int) ([]byte, error) { 28 | if len(tr.Images) <= index { 29 | return []byte{}, fmt.Errorf("%v", "Out of bound. Provided Index > len(Images) of struct.") 30 | } 31 | 32 | return utils.DecodeBase64(tr.Images[index]) 33 | } 34 | 35 | // Decode all data store in "Images" field and return it. You can access this later in "DecodedImages" Field. 36 | func (tr *txt2ImageRespond) DecodeAllImages() ([][]byte, error) { 37 | if tr.DecodedImages == nil || len(tr.DecodedImages) > 0 { 38 | tr.DecodedImages = [][]byte{} 39 | } 40 | 41 | for index := range tr.Images { 42 | imageData, err := tr.DecodeImage(index) 43 | if err != nil { 44 | return [][]byte{}, err 45 | } 46 | 47 | tr.DecodedImages = append(tr.DecodedImages, imageData) 48 | } 49 | return tr.DecodedImages, nil 50 | } 51 | 52 | // Make bytes.Reader from "DecodedImages" field. 53 | // 54 | // - It'll call "DecodeAllImages()" if "len(DecodedImages) <= 0" then continue to proceed. 55 | // 56 | // This is ready to send to discord. Or ready to png.Decode and save. 57 | func (tr *txt2ImageRespond) MakeBytesReader() (reader []*bytes.Reader, err error) { 58 | if tr.DecodedImages == nil || len(tr.DecodedImages) > 0 { 59 | tr.DecodedImages = [][]byte{} 60 | } 61 | 62 | for index := range tr.Images { 63 | imageData, err := tr.DecodeImage(index) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | reader = append(reader, bytes.NewReader(imageData)) 69 | tr.DecodedImages = append(tr.DecodedImages, imageData) 70 | } 71 | 72 | return reader, nil 73 | } 74 | 75 | // Info field contains generation parameters like "Parameters" field but in long string instead. 76 | // - So I wouldn't recommend doing this as it intend to be use as long string *UNLESS* you know what you're doing. 77 | func (tr *txt2ImageRespond) DecodeInfo() (res map[string]any, err error) { 78 | err = json.Unmarshal([]byte(tr.Info), &res) 79 | return 80 | } 81 | 82 | // Upscale any images in "Images" field. 83 | // 84 | // Leave `params.ImagesList` field empty to upscale all images. 85 | func (tr *txt2ImageRespond) Upscale(params *ExtraBatchImages) (*extraBatchImagesRespond, error) { 86 | if params.ImagesList == nil || len(params.ImagesList) <= 0 { 87 | params.ImagesList = tr.buildBatch() 88 | } 89 | 90 | return API.ExtraBatchImages(params) 91 | } 92 | 93 | func (tr *txt2ImageRespond) buildBatch() (res []ImageData) { 94 | for _, image := range tr.Images { 95 | res = append(res, ImageData{Data: "data:image/png;base64," + image}) 96 | } 97 | return 98 | } 99 | -------------------------------------------------------------------------------- /txt2img.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/goccy/go-json" 5 | ) 6 | 7 | type Txt2Image struct { 8 | EnableHR bool `json:"enable_hr,omitempty"` // Hi-res fix. 9 | DenoisingStrength float64 `json:"denoising_strength,omitempty"` // Hi-res fix option. Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. 10 | FirstphaseWidth int `json:"firstphase_width,omitempty"` // Hi-res fix option. Might not work anymore 11 | FirstphaseHeight int `json:"firstphase_height,omitempty"` // Hi-res fix option. Might not work anymore 12 | 13 | // Hi-res fix option. Multiplier to original width and height. 14 | // 15 | // HRScale = 2 will work like this: 384x512 will result in 768x1024 16 | // 17 | // Only HRScale or HRResizeX / HRResizeY will be used 18 | HRScale float64 `json:"hr_scale,omitempty"` 19 | 20 | // Hi-res fix option. Which Hi-res upscale model will be used. 21 | // 22 | // See: `upscaler` helper package (github.com/Meonako/webui-api/upscaler) 23 | HRUpscaler string `json:"hr_upscaler,omitempty"` 24 | 25 | // Hi-res fix option. After denoising and upscale, use this amount of steps instead of the amount before denoise and upscale. 26 | HRSecondPassSteps int `json:"hr_second_pass_steps,omitempty"` 27 | 28 | // Hi-res fix option. The width of the result image 29 | // 30 | // Only HRScale or HRResizeX / HRResizeY will be used 31 | HRResizeX int `json:"hr_resize_x,omitempty"` 32 | 33 | // Hi-res fix option. The height of the result image 34 | // 35 | // Only HRScale or HRResizeX / HRResizeY will be used 36 | HRResizeY int `json:"hr_resize_y,omitempty"` 37 | 38 | Prompt string `json:"prompt"` 39 | NegativePrompt string `json:"negative_prompt,omitempty"` 40 | Styles []string `json:"styles,omitempty"` 41 | Seed int64 `json:"seed,omitempty"` // A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result 42 | Subseed int `json:"subseed,omitempty"` 43 | SubseedStrength int `json:"subseed_strength,omitempty"` 44 | SeedResizeFromH int `json:"seed_resize_from_h,omitempty"` 45 | SeedResizeFromW int `json:"seed_resize_from_w,omitempty"` 46 | SamplerName string `json:"sampler_name,omitempty"` // Either SamplerName or SamplerIndex will be used. 47 | SamplerIndex string `json:"sampler_index,omitempty"` // Either SamplerName or SamplerIndex will be used. 48 | BatchSize int `json:"batch_size,omitempty"` // How many do you want to simultaneously generate. 49 | BatchCount int `json:"n_iter,omitempty"` // How many times do you want to generate. 50 | Steps int `json:"steps,omitempty"` // How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results 51 | CFGScale float64 `json:"cfg_scale,omitempty"` // Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results 52 | Width int `json:"width,omitempty"` 53 | Height int `json:"height,omitempty"` 54 | RestoreFaces bool `json:"restore_faces,omitempty"` 55 | Tiling bool `json:"tiling,omitempty"` 56 | DoNotSaveSamples bool `json:"do_not_save_samples,omitempty"` 57 | DoNotSaveGrid bool `json:"do_not_save_grid,omitempty"` 58 | Eta float64 `json:"eta,omitempty"` 59 | SChurn float64 `json:"s_churn,omitempty"` 60 | STmax int `json:"s_tmax,omitempty"` 61 | STmin float64 `json:"s_tmin,omitempty"` 62 | SNoise float64 `json:"s_noise,omitempty"` 63 | OverrideSettings map[string]any `json:"override_settings,omitempty"` 64 | 65 | // Original field was `OverrideSettingsRestoreAfterwards` but since the default value is `true`. it's quite tricky to do this in GO 66 | // 67 | // So I decided to reverse it. This set to true and "override_settings_restore_afterwards": false and vice versa 68 | DoNotOverrideSettingsRestoreAfterwards bool `json:"override_settings_restore_afterwards"` 69 | 70 | ScriptName string `json:"script_name,omitempty"` 71 | ScriptArgs []string `json:"script_args,omitempty"` 72 | 73 | // Original field was `SendImages` but since the default value is `true`. it's quite tricky to do this in GO 74 | // 75 | // So I decided to reverse it. This set to true and "send_images": false and vice versa 76 | DoNotSendImages bool `json:"send_images"` 77 | 78 | // Save image(s) to `outputs` folder where Stable Diffusion Web UI is running 79 | SaveImages bool `json:"save_images,omitempty"` 80 | 81 | AlwaysOnScripts map[string]any `json:"alwayson_scripts,omitempty"` 82 | 83 | // If true, Will Decode Images after received response from API 84 | DecodeAfterResult bool `json:"-"` 85 | } 86 | 87 | func (data *Txt2Image) processDefault(a *api) { 88 | if a.Config.Default == nil { 89 | return 90 | } 91 | 92 | if data.Width == 0 { 93 | data.Width = a.Config.Default.Width 94 | } 95 | 96 | if data.Height == 0 { 97 | data.Height = a.Config.Default.Height 98 | } 99 | 100 | if data.CFGScale == 0 { 101 | data.CFGScale = a.Config.Default.CFGScale 102 | } 103 | 104 | if data.Steps == 0 { 105 | data.Steps = a.Config.Default.Steps 106 | } 107 | 108 | if data.SamplerName == "" { 109 | data.SamplerName = a.Config.Default.Sampler 110 | } 111 | 112 | if data.SamplerIndex == "" { 113 | data.SamplerIndex = a.Config.Default.Sampler 114 | } 115 | 116 | data.DoNotOverrideSettingsRestoreAfterwards = !data.DoNotOverrideSettingsRestoreAfterwards 117 | data.DoNotSendImages = !data.DoNotSendImages 118 | } 119 | 120 | // Generate Image based on Text. Return Respond struct and Error object. 121 | func (a *api) Text2Image(params *Txt2Image) (*txt2ImageRespond, error) { 122 | params.processDefault(a) 123 | 124 | payload, err := json.Marshal(params) 125 | if err != nil { 126 | return &txt2ImageRespond{}, err 127 | } 128 | 129 | data, err := a.post(a.Config.Path.Txt2Img, payload) 130 | if err != nil { 131 | return &txt2ImageRespond{}, err 132 | } 133 | 134 | apiResp := newTxt2ImgResp() 135 | err = json.Unmarshal(data, &apiResp) 136 | 137 | if params.DecodeAfterResult { 138 | _, err := apiResp.DecodeAllImages() 139 | if err != nil { 140 | return &txt2ImageRespond{}, err 141 | } 142 | } 143 | 144 | return apiResp, err 145 | } 146 | -------------------------------------------------------------------------------- /upscaler/const.go: -------------------------------------------------------------------------------- 1 | package upscaler 2 | 3 | const ( 4 | LANCZOS = "Lanczos" 5 | NEAREST = "Nearest" 6 | LDSR = "LDSR" 7 | SWIN_IR_4x = "SwinIR 4x" 8 | ESRGAN_4x = "ESRGAN_4x" 9 | R_ESRGAN_4x_PLUS = "R-ESRGAN 4x+" 10 | R_ESRGAN_4x_PLUS_ANIME_6B = "R-ESRGAN 4x+ Anime6B" 11 | R_ESRGAN_GENERAL_4x_V3 = "R-ESRGAN General 4xV3" 12 | R_ESRGAN_GENERAL_WDN_4x_V3 = "R-ESRGAN General WDN 4xV3" 13 | R_ESRGAN_ANIME_VIDEO = "R-ESRGAN AnimeVideo" 14 | R_ESRGAN_2x_PLUS = "R-ESRGAN 2x+" 15 | ) 16 | -------------------------------------------------------------------------------- /utils/base64.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/base64" 5 | "os" 6 | ) 7 | 8 | func EncodeBase64(data []byte) string { 9 | return base64.StdEncoding.EncodeToString(data) 10 | } 11 | 12 | func DecodeBase64(data string) ([]byte, error) { 13 | return base64.StdEncoding.DecodeString(data) 14 | } 15 | 16 | // Convenience function to encode image in any format to base64 17 | func Base64FromFile(path string) (string, error) { 18 | fileBytes, err := os.ReadFile(path) 19 | if err != nil { 20 | return "", err 21 | } 22 | 23 | return EncodeBase64(fileBytes), nil 24 | } 25 | 26 | // Convenience function to encode image in any format to base64 and IGNORE ANY ERRORS that might occur. 27 | // 28 | // This may be helpful when you are SURE it doesn't give you any error 29 | func Base64FromFileIgnore(path string) string { 30 | fileBytes, _ := os.ReadFile(path) 31 | return EncodeBase64(fileBytes) 32 | } 33 | 34 | // Convenience function to encode image in any format to base64 35 | func Base64FromFiles(allPath ...string) (res []string, err error) { 36 | for _, path := range allPath { 37 | encoded, err := Base64FromFile(path) 38 | if err != nil { 39 | return []string{}, err 40 | } 41 | res = append(res, encoded) 42 | } 43 | 44 | return 45 | } 46 | --------------------------------------------------------------------------------