├── .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 |
--------------------------------------------------------------------------------